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