Coverage Report

Created: 2026-06-13 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/WKSChart.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* libwps
3
 * Version: MPL 2.0 / LGPLv2.1+
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * Major Contributor(s):
10
 * Copyright (C) 2006, 2007 Andrew Ziem
11
 * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
12
 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13
 *
14
 * For minor contributions see the git repository.
15
 *
16
 * Alternatively, the contents of this file may be used under the terms
17
 * of the GNU Lesser General Public License Version 2.1 or later
18
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
19
 * applicable instead of those above.
20
 */
21
22
/*
23
 * Structure to store and construct a chart from an unstructured list
24
 * of cell
25
 *
26
 */
27
28
#include <algorithm>
29
#include <iomanip>
30
#include <iostream>
31
#include <map>
32
#include <sstream>
33
34
#include <librevenge/librevenge.h>
35
36
#include "WPSListener.h"
37
#include "WPSPosition.h"
38
#include "WKSContentListener.h"
39
#include "WKSSubDocument.h"
40
41
#include "WKSChart.h"
42
43
/** Internal: the structures of a WKSChart */
44
namespace WKSChartInternal
45
{
46
////////////////////////////////////////
47
//! Internal: the subdocument of a WKSChart
48
class SubDocument final : public WKSSubDocument
49
{
50
public:
51
  SubDocument(WKSChart const *chart, WKSChart::TextZone::Type textZone)
52
22.5k
    : WKSSubDocument(RVNGInputStreamPtr(), nullptr)
53
22.5k
    , m_chart(chart)
54
22.5k
    , m_textZone(textZone)
55
22.5k
  {
56
22.5k
  }
57
58
  //! destructor
59
0
  ~SubDocument() final {}
60
61
  //! operator==
62
  bool operator==(std::shared_ptr<WPSSubDocument> const &doc) const final
63
0
  {
64
0
    if (!WKSSubDocument::operator==(doc) || !doc)
65
0
      return false;
66
0
    auto const *subDoc=dynamic_cast<SubDocument const *>(doc.get());
67
0
    if (!subDoc) return false;
68
0
    return m_chart==subDoc->m_chart && m_textZone==subDoc->m_textZone;
69
0
  }
70
71
  //! the parser function
72
  void parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType type) override;
73
protected:
74
  //! the chart
75
  WKSChart const *m_chart;
76
  //! the textzone type
77
  WKSChart::TextZone::Type m_textZone;
78
private:
79
  SubDocument(SubDocument const &orig) = delete;
80
  SubDocument &operator=(SubDocument const &orig) = delete;
81
};
82
83
void SubDocument::parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType /*type*/)
84
22.5k
{
85
22.5k
  if (!listener.get())
86
0
  {
87
0
    WPS_DEBUG_MSG(("WKSChartInternal::SubDocument::parse: no listener\n"));
88
0
    return;
89
0
  }
90
91
22.5k
  if (!m_chart)
92
0
  {
93
0
    WPS_DEBUG_MSG(("WKSChartInternal::SubDocument::parse: can not find the chart\n"));
94
0
    return;
95
0
  }
96
22.5k
  m_chart->sendTextZoneContent(m_textZone, listener);
97
22.5k
}
98
99
}
100
101
////////////////////////////////////////////////////////////
102
// WKSChart
103
////////////////////////////////////////////////////////////
104
WKSChart::WKSChart(Vec2f const &dim)
105
114k
  : m_dimension(dim)
106
114k
  , m_type(WKSChart::Serie::S_Bar)
107
114k
  , m_dataStacked(false)
108
114k
  , m_dataPercentStacked(false)
109
114k
  , m_dataVertical(false)
110
114k
  , m_is3D(false)
111
114k
  , m_is3DDeep(false)
112
113
114k
  , m_style(WPSGraphicStyle::emptyStyle())
114
114k
  , m_name()
115
116
114k
  , m_plotAreaPosition()
117
114k
  , m_plotAreaStyle(WPSGraphicStyle::emptyStyle())
118
119
114k
  , m_legendPosition()
120
121
114k
  , m_floorStyle()
122
114k
  , m_wallStyle()
123
124
114k
  , m_gridColor(179,179,179)
125
114k
  , m_legend()
126
114k
  , m_serieMap()
127
114k
  , m_textZoneMap()
128
114k
{
129
114k
  m_wallStyle.m_lineColor=m_floorStyle.m_lineColor=WPSColor(0xb3,0xb3, 0xb3);
130
114k
}
131
132
WKSChart::~WKSChart()
133
114k
{
134
114k
}
135
136
WKSChart::Axis &WKSChart::getAxis(int coord)
137
878k
{
138
878k
  if (coord<0 || coord>3)
139
305
  {
140
305
    WPS_DEBUG_MSG(("WKSChart::getAxis: called with bad coord\n"));
141
305
    return  m_axis[4];
142
305
  }
143
877k
  return m_axis[coord];
144
878k
}
145
146
WKSChart::Axis const &WKSChart::getAxis(int coord) const
147
0
{
148
0
  if (coord<0 || coord>3)
149
0
  {
150
0
    WPS_DEBUG_MSG(("WKSChart::getAxis: called with bad coord\n"));
151
0
    return  m_axis[4];
152
0
  }
153
0
  return m_axis[coord];
154
0
}
155
156
WKSChart::Serie *WKSChart::getSerie(int id, bool create)
157
2.36M
{
158
2.36M
  if (m_serieMap.find(id)!=m_serieMap.end())
159
1.44M
    return &m_serieMap.find(id)->second;
160
922k
  if (!create)
161
543k
    return nullptr;
162
378k
  m_serieMap[id]=WKSChart::Serie();
163
378k
  return &m_serieMap.find(id)->second;
164
922k
}
165
166
WKSChart::TextZone *WKSChart::getTextZone(WKSChart::TextZone::Type type, bool create)
167
67.0k
{
168
67.0k
  if (m_textZoneMap.find(type)!=m_textZoneMap.end())
169
15.3k
    return &m_textZoneMap.find(type)->second;
170
51.6k
  if (!create)
171
0
    return nullptr;
172
51.6k
  m_textZoneMap.insert(std::map<TextZone::Type, TextZone>::value_type(type,WKSChart::TextZone(type)));
173
51.6k
  return &m_textZoneMap.find(type)->second;
174
51.6k
}
175
176
void WKSChart::sendTextZoneContent(WKSChart::TextZone::Type type, WPSListenerPtr listener) const
177
22.5k
{
178
22.5k
  if (m_textZoneMap.find(type)==m_textZoneMap.end())
179
0
  {
180
0
    WPS_DEBUG_MSG(("WKSChart::sendTextZoneContent: called with unknown zone(%d)\n", int(type)));
181
0
    return;
182
0
  }
183
22.5k
  sendContent(m_textZoneMap.find(type)->second, listener);
184
22.5k
}
185
186
void WKSChart::sendChart(WKSContentListenerPtr &listener, librevenge::RVNGSpreadsheetInterface *interface) const
187
34.1k
{
188
34.1k
  if (!listener || !interface)
189
0
  {
190
0
    WPS_DEBUG_MSG(("WKSChart::sendChart: can not find listener or interface\n"));
191
0
    return;
192
0
  }
193
34.1k
  if (m_serieMap.empty())
194
141
  {
195
141
    WPS_DEBUG_MSG(("WKSChart::sendChart: can not find the series\n"));
196
141
    return;
197
141
  }
198
34.0k
  std::shared_ptr<WPSListener> genericListener=listener;
199
34.0k
  int styleId=0;
200
201
34.0k
  librevenge::RVNGPropertyList style;
202
34.0k
  style.insert("librevenge:chart-id", styleId);
203
34.0k
  m_style.addTo(style);
204
34.0k
  interface->defineChartStyle(style);
205
206
34.0k
  librevenge::RVNGPropertyList chart;
207
34.0k
  if (m_dimension[0]>0 && m_dimension[1]>0)
208
33.9k
  {
209
    // set the size if known
210
33.9k
    chart.insert("svg:width", double(m_dimension[0]), librevenge::RVNG_POINT);
211
33.9k
    chart.insert("svg:height", double(m_dimension[1]), librevenge::RVNG_POINT);
212
33.9k
  }
213
34.0k
  if (!m_serieMap.empty())
214
34.0k
    chart.insert("chart:class", Serie::getSerieTypeName(m_serieMap.begin()->second.m_type).c_str());
215
0
  else
216
0
    chart.insert("chart:class", Serie::getSerieTypeName(m_type).c_str());
217
34.0k
  chart.insert("librevenge:chart-id", styleId++);
218
34.0k
  interface->openChart(chart);
219
220
  // legend
221
34.0k
  if (m_legend.m_show)
222
18.6k
  {
223
18.6k
    bool autoPlace=m_legendPosition==WPSBox2f()||m_dimension==Vec2f();
224
18.6k
    style=librevenge::RVNGPropertyList();
225
18.6k
    m_legend.addStyleTo(style);
226
18.6k
    style.insert("librevenge:chart-id", styleId);
227
18.6k
    style.insert("chart:auto-position",autoPlace);
228
18.6k
    interface->defineChartStyle(style);
229
18.6k
    librevenge::RVNGPropertyList legend;
230
18.6k
    m_legend.addContentTo(legend);
231
18.6k
    legend.insert("librevenge:chart-id", styleId++);
232
18.6k
    legend.insert("librevenge:zone-type", "legend");
233
18.6k
    if (!autoPlace)
234
48
    {
235
48
      legend.insert("svg:x", double(m_legendPosition[0][0])*double(m_dimension[0]), librevenge::RVNG_POINT);
236
48
      legend.insert("svg:y", double(m_legendPosition[0][1])*double(m_dimension[1]), librevenge::RVNG_POINT);
237
48
      legend.insert("svg:width", double(m_legendPosition.size()[0])*double(m_dimension[0]), librevenge::RVNG_POINT);
238
48
      legend.insert("svg:height", double(m_legendPosition.size()[1])*double(m_dimension[1]), librevenge::RVNG_POINT);
239
48
    }
240
18.6k
    interface->openChartTextObject(legend);
241
18.6k
    interface->closeChartTextObject();
242
18.6k
  }
243
34.0k
  for (auto const &textIt : m_textZoneMap)
244
23.1k
  {
245
23.1k
    TextZone const &zone= textIt.second;
246
23.1k
    if (!zone.valid()) continue;
247
22.7k
    style=librevenge::RVNGPropertyList();
248
22.7k
    zone.addStyleTo(style);
249
22.7k
    style.insert("librevenge:chart-id", styleId);
250
22.7k
    interface->defineChartStyle(style);
251
22.7k
    librevenge::RVNGPropertyList textZone;
252
22.7k
    zone.addContentTo(textZone);
253
22.7k
    textZone.insert("librevenge:chart-id", styleId++);
254
22.7k
    textZone.insert("librevenge:zone-type",
255
22.7k
                    zone.m_type==TextZone::T_Title ? "title": zone.m_type==TextZone::T_SubTitle ?"subtitle" : "footer");
256
22.7k
    interface->openChartTextObject(textZone);
257
22.7k
    if (zone.m_contentType==TextZone::C_Text)
258
22.5k
    {
259
22.5k
      std::shared_ptr<WPSSubDocument> doc(new WKSChartInternal::SubDocument(this, zone.m_type));
260
22.5k
      listener->handleSubDocument(doc, libwps::DOC_CHART_ZONE);
261
22.5k
    }
262
22.7k
    interface->closeChartTextObject();
263
22.7k
  }
264
  // plot area
265
34.0k
  style=librevenge::RVNGPropertyList();
266
34.0k
  bool autoPlace=m_plotAreaPosition==WPSBox2f()||m_dimension==Vec2f();
267
34.0k
  m_plotAreaStyle.addTo(style);
268
34.0k
  style.insert("librevenge:chart-id", styleId);
269
34.0k
  style.insert("chart:include-hidden-cells","false");
270
34.0k
  style.insert("chart:auto-position",autoPlace);
271
34.0k
  style.insert("chart:auto-size",autoPlace);
272
34.0k
  style.insert("chart:treat-empty-cells","leave-gap");
273
34.0k
  style.insert("chart:right-angled-axes","true");
274
34.0k
  style.insert("chart:stacked", m_dataStacked);
275
34.0k
  style.insert("chart:percentage", m_dataPercentStacked);
276
34.0k
  if (m_dataVertical)
277
416
    style.insert("chart:vertical", true);
278
34.0k
  if (m_is3D)
279
856
  {
280
856
    style.insert("chart:three-dimensional", true);
281
856
    style.insert("chart:deep", m_is3DDeep);
282
856
  }
283
34.0k
  interface->defineChartStyle(style);
284
285
34.0k
  librevenge::RVNGPropertyList plotArea;
286
34.0k
  if (!autoPlace)
287
1.73k
  {
288
1.73k
    plotArea.insert("svg:x", double(m_plotAreaPosition[0][0])*double(m_dimension[0]), librevenge::RVNG_POINT);
289
1.73k
    plotArea.insert("svg:y", double(m_plotAreaPosition[0][1])*double(m_dimension[1]), librevenge::RVNG_POINT);
290
1.73k
    plotArea.insert("svg:width", double(m_plotAreaPosition.size()[0])*double(m_dimension[0]), librevenge::RVNG_POINT);
291
1.73k
    plotArea.insert("svg:height", double(m_plotAreaPosition.size()[1])*double(m_dimension[1]), librevenge::RVNG_POINT);
292
1.73k
  }
293
34.0k
  plotArea.insert("librevenge:chart-id", styleId++);
294
295
34.0k
  librevenge::RVNGPropertyList floor, wall;
296
34.0k
  librevenge::RVNGPropertyListVector vect;
297
34.0k
  style=librevenge::RVNGPropertyList();
298
  // add floor
299
34.0k
  m_floorStyle.addTo(style);
300
34.0k
  style.insert("librevenge:chart-id", styleId);
301
34.0k
  interface->defineChartStyle(style);
302
34.0k
  floor.insert("librevenge:type", "floor");
303
34.0k
  floor.insert("librevenge:chart-id", styleId++);
304
34.0k
  vect.append(floor);
305
306
  // add wall
307
34.0k
  style=librevenge::RVNGPropertyList();
308
34.0k
  m_wallStyle.addTo(style);
309
34.0k
  style.insert("librevenge:chart-id", styleId);
310
34.0k
  interface->defineChartStyle(style);
311
34.0k
  wall.insert("librevenge:type", "wall");
312
34.0k
  wall.insert("librevenge:chart-id", styleId++);
313
34.0k
  vect.append(wall);
314
315
34.0k
  plotArea.insert("librevenge:childs", vect);
316
317
34.0k
  interface->openChartPlotArea(plotArea);
318
  // axis : x, y, second, z
319
170k
  for (int i=0; i<4; ++i)
320
136k
  {
321
136k
    if (m_axis[i].m_type==Axis::A_None) continue;
322
80.4k
    style=librevenge::RVNGPropertyList();
323
80.4k
    m_axis[i].addStyleTo(style);
324
80.4k
    style.insert("librevenge:chart-id", styleId);
325
80.4k
    interface->defineChartStyle(style);
326
80.4k
    librevenge::RVNGPropertyList axis;
327
80.4k
    m_axis[i].addContentTo(i, axis);
328
80.4k
    axis.insert("librevenge:chart-id", styleId++);
329
80.4k
    interface->insertChartAxis(axis);
330
80.4k
  }
331
  // series
332
34.0k
  for (auto const &it : m_serieMap)
333
154k
  {
334
154k
    auto const &serie = it.second;
335
154k
    if (!serie.valid()) continue;
336
150k
    style=librevenge::RVNGPropertyList();
337
150k
    serie.addStyleTo(style);
338
150k
    style.insert("librevenge:chart-id", styleId);
339
150k
    interface->defineChartStyle(style);
340
150k
    librevenge::RVNGPropertyList series;
341
150k
    serie.addContentTo(series);
342
150k
    series.insert("librevenge:chart-id", styleId++);
343
150k
    interface->openChartSerie(series);
344
150k
    interface->closeChartSerie();
345
150k
  }
346
34.0k
  interface->closeChartPlotArea();
347
348
34.0k
  interface->closeChart();
349
34.0k
}
350
351
////////////////////////////////////////////////////////////
352
// Position
353
////////////////////////////////////////////////////////////
354
librevenge::RVNGString WKSChart::Position::getCellName() const
355
2.56k
{
356
2.56k
  if (!valid())
357
0
  {
358
0
    WPS_DEBUG_MSG(("WKSChart::Position::getCellName: called on invalid cell\n"));
359
0
    return librevenge::RVNGString();
360
0
  }
361
2.56k
  std::string cellName=libwps::getCellName(m_pos);
362
2.56k
  if (cellName.empty())
363
0
    return librevenge::RVNGString();
364
2.56k
  std::stringstream o;
365
2.56k
  o << m_sheetName.cstr() << "." << cellName;
366
2.56k
  return librevenge::RVNGString(o.str().c_str());
367
2.56k
}
368
std::ostream &operator<<(std::ostream &o, WKSChart::Position const &pos)
369
0
{
370
0
  if (pos.valid())
371
0
    o << pos.m_pos << "[" << pos.m_sheetName.cstr() << "]";
372
0
  else
373
0
    o << "_";
374
0
  return o;
375
0
}
376
////////////////////////////////////////////////////////////
377
// Axis
378
////////////////////////////////////////////////////////////
379
WKSChart::Axis::Axis()
380
574k
  : m_type(WKSChart::Axis::A_None)
381
574k
  , m_automaticScaling(true)
382
574k
  , m_scaling()
383
574k
  , m_showGrid(true)
384
574k
  , m_showLabel(true)
385
574k
  , m_showTitle(true)
386
574k
  , m_titleRange()
387
574k
  , m_title()
388
574k
  , m_subTitle()
389
574k
  , m_style()
390
574k
{
391
574k
  m_style.m_lineWidth=0;
392
574k
}
393
394
WKSChart::Axis::~Axis()
395
574k
{
396
574k
}
397
398
void WKSChart::Axis::addContentTo(int coord, librevenge::RVNGPropertyList &propList) const
399
80.4k
{
400
80.4k
  std::string axis("");
401
80.4k
  axis += coord==0 ? 'x' : coord==3 ? 'z' : 'y';
402
80.4k
  propList.insert("chart:dimension",axis.c_str());
403
80.4k
  axis = (coord==2 ? "secondary-y" : "primary-"+axis);
404
80.4k
  propList.insert("chart:name",axis.c_str());
405
80.4k
  librevenge::RVNGPropertyListVector childs;
406
80.4k
  if (m_showGrid && (m_type==A_Numeric || m_type==A_Logarithmic))
407
33.5k
  {
408
33.5k
    librevenge::RVNGPropertyList grid;
409
33.5k
    grid.insert("librevenge:type", "grid");
410
33.5k
    grid.insert("chart:class", "major");
411
33.5k
    childs.append(grid);
412
33.5k
  }
413
80.4k
  if (m_labelRanges[0].valid(m_labelRanges[1]) && m_showLabel)
414
23.2k
  {
415
23.2k
    librevenge::RVNGPropertyList range;
416
23.2k
    range.insert("librevenge:sheet-name", m_labelRanges[0].m_sheetName);
417
23.2k
    range.insert("librevenge:start-row", m_labelRanges[0].m_pos[1]);
418
23.2k
    range.insert("librevenge:start-column", m_labelRanges[0].m_pos[0]);
419
23.2k
    if (m_labelRanges[0].m_sheetName!=m_labelRanges[1].m_sheetName)
420
35
      range.insert("librevenge:end-sheet-name", m_labelRanges[1].m_sheetName);
421
23.2k
    range.insert("librevenge:end-row", m_labelRanges[1].m_pos[1]);
422
23.2k
    range.insert("librevenge:end-column", m_labelRanges[1].m_pos[0]);
423
23.2k
    librevenge::RVNGPropertyListVector vect;
424
23.2k
    vect.append(range);
425
23.2k
    librevenge::RVNGPropertyList categories;
426
23.2k
    categories.insert("librevenge:type", "categories");
427
23.2k
    categories.insert("table:cell-range-address", vect);
428
23.2k
    childs.append(categories);
429
23.2k
  }
430
80.4k
  if (m_showTitle && (!m_title.empty() || !m_subTitle.empty()))
431
20.6k
  {
432
20.6k
    auto finalString(m_title);
433
20.6k
    if (!m_title.empty() && !m_subTitle.empty()) finalString.append(" - ");
434
20.6k
    finalString.append(m_subTitle);
435
20.6k
    librevenge::RVNGPropertyList title;
436
20.6k
    title.insert("librevenge:type", "title");
437
20.6k
    title.insert("librevenge:text", finalString);
438
20.6k
    childs.append(title);
439
20.6k
  }
440
59.8k
  else if (m_showTitle && m_titleRange.valid())
441
48
  {
442
48
    librevenge::RVNGPropertyList title;
443
48
    title.insert("librevenge:type", "title");
444
48
    librevenge::RVNGPropertyList range;
445
48
    range.insert("librevenge:sheet-name", m_titleRange.m_sheetName);
446
48
    range.insert("librevenge:start-row", m_titleRange.m_pos[1]);
447
48
    range.insert("librevenge:start-column", m_titleRange.m_pos[0]);
448
48
    librevenge::RVNGPropertyListVector vect;
449
48
    vect.append(range);
450
48
    title.insert("table:cell-range", vect);
451
48
    childs.append(title);
452
48
  }
453
80.4k
  if (!childs.empty())
454
58.4k
    propList.insert("librevenge:childs", childs);
455
80.4k
}
456
457
void WKSChart::Axis::addStyleTo(librevenge::RVNGPropertyList &propList) const
458
80.4k
{
459
80.4k
  propList.insert("chart:display-label", m_showLabel);
460
80.4k
  propList.insert("chart:axis-position", 0, librevenge::RVNG_GENERIC);
461
80.4k
  propList.insert("chart:reverse-direction", false);
462
80.4k
  propList.insert("chart:logarithmic", m_type==WKSChart::Axis::A_Logarithmic);
463
80.4k
  propList.insert("text:line-break", false);
464
80.4k
  if (!m_automaticScaling)
465
835
  {
466
835
    propList.insert("chart:minimum", double(m_scaling[0]), librevenge::RVNG_GENERIC);
467
835
    propList.insert("chart:maximum", double(m_scaling[1]), librevenge::RVNG_GENERIC);
468
835
  }
469
80.4k
  m_style.addTo(propList, true);
470
80.4k
}
471
472
std::ostream &operator<<(std::ostream &o, WKSChart::Axis const &axis)
473
0
{
474
0
  switch (axis.m_type)
475
0
  {
476
0
  case WKSChart::Axis::A_None:
477
0
    o << "none,";
478
0
    break;
479
0
  case WKSChart::Axis::A_Numeric:
480
0
    o << "numeric,";
481
0
    break;
482
0
  case WKSChart::Axis::A_Logarithmic:
483
0
    o << "logarithmic,";
484
0
    break;
485
0
  case WKSChart::Axis::A_Sequence:
486
0
    o << "sequence,";
487
0
    break;
488
0
  case WKSChart::Axis::A_Sequence_Skip_Empty:
489
0
    o << "sequence[noEmpty],";
490
0
    break;
491
#if !defined(__clang__)
492
  default:
493
    o << "###type,";
494
    WPS_DEBUG_MSG(("WKSChart::Axis: unexpected type\n"));
495
    break;
496
#endif
497
0
  }
498
0
  if (axis.m_showGrid) o << "show[grid],";
499
0
  if (axis.m_showLabel) o << "show[label],";
500
0
  if (axis.m_labelRanges[0].valid(axis.m_labelRanges[1]))
501
0
    o << "label[range]=" << axis.m_labelRanges[0] << ":" << axis.m_labelRanges[1] << ",";
502
0
  if (axis.m_showTitle)
503
0
  {
504
0
    if (axis.m_titleRange.valid()) o << "title[range]=" << axis.m_titleRange << ",";
505
0
    if (!axis.m_title.empty()) o << "title=" << axis.m_title.cstr() << ",";
506
0
    if (!axis.m_subTitle.empty()) o << "subTitle=" << axis.m_subTitle.cstr() << ",";
507
0
  }
508
0
  if (!axis.m_automaticScaling && axis.m_scaling!=Vec2f())
509
0
    o << "scaling=manual[" << axis.m_scaling[0] << "->" << axis.m_scaling[1] << ",";
510
0
  o << axis.m_style;
511
0
  return o;
512
0
}
513
514
////////////////////////////////////////////////////////////
515
// Legend
516
////////////////////////////////////////////////////////////
517
void WKSChart::Legend::addContentTo(librevenge::RVNGPropertyList &propList) const
518
18.6k
{
519
18.6k
  if (m_position[0]>0 && m_position[1]>0)
520
0
  {
521
0
    propList.insert("svg:x", double(m_position[0]), librevenge::RVNG_POINT);
522
0
    propList.insert("svg:y", double(m_position[1]), librevenge::RVNG_POINT);
523
0
  }
524
18.6k
  if (!m_autoPosition || !m_relativePosition)
525
0
    return;
526
18.6k
  std::stringstream s;
527
18.6k
  if (m_relativePosition&WPSBorder::TopBit)
528
0
    s << "top";
529
18.6k
  else if (m_relativePosition&WPSBorder::BottomBit)
530
0
    s << "bottom";
531
18.6k
  if (s.str().length() && (m_relativePosition&(WPSBorder::LeftBit|WPSBorder::RightBit)))
532
0
    s << "-";
533
18.6k
  if (m_relativePosition&WPSBorder::LeftBit)
534
0
    s << "start";
535
18.6k
  else if (m_relativePosition&WPSBorder::RightBit)
536
18.6k
    s << "end";
537
18.6k
  propList.insert("chart:legend-position", s.str().c_str());
538
18.6k
}
539
540
void WKSChart::Legend::addStyleTo(librevenge::RVNGPropertyList &propList) const
541
18.6k
{
542
18.6k
  propList.insert("chart:auto-position", m_autoPosition);
543
18.6k
  m_font.addTo(propList);
544
18.6k
  m_style.addTo(propList);
545
18.6k
}
546
547
std::ostream &operator<<(std::ostream &o, WKSChart::Legend const &legend)
548
0
{
549
0
  if (legend.m_show)
550
0
    o << "show,";
551
0
  if (legend.m_autoPosition)
552
0
  {
553
0
    o << "automaticPos[";
554
0
    if (legend.m_relativePosition&WPSBorder::TopBit)
555
0
      o << "t";
556
0
    else if (legend.m_relativePosition&WPSBorder::RightBit)
557
0
      o << "b";
558
0
    else
559
0
      o << "c";
560
0
    if (legend.m_relativePosition&WPSBorder::LeftBit)
561
0
      o << "L";
562
0
    else if (legend.m_relativePosition&WPSBorder::BottomBit)
563
0
      o << "R";
564
0
    else
565
0
      o << "C";
566
0
    o << "]";
567
0
  }
568
0
  else
569
0
    o << "pos=" << legend.m_position << ",";
570
0
  o << legend.m_style;
571
0
  return o;
572
0
}
573
574
////////////////////////////////////////////////////////////
575
// Serie
576
////////////////////////////////////////////////////////////
577
WKSChart::Serie::Serie()
578
757k
  : m_type(WKSChart::Serie::S_Bar)
579
757k
  , m_useSecondaryY(false)
580
757k
  , m_font()
581
757k
  , m_legendRange()
582
757k
  , m_legendText()
583
757k
  , m_style()
584
757k
  , m_pointType(P_None)
585
757k
{
586
757k
  m_style.m_lineWidth=0;
587
757k
  m_style.setSurfaceColor(WPSColor(0x80,0x80,0xFF));
588
757k
}
589
590
WKSChart::Serie::~Serie()
591
773k
{
592
773k
}
593
594
std::string WKSChart::Serie::getSerieTypeName(Type type)
595
184k
{
596
184k
  switch (type)
597
184k
  {
598
2.10k
  case S_Area:
599
2.10k
    return "chart:area";
600
35.5k
  case S_Bar:
601
35.5k
    return "chart:bar";
602
194
  case S_Bubble:
603
194
    return "chart:bubble";
604
2.65k
  case S_Circle:
605
2.65k
    return "chart:circle";
606
0
  case S_Column:
607
0
    return "chart:column";
608
0
  case S_Gantt:
609
0
    return "chart:gantt";
610
7.18k
  case S_Line:
611
7.18k
    return "chart:line";
612
390
  case S_Radar:
613
390
    return "chart:radar";
614
0
  case S_Ring:
615
0
    return "chart:ring";
616
128k
  case S_Scatter:
617
128k
    return "chart:scatter";
618
7.38k
  case S_Stock:
619
7.38k
    return "chart:stock";
620
0
  case S_Surface:
621
0
    return "chart:surface";
622
#if !defined(__clang__)
623
  default:
624
    break;
625
#endif
626
184k
  }
627
0
  return "chart:bar";
628
184k
}
629
630
void WKSChart::Serie::addContentTo(librevenge::RVNGPropertyList &serie) const
631
150k
{
632
150k
  serie.insert("chart:class",getSerieTypeName(m_type).c_str());
633
150k
  if (m_useSecondaryY)
634
718
    serie.insert("chart:attached-axis","secondary-y");
635
150k
  librevenge::RVNGPropertyList datapoint;
636
150k
  librevenge::RVNGPropertyListVector vect;
637
150k
  if (m_ranges[0].valid(m_ranges[1]))
638
150k
  {
639
150k
    librevenge::RVNGPropertyList range;
640
150k
    range.insert("librevenge:sheet-name", m_ranges[0].m_sheetName);
641
150k
    range.insert("librevenge:start-row", m_ranges[0].m_pos[1]);
642
150k
    range.insert("librevenge:start-column", m_ranges[0].m_pos[0]);
643
150k
    if (m_ranges[0].m_sheetName != m_ranges[1].m_sheetName)
644
403
      range.insert("librevenge:end-sheet-name", m_ranges[1].m_sheetName);
645
150k
    range.insert("librevenge:end-row", m_ranges[1].m_pos[1]);
646
150k
    range.insert("librevenge:end-column", m_ranges[1].m_pos[0]);
647
150k
    vect.append(range);
648
150k
    serie.insert("chart:values-cell-range-address", vect);
649
150k
    vect.clear();
650
150k
  }
651
652
  // to do use labelRanges
653
654
  // USEME: set the font here
655
150k
  if (m_legendRange.valid())
656
298
  {
657
298
    librevenge::RVNGPropertyList label;
658
298
    label.insert("librevenge:sheet-name", m_legendRange.m_sheetName);
659
298
    label.insert("librevenge:start-row", m_legendRange.m_pos[1]);
660
298
    label.insert("librevenge:start-column", m_legendRange.m_pos[0]);
661
298
    vect.append(label);
662
298
    serie.insert("chart:label-cell-address", vect);
663
298
    vect.clear();
664
298
  }
665
150k
  if (!m_legendText.empty())
666
39.1k
  {
667
    // replace ' ' and non basic caracters by _ because this causes LibreOffice's problem
668
39.1k
    std::string basicString(m_legendText.cstr());
669
39.1k
    for (auto &c : basicString)
670
343k
    {
671
343k
      if (c==' ' || static_cast<unsigned char>(c)>=0x80)
672
126k
        c='_';
673
343k
    }
674
39.1k
    serie.insert("chart:label-string", basicString.c_str());
675
39.1k
  }
676
150k
  datapoint.insert("librevenge:type", "data-point");
677
150k
  Vec2i dataSize=m_ranges[1].m_pos-m_ranges[0].m_pos;
678
150k
  datapoint.insert("chart:repeated", 1+std::max(dataSize[0],dataSize[1]));
679
150k
  vect.append(datapoint);
680
150k
  serie.insert("librevenge:childs", vect);
681
150k
}
682
683
void WKSChart::Serie::addStyleTo(librevenge::RVNGPropertyList &propList) const
684
150k
{
685
150k
  m_style.addTo(propList);
686
150k
  if (m_pointType != WKSChart::Serie::P_None)
687
113k
  {
688
113k
    char const *what[] =
689
113k
    {
690
113k
      "none", "automatic", "square", "diamond", "arrow-down",
691
113k
      "arrow-up", "arrow-right", "arrow-left", "bow-tie", "hourglass",
692
113k
      "circle", "star", "x", "plus", "asterisk",
693
113k
      "horizontal-bar", "vertical-bar"
694
113k
    };
695
113k
    if (m_pointType == WKSChart::Serie::P_Automatic)
696
90.5k
      propList.insert("chart:symbol-type", "automatic");
697
23.0k
    else if (m_pointType < WPS_N_ELEMENTS(what))
698
23.0k
    {
699
23.0k
      propList.insert("chart:symbol-type", "named-symbol");
700
23.0k
      propList.insert("chart:symbol-name", what[m_pointType]);
701
23.0k
    }
702
113k
  }
703
  //propList.insert("chart:data-label-number","value");
704
  //propList.insert("chart:data-label-text","false");
705
150k
}
706
707
void WKSChart::Serie::setPrimaryPattern(WPSGraphicStyle::Pattern const &pattern, bool force1D)
708
165k
{
709
165k
  WPSColor finalColor;
710
165k
  if (pattern.getUniqueColor(finalColor))
711
160k
    setPrimaryColor(finalColor, 1, force1D);
712
4.95k
  else if (!force1D && !is1DStyle())
713
4.72k
    m_style.setPattern(pattern);
714
230
  else if (pattern.getAverageColor(finalColor))
715
230
    setPrimaryColor(finalColor);
716
165k
}
717
718
std::ostream &operator<<(std::ostream &o, WKSChart::Serie const &serie)
719
0
{
720
0
  switch (serie.m_type)
721
0
  {
722
0
  case WKSChart::Serie::S_Area:
723
0
    o << "area,";
724
0
    break;
725
0
  case WKSChart::Serie::S_Bar:
726
0
    o << "bar,";
727
0
    break;
728
0
  case WKSChart::Serie::S_Bubble:
729
0
    o << "bubble,";
730
0
    break;
731
0
  case WKSChart::Serie::S_Circle:
732
0
    o << "circle,";
733
0
    break;
734
0
  case WKSChart::Serie::S_Column:
735
0
    o << "column,";
736
0
    break;
737
0
  case WKSChart::Serie::S_Gantt:
738
0
    o << "gantt,";
739
0
    break;
740
0
  case WKSChart::Serie::S_Line:
741
0
    o << "line,";
742
0
    break;
743
0
  case WKSChart::Serie::S_Radar:
744
0
    o << "radar,";
745
0
    break;
746
0
  case WKSChart::Serie::S_Ring:
747
0
    o << "ring,";
748
0
    break;
749
0
  case WKSChart::Serie::S_Scatter:
750
0
    o << "scatter,";
751
0
    break;
752
0
  case WKSChart::Serie::S_Stock:
753
0
    o << "stock,";
754
0
    break;
755
0
  case WKSChart::Serie::S_Surface:
756
0
    o << "surface,";
757
0
    break;
758
#if !defined(__clang__)
759
  default:
760
    o << "###type,";
761
    WPS_DEBUG_MSG(("WKSChart::Serie: unexpected type\n"));
762
    break;
763
#endif
764
0
  }
765
0
  o << "range=" << serie.m_ranges[0] << ":" << serie.m_ranges[1] << ",";
766
0
  o << serie.m_style;
767
0
  if (serie.m_labelRanges[0].valid(serie.m_labelRanges[1]))
768
0
    o << "label[range]=" << serie.m_labelRanges[0] << "<->" << serie.m_labelRanges[1] << ",";
769
0
  if (serie.m_legendRange.valid())
770
0
    o << "legend[range]=" << serie.m_legendRange << ",";
771
0
  if (!serie.m_legendText.empty())
772
0
    o << "label[text]=" << serie.m_legendText.cstr() << ",";
773
0
  if (serie.m_pointType != WKSChart::Serie::P_None)
774
0
  {
775
0
    char const *what[] =
776
0
    {
777
0
      "none", "automatic", "square", "diamond", "arrow-down",
778
0
      "arrow-up", "arrow-right", "arrow-left", "bow-tie", "hourglass",
779
0
      "circle", "star", "x", "plus", "asterisk",
780
0
      "horizontal-bar", "vertical-bar"
781
0
    };
782
0
    if (serie.m_pointType>0 && serie.m_pointType < WPS_N_ELEMENTS(what))
783
0
    {
784
0
      o << "point=" << what[serie.m_pointType] << ",";
785
0
    }
786
0
    else if (serie.m_pointType>0)
787
0
      o << "#point=" << serie.m_pointType << ",";
788
0
  }
789
0
  return o;
790
0
}
791
792
////////////////////////////////////////////////////////////
793
// TextZone
794
////////////////////////////////////////////////////////////
795
WKSChart::TextZone::TextZone(Type type)
796
51.6k
  : m_type(type)
797
51.6k
  , m_contentType(WKSChart::TextZone::C_Text)
798
51.6k
  , m_show(true)
799
51.6k
  , m_position(-1,-1)
800
51.6k
  , m_cell()
801
51.6k
  , m_textEntryList()
802
51.6k
  , m_font()
803
51.6k
  , m_style()
804
51.6k
{
805
51.6k
  m_style.m_lineWidth=0;
806
51.6k
}
807
808
WKSChart::TextZone::~TextZone()
809
155k
{
810
155k
}
811
812
void WKSChart::TextZone::addContentTo(librevenge::RVNGPropertyList &propList) const
813
22.7k
{
814
22.7k
  if (m_position[0]>0 && m_position[1]>0)
815
0
  {
816
0
    propList.insert("svg:x", double(m_position[0]), librevenge::RVNG_POINT);
817
0
    propList.insert("svg:y", double(m_position[1]), librevenge::RVNG_POINT);
818
0
  }
819
22.7k
  else
820
22.7k
    propList.insert("chart:auto-position",true);
821
22.7k
  propList.insert("chart:auto-size",true);
822
22.7k
  switch (m_type)
823
22.7k
  {
824
30
  case T_Footer:
825
30
    propList.insert("librevenge:zone-type", "footer");
826
30
    break;
827
11.7k
  case T_Title:
828
11.7k
    propList.insert("librevenge:zone-type", "title");
829
11.7k
    break;
830
10.9k
  case T_SubTitle:
831
10.9k
    propList.insert("librevenge:zone-type", "subtitle");
832
10.9k
    break;
833
#if !defined(__clang__)
834
  default:
835
    propList.insert("librevenge:zone-type", "label");
836
    WPS_DEBUG_MSG(("WKSChart::TextZone:addContentTo: unexpected type\n"));
837
    break;
838
#endif
839
22.7k
  }
840
22.7k
  if (m_contentType==C_Cell && m_cell.valid())
841
194
  {
842
194
    librevenge::RVNGPropertyList range;
843
194
    librevenge::RVNGPropertyListVector vect;
844
194
    range.insert("librevenge:sheet-name", m_cell.m_sheetName);
845
194
    range.insert("librevenge:row", m_cell.m_pos[1]);
846
194
    range.insert("librevenge:column", m_cell.m_pos[0]);
847
194
    vect.append(range);
848
194
    propList.insert("table:cell-range", vect);
849
194
  }
850
22.7k
}
851
852
void WKSChart::TextZone::addStyleTo(librevenge::RVNGPropertyList &propList) const
853
22.7k
{
854
22.7k
  m_font.addTo(propList);
855
22.7k
  m_style.addTo(propList);
856
22.7k
}
857
858
std::ostream &operator<<(std::ostream &o, WKSChart::TextZone const &zone)
859
0
{
860
0
  switch (zone.m_type)
861
0
  {
862
0
  case WKSChart::TextZone::T_SubTitle:
863
0
    o << "sub";
864
0
    WPS_FALLTHROUGH;
865
0
  case WKSChart::TextZone::T_Title:
866
0
    o << "title,";
867
0
    break;
868
0
  case WKSChart::TextZone::T_Footer:
869
0
    o << "footer,";
870
0
    break;
871
#if !defined(__clang__)
872
  default:
873
    o << "###type,";
874
    WPS_DEBUG_MSG(("WKSChart::TextZone: unexpected type\n"));
875
    break;
876
#endif
877
0
  }
878
0
  if (zone.m_contentType==WKSChart::TextZone::C_Text)
879
0
    o << "text,";
880
0
  else if (zone.m_contentType==WKSChart::TextZone::C_Cell)
881
0
    o << "cell=" << zone.m_cell << ",";
882
0
  if (zone.m_position[0]>0 || zone.m_position[1]>0)
883
0
    o << "pos=" << zone.m_position << ",";
884
0
  o << zone.m_style;
885
0
  return o;
886
0
}
887
888
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */