/src/libqxp/src/lib/QXPContentCollector.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 libqxp 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 "QXPContentCollector.h" |
11 | | |
12 | | #include <algorithm> |
13 | | #include <utility> |
14 | | #include <iterator> |
15 | | |
16 | | #include <boost/range/adaptor/reversed.hpp> |
17 | | #include <boost/variant.hpp> |
18 | | |
19 | | namespace libqxp |
20 | | { |
21 | | |
22 | | using librevenge::RVNG_POINT; |
23 | | using librevenge::RVNG_PERCENT; |
24 | | using librevenge::RVNGPropertyList; |
25 | | using librevenge::RVNGPropertyListVector; |
26 | | using librevenge::RVNGString; |
27 | | using std::vector; |
28 | | using std::string; |
29 | | |
30 | | namespace |
31 | | { |
32 | | |
33 | | void writeBorder(RVNGPropertyList &propList, const char *const name, const double width, const Color &color, const LineStyle *lineStyle) |
34 | 9.55k | { |
35 | 9.55k | RVNGString border; |
36 | | |
37 | 9.55k | border.sprintf("%fpt", width); |
38 | | |
39 | 9.55k | border.append(" "); |
40 | 9.55k | if (lineStyle) |
41 | 6.02k | { |
42 | 6.02k | if (lineStyle->isStripe) |
43 | 130 | { |
44 | 130 | border.append("double"); |
45 | 130 | } |
46 | 6.02k | if (lineStyle->segmentLengths.size() == 2) |
47 | 1.11k | { |
48 | 1.11k | border.append("dotted"); |
49 | 1.11k | } |
50 | 6.02k | if (lineStyle->segmentLengths.size() > 2) |
51 | 348 | { |
52 | 348 | border.append("dashed"); |
53 | 348 | } |
54 | 5.67k | else |
55 | 5.67k | { |
56 | 5.67k | border.append("solid"); |
57 | 5.67k | } |
58 | 6.02k | } |
59 | 3.52k | else |
60 | 3.52k | { |
61 | 3.52k | border.append("solid"); |
62 | 3.52k | } |
63 | | |
64 | 9.55k | border.append(" "); |
65 | 9.55k | border.append(color.toString()); |
66 | | |
67 | 9.55k | propList.insert(name, border); |
68 | 9.55k | } |
69 | | |
70 | | void writeBorder(RVNGPropertyList &propList, const char *const name, const std::shared_ptr<ParagraphRule> &rule) |
71 | 9.55k | { |
72 | 9.55k | writeBorder(propList, name, rule->width, rule->color, rule->lineStyle); |
73 | 9.55k | } |
74 | | |
75 | | RVNGPropertyListVector createLinePath(const vector<Point> &points, bool closed) |
76 | 148k | { |
77 | 148k | RVNGPropertyListVector path; |
78 | | |
79 | 681k | for (size_t i = 0; i < points.size(); i++) |
80 | 533k | { |
81 | 533k | librevenge::RVNGPropertyList pathPart; |
82 | 533k | pathPart.insert("librevenge:path-action", i == 0 ? "M" : "L"); |
83 | 533k | pathPart.insert("svg:x", points[i].x, RVNG_POINT); |
84 | 533k | pathPart.insert("svg:y", points[i].y, RVNG_POINT); |
85 | | |
86 | 533k | path.append(pathPart); |
87 | 533k | } |
88 | | |
89 | 148k | if (closed) |
90 | 80.1k | { |
91 | 80.1k | librevenge::RVNGPropertyList pathPart; |
92 | 80.1k | pathPart.insert("librevenge:path-action", "Z"); |
93 | | |
94 | 80.1k | path.append(pathPart); |
95 | 80.1k | } |
96 | | |
97 | 148k | return path; |
98 | 148k | } |
99 | | |
100 | | void addBezierPath(RVNGPropertyListVector &path, const vector<Point> &points, bool canBeClosed) |
101 | 16.3k | { |
102 | 16.3k | if (points.size() < 6) |
103 | 15.2k | { |
104 | 15.2k | QXP_DEBUG_MSG(("Not enough bezier points, %lu\n", points.size())); |
105 | 15.2k | return; |
106 | 15.2k | } |
107 | | |
108 | 1.05k | { |
109 | 1.05k | RVNGPropertyList pathPart; |
110 | 1.05k | pathPart.insert("librevenge:path-action", "M"); |
111 | 1.05k | pathPart.insert("svg:x", points[1].x, RVNG_POINT); |
112 | 1.05k | pathPart.insert("svg:y", points[1].y, RVNG_POINT); |
113 | | |
114 | 1.05k | path.append(pathPart); |
115 | 1.05k | } |
116 | | |
117 | 1.05k | { |
118 | 1.05k | RVNGPropertyList pathPart; |
119 | 1.05k | pathPart.insert("librevenge:path-action", "C"); |
120 | 1.05k | pathPart.insert("svg:x1", points[2].x, RVNG_POINT); |
121 | 1.05k | pathPart.insert("svg:y1", points[2].y, RVNG_POINT); |
122 | 1.05k | pathPart.insert("svg:x2", points[3].x, RVNG_POINT); |
123 | 1.05k | pathPart.insert("svg:y2", points[3].y, RVNG_POINT); |
124 | 1.05k | pathPart.insert("svg:x", points[4].x, RVNG_POINT); |
125 | 1.05k | pathPart.insert("svg:y", points[4].y, RVNG_POINT); |
126 | | |
127 | 1.05k | path.append(pathPart); |
128 | 1.05k | } |
129 | | |
130 | 44.3k | for (unsigned i = 6; i < points.size(); i += 3) |
131 | 43.7k | { |
132 | 43.7k | if (i + 1 >= points.size()) |
133 | 461 | { |
134 | 461 | QXP_DEBUG_MSG(("Unexpected end of bezier points, %u / %lu\n", i, points.size())); |
135 | 461 | break; |
136 | 461 | } |
137 | 43.2k | RVNGPropertyList pathPart; |
138 | 43.2k | pathPart.insert("librevenge:path-action", "S"); |
139 | 43.2k | pathPart.insert("svg:x1", points[i].x, RVNG_POINT); |
140 | 43.2k | pathPart.insert("svg:y1", points[i].y, RVNG_POINT); |
141 | 43.2k | pathPart.insert("svg:x2", points[3].x, RVNG_POINT); |
142 | 43.2k | pathPart.insert("svg:y2", points[3].y, RVNG_POINT); |
143 | 43.2k | pathPart.insert("svg:x", points[i + 1].x, RVNG_POINT); |
144 | 43.2k | pathPart.insert("svg:y", points[i + 1].y, RVNG_POINT); |
145 | | |
146 | 43.2k | path.append(pathPart); |
147 | 43.2k | } |
148 | | |
149 | 1.05k | if (canBeClosed && points[1] == points[points.size() - 2]) |
150 | 345 | { |
151 | 345 | RVNGPropertyList pathPart; |
152 | 345 | pathPart.insert("librevenge:path-action", "Z"); |
153 | | |
154 | 345 | path.append(pathPart); |
155 | 345 | } |
156 | 1.05k | } |
157 | | |
158 | | void writeZIndex(librevenge::RVNGPropertyList &propList, unsigned value) |
159 | 230k | { |
160 | 230k | propList.insert("draw:z-index", static_cast<int>(value)); |
161 | 230k | } |
162 | | |
163 | | void flushText(librevenge::RVNGDrawingInterface *painter, string &text) |
164 | 31.2k | { |
165 | 31.2k | if (!text.empty()) |
166 | 8.72k | { |
167 | 8.72k | painter->insertText(RVNGString(text.c_str())); |
168 | 8.72k | text.clear(); |
169 | 8.72k | } |
170 | 31.2k | } |
171 | | |
172 | | void insertText(librevenge::RVNGDrawingInterface *painter, const RVNGString &text) |
173 | 24.9k | { |
174 | | // TODO: need to remember wasSpace on span change? |
175 | 24.9k | bool wasSpace = false; |
176 | 24.9k | std::string curText; |
177 | | |
178 | 24.9k | RVNGString::Iter iter(text); |
179 | 24.9k | iter.rewind(); |
180 | 111k | while (iter.next()) |
181 | 86.3k | { |
182 | 86.3k | const char *const utf8Char = iter(); |
183 | 86.3k | switch (utf8Char[0]) |
184 | 86.3k | { |
185 | 405 | case '\r': |
186 | 405 | wasSpace = false; |
187 | 405 | break; |
188 | 1.54k | case '\n': |
189 | 1.54k | wasSpace = false; |
190 | 1.54k | flushText(painter, curText); |
191 | 1.54k | painter->insertLineBreak(); |
192 | 1.54k | break; |
193 | 1.44k | case '\t': |
194 | 1.44k | wasSpace = false; |
195 | 1.44k | flushText(painter, curText); |
196 | 1.44k | painter->insertTab(); |
197 | 1.44k | break; |
198 | 5.54k | case ' ': |
199 | 5.54k | if (wasSpace) |
200 | 3.29k | { |
201 | 3.29k | flushText(painter, curText); |
202 | 3.29k | painter->insertSpace(); |
203 | 3.29k | } |
204 | 2.25k | else |
205 | 2.25k | { |
206 | 2.25k | wasSpace = true; |
207 | 2.25k | curText.push_back(' '); |
208 | 2.25k | } |
209 | 5.54k | break; |
210 | 77.4k | default: |
211 | 77.4k | wasSpace = false; |
212 | 77.4k | curText.append(utf8Char); |
213 | 77.4k | break; |
214 | 86.3k | } |
215 | 86.3k | } |
216 | | |
217 | 24.9k | flushText(painter, curText); |
218 | 24.9k | } |
219 | | |
220 | | void writeArrow(RVNGPropertyList &propList, const char *const name, const Arrow &arrow, double width) |
221 | 9.09k | { |
222 | 9.09k | librevenge::RVNGString propName; |
223 | | |
224 | 9.09k | propName.sprintf("draw:marker-%s-viewbox", name); |
225 | 9.09k | propList.insert(propName.cstr(), arrow.viewbox.c_str()); |
226 | 9.09k | propName.sprintf("draw:marker-%s-path", name); |
227 | 9.09k | propList.insert(propName.cstr(), arrow.path.c_str()); |
228 | 9.09k | propName.sprintf("draw:marker-%s-width", name); |
229 | 9.09k | propList.insert(propName.cstr(), width * arrow.scale, RVNG_POINT); |
230 | 9.09k | } |
231 | | |
232 | | class FillWriter : public boost::static_visitor<void> |
233 | | { |
234 | | public: |
235 | | explicit FillWriter(RVNGPropertyList &propList) |
236 | 72.1k | : m_propList(propList) |
237 | 72.1k | { } |
238 | | |
239 | | void operator()(const Color &color) |
240 | 53.2k | { |
241 | 53.2k | m_propList.insert("draw:fill", "solid"); |
242 | 53.2k | m_propList.insert("draw:fill-color", color.toString()); |
243 | 53.2k | } |
244 | | |
245 | | void operator()(const Gradient &gradient) |
246 | 18.8k | { |
247 | 18.8k | m_propList.insert("draw:fill", "gradient"); |
248 | 18.8k | m_propList.insert("draw:start-color", gradient.color1.toString()); |
249 | 18.8k | m_propList.insert("draw:end-color", gradient.color2.toString()); |
250 | | |
251 | 18.8k | switch (gradient.type) |
252 | 18.8k | { |
253 | 19 | default: |
254 | 18.6k | case GradientType::LINEAR: |
255 | 18.6k | m_propList.insert("draw:style", "linear"); |
256 | 18.6k | m_propList.insert("draw:angle", int(normalizeDegAngle(gradient.angle + 90))); |
257 | 18.6k | break; |
258 | 133 | case GradientType::CIRCULAR: |
259 | 159 | case GradientType::FULLCIRCULAR: |
260 | 159 | m_propList.insert("draw:style", "radial"); |
261 | 159 | m_propList.insert("draw:cx", 0.5, librevenge::RVNG_PERCENT); |
262 | 159 | m_propList.insert("draw:cy", 0.5, librevenge::RVNG_PERCENT); |
263 | 159 | m_propList.insert("draw:border", GradientType::CIRCULAR == gradient.type ? 0.25 : 0.0, librevenge::RVNG_PERCENT); |
264 | 159 | m_propList.insert("draw:angle", int(normalizeDegAngle(gradient.angle))); |
265 | 159 | break; |
266 | 13 | case GradientType::RECTANGULAR: |
267 | 27 | case GradientType::DIAMOND: |
268 | 27 | m_propList.insert("draw:style", "square"); |
269 | 27 | m_propList.insert("draw:cx", 0.5, librevenge::RVNG_PERCENT); |
270 | 27 | m_propList.insert("draw:cy", 0.5, librevenge::RVNG_PERCENT); |
271 | 27 | m_propList.insert("draw:border", 0.0, librevenge::RVNG_PERCENT); |
272 | 27 | m_propList.insert("draw:angle", int(normalizeDegAngle(gradient.angle))); |
273 | 27 | break; |
274 | 18.8k | } |
275 | 18.8k | } |
276 | | |
277 | | private: |
278 | | librevenge::RVNGPropertyList &m_propList; |
279 | | }; |
280 | | |
281 | | void writeTextPosition(RVNGPropertyList &propList, const double offset, const double scale = 1.0) |
282 | 16.8k | { |
283 | 16.8k | RVNGString pos; |
284 | 16.8k | pos.sprintf("%f%% %f%%", 100 * offset, 100 * scale); |
285 | 16.8k | propList.insert("style:text-position", pos); |
286 | 16.8k | } |
287 | | |
288 | | } |
289 | | |
290 | | QXPContentCollector::QXPContentCollector(librevenge::RVNGDrawingInterface *painter) |
291 | 5.99k | : m_painter(painter) |
292 | 5.99k | , m_isDocumentStarted(false) |
293 | 5.99k | , m_isCollectingFacingPage(false) |
294 | 5.99k | , m_currentObjectIndex(0) |
295 | 5.99k | , m_unprocessedPages() |
296 | 5.99k | , m_linkTextMap() |
297 | 5.99k | , m_linkIndexedTextObjectsMap() |
298 | 5.99k | , m_docProps() |
299 | 5.99k | { |
300 | 5.99k | } |
301 | | |
302 | | QXPContentCollector::~QXPContentCollector() |
303 | 5.99k | { |
304 | 5.99k | if (m_isDocumentStarted) |
305 | 5.59k | { |
306 | 5.59k | endDocument(); |
307 | 5.59k | } |
308 | 5.99k | } |
309 | | |
310 | | void QXPContentCollector::startDocument() |
311 | 5.99k | { |
312 | 5.99k | if (m_isDocumentStarted) |
313 | 0 | return; |
314 | | |
315 | 5.99k | m_painter->startDocument(RVNGPropertyList()); |
316 | | |
317 | 5.99k | m_isDocumentStarted = true; |
318 | 5.99k | } |
319 | | |
320 | | void QXPContentCollector::endDocument() |
321 | 5.99k | { |
322 | 5.99k | if (!m_isDocumentStarted) |
323 | 0 | return; |
324 | | |
325 | 5.99k | if (!m_unprocessedPages.empty()) |
326 | 3.79k | endPage(); |
327 | | |
328 | 5.99k | if (!m_unprocessedPages.empty()) |
329 | 2.59k | draw(true); |
330 | | |
331 | 5.99k | m_painter->endDocument(); |
332 | | |
333 | 5.99k | m_isDocumentStarted = false; |
334 | 5.99k | } |
335 | | |
336 | | void QXPContentCollector::startPage(const Page &page) |
337 | 7.44k | { |
338 | 7.44k | m_unprocessedPages.push_back(CollectedPage(page.pageSettings[0])); |
339 | 7.44k | if (page.isFacing()) |
340 | 585 | { |
341 | 585 | m_unprocessedPages.push_back(CollectedPage(page.pageSettings[1])); |
342 | 585 | } |
343 | 7.44k | m_isCollectingFacingPage = page.isFacing(); |
344 | 7.44k | m_currentObjectIndex = 0; |
345 | 7.44k | } |
346 | | |
347 | | void QXPContentCollector::endPage() |
348 | 7.57k | { |
349 | 7.57k | if (!m_unprocessedPages.empty()) |
350 | 7.57k | draw(); |
351 | 7.57k | } |
352 | | |
353 | | void QXPContentCollector::collectDocumentProperties(const QXPDocumentProperties &props) |
354 | 5.78k | { |
355 | 5.78k | m_docProps = props; |
356 | 5.78k | } |
357 | | |
358 | | void QXPContentCollector::collectLine(const std::shared_ptr<Line> &line) |
359 | 34.7k | { |
360 | 34.7k | addObject<Line>(line, &QXPContentCollector::drawLine); |
361 | 34.7k | } |
362 | | |
363 | | void QXPContentCollector::collectBox(const std::shared_ptr<Box> &box) |
364 | 10.4k | { |
365 | 10.4k | addObject<Box>(box, &QXPContentCollector::drawBox); |
366 | 10.4k | } |
367 | | |
368 | | void QXPContentCollector::collectTextBox(const std::shared_ptr<TextBox> &textbox) |
369 | 73.8k | { |
370 | 73.8k | addObject<TextBox>(textbox, &QXPContentCollector::drawTextBox); |
371 | | |
372 | 73.8k | if (0 == textbox->linkSettings.linkId) |
373 | 57.1k | { |
374 | 57.1k | QXP_DEBUG_MSG(("Collected textbox with link ID 0")); |
375 | 57.1k | } |
376 | | |
377 | 73.8k | collectTextObject(textbox, getInsertionPage(textbox)); |
378 | 73.8k | } |
379 | | |
380 | | void QXPContentCollector::collectTextPath(const std::shared_ptr<TextPath> &textPath) |
381 | 33.4k | { |
382 | 33.4k | addObject<TextPath>(textPath, &QXPContentCollector::drawTextPath); |
383 | | |
384 | 33.4k | if (0 == textPath->linkSettings.linkId) |
385 | 632 | { |
386 | 632 | QXP_DEBUG_MSG(("Collected text path with link ID 0")); |
387 | 632 | } |
388 | | |
389 | 33.4k | collectTextObject(textPath, getInsertionPage(textPath)); |
390 | 33.4k | } |
391 | | |
392 | | void QXPContentCollector::collectGroup(const std::shared_ptr<Group> &group) |
393 | 25.9k | { |
394 | 25.9k | auto collectedObj = addObject<Group>(group, &QXPContentCollector::drawGroup); |
395 | | |
396 | 25.9k | getInsertionPage(group).groups.push_back(collectedObj); |
397 | 25.9k | } |
398 | | |
399 | | void QXPContentCollector::collectText(const std::shared_ptr<Text> &text, const unsigned linkId) |
400 | 14.1k | { |
401 | 14.1k | m_linkTextMap[linkId] = text; |
402 | | |
403 | 14.1k | auto it = m_linkIndexedTextObjectsMap.find(linkId); |
404 | 14.1k | if (it != m_linkIndexedTextObjectsMap.end()) |
405 | 6.14k | { |
406 | 6.14k | for (const auto &indexTextbox : it->second) |
407 | 44.5k | { |
408 | 44.5k | if (!indexTextbox.second->text) |
409 | 3.82k | { |
410 | 3.82k | indexTextbox.second->text = text; |
411 | 3.82k | } |
412 | 44.5k | } |
413 | 6.14k | } |
414 | 14.1k | } |
415 | | |
416 | | QXPContentCollector::CollectedPage &QXPContentCollector::getInsertionPage(const std::shared_ptr<Object> &obj) |
417 | 311k | { |
418 | 311k | if (m_isCollectingFacingPage && obj->boundingBox.left < m_unprocessedPages.back().settings.offset.left) |
419 | 44.0k | { |
420 | 44.0k | return m_unprocessedPages[m_unprocessedPages.size() - 2]; |
421 | 44.0k | } |
422 | 267k | return m_unprocessedPages.back(); |
423 | 311k | } |
424 | | |
425 | | void QXPContentCollector::draw(bool force) |
426 | 10.1k | { |
427 | 10.1k | updateLinkedTexts(); |
428 | | |
429 | 10.1k | if (hasUnfinishedLinkedTexts()) |
430 | 5.42k | { |
431 | 5.42k | if (!force) |
432 | 2.83k | { |
433 | 2.83k | return; |
434 | 2.83k | } |
435 | 2.59k | QXP_DEBUG_MSG(("Drawing with unfinished linked texts\n")); |
436 | 2.59k | } |
437 | | |
438 | 7.33k | for (auto &page : m_unprocessedPages) |
439 | 8.02k | { |
440 | 8.02k | RVNGPropertyList propList; |
441 | 8.02k | propList.insert("svg:width", page.settings.offset.width(), RVNG_POINT); |
442 | 8.02k | propList.insert("svg:height", page.settings.offset.height(), RVNG_POINT); |
443 | 8.02k | m_painter->startPage(propList); |
444 | | |
445 | 8.02k | { |
446 | 8.02k | unsigned i = 0; |
447 | 8.02k | for (auto &obj : boost::adaptors::reverse(page.objects)) |
448 | 178k | { |
449 | 178k | obj.second->setZIndex(i); |
450 | | // we can't just increment by 1 because some objects may need to create several elements (such as box + text) |
451 | | // also we can't just have a counter instead of this field because groups may not be consecutive |
452 | 178k | i += 100; |
453 | 178k | } |
454 | 8.02k | } |
455 | | |
456 | | // handle groups first |
457 | | // afaik groups that are part of other group never go before that group |
458 | 8.02k | for (const auto &group : page.groups) |
459 | 25.9k | { |
460 | 25.9k | group->draw(page); |
461 | 25.9k | } |
462 | | |
463 | 8.02k | for (auto &obj : page.objects) |
464 | 178k | { |
465 | 178k | obj.second->draw(page); |
466 | 178k | } |
467 | | |
468 | 8.02k | m_painter->endPage(); |
469 | 8.02k | } |
470 | | |
471 | 7.33k | m_unprocessedPages.clear(); |
472 | 7.33k | } |
473 | | |
474 | | void QXPContentCollector::collectTextObject(const std::shared_ptr<TextObject> &textObj, CollectedPage &page) |
475 | 107k | { |
476 | 107k | if (textObj->linkSettings.linkedIndex > 0) |
477 | 73.8k | { |
478 | 73.8k | m_linkIndexedTextObjectsMap[textObj->linkSettings.linkId][textObj->linkSettings.linkedIndex] = textObj; |
479 | 73.8k | } |
480 | | |
481 | 107k | if (textObj->isLinked()) |
482 | 85.4k | { |
483 | 85.4k | page.linkedTextObjects.push_back(textObj); |
484 | 85.4k | } |
485 | | |
486 | 107k | if (!textObj->text) |
487 | 73.8k | { |
488 | 73.8k | auto textIt = m_linkTextMap.find(textObj->linkSettings.linkId); |
489 | 73.8k | if (textIt != m_linkTextMap.end()) |
490 | 27.5k | { |
491 | 27.5k | textObj->text = textIt->second; |
492 | 27.5k | } |
493 | 73.8k | } |
494 | 107k | } |
495 | | |
496 | | void QXPContentCollector::updateLinkedTexts() |
497 | 10.1k | { |
498 | 10.1k | for (const auto &page : m_unprocessedPages) |
499 | 12.4k | { |
500 | 12.4k | for (const auto &textObj : page.linkedTextObjects) |
501 | 171k | { |
502 | 171k | if (textObj->linkSettings.nextLinkedIndex > 0 && !textObj->linkSettings.textLength) |
503 | 132k | { |
504 | 132k | const auto textObjectsIt = m_linkIndexedTextObjectsMap.find(textObj->linkSettings.linkId); |
505 | 132k | if (textObjectsIt != m_linkIndexedTextObjectsMap.end()) |
506 | 124k | { |
507 | 124k | const auto &textObjects = textObjectsIt->second; |
508 | 124k | const auto nextTextObjectIt = textObjects.find(textObj->linkSettings.nextLinkedIndex); |
509 | 124k | if (nextTextObjectIt != textObjects.end()) |
510 | 1.13k | { |
511 | 1.13k | textObj->linkSettings.textLength = nextTextObjectIt->second->linkSettings.offsetIntoText - textObj->linkSettings.offsetIntoText; |
512 | 1.13k | } |
513 | 124k | } |
514 | 132k | } |
515 | 171k | } |
516 | 12.4k | } |
517 | 10.1k | } |
518 | | |
519 | | bool QXPContentCollector::hasUnfinishedLinkedTexts() |
520 | 10.1k | { |
521 | 10.1k | for (const auto &page : m_unprocessedPages) |
522 | 10.7k | { |
523 | 10.7k | for (const auto &textObj : page.linkedTextObjects) |
524 | 6.20k | { |
525 | 6.20k | if (!textObj->text || (textObj->linkSettings.nextLinkedIndex > 0 && !textObj->linkSettings.textLength)) |
526 | 5.42k | { |
527 | 5.42k | return true; |
528 | 5.42k | } |
529 | 6.20k | } |
530 | 10.7k | } |
531 | | |
532 | 4.74k | return false; |
533 | 10.1k | } |
534 | | |
535 | | void QXPContentCollector::drawLine(const std::shared_ptr<Line> &line, const QXPContentCollector::CollectedPage &page) |
536 | 68.2k | { |
537 | 68.2k | RVNGPropertyListVector path; |
538 | 68.2k | if (line->curveComponents.empty()) |
539 | 68.1k | { |
540 | 68.1k | vector<Point> points = |
541 | 68.1k | { |
542 | 68.1k | page.getPoint(line->boundingBox.topLeft().rotateDeg(-line->rotation, line->boundingBox.center())), |
543 | 68.1k | page.getPoint(line->boundingBox.bottomRight().rotateDeg(-line->rotation, line->boundingBox.center())) |
544 | 68.1k | }; |
545 | | |
546 | 68.1k | path = createLinePath(points, false); |
547 | 68.1k | } |
548 | 108 | else |
549 | 108 | { |
550 | 108 | for (const auto &curve : line->curveComponents) |
551 | 13.6k | { |
552 | 13.6k | vector<Point> points; |
553 | 13.6k | points.reserve(curve.points.size()); |
554 | 13.6k | std::transform(curve.points.begin(), curve.points.end(), std::back_inserter(points), |
555 | 13.6k | [page, &line](const Point &p) |
556 | 49.0k | { |
557 | 49.0k | return page.getPoint(p.rotateDeg(-line->rotation, line->boundingBox.center())); |
558 | 49.0k | }); |
559 | | |
560 | 13.6k | addBezierPath(path, points, false); |
561 | 13.6k | } |
562 | 108 | } |
563 | | |
564 | 68.2k | RVNGPropertyList propList; |
565 | | |
566 | 68.2k | writeFrame(propList, line->style, line->runaround, true); |
567 | | |
568 | 68.2k | m_painter->setStyle(propList); |
569 | 68.2k | propList.clear(); |
570 | | |
571 | 68.2k | propList.insert("svg:d", path); |
572 | 68.2k | writeZIndex(propList, line->zIndex); |
573 | | |
574 | 68.2k | m_painter->drawPath(propList); |
575 | 68.2k | } |
576 | | |
577 | | void QXPContentCollector::drawBox(const std::shared_ptr<Box> &box, const QXPContentCollector::CollectedPage &page) |
578 | 84.2k | { |
579 | 84.2k | switch (box->boxType) |
580 | 84.2k | { |
581 | 15 | default: |
582 | 79.7k | case BoxType::RECTANGLE: |
583 | 79.7k | drawRectangle(box, page); |
584 | 79.7k | break; |
585 | 3.93k | case BoxType::OVAL: |
586 | 3.93k | drawOval(box, page); |
587 | 3.93k | break; |
588 | 404 | case BoxType::POLYGON: |
589 | 404 | drawPolygon(box, page); |
590 | 404 | break; |
591 | 127 | case BoxType::BEZIER: |
592 | 127 | drawBezierBox(box, page); |
593 | 127 | break; |
594 | 84.2k | } |
595 | 84.2k | } |
596 | | |
597 | | void QXPContentCollector::drawRectangle(const std::shared_ptr<Box> &box, const QXPContentCollector::CollectedPage &page) |
598 | 79.7k | { |
599 | 79.7k | const auto bbox = box->boundingBox.shrink(box->frame.width / 2); |
600 | 79.7k | vector<Point> points = |
601 | 79.7k | { |
602 | 79.7k | page.getPoint(bbox.topLeft()), |
603 | 79.7k | page.getPoint(bbox.topRight()), |
604 | 79.7k | page.getPoint(bbox.bottomRight()), |
605 | 79.7k | page.getPoint(bbox.bottomLeft()) |
606 | 79.7k | }; |
607 | | |
608 | 79.7k | if (!QXP_ALMOST_ZERO(box->rotation)) |
609 | 31.4k | { |
610 | 31.4k | for (auto &p : points) |
611 | 125k | { |
612 | 125k | p = p.rotateDeg(-box->rotation, page.getPoint(box->boundingBox.center())); |
613 | 125k | } |
614 | 31.4k | } |
615 | | |
616 | 79.7k | auto path = createLinePath(points, true); |
617 | | |
618 | 79.7k | RVNGPropertyList propList; |
619 | | |
620 | 79.7k | writeFrame(propList, box->frame, box->runaround); |
621 | 79.7k | writeFill(propList, box->fill); |
622 | | |
623 | 79.7k | m_painter->setStyle(propList); |
624 | 79.7k | propList.clear(); |
625 | | |
626 | 79.7k | propList.insert("svg:d", path); |
627 | 79.7k | writeZIndex(propList, box->zIndex); |
628 | | |
629 | 79.7k | m_painter->drawPath(propList); |
630 | 79.7k | } |
631 | | |
632 | | void QXPContentCollector::drawOval(const std::shared_ptr<Box> &oval, const QXPContentCollector::CollectedPage &page) |
633 | 3.93k | { |
634 | 3.93k | librevenge::RVNGPropertyList propList; |
635 | | |
636 | 3.93k | writeFrame(propList, oval->frame, oval->runaround); |
637 | 3.93k | writeFill(propList, oval->fill); |
638 | | |
639 | 3.93k | m_painter->setStyle(propList); |
640 | 3.93k | propList.clear(); |
641 | | |
642 | 3.93k | propList.insert("svg:cx", page.getX(oval->boundingBox.center().x), RVNG_POINT); |
643 | 3.93k | propList.insert("svg:cy", page.getY(oval->boundingBox.center().y), RVNG_POINT); |
644 | 3.93k | propList.insert("svg:rx", oval->boundingBox.width() / 2 - oval->frame.width / 2, RVNG_POINT); |
645 | 3.93k | propList.insert("svg:ry", oval->boundingBox.height() / 2 - oval->frame.width / 2, RVNG_POINT); |
646 | 3.93k | if (!QXP_ALMOST_ZERO(oval->rotation)) |
647 | 1.00k | { |
648 | 1.00k | propList.insert("librevenge:rotate", oval->rotation); |
649 | 1.00k | } |
650 | | |
651 | 3.93k | writeZIndex(propList, oval->zIndex); |
652 | | |
653 | 3.93k | m_painter->drawEllipse(propList); |
654 | 3.93k | } |
655 | | |
656 | | void QXPContentCollector::drawPolygon(const std::shared_ptr<Box> &polygon, const QXPContentCollector::CollectedPage &page) |
657 | 404 | { |
658 | 404 | vector<Point> points; |
659 | 404 | points.reserve(polygon->customPoints.size()); |
660 | 404 | std::transform(polygon->customPoints.begin(), polygon->customPoints.end(), std::back_inserter(points), |
661 | 404 | [page, &polygon](const Point &p) |
662 | 78.3k | { |
663 | 78.3k | return page.getPoint(p.rotateDeg(-polygon->rotation, polygon->boundingBox.center())); |
664 | 78.3k | }); |
665 | | |
666 | 404 | auto path = createLinePath(points, true); |
667 | | |
668 | 404 | RVNGPropertyList propList; |
669 | | |
670 | 404 | writeFrame(propList, polygon->frame, polygon->runaround); |
671 | 404 | writeFill(propList, polygon->fill); |
672 | | |
673 | 404 | m_painter->setStyle(propList); |
674 | 404 | propList.clear(); |
675 | | |
676 | 404 | propList.insert("svg:d", path); |
677 | 404 | writeZIndex(propList, polygon->zIndex); |
678 | | |
679 | 404 | m_painter->drawPath(propList); |
680 | 404 | } |
681 | | |
682 | | void QXPContentCollector::drawBezierBox(const std::shared_ptr<Box> &box, const QXPContentCollector::CollectedPage &page) |
683 | 127 | { |
684 | 127 | RVNGPropertyListVector path; |
685 | 127 | for (const auto &curve : box->curveComponents) |
686 | 2.66k | { |
687 | 2.66k | vector<Point> points; |
688 | 2.66k | points.reserve(curve.points.size()); |
689 | 2.66k | std::transform(curve.points.begin(), curve.points.end(), std::back_inserter(points), |
690 | 2.66k | [page, &box](const Point &p) |
691 | 87.5k | { |
692 | 87.5k | return page.getPoint(p.rotateDeg(-box->rotation, box->boundingBox.center())); |
693 | 87.5k | }); |
694 | | |
695 | 2.66k | addBezierPath(path, points, true); |
696 | 2.66k | } |
697 | | |
698 | 127 | RVNGPropertyList propList; |
699 | | |
700 | 127 | writeFrame(propList, box->frame, box->runaround); |
701 | 127 | writeFill(propList, box->fill); |
702 | | |
703 | 127 | m_painter->setStyle(propList); |
704 | 127 | propList.clear(); |
705 | | |
706 | 127 | propList.insert("svg:d", path); |
707 | 127 | writeZIndex(propList, box->zIndex); |
708 | | |
709 | 127 | m_painter->drawPath(propList); |
710 | 127 | } |
711 | | |
712 | | void QXPContentCollector::drawTextBox(const std::shared_ptr<TextBox> &textbox, const QXPContentCollector::CollectedPage &page) |
713 | 73.8k | { |
714 | 73.8k | drawBox(textbox, page); |
715 | | |
716 | 73.8k | const auto bbox = textbox->boundingBox.shrink(textbox->frame.width); |
717 | | |
718 | 73.8k | librevenge::RVNGPropertyList textObjPropList; |
719 | | |
720 | 73.8k | textObjPropList.insert("svg:x", page.getX(bbox.left), RVNG_POINT); |
721 | 73.8k | textObjPropList.insert("svg:y", page.getY(bbox.top), RVNG_POINT); |
722 | 73.8k | textObjPropList.insert("svg:width", bbox.width(), RVNG_POINT); |
723 | 73.8k | textObjPropList.insert("svg:height", bbox.height(), RVNG_POINT); |
724 | | |
725 | 73.8k | textObjPropList.insert("fo:padding-top", 0, RVNG_POINT); |
726 | 73.8k | textObjPropList.insert("fo:padding-right", 0, RVNG_POINT); |
727 | 73.8k | textObjPropList.insert("fo:padding-bottom", 0, RVNG_POINT); |
728 | 73.8k | textObjPropList.insert("fo:padding-left", 3, RVNG_POINT); |
729 | | |
730 | 73.8k | switch (textbox->settings.verticalAlignment) |
731 | 73.8k | { |
732 | 70.3k | case VerticalAlignment::TOP: |
733 | 70.3k | textObjPropList.insert("draw:textarea-vertical-align", "top"); |
734 | 70.3k | break; |
735 | 502 | case VerticalAlignment::CENTER: |
736 | 502 | textObjPropList.insert("draw:textarea-vertical-align", "middle"); |
737 | 502 | break; |
738 | 1.28k | case VerticalAlignment::BOTTOM: |
739 | 1.28k | textObjPropList.insert("draw:textarea-vertical-align", "bottom"); |
740 | 1.28k | break; |
741 | 1.65k | case VerticalAlignment::JUSTIFIED: |
742 | 1.65k | textObjPropList.insert("draw:textarea-vertical-align", "justify"); |
743 | 1.65k | break; |
744 | 73.8k | } |
745 | 73.8k | if (!QXP_ALMOST_ZERO(textbox->rotation)) |
746 | 27.9k | { |
747 | 27.9k | textObjPropList.insert("librevenge:rotate", -textbox->rotation); |
748 | 27.9k | } |
749 | | |
750 | 73.8k | writeZIndex(textObjPropList, textbox->zIndex + 1); |
751 | | |
752 | 73.8k | m_painter->startTextObject(textObjPropList); |
753 | | |
754 | 73.8k | if (textbox->text) |
755 | 60.9k | { |
756 | 60.9k | drawText(textbox->text.get(), textbox->linkSettings); |
757 | 60.9k | } |
758 | | |
759 | 73.8k | m_painter->endTextObject(); |
760 | 73.8k | } |
761 | | |
762 | | void QXPContentCollector::drawTextPath(const std::shared_ptr<TextPath> &textPath, const QXPContentCollector::CollectedPage &page) |
763 | 33.4k | { |
764 | 33.4k | drawLine(textPath, page); |
765 | | |
766 | 33.4k | if (!textPath->text) |
767 | 29.6k | { |
768 | 29.6k | return; |
769 | 29.6k | } |
770 | | |
771 | 3.79k | double lineY; |
772 | 3.79k | switch (textPath->settings.lineAlignment) |
773 | 3.79k | { |
774 | 0 | default: |
775 | 3.48k | case TextPathLineAlignment::TOP: |
776 | 3.48k | lineY = textPath->boundingBox.top - textPath->style.width / 2; |
777 | 3.48k | break; |
778 | 284 | case TextPathLineAlignment::CENTER: |
779 | 284 | lineY = textPath->boundingBox.top; |
780 | 284 | break; |
781 | 27 | case TextPathLineAlignment::BOTTOM: |
782 | 27 | lineY = textPath->boundingBox.top + textPath->style.width / 2; |
783 | 27 | break; |
784 | 3.79k | } |
785 | | |
786 | 3.79k | const double height = textPath->text.get()->maxFontSize(); |
787 | | |
788 | 3.79k | double textY; |
789 | 3.79k | switch (textPath->settings.alignment) |
790 | 3.79k | { |
791 | 0 | default: |
792 | 28 | case TextPathAlignment::DESCENT: |
793 | 1.70k | case TextPathAlignment::BASELINE: |
794 | 1.70k | textY = lineY - height; |
795 | 1.70k | break; |
796 | 361 | case TextPathAlignment::CENTER: |
797 | 361 | textY = lineY - height / 2; |
798 | 361 | break; |
799 | 1.72k | case TextPathAlignment::ASCENT: |
800 | 1.72k | textY = lineY; |
801 | 1.72k | break; |
802 | 3.79k | } |
803 | | |
804 | 3.79k | librevenge::RVNGPropertyList textObjPropList; |
805 | | |
806 | 3.79k | textObjPropList.insert("svg:x", page.getX(textPath->boundingBox.left), RVNG_POINT); |
807 | 3.79k | textObjPropList.insert("svg:y", page.getY(textY), RVNG_POINT); |
808 | | // shouldn't grow vertically |
809 | 3.79k | textObjPropList.insert("svg:width", textPath->boundingBox.width() + height, RVNG_POINT); |
810 | 3.79k | textObjPropList.insert("svg:height", height, RVNG_POINT); |
811 | | |
812 | 3.79k | textObjPropList.insert("fo:padding-top", 0, RVNG_POINT); |
813 | 3.79k | textObjPropList.insert("fo:padding-right", 0, RVNG_POINT); |
814 | 3.79k | textObjPropList.insert("fo:padding-bottom", 0, RVNG_POINT); |
815 | 3.79k | textObjPropList.insert("fo:padding-left", 0, RVNG_POINT); |
816 | | |
817 | 3.79k | if (!QXP_ALMOST_ZERO(textPath->rotation)) |
818 | 3.14k | { |
819 | 3.14k | textObjPropList.insert("librevenge:rotate", -textPath->rotation); |
820 | 3.14k | } |
821 | | |
822 | 3.79k | writeZIndex(textObjPropList, textPath->zIndex + 1); |
823 | | |
824 | 3.79k | m_painter->startTextObject(textObjPropList); |
825 | | |
826 | 3.79k | drawText(textPath->text.get(), textPath->linkSettings); |
827 | | |
828 | 3.79k | m_painter->endTextObject(); |
829 | 3.79k | } |
830 | | |
831 | | void QXPContentCollector::drawText(const std::shared_ptr<Text> &text, const LinkedTextSettings &linkSettings) |
832 | 64.7k | { |
833 | 64.7k | unsigned long spanTextStart = linkSettings.offsetIntoText; |
834 | 64.7k | const unsigned long textEnd = linkSettings.textLength ? (spanTextStart + linkSettings.textLength.get()) : text->text.length(); |
835 | | |
836 | 64.7k | unsigned paragraphInd = 0; |
837 | | |
838 | 64.7k | for (auto ¶graph : text->paragraphs) |
839 | 50.8k | { |
840 | 50.8k | if (paragraph.startIndex >= textEnd) |
841 | 7.71k | { |
842 | 7.71k | break; |
843 | 7.71k | } |
844 | 43.1k | if (spanTextStart > paragraph.endIndex()) |
845 | 16.1k | { |
846 | 16.1k | continue; |
847 | 16.1k | } |
848 | | |
849 | 27.0k | librevenge::RVNGPropertyList paragraphPropList; |
850 | | |
851 | 27.0k | paragraphPropList.insert("fo:margin-top", paragraph.format->margin.top, RVNG_POINT); |
852 | 27.0k | paragraphPropList.insert("fo:margin-right", paragraph.format->margin.right, RVNG_POINT); |
853 | 27.0k | paragraphPropList.insert("fo:margin-bottom", paragraph.format->margin.bottom, RVNG_POINT); |
854 | 27.0k | paragraphPropList.insert("fo:margin-left", paragraph.format->margin.left, RVNG_POINT); |
855 | | |
856 | 27.0k | paragraphPropList.insert("fo:text-indent", paragraph.format->firstLineIndent, RVNG_POINT); |
857 | | |
858 | 27.0k | if (!QXP_ALMOST_ZERO(paragraph.format->leading) && !paragraph.format->incrementalLeading) |
859 | 3.13k | { |
860 | 3.13k | paragraphPropList.insert("fo:line-height", paragraph.format->leading, RVNG_POINT); |
861 | 3.13k | } |
862 | 23.9k | else |
863 | 23.9k | { |
864 | 23.9k | const double fontSize = text->maxFontSize(paragraph); |
865 | 23.9k | const double initialLeading = fontSize + (m_docProps.isIncrementalAutoLeading() ? m_docProps.autoLeading() : (fontSize * m_docProps.autoLeading())); |
866 | 23.9k | const double lineHeight = initialLeading + paragraph.format->leading; |
867 | 23.9k | paragraphPropList.insert("fo:line-height", lineHeight, RVNG_POINT); |
868 | 23.9k | } |
869 | | |
870 | 27.0k | switch (paragraph.format->alignment) |
871 | 27.0k | { |
872 | 25.3k | case HorizontalAlignment::LEFT: |
873 | 25.3k | paragraphPropList.insert("fo:text-align", "left"); |
874 | 25.3k | break; |
875 | 564 | case HorizontalAlignment::RIGHT: |
876 | 564 | paragraphPropList.insert("fo:text-align", "end"); |
877 | 564 | break; |
878 | 296 | case HorizontalAlignment::CENTER: |
879 | 296 | paragraphPropList.insert("fo:text-align", "center"); |
880 | 296 | break; |
881 | 27 | case HorizontalAlignment::JUSTIFIED: |
882 | 818 | case HorizontalAlignment::FORCED: |
883 | 818 | paragraphPropList.insert("fo:text-align", "justify"); |
884 | 818 | break; |
885 | 27.0k | } |
886 | 27.0k | paragraphPropList.insert("fo:text-align-last", "start"); |
887 | | |
888 | 27.0k | if (paragraph.format->hj) |
889 | 1.86k | { |
890 | 1.86k | paragraphPropList.insert("fo:hyphenate", paragraph.format->hj->hyphenate); |
891 | 1.86k | if (paragraph.format->hj->maxInRow == 0) |
892 | 1.70k | paragraphPropList.insert("fo:hyphenation-ladder-count", "no-limit"); |
893 | 164 | else |
894 | 164 | paragraphPropList.insert("fo:hyphenation-ladder-count", int(paragraph.format->hj->maxInRow)); |
895 | 1.86k | paragraphPropList.insert("style:justify-single-word", paragraph.format->hj->singleWordJustify); |
896 | 1.86k | } |
897 | | |
898 | 27.0k | if (!paragraph.format->tabStops.empty()) |
899 | 11.0k | { |
900 | 11.0k | RVNGPropertyListVector tabs; |
901 | 11.0k | for (const auto &tab : paragraph.format->tabStops) |
902 | 168k | { |
903 | 168k | RVNGPropertyList tabProps; |
904 | 168k | tabProps.insert("style:position", tab.position, RVNG_POINT); |
905 | 168k | if (!tab.fillChar.empty()) |
906 | 168k | { |
907 | 168k | tabProps.insert("style:leader-text", tab.fillChar); |
908 | 168k | } |
909 | 168k | switch (tab.type) |
910 | 168k | { |
911 | 159k | case TabStopType::LEFT: |
912 | 159k | tabProps.insert("style:type", "left"); |
913 | 159k | break; |
914 | 3.06k | case TabStopType::RIGHT: |
915 | 3.06k | tabProps.insert("style:type", "right"); |
916 | 3.06k | break; |
917 | 3.15k | case TabStopType::CENTER: |
918 | 3.15k | tabProps.insert("style:type", "center"); |
919 | 3.15k | break; |
920 | 3.37k | case TabStopType::ALIGN: |
921 | 3.37k | tabProps.insert("style:type", "char"); |
922 | 3.37k | tabProps.insert("style:char", tab.alignChar); |
923 | 3.37k | break; |
924 | 168k | } |
925 | 168k | tabs.append(tabProps); |
926 | 168k | } |
927 | 11.0k | paragraphPropList.insert("librevenge:tab-stops", tabs); |
928 | 11.0k | } |
929 | | |
930 | 27.0k | if (paragraph.format->ruleAbove && paragraphInd > 0) |
931 | 3.68k | { |
932 | 3.68k | writeBorder(paragraphPropList, "fo:border-top", paragraph.format->ruleAbove); |
933 | 3.68k | } |
934 | 27.0k | if (paragraph.format->ruleBelow && paragraphInd < text->paragraphs.size() - 1) |
935 | 5.86k | { |
936 | 5.86k | writeBorder(paragraphPropList, "fo:border-bottom", paragraph.format->ruleBelow); |
937 | 5.86k | } |
938 | | |
939 | 27.0k | m_painter->openParagraph(paragraphPropList); |
940 | | |
941 | 27.0k | for (auto &charFormat : text->charFormats) |
942 | 53.8k | { |
943 | 53.8k | if (spanTextStart > paragraph.endIndex() || spanTextStart >= textEnd || charFormat.startIndex > paragraph.endIndex() || charFormat.startIndex >= textEnd) |
944 | 8.50k | { |
945 | 8.50k | break; |
946 | 8.50k | } |
947 | | |
948 | 45.3k | if (spanTextStart > charFormat.endIndex()) |
949 | 8.19k | { |
950 | 8.19k | continue; |
951 | 8.19k | } |
952 | | |
953 | 37.1k | if (spanTextStart >= text->text.length()) |
954 | 3.09k | { |
955 | 3.09k | QXP_DEBUG_MSG(("Span start %lu out of range\n", spanTextStart)); |
956 | 3.09k | break; |
957 | 3.09k | } |
958 | | |
959 | 34.0k | const auto spanTextEnd = static_cast<unsigned long>( |
960 | 34.0k | std::min<uint64_t>({ charFormat.afterEndIndex(), paragraph.afterEndIndex(), text->text.length(), textEnd }) |
961 | 34.0k | ); |
962 | | |
963 | 34.0k | if (charFormat.format->isControlChars) |
964 | 9.11k | { |
965 | 9.11k | spanTextStart = spanTextEnd; |
966 | 9.11k | continue; |
967 | 9.11k | } |
968 | | |
969 | 24.9k | librevenge::RVNGPropertyList spanPropList; |
970 | | |
971 | 24.9k | const double fontSize = std::max(charFormat.format->fontSize, 1.); |
972 | | |
973 | 24.9k | spanPropList.insert("style:font-name", charFormat.format->fontName); |
974 | 24.9k | spanPropList.insert("fo:font-size", fontSize, librevenge::RVNG_POINT); |
975 | 24.9k | spanPropList.insert("fo:font-weight", charFormat.format->bold ? "bold" : "normal"); |
976 | 24.9k | spanPropList.insert("fo:font-style", charFormat.format->italic ? "italic" : "normal"); |
977 | 24.9k | if (charFormat.format->underline || charFormat.format->wordUnderline) |
978 | 9.26k | { |
979 | 9.26k | spanPropList.insert("style:text-underline-color", "font-color"); |
980 | 9.26k | spanPropList.insert("style:text-underline-type", "single"); |
981 | 9.26k | spanPropList.insert("style:text-underline-style", "solid"); |
982 | 9.26k | spanPropList.insert("style:text-underline-mode", charFormat.format->wordUnderline ? "skip-white-space" : "continuous"); |
983 | 9.26k | } |
984 | 24.9k | if (charFormat.format->strike) |
985 | 8.03k | { |
986 | 8.03k | spanPropList.insert("style:text-line-through-color", "font-color"); |
987 | 8.03k | spanPropList.insert("style:text-line-through-mode", "continuous"); |
988 | 8.03k | spanPropList.insert("style:text-line-through-type", "single"); |
989 | 8.03k | spanPropList.insert("style:text-line-through-style", "solid"); |
990 | 8.03k | spanPropList.insert("style:text-line-through-width", "1pt"); |
991 | 8.03k | } |
992 | 24.9k | spanPropList.insert("fo:font-variant", charFormat.format->smallCaps ? "small-caps" : "normal"); |
993 | 24.9k | if (charFormat.format->allCaps) |
994 | 7.85k | spanPropList.insert("fo:text-transform", "capitalize"); |
995 | 24.9k | spanPropList.insert("style:text-outline", charFormat.format->outline); |
996 | 24.9k | if (charFormat.format->shadow) |
997 | 8.95k | spanPropList.insert("fo:text-shadow", "1pt 1pt"); |
998 | 24.9k | spanPropList.insert("fo:color", charFormat.format->color.toString()); |
999 | | |
1000 | 24.9k | if (charFormat.format->subscript) |
1001 | 8.75k | { |
1002 | 8.75k | writeTextPosition(spanPropList, m_docProps.subscriptOffset + charFormat.format->baselineShift, m_docProps.subscriptVScale); |
1003 | 8.75k | spanPropList.insert("style:text-scale", m_docProps.subscriptHScale, librevenge::RVNG_PERCENT); |
1004 | 8.75k | } |
1005 | 16.2k | else if (charFormat.format->superscript) |
1006 | 286 | { |
1007 | 286 | writeTextPosition(spanPropList, m_docProps.superscriptOffset + charFormat.format->baselineShift, m_docProps.superscriptVScale); |
1008 | 286 | spanPropList.insert("style:text-scale", m_docProps.superscriptHScale, librevenge::RVNG_PERCENT); |
1009 | 286 | } |
1010 | 15.9k | else if (charFormat.format->superior) |
1011 | 962 | { |
1012 | | // approximate "superior" positioning (char ascents are aligned with the cap height of the current font) |
1013 | 962 | const double offset = (fontSize * (1.0 - m_docProps.superiorVScale)) / fontSize; |
1014 | 962 | writeTextPosition(spanPropList, offset + charFormat.format->baselineShift, m_docProps.superiorVScale); |
1015 | 962 | spanPropList.insert("style:text-scale", m_docProps.superiorHScale, librevenge::RVNG_PERCENT); |
1016 | 962 | } |
1017 | 14.9k | else if (charFormat.format->baselineShift != 0.0) |
1018 | 6.84k | { |
1019 | 6.84k | writeTextPosition(spanPropList, charFormat.format->baselineShift); |
1020 | 6.84k | } |
1021 | | |
1022 | 24.9k | if (paragraph.format->hj) |
1023 | 13.8k | { |
1024 | 13.8k | spanPropList.insert("fo:hyphenation-remain-char-count", std::max(int(paragraph.format->hj->minBefore), 1)); |
1025 | 13.8k | spanPropList.insert("fo:hyphenation-push-char-count", std::max(int(paragraph.format->hj->minAfter), 1)); |
1026 | 13.8k | } |
1027 | | |
1028 | 24.9k | m_painter->openSpan(spanPropList); |
1029 | | |
1030 | 24.9k | auto sourceStr = text->text.substr(spanTextStart, spanTextEnd - spanTextStart); |
1031 | 24.9k | RVNGString str; |
1032 | 24.9k | appendCharacters(str, sourceStr.c_str(), sourceStr.length(), text->encoding); |
1033 | | |
1034 | 24.9k | insertText(m_painter, str); |
1035 | | |
1036 | 24.9k | m_painter->closeSpan(); |
1037 | | |
1038 | 24.9k | spanTextStart = spanTextEnd; |
1039 | 24.9k | } |
1040 | | |
1041 | 27.0k | m_painter->closeParagraph(); |
1042 | | |
1043 | 27.0k | paragraphInd++; |
1044 | 27.0k | } |
1045 | 64.7k | } |
1046 | | |
1047 | | void QXPContentCollector::drawGroup(const std::shared_ptr<Group> &group, const QXPContentCollector::CollectedPage &page) |
1048 | 25.9k | { |
1049 | 25.9k | bool groupOpened = false; |
1050 | | |
1051 | 25.9k | for (const unsigned &ind : group->objectsIndexes) |
1052 | 20.0k | { |
1053 | 20.0k | const auto it = page.objects.find(ind); |
1054 | 20.0k | if (it == page.objects.end()) |
1055 | 18.0k | { |
1056 | 18.0k | QXP_DEBUG_MSG(("Group element %u not found\n", ind)); |
1057 | 18.0k | continue; |
1058 | 18.0k | } |
1059 | 2.00k | const auto &obj = it->second; |
1060 | | |
1061 | 2.00k | if (!groupOpened) |
1062 | 537 | { |
1063 | 537 | RVNGPropertyList propList; |
1064 | 537 | writeZIndex(propList, obj->zIndex() - 1); |
1065 | 537 | m_painter->openGroup(propList); |
1066 | | |
1067 | 537 | groupOpened = true; |
1068 | 537 | } |
1069 | | |
1070 | 2.00k | obj->draw(page); |
1071 | 2.00k | } |
1072 | | |
1073 | 25.9k | if (groupOpened) |
1074 | 537 | { |
1075 | 537 | m_painter->closeGroup(); |
1076 | 537 | } |
1077 | 25.9k | } |
1078 | | |
1079 | | void QXPContentCollector::writeFill(librevenge::RVNGPropertyList &propList, const boost::optional<Fill> &fill) |
1080 | 84.2k | { |
1081 | 84.2k | propList.insert("draw:fill", "none"); |
1082 | | |
1083 | 84.2k | if (fill) |
1084 | 72.1k | { |
1085 | 72.1k | FillWriter fillWriter(propList); |
1086 | 72.1k | boost::apply_visitor(fillWriter, fill.get()); |
1087 | 72.1k | } |
1088 | 84.2k | } |
1089 | | |
1090 | | void QXPContentCollector::writeFrame(librevenge::RVNGPropertyList &propList, const Frame &frame, const bool runaround, const bool allowHairline) |
1091 | 152k | { |
1092 | 152k | propList.insert("draw:stroke", "none"); |
1093 | | |
1094 | 152k | if (frame.color && (allowHairline || !QXP_ALMOST_ZERO(frame.width))) |
1095 | 96.4k | { |
1096 | 96.4k | propList.insert("draw:stroke", "solid"); |
1097 | 96.4k | propList.insert("svg:stroke-color", frame.color->toString()); |
1098 | 96.4k | propList.insert("svg:stroke-width", frame.width, RVNG_POINT); |
1099 | | |
1100 | 96.4k | if (frame.lineStyle) |
1101 | 24.3k | { |
1102 | 24.3k | if (frame.lineStyle->segmentLengths.size() > 1 && !frame.lineStyle->isStripe) |
1103 | 4.81k | { |
1104 | 4.81k | const double dots1 = frame.lineStyle->segmentLengths[0]; |
1105 | 4.81k | const double dist = frame.lineStyle->segmentLengths[1]; |
1106 | 4.81k | const double dots2 = frame.lineStyle->segmentLengths[frame.lineStyle->segmentLengths.size() >= 3 ? 2 : 0]; |
1107 | 4.81k | const double scale = frame.lineStyle->isProportional ? frame.lineStyle->patternLength : 1.0; |
1108 | 4.81k | const auto unit = frame.lineStyle->isProportional ? RVNG_PERCENT : RVNG_POINT; |
1109 | | |
1110 | 4.81k | propList.insert("draw:stroke", "dash"); |
1111 | 4.81k | propList.insert("draw:dots1", 1); |
1112 | 4.81k | propList.insert("draw:dots1-length", dots1 * scale, unit); |
1113 | 4.81k | propList.insert("draw:dots2", 1); |
1114 | 4.81k | propList.insert("draw:dots2-length", dots2 * scale, unit); |
1115 | 4.81k | propList.insert("draw:distance", dist * scale, unit); |
1116 | 4.81k | } |
1117 | | |
1118 | 24.3k | switch (frame.lineStyle->endcapType) |
1119 | 24.3k | { |
1120 | 0 | default: |
1121 | 24.2k | case LineCapType::BUTT: |
1122 | 24.2k | propList.insert("svg:stroke-linecap", "butt"); |
1123 | 24.2k | break; |
1124 | 24 | case LineCapType::ROUND: |
1125 | 24 | propList.insert("svg:stroke-linecap", "round"); |
1126 | 24 | break; |
1127 | 2 | case LineCapType::RECT: |
1128 | 2 | propList.insert("svg:stroke-linecap", "square"); |
1129 | 2 | break; |
1130 | 24.3k | } |
1131 | | |
1132 | 24.3k | switch (frame.lineStyle->joinType) |
1133 | 24.3k | { |
1134 | 0 | default: |
1135 | 2 | case LineJoinType::BEVEL: |
1136 | 2 | propList.insert("svg:stroke-linejoin", "bevel"); |
1137 | 2 | break; |
1138 | 24.2k | case LineJoinType::MITER: |
1139 | 24.2k | propList.insert("svg:stroke-linejoin", "miter"); |
1140 | 24.2k | break; |
1141 | 29 | case LineJoinType::ROUND: |
1142 | 29 | propList.insert("svg:stroke-linejoin", "round"); |
1143 | 29 | break; |
1144 | 24.3k | } |
1145 | 24.3k | } |
1146 | | |
1147 | 96.4k | if (frame.startArrow) |
1148 | 4.39k | { |
1149 | 4.39k | writeArrow(propList, "start", *frame.startArrow, frame.width); |
1150 | 4.39k | } |
1151 | 96.4k | if (frame.endArrow) |
1152 | 4.69k | { |
1153 | 4.69k | writeArrow(propList, "end", *frame.endArrow, frame.width); |
1154 | 4.69k | } |
1155 | 96.4k | } |
1156 | | |
1157 | 152k | if (runaround) |
1158 | 98.8k | { |
1159 | 98.8k | propList.insert("style:wrap", "biggest"); |
1160 | 98.8k | } |
1161 | 152k | } |
1162 | | |
1163 | | double QXPContentCollector::CollectedPage::getX(const double x) const |
1164 | 877k | { |
1165 | 877k | return x - settings.offset.left; |
1166 | 877k | } |
1167 | | |
1168 | | double QXPContentCollector::CollectedPage::getX(const std::shared_ptr<Object> &obj) const |
1169 | 0 | { |
1170 | 0 | return getX(obj->boundingBox.left); |
1171 | 0 | } |
1172 | | |
1173 | | double QXPContentCollector::CollectedPage::getY(const double y) const |
1174 | 877k | { |
1175 | 877k | return y - settings.offset.top; |
1176 | 877k | } |
1177 | | |
1178 | | double QXPContentCollector::CollectedPage::getY(const std::shared_ptr<Object> &obj) const |
1179 | 0 | { |
1180 | 0 | return getY(obj->boundingBox.top); |
1181 | 0 | } |
1182 | | |
1183 | | Point QXPContentCollector::CollectedPage::getPoint(const Point &p) const |
1184 | 796k | { |
1185 | 796k | return Point(getX(p.x), getY(p.y)); |
1186 | 796k | } |
1187 | | |
1188 | | |
1189 | | } |
1190 | | |
1191 | | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ |