/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: |