Coverage Report

Created: 2026-09-06 07:18

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