/src/libpagemaker/src/lib/PMDCollector.cpp
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* |
3 | | * This file is part of the libpagemaker project. |
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 | | |
10 | | #include "PMDCollector.h" |
11 | | |
12 | | #include <iostream> |
13 | | #include <math.h> |
14 | | #include <string> |
15 | | #include <utility> |
16 | | |
17 | | #include "OutputShape.h" |
18 | | #include "constants.h" |
19 | | #include "libpagemaker_utils.h" |
20 | | |
21 | | namespace libpagemaker |
22 | | { |
23 | | |
24 | | static const double EM2PT = 11.95516799999881; |
25 | | |
26 | | namespace |
27 | | { |
28 | | |
29 | | void flushText(std::string &text, librevenge::RVNGDrawingInterface *const painter) |
30 | 0 | { |
31 | 0 | if (!text.empty()) |
32 | 0 | { |
33 | 0 | painter->insertText(text.c_str()); |
34 | 0 | text.clear(); |
35 | 0 | } |
36 | 0 | } |
37 | | |
38 | | void writeTextSpan(const std::string &text, const std::size_t charStart, std::size_t charEnd, librevenge::RVNGDrawingInterface *const painter) |
39 | 0 | { |
40 | 0 | ++charEnd; |
41 | 0 | if (charEnd > text.size()) |
42 | 0 | charEnd = text.size(); |
43 | |
|
44 | 0 | std::string currentText; |
45 | 0 | bool wasSpace = false; |
46 | 0 | for (std::size_t i = charStart; i < charEnd; ++i) |
47 | 0 | { |
48 | 0 | const char c = text[i]; |
49 | |
|
50 | 0 | switch (c) |
51 | 0 | { |
52 | 0 | case '\t' : |
53 | 0 | flushText(currentText, painter); |
54 | 0 | painter->insertTab(); |
55 | 0 | break; |
56 | 0 | case '\r' : |
57 | 0 | flushText(currentText, painter); |
58 | 0 | painter->insertLineBreak(); |
59 | 0 | break; |
60 | 0 | case ' ' : |
61 | 0 | if (wasSpace) |
62 | 0 | { |
63 | 0 | flushText(currentText, painter); |
64 | 0 | painter->insertSpace(); |
65 | 0 | } |
66 | 0 | else |
67 | 0 | { |
68 | 0 | currentText.push_back(c); |
69 | 0 | } |
70 | 0 | break; |
71 | 0 | default: |
72 | | // Ignore control characters that do not have known use in PageMaker. |
73 | | // Specific control characters are handled in the switch already. |
74 | 0 | if (c < 0x20) |
75 | 0 | { |
76 | 0 | PMD_DEBUG_MSG(("skipping control character %#x\n", c)); |
77 | 0 | break; |
78 | 0 | } |
79 | 0 | else |
80 | 0 | { |
81 | 0 | currentText.push_back(c); |
82 | 0 | } |
83 | 0 | } |
84 | | |
85 | 0 | wasSpace = ' ' == c; |
86 | 0 | } |
87 | | |
88 | 0 | flushText(currentText, painter); |
89 | 0 | } |
90 | | |
91 | | void writeBorder(librevenge::RVNGPropertyList &props, const char *const name, const PMDStrokeProperties &stroke, const std::vector<PMDColor> &colors) |
92 | 0 | { |
93 | 0 | librevenge::RVNGString border; |
94 | |
|
95 | 0 | border.sprintf("%fpt", stroke.m_strokeWidth / 5.0); |
96 | 0 | border.append(" "); |
97 | 0 | switch (stroke.m_strokeType) |
98 | 0 | { |
99 | 0 | default: |
100 | 0 | PMD_DEBUG_MSG(("unexpected stroke type %u\n", unsigned(stroke.m_strokeType))); |
101 | 0 | PMD_FALLTHROUGH; |
102 | 0 | case STROKE_NORMAL: |
103 | 0 | border.append("solid"); |
104 | 0 | break; |
105 | 0 | case STROKE_LIGHT_LIGHT: |
106 | 0 | case STROKE_DARK_LIGHT: |
107 | 0 | case STROKE_LIGHT_DARK: |
108 | 0 | case STROKE_LIGHT_DARK_LIGHT: |
109 | 0 | border.append("double"); |
110 | 0 | break; |
111 | 0 | case STROKE_DASHED: |
112 | 0 | border.append("dashed"); |
113 | 0 | break; |
114 | 0 | case STROKE_SQUARE_DOTS: |
115 | 0 | case STROKE_CIRCULAR_DOTS: |
116 | 0 | border.append("dotted"); |
117 | 0 | break; |
118 | 0 | } |
119 | 0 | border.append(" "); |
120 | 0 | if (stroke.m_strokeColor < colors.size()) |
121 | 0 | { |
122 | 0 | const auto &color = colors[stroke.m_strokeColor]; |
123 | 0 | librevenge::RVNGString colorStr; |
124 | 0 | colorStr.sprintf("#%.2x%.2x%.2x", color.m_red, color.m_green, color.m_blue); |
125 | 0 | border.append(colorStr); |
126 | 0 | } |
127 | 0 | else |
128 | 0 | { |
129 | 0 | border.append("#000000"); |
130 | 0 | } |
131 | |
|
132 | 0 | props.insert(name, border); |
133 | 0 | } |
134 | | |
135 | | } |
136 | | |
137 | | PMDCollector::PMDCollector() : |
138 | 4 | m_pageWidth(), m_pageHeight(), m_pages(), m_color(),m_font(), |
139 | 4 | m_doubleSided(false) |
140 | 4 | { } |
141 | | |
142 | | void PMDCollector::setDoubleSided(bool doubleSided) |
143 | 0 | { |
144 | 0 | m_doubleSided = doubleSided; |
145 | 0 | } |
146 | | |
147 | | /* State-mutating functions */ |
148 | | void PMDCollector::setPageWidth(PMDShapeUnit pageWidth) |
149 | 0 | { |
150 | 0 | m_pageWidth = pageWidth; |
151 | 0 | } |
152 | | |
153 | | void PMDCollector::setPageHeight(PMDShapeUnit pageHeight) |
154 | 0 | { |
155 | 0 | m_pageHeight = pageHeight; |
156 | 0 | } |
157 | | |
158 | | unsigned PMDCollector::addPage() |
159 | 0 | { |
160 | 0 | m_pages.push_back((PMDPage())); |
161 | 0 | return m_pages.size() - 1; |
162 | 0 | } |
163 | | |
164 | | void PMDCollector::addColor(const PMDColor &color) |
165 | 0 | { |
166 | 0 | m_color.push_back(color); |
167 | 0 | } |
168 | | |
169 | | void PMDCollector::addFont(const PMDFont &font) |
170 | 0 | { |
171 | 0 | m_font.push_back(font); |
172 | 0 | } |
173 | | |
174 | | void PMDCollector::addShapeToPage(unsigned pageID, const std::shared_ptr<PMDLineSet> &shape) |
175 | 0 | { |
176 | 0 | m_pages.at(pageID).addShape(shape); |
177 | 0 | } |
178 | | |
179 | | void PMDCollector::paintShape(const OutputShape &shape, |
180 | | librevenge::RVNGDrawingInterface *painter) const |
181 | 0 | { |
182 | 0 | if (shape.shapeType() == SHAPE_TYPE_LINE || shape.shapeType() == SHAPE_TYPE_POLY || shape.shapeType() == SHAPE_TYPE_RECT) |
183 | 0 | { |
184 | 0 | librevenge::RVNGPropertyListVector vertices; |
185 | 0 | for (unsigned i = 0; i < shape.numPoints(); ++i) |
186 | 0 | { |
187 | 0 | librevenge::RVNGPropertyList vertex; |
188 | 0 | vertex.insert("svg:x", shape.getPoint(i).m_x); |
189 | 0 | vertex.insert("svg:y", shape.getPoint(i).m_y); |
190 | 0 | vertices.append(vertex); |
191 | 0 | } |
192 | 0 | librevenge::RVNGPropertyList points; |
193 | 0 | points.insert("svg:points", vertices); |
194 | |
|
195 | 0 | PMDFillProperties fillProps = shape.getFillProperties(); |
196 | 0 | PMDStrokeProperties strokeProps = shape.getStrokeProperties(); |
197 | |
|
198 | 0 | switch (fillProps.m_fillType) |
199 | 0 | { |
200 | 0 | case FILL_SOLID: |
201 | 0 | points.insert("draw:fill", "solid"); |
202 | 0 | break; |
203 | 0 | case FILL_NONE: |
204 | 0 | points.insert("draw:fill", "none"); |
205 | 0 | break; |
206 | 0 | default: |
207 | 0 | points.insert("draw:fill", "none"); |
208 | 0 | } |
209 | | |
210 | 0 | if (fillProps.m_fillColor < m_color.size()) |
211 | 0 | { |
212 | 0 | PMDColor tempFillColor = m_color[fillProps.m_fillColor]; |
213 | 0 | librevenge::RVNGString tempFillColorString; |
214 | 0 | tempFillColorString.sprintf("#%.2x%.2x%.2x", tempFillColor.m_red,tempFillColor.m_green,tempFillColor.m_blue); |
215 | 0 | points.insert("draw:fill-color", tempFillColorString); |
216 | 0 | } |
217 | 0 | else |
218 | 0 | { |
219 | 0 | PMD_DEBUG_MSG(("Fill Color Not Available")); |
220 | 0 | } |
221 | |
|
222 | 0 | if (fillProps.m_fillColor == 0) |
223 | 0 | points.insert("draw:opacity", 0); |
224 | 0 | else |
225 | 0 | points.insert("draw:opacity", fillProps.m_fillTint); |
226 | |
|
227 | 0 | switch (strokeProps.m_strokeType) |
228 | 0 | { |
229 | 0 | case STROKE_NORMAL: |
230 | 0 | points.insert("draw:stroke", "solid"); |
231 | 0 | break; |
232 | 0 | case STROKE_DASHED: |
233 | 0 | points.insert("draw:stroke","dash"); |
234 | 0 | break; |
235 | 0 | default: |
236 | 0 | points.insert("draw:stroke", "solid"); |
237 | 0 | } |
238 | | |
239 | 0 | points.insert("svg:stroke-width", (double)strokeProps.m_strokeWidth/5.0,librevenge::RVNG_POINT); |
240 | |
|
241 | 0 | if (strokeProps.m_strokeColor < m_color.size()) |
242 | 0 | { |
243 | 0 | PMDColor tempStrokeColor = m_color[strokeProps.m_strokeColor]; |
244 | 0 | librevenge::RVNGString tempStrokeColorString; |
245 | 0 | tempStrokeColorString.sprintf("#%.2x%.2x%.2x", tempStrokeColor.m_red,tempStrokeColor.m_green,tempStrokeColor.m_blue); |
246 | 0 | points.insert("svg:stroke-color", tempStrokeColorString); |
247 | 0 | } |
248 | 0 | else |
249 | 0 | { |
250 | 0 | PMD_DEBUG_MSG(("Stroke Color Not Available")); |
251 | 0 | } |
252 | |
|
253 | 0 | points.insert("svg:stroke-opacity", (double)strokeProps.m_strokeTint/100.0,librevenge::RVNG_PERCENT); |
254 | |
|
255 | 0 | if (shape.getIsClosed()) |
256 | 0 | { |
257 | 0 | painter->drawPolygon(points); |
258 | 0 | } |
259 | 0 | else |
260 | 0 | { |
261 | 0 | painter->drawPolyline(points); |
262 | 0 | } |
263 | 0 | } |
264 | 0 | else if (shape.shapeType() == SHAPE_TYPE_TEXTBOX) |
265 | 0 | { |
266 | 0 | librevenge::RVNGPropertyList textbox; |
267 | |
|
268 | 0 | textbox.insert("svg:x",shape.getPoint(0).m_x, librevenge::RVNG_INCH); |
269 | 0 | textbox.insert("svg:y",shape.getPoint(0).m_y, librevenge::RVNG_INCH); |
270 | 0 | textbox.insert("svg:width",shape.getWidth(), librevenge::RVNG_INCH); |
271 | 0 | textbox.insert("svg:height",shape.getHeight(), librevenge::RVNG_INCH); |
272 | | //textbox.insert("text:anchor-type", "page"); |
273 | | //textbox.insert("text:anchor-page-number", 1); |
274 | | //textbox.insert("style:vertical-rel", "page"); |
275 | | //textbox.insert("style:horizontal-rel", "page"); |
276 | | //textbox.insert("style:horizontal-pos", "from-left"); |
277 | | //textbox.insert("style:vertical-pos", "from-top"); |
278 | 0 | textbox.insert("draw:stroke", "none"); |
279 | 0 | textbox.insert("draw:fill", "none"); |
280 | 0 | textbox.insert("librevenge:rotate", shape.getRotation() * 180 / M_PI); |
281 | |
|
282 | 0 | painter->startTextObject(textbox); |
283 | |
|
284 | 0 | uint16_t paraStart = 0; |
285 | 0 | uint16_t paraEnd = 0; |
286 | 0 | uint16_t paraLength = 0; |
287 | |
|
288 | 0 | std::vector<PMDParaProperties> paraProperties = shape.getParaProperties(); |
289 | |
|
290 | 0 | for (auto ¶Property : paraProperties) |
291 | 0 | { |
292 | |
|
293 | 0 | paraLength = paraProperty.m_length; |
294 | 0 | paraEnd = paraStart + paraLength - 1; |
295 | |
|
296 | 0 | librevenge::RVNGPropertyList paraProps; |
297 | |
|
298 | 0 | switch (paraProperty.m_align) |
299 | 0 | { |
300 | 0 | case 1: |
301 | 0 | paraProps.insert("fo:text-align", "right"); |
302 | 0 | break; |
303 | 0 | case 2: |
304 | 0 | paraProps.insert("fo:text-align", "center"); |
305 | 0 | break; |
306 | 0 | case 3: |
307 | 0 | paraProps.insert("fo:text-align", "justify"); |
308 | 0 | break; |
309 | 0 | case 4: // force-justify |
310 | | // Strictly speaking, this is not equivalent to the real force-justify |
311 | | // layout. But it is the best approximation ODF can do. |
312 | 0 | paraProps.insert("fo:text-align", "justify"); |
313 | 0 | paraProps.insert("fo:text-align-last", "justify"); |
314 | 0 | break; |
315 | 0 | case 0: |
316 | 0 | default: |
317 | 0 | paraProps.insert("fo:text-align", "left"); |
318 | 0 | break; |
319 | 0 | } |
320 | | |
321 | 0 | if (paraProperty.m_afterIndent != 0) |
322 | 0 | { |
323 | 0 | paraProps.insert("fo:margin-bottom", (double)paraProperty.m_afterIndent/SHAPE_UNITS_PER_INCH,librevenge::RVNG_INCH); |
324 | 0 | } |
325 | 0 | if (paraProperty.m_beforeIndent != 0) |
326 | 0 | { |
327 | 0 | paraProps.insert("fo:margin-top", (double)paraProperty.m_beforeIndent/SHAPE_UNITS_PER_INCH,librevenge::RVNG_INCH); |
328 | 0 | } |
329 | 0 | if (paraProperty.m_firstIndent != 0) |
330 | 0 | { |
331 | 0 | paraProps.insert("fo:text-indent", (double)paraProperty.m_firstIndent/SHAPE_UNITS_PER_INCH,librevenge::RVNG_INCH); |
332 | 0 | } |
333 | 0 | if (paraProperty.m_leftIndent != 0) |
334 | 0 | { |
335 | 0 | paraProps.insert("fo:margin-left", (double)paraProperty.m_leftIndent/SHAPE_UNITS_PER_INCH,librevenge::RVNG_INCH); |
336 | 0 | } |
337 | 0 | if (paraProperty.m_rightIndent != 0) |
338 | 0 | { |
339 | 0 | paraProps.insert("fo:margin-right", (double)paraProperty.m_rightIndent/SHAPE_UNITS_PER_INCH,librevenge::RVNG_INCH); |
340 | 0 | } |
341 | |
|
342 | 0 | paraProps.insert("fo:orphans", int16_t(paraProperty.m_orphans)); |
343 | 0 | paraProps.insert("fo:widows", int16_t(paraProperty.m_widows)); |
344 | 0 | paraProps.insert("fo:keep-together", paraProperty.m_keepTogether ? "always" : "auto"); |
345 | 0 | paraProps.insert("fo:keep-with-next", paraProperty.m_keepWithNext > 0 ? "always" : "auto"); |
346 | |
|
347 | 0 | paraProps.insert("fo:hyphenate", paraProperty.m_hyphenate); |
348 | 0 | if (paraProperty.m_hyphenate) |
349 | 0 | { |
350 | 0 | if (paraProperty.m_hyphensCount > 0) |
351 | 0 | paraProps.insert("fo:hyphenation-ladder-count", int16_t(paraProperty.m_hyphensCount)); |
352 | 0 | else |
353 | 0 | paraProps.insert("fo:hyphenation-ladder-count", "no-limit"); |
354 | 0 | } |
355 | |
|
356 | 0 | if (paraProperty.m_ruleAbove) |
357 | 0 | writeBorder(paraProps, "fo:border-top", get(paraProperty.m_ruleAbove), m_color); |
358 | 0 | if (paraProperty.m_ruleBelow) |
359 | 0 | writeBorder(paraProps, "fo:border-bottom", get(paraProperty.m_ruleBelow), m_color); |
360 | |
|
361 | 0 | painter->openParagraph(paraProps); |
362 | 0 | PMD_DEBUG_MSG(("\n\nPara Start is %d \n",paraStart)); |
363 | 0 | PMD_DEBUG_MSG(("Para End is %d \n\n",paraEnd)); |
364 | | |
365 | | //charProps.insert("style:font-name", "Ubuntu"); |
366 | |
|
367 | 0 | std::string tempText = shape.getText(); |
368 | 0 | std::vector<PMDCharProperties> charProperties = shape.getCharProperties(); |
369 | |
|
370 | 0 | uint16_t charStart = 0; |
371 | 0 | uint16_t charEnd = 0; |
372 | 0 | uint16_t charLength = 0; |
373 | |
|
374 | 0 | for (auto &charProperty : charProperties) |
375 | 0 | { |
376 | 0 | charLength = charProperty.m_length; |
377 | 0 | uint16_t charEndTemp = charStart + charLength -1; |
378 | |
|
379 | 0 | if (paraStart > charStart) |
380 | 0 | charStart = paraStart; |
381 | |
|
382 | 0 | if (charEndTemp > paraEnd) |
383 | 0 | charEnd = paraEnd; |
384 | 0 | else |
385 | 0 | charEnd = charEndTemp; |
386 | |
|
387 | 0 | if (charStart <= charEnd && paraStart <= charEndTemp) |
388 | 0 | { |
389 | 0 | PMD_DEBUG_MSG(("Start is %d \n",charStart)); |
390 | 0 | PMD_DEBUG_MSG(("End is %d \n",charEnd)); |
391 | |
|
392 | 0 | librevenge::RVNGPropertyList charProps; |
393 | 0 | charProps.insert("fo:font-size",(double)charProperty.m_fontSize/10,librevenge::RVNG_POINT); |
394 | |
|
395 | 0 | if (charProperty.m_fontFace < m_font.size()) |
396 | 0 | { |
397 | 0 | PMDFont tempFont = m_font[charProperty.m_fontFace]; |
398 | 0 | std::string tempFontString = tempFont.m_fontName; |
399 | 0 | charProps.insert("style:font-name", tempFontString.c_str()); |
400 | 0 | } |
401 | 0 | else |
402 | 0 | { |
403 | 0 | PMD_DEBUG_MSG(("Font Not Available")); |
404 | 0 | } |
405 | |
|
406 | 0 | if (charProperty.m_fontColor < m_color.size()) |
407 | 0 | { |
408 | 0 | PMDColor tempColor = m_color[charProperty.m_fontColor]; |
409 | 0 | double charTint = (double)charProperty.m_tint/100; |
410 | 0 | double temp_bgcolor = (1 - charTint) * 255; |
411 | 0 | librevenge::RVNGString tempColorString; |
412 | 0 | tempColorString.sprintf("#%.2x%.2x%.2x",(uint16_t)(tempColor.m_red * charTint + temp_bgcolor),(uint16_t)(tempColor.m_green * charTint + temp_bgcolor),(uint16_t)(tempColor.m_blue * charTint + temp_bgcolor)); |
413 | 0 | charProps.insert("fo:color", tempColorString); |
414 | 0 | } |
415 | 0 | else |
416 | 0 | { |
417 | 0 | PMD_DEBUG_MSG(("Color Not Available")); |
418 | 0 | } |
419 | |
|
420 | 0 | if (charProperty.m_bold) |
421 | 0 | charProps.insert("fo:font-weight", "bold"); |
422 | 0 | if (charProperty.m_italic) |
423 | 0 | charProps.insert("fo:font-style", "italic"); |
424 | 0 | if (charProperty.m_underline) |
425 | 0 | charProps.insert("style:text-underline-type", "single"); |
426 | 0 | if (charProperty.m_outline) |
427 | 0 | charProps.insert("style:text-outline", true); |
428 | 0 | if (charProperty.m_shadow) |
429 | 0 | charProps.insert("fo:text-shadow", "1pt 1pt"); |
430 | |
|
431 | 0 | if (charProperty.m_strike) |
432 | 0 | charProps.insert("style:text-line-through-style","solid"); |
433 | 0 | if (charProperty.m_super || charProperty.m_sub) |
434 | 0 | { |
435 | 0 | const int32_t intPos = charProperty.m_sub ? -int32_t(charProperty.m_subPos) : int32_t(charProperty.m_superPos); |
436 | 0 | librevenge::RVNGString pos; |
437 | 0 | pos.sprintf("%.1f%% %.1f%%", intPos / 10.0, charProperty.m_superSubSize / 10.0); |
438 | 0 | charProps.insert("style:text-position", pos); |
439 | 0 | } |
440 | |
|
441 | 0 | if (charProperty.m_smallCaps) |
442 | 0 | charProps.insert("fo:font-variant","small-caps"); |
443 | 0 | if (charProperty.m_allCaps) |
444 | 0 | charProps.insert("fo:text-transform", "uppercase"); |
445 | |
|
446 | 0 | if (charProperty.m_kerning != 0) |
447 | 0 | { |
448 | 0 | charProps.insert("style:letter-kerning","true"); |
449 | 0 | charProps.insert("fo:letter-spacing",((double)charProperty.m_kerning/1000)*EM2PT,librevenge::RVNG_POINT); |
450 | 0 | } |
451 | | |
452 | |
|
453 | 0 | painter->openSpan(charProps); |
454 | 0 | writeTextSpan(tempText, charStart, charEnd, painter); |
455 | 0 | painter->closeSpan(); |
456 | 0 | } |
457 | |
|
458 | 0 | charStart = charEnd + 1; |
459 | 0 | } |
460 | |
|
461 | 0 | painter->closeParagraph(); |
462 | |
|
463 | 0 | paraStart = paraEnd + 1; |
464 | |
|
465 | 0 | } |
466 | 0 | painter->endTextObject(); |
467 | 0 | } |
468 | 0 | else if (shape.shapeType() == SHAPE_TYPE_BITMAP) |
469 | 0 | { |
470 | 0 | librevenge::RVNGPropertyList props; |
471 | 0 | props.insert("svg:x", shape.getPoint(0).m_x,librevenge::RVNG_INCH); |
472 | 0 | props.insert("svg:y", shape.getPoint(0).m_y,librevenge::RVNG_INCH); |
473 | 0 | props.insert("svg:width", shape.getWidth(),librevenge::RVNG_INCH); |
474 | 0 | props.insert("svg:height", shape.getHeight(),librevenge::RVNG_INCH); |
475 | |
|
476 | 0 | if (shape.getRotation() != 0.0) |
477 | 0 | props.insert("librevenge:rotate", shape.getRotation() * 180 / M_PI, librevenge::RVNG_GENERIC); |
478 | |
|
479 | 0 | props.insert("librevenge:mime-type", "image/tiff"); |
480 | 0 | props.insert("office:binary-data", shape.getBitmap()); |
481 | 0 | painter->drawGraphicObject(props); |
482 | 0 | } |
483 | 0 | else |
484 | 0 | { |
485 | 0 | double cx = shape.getPoint(0).m_x; |
486 | 0 | double cy = shape.getPoint(0).m_y; |
487 | 0 | double rx = shape.getPoint(1).m_x; |
488 | 0 | double ry = shape.getPoint(1).m_y; |
489 | |
|
490 | 0 | double rotation = shape.getRotation(); |
491 | | #ifdef DEBUG |
492 | | double skew = shape.getSkew(); |
493 | | #endif |
494 | |
|
495 | 0 | PMD_DEBUG_MSG(("\n\nCx and Cy are %f , %f \n",cx,cy)); |
496 | 0 | PMD_DEBUG_MSG(("Rx and Ry are %f , %f \n",rx,ry)); |
497 | 0 | PMD_DEBUG_MSG(("Rotation is %f \n",rotation)); |
498 | 0 | PMD_DEBUG_MSG(("Skew is %f \n",skew)); |
499 | 0 | librevenge::RVNGPropertyList propList; |
500 | |
|
501 | 0 | if (false) |
502 | 0 | { |
503 | 0 | propList.insert("svg:rx",rx); |
504 | 0 | propList.insert("svg:ry",ry); |
505 | 0 | propList.insert("svg:cx",cx); |
506 | 0 | propList.insert("svg:cy",cy); |
507 | 0 | painter->drawEllipse(propList); |
508 | 0 | } |
509 | 0 | else |
510 | 0 | { |
511 | 0 | double sx = cx - rx*cos(rotation); |
512 | 0 | double sy = cy - rx*sin(rotation); |
513 | |
|
514 | 0 | double ex = cx + rx*cos(rotation); |
515 | 0 | double ey = cy + rx*sin(rotation); |
516 | | |
517 | | //if ((rotation == 0 || rotation < skew) && skew != 0) |
518 | | //rotation += (ry*skew/rx)/2; |
519 | |
|
520 | 0 | librevenge::RVNGPropertyListVector vec; |
521 | 0 | librevenge::RVNGPropertyList node; |
522 | |
|
523 | 0 | node.insert("librevenge:path-action", "M"); |
524 | 0 | node.insert("svg:x", sx); |
525 | 0 | node.insert("svg:y", sy); |
526 | 0 | vec.append(node); |
527 | |
|
528 | 0 | node.clear(); |
529 | 0 | node.insert("librevenge:path-action", "A"); |
530 | 0 | node.insert("svg:rx", rx); |
531 | 0 | node.insert("svg:ry", ry); |
532 | 0 | node.insert("librevenge:rotate", rotation * 180 / M_PI, librevenge::RVNG_GENERIC); |
533 | 0 | node.insert("librevenge:large-arc", false); |
534 | 0 | node.insert("librevenge:sweep", false); |
535 | 0 | node.insert("svg:x", ex); |
536 | 0 | node.insert("svg:y", ey); |
537 | 0 | vec.append(node); |
538 | |
|
539 | 0 | node.clear(); |
540 | 0 | node.insert("librevenge:path-action", "A"); |
541 | 0 | node.insert("svg:rx", rx); |
542 | 0 | node.insert("svg:ry", ry); |
543 | 0 | node.insert("librevenge:rotate", rotation * 180 / M_PI, librevenge::RVNG_GENERIC); |
544 | 0 | node.insert("librevenge:large-arc", true); |
545 | 0 | node.insert("librevenge:sweep", false); |
546 | 0 | node.insert("svg:x", sx); |
547 | 0 | node.insert("svg:y", sy); |
548 | 0 | vec.append(node); |
549 | |
|
550 | 0 | node.clear(); |
551 | 0 | node.insert("librevenge:path-action", "Z"); |
552 | 0 | vec.append(node); |
553 | |
|
554 | 0 | propList.insert("svg:d",vec); |
555 | |
|
556 | 0 | PMDFillProperties fillProps = shape.getFillProperties(); |
557 | 0 | PMDStrokeProperties strokeProps = shape.getStrokeProperties(); |
558 | |
|
559 | 0 | switch (fillProps.m_fillType) |
560 | 0 | { |
561 | 0 | case FILL_SOLID: |
562 | 0 | propList.insert("draw:fill", "solid"); |
563 | 0 | break; |
564 | 0 | case FILL_NONE: |
565 | 0 | propList.insert("draw:fill", "none"); |
566 | 0 | break; |
567 | 0 | default: |
568 | 0 | propList.insert("draw:fill", "none"); |
569 | 0 | } |
570 | | |
571 | 0 | if (fillProps.m_fillColor < m_color.size()) |
572 | 0 | { |
573 | 0 | PMDColor tempFillColor = m_color[fillProps.m_fillColor]; |
574 | 0 | librevenge::RVNGString tempFillColorString; |
575 | 0 | tempFillColorString.sprintf("#%.2x%.2x%.2x", tempFillColor.m_red,tempFillColor.m_green,tempFillColor.m_blue); |
576 | 0 | propList.insert("draw:fill-color", tempFillColorString); |
577 | 0 | } |
578 | 0 | else |
579 | 0 | { |
580 | 0 | PMD_DEBUG_MSG(("Fill Color Not Available")); |
581 | 0 | } |
582 | |
|
583 | 0 | if (fillProps.m_fillColor == 0) |
584 | 0 | propList.insert("draw:opacity", 0); |
585 | 0 | else |
586 | 0 | propList.insert("draw:opacity", fillProps.m_fillTint); |
587 | |
|
588 | 0 | switch (strokeProps.m_strokeType) |
589 | 0 | { |
590 | 0 | case STROKE_NORMAL: |
591 | 0 | propList.insert("draw:stroke", "solid"); |
592 | 0 | break; |
593 | 0 | case STROKE_DASHED: |
594 | 0 | propList.insert("draw:stroke","dash"); |
595 | 0 | break; |
596 | 0 | default: |
597 | 0 | propList.insert("draw:stroke", "solid"); |
598 | 0 | } |
599 | | |
600 | 0 | propList.insert("svg:stroke-width", (double)strokeProps.m_strokeWidth/5.0,librevenge::RVNG_POINT); |
601 | |
|
602 | 0 | if (strokeProps.m_strokeColor < m_color.size()) |
603 | 0 | { |
604 | 0 | PMDColor tempStrokeColor = m_color[strokeProps.m_strokeColor]; |
605 | 0 | librevenge::RVNGString tempStrokeColorString; |
606 | 0 | tempStrokeColorString.sprintf("#%.2x%.2x%.2x", tempStrokeColor.m_red,tempStrokeColor.m_green,tempStrokeColor.m_blue); |
607 | 0 | propList.insert("svg:stroke-color", tempStrokeColorString); |
608 | 0 | } |
609 | 0 | else |
610 | 0 | { |
611 | 0 | PMD_DEBUG_MSG(("Stroke Color Not Available")); |
612 | 0 | } |
613 | |
|
614 | 0 | propList.insert("svg:stroke-opacity", (double)strokeProps.m_strokeTint/100.0,librevenge::RVNG_PERCENT); |
615 | |
|
616 | 0 | painter->drawPath(propList); |
617 | 0 | } |
618 | 0 | } |
619 | 0 | } |
620 | | |
621 | | |
622 | | |
623 | | void PMDCollector::writePage(const PMDPage & /*page*/, |
624 | | librevenge::RVNGDrawingInterface *painter, |
625 | | const std::vector<std::shared_ptr<const OutputShape> > &outputShapes) const |
626 | 0 | { |
627 | 0 | librevenge::RVNGPropertyList pageProps; |
628 | 0 | if (m_pageWidth.is_initialized()) |
629 | 0 | { |
630 | 0 | double widthInInches = m_pageWidth.get().toInches(); |
631 | 0 | pageProps.insert("svg:width", widthInInches); |
632 | 0 | } |
633 | 0 | if (m_pageHeight.is_initialized()) |
634 | 0 | { |
635 | 0 | double heightInInches = m_pageHeight.get().toInches(); |
636 | 0 | pageProps.insert("svg:height", heightInInches); |
637 | 0 | } |
638 | 0 | painter->startPage(pageProps); |
639 | 0 | for (const auto &outputShape : outputShapes) |
640 | 0 | { |
641 | 0 | paintShape(*outputShape, painter); |
642 | 0 | } |
643 | 0 | painter->endPage(); |
644 | 0 | } |
645 | | |
646 | | void PMDCollector::fillOutputShapesByPage_TwoSided(PageShapesList_t &pageShapes) const |
647 | 0 | { |
648 | 0 | pageShapes.assign(m_pages.size() * 2 - 1, PageShapes_t()); // the first "page" only has right side |
649 | |
|
650 | 0 | double centerToEdge_x = m_pageWidth.get_value_or(0).toInches() / 2; |
651 | 0 | double centerToEdge_y = m_pageHeight.get_value_or(0).toInches() / 2; |
652 | 0 | InchPoint translateForLeftPage(centerToEdge_x * 2, centerToEdge_y); |
653 | 0 | InchPoint translateForRightPage(0, centerToEdge_y); |
654 | |
|
655 | 0 | for (size_t i = 0; i < m_pages.size(); ++i) |
656 | 0 | { |
657 | 0 | const bool leftPageExists = (i > 0); |
658 | |
|
659 | 0 | const PMDPage &page = m_pages[i]; |
660 | 0 | for (unsigned j = 0; j < page.numShapes(); ++j) |
661 | 0 | { |
662 | 0 | std::shared_ptr<const OutputShape> right = newOutputShape(page.getShape(j), translateForRightPage); |
663 | 0 | if (right->getBoundingBox().second.m_x >= 0) |
664 | 0 | { |
665 | 0 | pageShapes[i].push_back(right); |
666 | 0 | continue; |
667 | 0 | } |
668 | 0 | if (leftPageExists) |
669 | 0 | { |
670 | 0 | std::shared_ptr<const OutputShape> left = newOutputShape(page.getShape(j), translateForLeftPage); |
671 | 0 | if (left->getBoundingBox().first.m_x <= centerToEdge_x * 2) |
672 | 0 | { |
673 | 0 | pageShapes[i - 1].push_back(left); |
674 | 0 | } |
675 | 0 | } |
676 | 0 | } |
677 | 0 | } |
678 | |
|
679 | 0 | if ((pageShapes.size() > 1) && pageShapes.back().empty()) // the last "page" only has left side |
680 | 0 | pageShapes.pop_back(); |
681 | 0 | } |
682 | | |
683 | | void PMDCollector::fillOutputShapesByPage_OneSided(PageShapesList_t &pageShapes) const |
684 | 0 | { |
685 | 0 | pageShapes.reserve(m_pages.size()); |
686 | 0 | pageShapes.assign(m_pages.size(), PageShapes_t()); |
687 | |
|
688 | 0 | double centerToEdge_x = m_pageWidth.get().toInches() / 2; |
689 | 0 | double centerToEdge_y = m_pageHeight.get().toInches() / 2; |
690 | 0 | InchPoint translateShapes(centerToEdge_x, centerToEdge_y); |
691 | |
|
692 | 0 | for (size_t i = 0; i < m_pages.size(); ++i) |
693 | 0 | { |
694 | 0 | const PMDPage &page = m_pages[i]; |
695 | 0 | for (unsigned j = 0; j < page.numShapes(); ++j) |
696 | 0 | { |
697 | 0 | pageShapes[i].push_back(newOutputShape(page.getShape(j), translateShapes)); |
698 | 0 | } |
699 | 0 | } |
700 | 0 | } |
701 | | |
702 | | void PMDCollector::fillOutputShapesByPage(PageShapesList_t &pageShapes) const |
703 | 0 | { |
704 | 0 | if (m_doubleSided) |
705 | 0 | fillOutputShapesByPage_TwoSided(pageShapes); |
706 | 0 | else |
707 | 0 | fillOutputShapesByPage_OneSided(pageShapes); |
708 | 0 | } |
709 | | |
710 | | /* Output functions */ |
711 | | void PMDCollector::draw(librevenge::RVNGDrawingInterface *painter) const |
712 | 0 | { |
713 | 0 | painter->startDocument(librevenge::RVNGPropertyList()); |
714 | |
|
715 | 0 | PageShapesList_t shapesByPage; |
716 | 0 | fillOutputShapesByPage(shapesByPage); |
717 | 0 | for (size_t i = 0; i < m_pages.size(); ++i) |
718 | 0 | { |
719 | 0 | PageShapes_t shapes = shapesByPage[i]; |
720 | 0 | writePage(m_pages[i], painter, shapes); |
721 | 0 | } |
722 | 0 | painter->endDocument(); |
723 | 0 | } |
724 | | |
725 | | } |
726 | | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ |