Coverage Report

Created: 2026-09-06 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcdr/src/lib/CDRContentCollector.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 libcdr 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 "CDRContentCollector.h"
11
12
#include <math.h>
13
#include <string.h>
14
#include <librevenge/librevenge.h>
15
#include <libcdr/libcdr.h>
16
#include "CDROutputElementList.h"
17
#include "libcdr_utils.h"
18
19
#ifndef DUMP_PATTERN
20
#define DUMP_PATTERN 0
21
#endif
22
23
#ifndef DUMP_VECT
24
#define DUMP_VECT 0
25
#endif
26
27
namespace libcdr
28
{
29
namespace
30
{
31
32
/// Move the number into [0;1] range.
33
void normalize(double &d)
34
0
{
35
0
  if (d < 0.0)
36
0
  {
37
0
    const double n = d + static_cast<unsigned long>(-d) + 1.0;
38
0
    if ((n < 0.0) || (n > 1.0))
39
0
      d = 0.0; // The number was too big, thus rounded. Just pick a value.
40
0
    else
41
0
      d = n;
42
0
  }
43
0
  if (d > 1.0)
44
0
  {
45
0
    const double n = d - static_cast<unsigned long>(d);
46
0
    if ((n < 0.0) || (n > 1.0))
47
0
      d = 0.0; // The number was too big, thus rounded. Just pick a value.
48
0
    else
49
0
      d = n;
50
0
  }
51
0
}
52
53
void normalizeAngle(double &angle)
54
43.4k
{
55
43.4k
  angle = fmod(angle, 360);
56
43.4k
  if (angle < 0)
57
1.07k
    angle += 360;
58
43.4k
}
59
60
}
61
}
62
63
libcdr::CDRContentCollector::CDRContentCollector(libcdr::CDRParserState &ps, librevenge::RVNGDrawingInterface *painter,
64
                                                 bool reverseOrder)
65
8.19k
  : m_painter(painter), m_isDocumentStarted(false), m_isPageProperties(false), m_isPageStarted(false),
66
8.19k
    m_ignorePage(false), m_page(ps.m_pages[0]), m_pageIndex(0), m_currentFillStyle(), m_currentLineStyle(),
67
8.19k
    m_spnd(0), m_currentObjectLevel(0), m_currentGroupLevel(0), m_currentVectLevel(0), m_currentPageLevel(0),
68
8.19k
    m_currentStyleId(0), m_currentImage(), m_currentText(nullptr), m_currentBBox(), m_currentTextBox(), m_currentPath(),
69
8.19k
    m_currentTransforms(), m_fillTransforms(), m_polygon(), m_isInPolygon(false), m_isInSpline(false),
70
8.19k
    m_outputElementsStack(nullptr), m_contentOutputElementsStack(), m_fillOutputElementsStack(),
71
8.19k
    m_outputElementsQueue(nullptr), m_contentOutputElementsQueue(), m_fillOutputElementsQueue(),
72
8.19k
    m_groupLevels(), m_groupTransforms(), m_splineData(), m_fillOpacity(1.0), m_reverseOrder(reverseOrder),
73
8.19k
    m_ps(ps)
74
8.19k
{
75
8.19k
  m_outputElementsStack = &m_contentOutputElementsStack;
76
8.19k
  m_outputElementsQueue = &m_contentOutputElementsQueue;
77
8.19k
}
78
79
libcdr::CDRContentCollector::~CDRContentCollector()
80
8.19k
{
81
8.19k
  if (m_isPageStarted)
82
0
    _endPage();
83
8.19k
  if (m_isDocumentStarted)
84
714
    _endDocument();
85
8.19k
}
86
87
void libcdr::CDRContentCollector::_startDocument()
88
714
{
89
714
  if (m_isDocumentStarted)
90
0
    return;
91
714
  librevenge::RVNGPropertyList propList;
92
714
  if (m_painter)
93
714
    m_painter->startDocument(propList);
94
714
  m_isDocumentStarted = true;
95
714
}
96
97
void libcdr::CDRContentCollector::_endDocument()
98
714
{
99
714
  if (!m_isDocumentStarted)
100
0
    return;
101
714
  if (m_isPageStarted)
102
0
    _endPage();
103
714
  if (m_painter)
104
714
    m_painter->endDocument();
105
714
  m_isDocumentStarted = false;
106
714
}
107
108
void libcdr::CDRContentCollector::_startPage(double width, double height)
109
4.00k
{
110
4.00k
  if (m_ignorePage)
111
0
    return;
112
4.00k
  if (!m_isDocumentStarted)
113
714
    _startDocument();
114
4.00k
  librevenge::RVNGPropertyList propList;
115
4.00k
  propList.insert("svg:width", width);
116
4.00k
  propList.insert("svg:height", height);
117
4.00k
  if (m_painter)
118
4.00k
    m_painter->startPage(propList);
119
4.00k
  m_isPageStarted = true;
120
4.00k
}
121
122
void libcdr::CDRContentCollector::_endPage()
123
513k
{
124
513k
  if (!m_isPageStarted)
125
509k
    return;
126
23.5k
  while (!m_contentOutputElementsStack.empty())
127
19.5k
  {
128
19.5k
    m_contentOutputElementsStack.top().draw(m_painter);
129
19.5k
    m_contentOutputElementsStack.pop();
130
19.5k
  }
131
4.00k
  while (!m_contentOutputElementsQueue.empty())
132
0
  {
133
0
    m_contentOutputElementsQueue.front().draw(m_painter);
134
0
    m_contentOutputElementsQueue.pop();
135
0
  }
136
4.00k
  if (m_painter)
137
4.00k
    m_painter->endPage();
138
4.00k
  m_isPageStarted = false;
139
4.00k
}
140
141
void libcdr::CDRContentCollector::collectPage(unsigned level)
142
26.4k
{
143
26.4k
  m_isPageProperties = true;
144
26.4k
  m_ignorePage = false;
145
26.4k
  m_currentPageLevel = level;
146
26.4k
  m_page = m_ps.m_pages[m_pageIndex++];
147
148
26.4k
}
149
150
void libcdr::CDRContentCollector::collectObject(unsigned level)
151
9.42k
{
152
9.42k
  if (!m_isPageStarted && !m_currentVectLevel && !m_ignorePage)
153
2.12k
    _startPage(m_page.width, m_page.height);
154
9.42k
  m_currentObjectLevel = level;
155
9.42k
  m_currentFillStyle = CDRFillStyle();
156
9.42k
  m_currentLineStyle = CDRLineStyle();
157
9.42k
  m_currentStyleId = 0;
158
9.42k
  m_currentBBox = CDRBox();
159
9.42k
}
160
161
void libcdr::CDRContentCollector::collectGroup(unsigned level)
162
2.62k
{
163
2.62k
  if (!m_isPageStarted && !m_currentVectLevel && !m_ignorePage)
164
1.52k
    _startPage(m_page.width, m_page.height);
165
2.62k
  CDROutputElementList outputElement;
166
2.62k
  if (m_reverseOrder)
167
2.62k
  {
168
    // Since the CDR objects are drawn in reverse order, reverse the logic of groups too
169
2.62k
    outputElement.addEndGroup();
170
2.62k
    m_outputElementsStack->push(outputElement);
171
2.62k
  }
172
0
  else
173
0
  {
174
0
    librevenge::RVNGPropertyList propList;
175
0
    outputElement.addStartGroup(propList);
176
0
    m_outputElementsQueue->push(outputElement);
177
0
  }
178
2.62k
  m_groupLevels.push(level);
179
2.62k
  m_groupTransforms.push(CDRTransforms());
180
2.62k
}
181
182
void libcdr::CDRContentCollector::collectVect(unsigned level)
183
339
{
184
339
  m_currentVectLevel = level;
185
339
  m_outputElementsStack = &m_fillOutputElementsStack;
186
339
  m_outputElementsQueue = &m_fillOutputElementsQueue;
187
339
  m_page.width = 0.0;
188
339
  m_page.height = 0.0;
189
339
  m_page.offsetX = 0.0;
190
339
  m_page.offsetY = 0.0;
191
339
}
192
193
void libcdr::CDRContentCollector::collectFlags(unsigned flags, bool considerFlags)
194
9.63k
{
195
9.63k
  if (m_isPageProperties && !(flags & 0x00ff0000))
196
427
  {
197
427
    if (!m_isPageStarted)
198
359
      _startPage(m_page.width, m_page.height);
199
427
  }
200
9.20k
  else if (m_isPageProperties && considerFlags)
201
400
    m_ignorePage = true;
202
9.63k
  m_isPageProperties = false;
203
9.63k
}
204
205
void libcdr::CDRContentCollector::collectOtherList()
206
139k
{
207
//  m_isPageProperties = false;
208
139k
}
209
210
void libcdr::CDRContentCollector::collectPath(const CDRPath &path)
211
82.0k
{
212
82.0k
  CDR_DEBUG_MSG(("CDRContentCollector::collectPath\n"));
213
82.0k
  m_currentPath.appendPath(path);
214
82.0k
}
215
216
void libcdr::CDRContentCollector::_flushCurrentPath()
217
519k
{
218
519k
  CDR_DEBUG_MSG(("CDRContentCollector::_flushCurrentPath\n"));
219
519k
  CDROutputElementList outputElement;
220
519k
  if (!m_currentPath.empty() || (!m_splineData.empty() && m_isInSpline))
221
66.6k
  {
222
66.6k
    if (m_polygon && m_isInPolygon)
223
73
      m_polygon->create(m_currentPath);
224
66.6k
    m_polygon.reset();
225
66.6k
    m_isInPolygon = false;
226
66.6k
    if (!m_splineData.empty() && m_isInSpline)
227
202
      m_splineData.create(m_currentPath);
228
66.6k
    m_splineData.clear();
229
66.6k
    m_isInSpline = false;
230
66.6k
    bool firstPoint = true;
231
66.6k
    bool wasMove = false;
232
66.6k
    double initialX = 0.0;
233
66.6k
    double initialY = 0.0;
234
66.6k
    double previousX = 0.0;
235
66.6k
    double previousY = 0.0;
236
66.6k
    double x = 0.0;
237
66.6k
    double y = 0.0;
238
66.6k
    librevenge::RVNGPropertyList style;
239
66.6k
    _fillProperties(style);
240
66.6k
    _lineProperties(style);
241
66.6k
    outputElement.addStyle(style);
242
66.6k
    m_currentPath.transform(m_currentTransforms);
243
66.6k
    if (!m_groupTransforms.empty())
244
353
      m_currentPath.transform(m_groupTransforms.top());
245
66.6k
    CDRTransform tmpTrafo(1.0, 0.0, -m_page.offsetX, 0.0, 1.0, -m_page.offsetY);
246
66.6k
    m_currentPath.transform(tmpTrafo);
247
66.6k
    tmpTrafo = CDRTransform(1.0, 0.0, 0.0, 0.0, -1.0, m_page.height);
248
66.6k
    m_currentPath.transform(tmpTrafo);
249
250
66.6k
    std::vector<librevenge::RVNGPropertyList> tmpPath;
251
252
66.6k
    librevenge::RVNGPropertyListVector path;
253
66.6k
    m_currentPath.writeOut(path);
254
255
66.6k
    bool isPathClosed = m_currentPath.isClosed();
256
257
66.6k
    librevenge::RVNGPropertyListVector::Iter i(path);
258
2.24M
    for (i.rewind(); i.next();)
259
2.17M
    {
260
2.17M
      if (!i()["librevenge:path-action"])
261
0
        continue;
262
2.17M
      if (i()["svg:x"] && i()["svg:y"])
263
1.93M
      {
264
1.93M
        bool ignoreM = false;
265
1.93M
        x = i()["svg:x"]->getDouble();
266
1.93M
        y = i()["svg:y"]->getDouble();
267
1.93M
        if (firstPoint)
268
66.6k
        {
269
66.6k
          initialX = x;
270
66.6k
          initialY = y;
271
66.6k
          firstPoint = false;
272
66.6k
          wasMove = true;
273
66.6k
        }
274
1.86M
        else if (i()["librevenge:path-action"]->getStr() == "M")
275
1.39M
        {
276
          // This is needed for a good generation of path from polygon
277
1.39M
          if (CDR_ALMOST_ZERO(previousX - x) && CDR_ALMOST_ZERO(previousY - y))
278
364k
            ignoreM = true;
279
1.03M
          else
280
1.03M
          {
281
1.03M
            if (!tmpPath.empty())
282
1.03M
            {
283
1.03M
              if (!wasMove)
284
101k
              {
285
101k
                if ((CDR_ALMOST_ZERO(initialX - previousX) && CDR_ALMOST_ZERO(initialY - previousY)) || isPathClosed)
286
27.5k
                {
287
27.5k
                  librevenge::RVNGPropertyList node;
288
27.5k
                  node.insert("librevenge:path-action", "Z");
289
27.5k
                  tmpPath.push_back(node);
290
27.5k
                }
291
101k
              }
292
931k
              else
293
931k
                tmpPath.pop_back();
294
1.03M
            }
295
1.03M
          }
296
297
1.39M
          if (!ignoreM)
298
1.03M
          {
299
1.03M
            initialX = x;
300
1.03M
            initialY = y;
301
1.03M
            wasMove = true;
302
1.03M
          }
303
304
1.39M
        }
305
466k
        else
306
466k
          wasMove = false;
307
308
1.93M
        if (!ignoreM)
309
1.56M
        {
310
1.56M
          tmpPath.push_back(i());
311
1.56M
          previousX = x;
312
1.56M
          previousY = y;
313
1.56M
        }
314
315
1.93M
      }
316
247k
      else if (i()["librevenge:path-action"]->getStr() == "Z")
317
247k
      {
318
247k
        if (!tmpPath.empty() && tmpPath.back()["librevenge:path-action"] && tmpPath.back()["librevenge:path-action"]->getStr() != "Z")
319
228k
          tmpPath.push_back(i());
320
247k
      }
321
2.17M
    }
322
66.6k
    if (!tmpPath.empty())
323
66.6k
    {
324
66.6k
      if (!wasMove)
325
22.6k
      {
326
22.6k
        if ((CDR_ALMOST_ZERO(initialX - previousX) && CDR_ALMOST_ZERO(initialY - previousY)) || isPathClosed)
327
19.3k
        {
328
19.3k
          if (tmpPath.back()["librevenge:path-action"] && tmpPath.back()["librevenge:path-action"]->getStr() != "Z")
329
7.83k
          {
330
7.83k
            librevenge::RVNGPropertyList closedPath;
331
7.83k
            closedPath.insert("librevenge:path-action", "Z");
332
7.83k
            tmpPath.push_back(closedPath);
333
7.83k
          }
334
19.3k
        }
335
22.6k
      }
336
43.9k
      else
337
43.9k
        tmpPath.pop_back();
338
66.6k
    }
339
66.6k
    if (!tmpPath.empty())
340
25.6k
    {
341
25.6k
      librevenge::RVNGPropertyListVector outputPath;
342
880k
      for (std::vector<librevenge::RVNGPropertyList>::const_iterator iter = tmpPath.begin(); iter != tmpPath.end(); ++iter)
343
855k
        outputPath.append(*iter);
344
25.6k
      librevenge::RVNGPropertyList propList;
345
25.6k
      propList.insert("svg:d", outputPath);
346
25.6k
      outputElement.addPath(propList);
347
348
25.6k
    }
349
66.6k
    m_currentPath.clear();
350
66.6k
  }
351
352
519k
  if (m_currentImage.getImage().size())
353
4.73k
  {
354
4.73k
    double cx = m_currentImage.getMiddleX();
355
4.73k
    double cy = m_currentImage.getMiddleY();
356
4.73k
    double corner1x = m_currentImage.m_x1;
357
4.73k
    double corner1y = m_currentImage.m_y1;
358
4.73k
    double corner2x = m_currentImage.m_x1;
359
4.73k
    double corner2y = m_currentImage.m_y2;
360
4.73k
    double corner3x = m_currentImage.m_x2;
361
4.73k
    double corner3y = m_currentImage.m_y2;
362
4.73k
    m_currentTransforms.applyToPoint(cx, cy);
363
4.73k
    m_currentTransforms.applyToPoint(corner1x, corner1y);
364
4.73k
    m_currentTransforms.applyToPoint(corner2x, corner2y);
365
4.73k
    m_currentTransforms.applyToPoint(corner3x, corner3y);
366
4.73k
    if (!m_groupTransforms.empty())
367
14
    {
368
14
      m_groupTransforms.top().applyToPoint(cx, cy);
369
14
      m_groupTransforms.top().applyToPoint(corner1x, corner1y);
370
14
      m_groupTransforms.top().applyToPoint(corner2x, corner2y);
371
14
      m_groupTransforms.top().applyToPoint(corner3x, corner3y);
372
14
    }
373
4.73k
    CDRTransform tmpTrafo(1.0, 0.0, -m_page.offsetX, 0.0, 1.0, -m_page.offsetY);
374
4.73k
    tmpTrafo.applyToPoint(cx, cy);
375
4.73k
    tmpTrafo.applyToPoint(corner1x, corner1y);
376
4.73k
    tmpTrafo.applyToPoint(corner2x, corner2y);
377
4.73k
    tmpTrafo.applyToPoint(corner3x, corner3y);
378
4.73k
    tmpTrafo = CDRTransform(1.0, 0.0, 0.0, 0.0, -1.0, m_page.height);
379
4.73k
    tmpTrafo.applyToPoint(cx, cy);
380
4.73k
    tmpTrafo.applyToPoint(corner1x, corner1y);
381
4.73k
    tmpTrafo.applyToPoint(corner2x, corner2y);
382
4.73k
    tmpTrafo.applyToPoint(corner3x, corner3y);
383
4.73k
    bool flipX(m_currentTransforms.getFlipX());
384
4.73k
    bool flipY(m_currentTransforms.getFlipY());
385
4.73k
    double width = sqrt((corner2x - corner3x)*(corner2x - corner3x) + (corner2y - corner3y)*(corner2y - corner3y));
386
4.73k
    double height = sqrt((corner2x - corner1x)*(corner2x - corner1x) + (corner2y - corner1y)*(corner2y - corner1y));
387
4.73k
    double rotate = atan2(corner3y-corner2y, corner3x-corner2x);
388
389
4.73k
    librevenge::RVNGPropertyList propList;
390
391
4.73k
    propList.insert("svg:x", cx - width / 2.0);
392
4.73k
    propList.insert("svg:width", width);
393
4.73k
    propList.insert("svg:y", cy - height / 2.0);
394
4.73k
    propList.insert("svg:height", height);
395
396
4.73k
    if (flipX)
397
0
    {
398
0
      propList.insert("draw:mirror-horizontal", true);
399
0
      rotate = M_PI - rotate;
400
0
    }
401
4.73k
    if (flipY)
402
0
    {
403
0
      propList.insert("draw:mirror-vertical", true);
404
0
      rotate *= -1.0;
405
0
    }
406
407
4.73k
    while (rotate < 0.0)
408
1
      rotate += 2.0*M_PI;
409
4.73k
    while (rotate > 2.0*M_PI)
410
0
      rotate -= 2.0*M_PI;
411
412
4.73k
    if (!CDR_ALMOST_ZERO(rotate))
413
1.04k
      propList.insert("librevenge:rotate", rotate * 180 / M_PI, librevenge::RVNG_GENERIC);
414
415
4.73k
    propList.insert("librevenge:mime-type", "image/bmp");
416
4.73k
    propList.insert("office:binary-data", m_currentImage.getImage());
417
4.73k
    outputElement.addGraphicObject(propList);
418
4.73k
  }
419
519k
  if (m_currentText && !m_currentText->empty())
420
5.04k
  {
421
5.04k
    double x1 = m_currentTextBox.m_x;
422
5.04k
    double y1 = m_currentTextBox.m_y;
423
5.04k
    double x2 = m_currentTextBox.m_x + m_currentTextBox.m_w;
424
5.04k
    double y2 = m_currentTextBox.m_y - m_currentTextBox.m_h;
425
5.04k
    if (!CDR_ALMOST_ZERO(m_currentTextBox.m_h) && !CDR_ALMOST_ZERO(m_currentTextBox.m_w))
426
293
    {
427
293
      m_currentTransforms.applyToPoint(x1, y1);
428
293
      m_currentTransforms.applyToPoint(x2, y2);
429
293
      if (!m_groupTransforms.empty())
430
3
      {
431
3
        m_groupTransforms.top().applyToPoint(x1, y1);
432
3
        m_groupTransforms.top().applyToPoint(x2, y2);
433
3
      }
434
293
    }
435
4.74k
    else if (!CDR_ALMOST_ZERO(m_currentBBox.getWidth()) && !CDR_ALMOST_ZERO(m_currentBBox.getHeight()))
436
2.30k
    {
437
2.30k
      y1 = m_currentBBox.getMinY();
438
2.30k
      y2 = m_currentBBox.getMinY() + m_currentBBox.getHeight();
439
2.30k
      if ((*m_currentText)[0].m_line[0].m_style.m_align == 2) // Center
440
1.26k
      {
441
1.26k
        x1 = m_currentBBox.getMinX() - m_currentBBox.getWidth() / 4.0;
442
1.26k
        x2 = m_currentBBox.getMinX() + (3.0 * m_currentBBox.getWidth() / 4.0);
443
1.26k
      }
444
1.04k
      else if ((*m_currentText)[0].m_line[0].m_style.m_align == 3) // Right
445
117
      {
446
117
        x1 = m_currentBBox.getMinX() - m_currentBBox.getWidth() / 2.0;
447
117
        x2 = m_currentBBox.getMinX() + m_currentBBox.getWidth() / 2.0;
448
117
      }
449
927
      else
450
927
      {
451
927
        x1 = m_currentBBox.getMinX();
452
927
        x2 = m_currentBBox.getMinX() + m_currentBBox.getWidth();
453
927
      }
454
2.30k
    }
455
2.44k
    else if (!m_currentTransforms.empty())
456
46
    {
457
46
      x1 = m_currentTransforms.getTranslateX();
458
46
      y1 = m_currentTransforms.getTranslateY();
459
46
      x2 = x1;
460
46
      y2 = y1;
461
46
    }
462
463
5.04k
    CDRTransform tmpTrafo(1.0, 0.0, -m_page.offsetX, 0.0, 1.0, -m_page.offsetY);
464
5.04k
    tmpTrafo.applyToPoint(x1, y1);
465
5.04k
    tmpTrafo.applyToPoint(x2, y2);
466
5.04k
    tmpTrafo = CDRTransform(1.0, 0.0, 0.0, 0.0, -1.0, m_page.height);
467
5.04k
    tmpTrafo.applyToPoint(x1, y1);
468
5.04k
    tmpTrafo.applyToPoint(x2, y2);
469
5.04k
    if (x1 > x2)
470
88
      std::swap(x1, x2);
471
5.04k
    if (y1 > y2)
472
2.45k
      std::swap(y1, y2);
473
474
5.04k
    librevenge::RVNGPropertyList textFrameProps;
475
5.04k
    textFrameProps.insert("svg:width", fabs(x2-x1));
476
5.04k
    textFrameProps.insert("svg:height", fabs(y2-y1));
477
5.04k
    textFrameProps.insert("svg:x", x1);
478
5.04k
    textFrameProps.insert("svg:y", y1);
479
5.04k
    textFrameProps.insert("fo:padding-top", 0.0);
480
5.04k
    textFrameProps.insert("fo:padding-bottom", 0.0);
481
5.04k
    textFrameProps.insert("fo:padding-left", 0.0);
482
5.04k
    textFrameProps.insert("fo:padding-right", 0.0);
483
5.04k
    outputElement.addStartTextObject(textFrameProps);
484
5.04k
    for (const auto &i : *m_currentText)
485
11.4k
    {
486
11.4k
      const std::vector<CDRText> &currentLine = i.m_line;
487
11.4k
      if (currentLine.empty())
488
0
        continue;
489
11.4k
      librevenge::RVNGPropertyList paraProps;
490
11.4k
      bool rtl = false;
491
11.4k
      switch (currentLine[0].m_style.m_align)
492
11.4k
      {
493
614
      case 1:  // Left
494
614
        if (!rtl)
495
614
          paraProps.insert("fo:text-align", "left");
496
0
        else
497
0
          paraProps.insert("fo:text-align", "end");
498
614
        break;
499
2.06k
      case 2:  // Center
500
2.06k
        paraProps.insert("fo:text-align", "center");
501
2.06k
        break;
502
117
      case 3:  // Right
503
117
        if (!rtl)
504
117
          paraProps.insert("fo:text-align", "end");
505
0
        else
506
0
          paraProps.insert("fo:text-align", "left");
507
117
        break;
508
1
      case 4:  // Full justify
509
1
        paraProps.insert("fo:text-align", "justify");
510
1
        break;
511
1
      case 5:  // Force justify
512
1
        paraProps.insert("fo:text-align", "full");
513
1
        break;
514
8.66k
      case 0:  // None
515
8.66k
      default:
516
8.66k
        break;
517
11.4k
      }
518
11.4k
      outputElement.addOpenParagraph(paraProps);
519
11.4k
      for (const auto &j : currentLine)
520
3.93M
      {
521
3.93M
        if (!j.m_text.empty())
522
1.84M
        {
523
1.84M
          librevenge::RVNGPropertyList spanProps;
524
1.84M
          double fontSize = (double)cdr_round(144.0*j.m_style.m_fontSize) / 2.0;
525
1.84M
          spanProps.insert("fo:font-size", fontSize, librevenge::RVNG_POINT);
526
1.84M
          if (j.m_style.m_fontName.len())
527
65.2k
            spanProps.insert("style:font-name", j.m_style.m_fontName);
528
1.84M
          if (j.m_style.m_fillStyle.fillType != (unsigned short)-1)
529
11.4k
            spanProps.insert("fo:color", m_ps.getRGBColorString(j.m_style.m_fillStyle.color1));
530
1.84M
          outputElement.addOpenSpan(spanProps);
531
1.84M
          outputElement.addInsertText(j.m_text);
532
1.84M
          outputElement.addCloseSpan();
533
1.84M
        }
534
3.93M
      }
535
11.4k
      outputElement.addCloseParagraph();
536
11.4k
    }
537
5.04k
    outputElement.addEndTextObject();
538
5.04k
  }
539
519k
  m_currentImage = libcdr::CDRImage();
540
519k
  if (!outputElement.empty())
541
71.6k
  {
542
71.6k
    if (m_reverseOrder)
543
71.6k
      m_outputElementsStack->push(outputElement);
544
0
    else
545
0
      m_outputElementsQueue->push(outputElement);
546
71.6k
  }
547
519k
  m_currentTransforms.clear();
548
519k
  m_fillTransforms = libcdr::CDRTransforms();
549
519k
  m_fillOpacity = 1.0;
550
519k
  m_currentText = nullptr;
551
519k
}
552
553
void libcdr::CDRContentCollector::collectTransform(const CDRTransforms &transforms, bool considerGroupTransform)
554
11.7k
{
555
11.7k
  if (m_currentObjectLevel)
556
6.34k
    m_currentTransforms = transforms;
557
5.40k
  else if (!m_groupLevels.empty() && considerGroupTransform)
558
486
    m_groupTransforms.top() = transforms;
559
11.7k
}
560
561
void libcdr::CDRContentCollector::collectFillTransform(const CDRTransforms &fillTrafos)
562
3.69k
{
563
3.69k
  m_fillTransforms = fillTrafos;
564
3.69k
}
565
566
void libcdr::CDRContentCollector::collectLevel(unsigned level)
567
894k
{
568
894k
  if (level <= m_currentObjectLevel)
569
519k
  {
570
519k
    _flushCurrentPath();
571
519k
    m_currentObjectLevel = 0;
572
519k
  }
573
896k
  while (!m_groupLevels.empty() && level <= m_groupLevels.top())
574
2.62k
  {
575
2.62k
    CDROutputElementList outputElement;
576
    // since the CDR objects are drawn in reverse order, reverse group marks too
577
2.62k
    if (m_reverseOrder)
578
2.62k
    {
579
2.62k
      librevenge::RVNGPropertyList propList;
580
2.62k
      outputElement.addStartGroup(propList);
581
2.62k
      m_outputElementsStack->push(outputElement);
582
2.62k
    }
583
0
    else
584
0
    {
585
0
      outputElement.addEndGroup();
586
0
      m_outputElementsQueue->push(outputElement);
587
0
    }
588
2.62k
    m_groupLevels.pop();
589
2.62k
    m_groupTransforms.pop();
590
2.62k
  }
591
894k
  if (m_currentVectLevel && m_spnd && m_groupLevels.empty() && (!m_fillOutputElementsStack.empty() || !m_fillOutputElementsQueue.empty()))
592
125
  {
593
125
    librevenge::RVNGStringVector svgOutput;
594
125
    librevenge::RVNGSVGDrawingGenerator generator(svgOutput, "");
595
125
    librevenge::RVNGPropertyList propList;
596
125
    propList.insert("svg:width", m_page.width);
597
125
    propList.insert("svg:height", m_page.height);
598
125
    generator.startPage(propList);
599
1.62k
    while (!m_fillOutputElementsStack.empty())
600
1.49k
    {
601
1.49k
      m_fillOutputElementsStack.top().draw(&generator);
602
1.49k
      m_fillOutputElementsStack.pop();
603
1.49k
    }
604
125
    while (!m_fillOutputElementsQueue.empty())
605
0
    {
606
0
      m_fillOutputElementsQueue.front().draw(&generator);
607
0
      m_fillOutputElementsQueue.pop();
608
0
    }
609
610
125
    generator.endPage();
611
125
    if (!svgOutput.empty())
612
125
    {
613
125
      const char *header =
614
125
        "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
615
125
      librevenge::RVNGBinaryData output((const unsigned char *)header, strlen(header));
616
125
      output.append((const unsigned char *)svgOutput[0].cstr(), strlen(svgOutput[0].cstr()));
617
125
      m_ps.m_vects[m_spnd] = output;
618
125
    }
619
#if DUMP_VECT
620
    librevenge::RVNGString filename;
621
    filename.sprintf("vect%.8x.svg", m_spnd);
622
    FILE *f = fopen(filename.cstr(), "wb");
623
    if (f)
624
    {
625
      const unsigned char *tmpBuffer = m_ps.m_vects[m_spnd].getDataBuffer();
626
      for (unsigned long k = 0; k < m_ps.m_vects[m_spnd].size(); k++)
627
        fprintf(f, "%c",tmpBuffer[k]);
628
      fclose(f);
629
    }
630
#endif
631
125
    m_spnd = 0;
632
125
    m_page.width = 0.0;
633
125
    m_page.height = 0.0;
634
125
    m_page.offsetX = 0.0;
635
125
    m_page.offsetY = 0.0;
636
125
  }
637
894k
  if (level <= m_currentVectLevel)
638
512k
  {
639
512k
    m_currentVectLevel = 0;
640
512k
    m_outputElementsStack = &m_contentOutputElementsStack;
641
512k
    m_outputElementsQueue = &m_contentOutputElementsQueue;
642
512k
    m_page = m_ps.m_pages[m_pageIndex ? m_pageIndex-1 : 0];
643
512k
  }
644
894k
  if (level <= m_currentPageLevel)
645
513k
  {
646
513k
    _endPage();
647
513k
    m_currentPageLevel = 0;
648
513k
  }
649
894k
}
650
651
void libcdr::CDRContentCollector::collectFillStyleId(unsigned id)
652
16.3k
{
653
16.3k
  std::map<unsigned, CDRFillStyle>::const_iterator iter = m_ps.m_fillStyles.find(id);
654
16.3k
  if (iter != m_ps.m_fillStyles.end())
655
13.2k
    m_currentFillStyle = iter->second;
656
16.3k
}
657
658
void libcdr::CDRContentCollector::collectLineStyleId(unsigned id)
659
14.6k
{
660
14.6k
  std::map<unsigned, CDRLineStyle>::const_iterator iter = m_ps.m_lineStyles.find(id);
661
14.6k
  if (iter != m_ps.m_lineStyles.end())
662
10.8k
    m_currentLineStyle = iter->second;
663
14.6k
}
664
665
void libcdr::CDRContentCollector::collectRotate(double angle, double cx, double cy)
666
0
{
667
0
  CDRTransform trafo1(1.0, 0.0, -cx, 0.0, 1.0, -cy);
668
0
  m_currentPath.transform(trafo1);
669
0
  CDRTransform trafo2(cos(angle), -sin(angle), 0, sin(angle), cos(angle), 0);
670
0
  m_currentPath.transform(trafo2);
671
0
  CDRTransform trafo3(1.0, 0.0, cx, 0.0, 1.0, cy);
672
0
  m_currentPath.transform(trafo3);
673
0
}
674
675
void libcdr::CDRContentCollector::collectPolygon()
676
368
{
677
368
  m_isInPolygon = true;
678
368
}
679
680
void libcdr::CDRContentCollector::collectSpline()
681
362
{
682
362
  m_isInSpline = true;
683
362
}
684
685
void libcdr::CDRContentCollector::collectPolygonTransform(unsigned numAngles, unsigned nextPoint, double rx, double ry, double cx, double cy)
686
2.05k
{
687
2.05k
  m_polygon.reset(new CDRPolygon(numAngles, nextPoint, rx, ry, cx, cy));
688
2.05k
}
689
690
void libcdr::CDRContentCollector::_fillProperties(librevenge::RVNGPropertyList &propList)
691
66.6k
{
692
66.6k
  if (m_currentFillStyle.fillType == (unsigned short)-1 && m_currentStyleId)
693
6.46k
  {
694
6.46k
    CDRStyle tmpStyle;
695
6.46k
    m_ps.getRecursedStyle(tmpStyle, m_currentStyleId);
696
6.46k
    m_currentFillStyle = tmpStyle.m_fillStyle;
697
6.46k
  }
698
699
66.6k
  if (m_fillOpacity < 1.0)
700
33
    propList.insert("draw:opacity", m_fillOpacity, librevenge::RVNG_PERCENT);
701
66.6k
  if (m_currentFillStyle.fillType == 0)
702
4.69k
    propList.insert("draw:fill", "none");
703
61.9k
  else
704
61.9k
  {
705
61.9k
    if (m_currentFillStyle.fillType == (unsigned short)-1)
706
11.8k
      propList.insert("draw:fill", "none");
707
50.0k
    else
708
50.0k
    {
709
50.0k
      switch (m_currentFillStyle.fillType)
710
50.0k
      {
711
1.47k
      case 1: // Solid
712
1.47k
        propList.insert("draw:fill", "solid");
713
1.47k
        propList.insert("draw:fill-color", m_ps.getRGBColorString(m_currentFillStyle.color1));
714
1.47k
        propList.insert("svg:fill-rule", "evenodd");
715
1.47k
        break;
716
43.0k
      case 2: // Gradient
717
43.0k
        if (m_currentFillStyle.gradient.m_stops.empty())
718
235
          propList.insert("draw:fill", "none");
719
42.8k
        else if (m_currentFillStyle.gradient.m_stops.size() == 1)
720
88
        {
721
88
          propList.insert("draw:fill", "solid");
722
88
          propList.insert("draw:fill-color", m_ps.getRGBColorString(m_currentFillStyle.gradient.m_stops[0].m_color));
723
88
          propList.insert("svg:fill-rule", "evenodd");
724
88
        }
725
42.7k
        else if (m_currentFillStyle.gradient.m_stops.size() == 2)
726
41.0k
        {
727
41.0k
          double angle = m_currentFillStyle.gradient.m_angle * 180 / M_PI;
728
41.0k
          normalizeAngle(angle);
729
41.0k
          propList.insert("draw:fill", "gradient");
730
41.0k
          propList.insert("draw:start-color", m_ps.getRGBColorString(m_currentFillStyle.gradient.m_stops[0].m_color));
731
41.0k
          propList.insert("draw:end-color", m_ps.getRGBColorString(m_currentFillStyle.gradient.m_stops[1].m_color));
732
41.0k
          propList.insert("draw:angle", (int)angle);
733
41.0k
          switch (m_currentFillStyle.gradient.m_type)
734
41.0k
          {
735
460
          case 1: // linear
736
467
          case 3: // conical
737
467
            propList.insert("draw:style", "linear");
738
467
            angle += 90.0;
739
467
            normalizeAngle(angle);
740
467
            propList.insert("draw:angle", (int)angle);
741
467
            propList.insert("draw:border", (double)(m_currentFillStyle.gradient.m_edgeOffset)/100.0, librevenge::RVNG_PERCENT);
742
467
            break;
743
40.3k
          case 2: // radial
744
40.3k
            propList.insert("draw:border", (2.0 * (double)(m_currentFillStyle.gradient.m_edgeOffset)/100.0), librevenge::RVNG_PERCENT);
745
40.3k
            propList.insert("draw:style", "radial");
746
40.3k
            propList.insert("svg:cx", (double)(0.5 + m_currentFillStyle.gradient.m_centerXOffset/200.0), librevenge::RVNG_PERCENT);
747
40.3k
            propList.insert("svg:cy", (double)(0.5 + m_currentFillStyle.gradient.m_centerXOffset/200.0), librevenge::RVNG_PERCENT);
748
40.3k
            break;
749
42
          case 4: // square
750
42
            propList.insert("draw:border", (2.0 * (double)(m_currentFillStyle.gradient.m_edgeOffset)/100.0), librevenge::RVNG_PERCENT);
751
42
            propList.insert("draw:style", "square");
752
42
            propList.insert("svg:cx", (double)(0.5 + m_currentFillStyle.gradient.m_centerXOffset/200.0), librevenge::RVNG_PERCENT);
753
42
            propList.insert("svg:cy", (double)(0.5 + m_currentFillStyle.gradient.m_centerXOffset/200.0), librevenge::RVNG_PERCENT);
754
42
            break;
755
206
          default:
756
206
            propList.insert("draw:style", "linear");
757
206
            angle += 90.0;
758
206
            normalizeAngle(angle);
759
206
            propList.insert("draw:angle", (int)angle);
760
206
            librevenge::RVNGPropertyListVector vec;
761
206
            for (auto &gradStop : m_currentFillStyle.gradient.m_stops)
762
412
            {
763
412
              librevenge::RVNGPropertyList stopElement;
764
412
              stopElement.insert("svg:offset", gradStop.m_offset, librevenge::RVNG_PERCENT);
765
412
              stopElement.insert("svg:stop-color", m_ps.getRGBColorString(gradStop.m_color));
766
412
              stopElement.insert("svg:stop-opacity", m_fillOpacity, librevenge::RVNG_PERCENT);
767
412
              vec.append(stopElement);
768
412
            }
769
206
            propList.insert("svg:linearGradient", vec);
770
206
            break;
771
41.0k
          }
772
41.0k
        }
773
1.64k
        else // output svg gradient as a hail mary pass towards ODG that does not really support it
774
1.64k
        {
775
1.64k
          propList.insert("draw:fill", "gradient");
776
1.64k
          propList.insert("draw:style", "linear");
777
1.64k
          double angle = m_currentFillStyle.gradient.m_angle * 180 / M_PI;
778
1.64k
          angle += 90.0;
779
1.64k
          normalizeAngle(angle);
780
1.64k
          propList.insert("draw:angle", (int)angle);
781
1.64k
          librevenge::RVNGPropertyListVector vec;
782
1.64k
          for (auto &gradStop : m_currentFillStyle.gradient.m_stops)
783
512k
          {
784
512k
            librevenge::RVNGPropertyList stopElement;
785
512k
            stopElement.insert("svg:offset", gradStop.m_offset, librevenge::RVNG_PERCENT);
786
512k
            stopElement.insert("svg:stop-color", m_ps.getRGBColorString(gradStop.m_color));
787
512k
            stopElement.insert("svg:stop-opacity", m_fillOpacity, librevenge::RVNG_PERCENT);
788
512k
            vec.append(stopElement);
789
512k
          }
790
1.64k
          propList.insert("svg:linearGradient", vec);
791
1.64k
        }
792
43.0k
        break;
793
43.0k
      case 7: // Pattern
794
1.20k
      case 8: // Pattern
795
1.20k
      {
796
1.20k
        auto iterPattern = m_ps.m_patterns.find(m_currentFillStyle.imageFill.id);
797
1.20k
        if (iterPattern != m_ps.m_patterns.end())
798
323
        {
799
323
          propList.insert("draw:fill", "bitmap");
800
323
          librevenge::RVNGBinaryData image;
801
323
          _generateBitmapFromPattern(image, iterPattern->second, m_currentFillStyle.color1, m_currentFillStyle.color2);
802
#if DUMP_PATTERN
803
          librevenge::RVNGString filename;
804
          filename.sprintf("pattern%.8x.bmp", m_currentFillStyle.imageFill.id);
805
          FILE *f = fopen(filename.cstr(), "wb");
806
          if (f)
807
          {
808
            const unsigned char *tmpBuffer = image.getDataBuffer();
809
            for (unsigned long k = 0; k < image.size(); k++)
810
              fprintf(f, "%c",tmpBuffer[k]);
811
            fclose(f);
812
          }
813
#endif
814
323
          propList.insert("draw:fill-image", image);
815
323
          propList.insert("librevenge:mime-type", "image/bmp");
816
323
          propList.insert("style:repeat", "repeat");
817
323
          if (m_currentFillStyle.imageFill.isRelative)
818
0
          {
819
0
            propList.insert("svg:width", m_currentFillStyle.imageFill.width, librevenge::RVNG_PERCENT);
820
0
            propList.insert("svg:height", m_currentFillStyle.imageFill.height, librevenge::RVNG_PERCENT);
821
0
          }
822
323
          else
823
323
          {
824
323
            double scaleX = 1.0;
825
323
            double scaleY = 1.0;
826
323
            if (m_currentFillStyle.imageFill.flags & 0x04) // scale fill with image
827
0
            {
828
0
              scaleX = m_currentTransforms.getScaleX();
829
0
              scaleY = m_currentTransforms.getScaleY();
830
0
            }
831
323
            propList.insert("svg:width", m_currentFillStyle.imageFill.width * scaleX);
832
323
            propList.insert("svg:height", m_currentFillStyle.imageFill.height * scaleY);
833
323
          }
834
323
          propList.insert("draw:fill-image-ref-point", "bottom-left");
835
323
          if (m_currentFillStyle.imageFill.isRelative)
836
0
          {
837
0
            if (!CDR_ALMOST_ZERO(m_currentFillStyle.imageFill.xOffset) && !CDR_ALMOST_ZERO(m_currentFillStyle.imageFill.xOffset))
838
0
              propList.insert("draw:fill-image-ref-point-x", m_currentFillStyle.imageFill.xOffset, librevenge::RVNG_PERCENT);
839
0
            if (!CDR_ALMOST_ZERO(m_currentFillStyle.imageFill.yOffset) && !CDR_ALMOST_ZERO(m_currentFillStyle.imageFill.yOffset))
840
0
              propList.insert("draw:fill-image-ref-point-y", m_currentFillStyle.imageFill.yOffset, librevenge::RVNG_PERCENT);
841
0
          }
842
323
          else
843
323
          {
844
323
            if (!CDR_ALMOST_ZERO(m_fillTransforms.getTranslateX()))
845
0
            {
846
0
              double xOffset = m_fillTransforms.getTranslateX() / m_currentFillStyle.imageFill.width;
847
0
              normalize(xOffset);
848
0
              propList.insert("draw:fill-image-ref-point-x", xOffset, librevenge::RVNG_PERCENT);
849
0
            }
850
323
            if (!CDR_ALMOST_ZERO(m_fillTransforms.getTranslateY()))
851
0
            {
852
0
              double yOffset = m_fillTransforms.getTranslateY() / m_currentFillStyle.imageFill.width;
853
0
              normalize(yOffset);
854
0
              propList.insert("draw:fill-image-ref-point-y", 1.0 - yOffset, librevenge::RVNG_PERCENT);
855
0
            }
856
323
          }
857
323
        }
858
886
        else
859
886
        {
860
          // We did not find the pattern, so fill solid with the background colour
861
886
          propList.insert("draw:fill", "solid");
862
886
          propList.insert("draw:fill-color", m_ps.getRGBColorString(m_currentFillStyle.color2));
863
886
          propList.insert("svg:fill-rule", "evenodd");
864
886
        }
865
1.20k
      }
866
1.20k
      break;
867
1.77k
      case 9: // Bitmap
868
3.01k
      case 11: // Texture
869
3.01k
      {
870
3.01k
        auto iterBmp = m_ps.m_bmps.find(m_currentFillStyle.imageFill.id);
871
3.01k
        if (iterBmp != m_ps.m_bmps.end())
872
1.44k
        {
873
1.44k
          propList.insert("librevenge:mime-type", "image/bmp");
874
1.44k
          propList.insert("draw:fill", "bitmap");
875
1.44k
          propList.insert("draw:fill-image", iterBmp->second);
876
1.44k
          propList.insert("style:repeat", "repeat");
877
1.44k
          if (m_currentFillStyle.imageFill.isRelative)
878
0
          {
879
0
            propList.insert("svg:width", m_currentFillStyle.imageFill.width, librevenge::RVNG_PERCENT);
880
0
            propList.insert("svg:height", m_currentFillStyle.imageFill.height, librevenge::RVNG_PERCENT);
881
0
          }
882
1.44k
          else
883
1.44k
          {
884
1.44k
            double scaleX = 1.0;
885
1.44k
            double scaleY = 1.0;
886
1.44k
            if (m_currentFillStyle.imageFill.flags & 0x04) // scale fill with image
887
0
            {
888
0
              scaleX = m_currentTransforms.getScaleX();
889
0
              scaleY = m_currentTransforms.getScaleY();
890
0
            }
891
1.44k
            propList.insert("svg:width", m_currentFillStyle.imageFill.width * scaleX);
892
1.44k
            propList.insert("svg:height", m_currentFillStyle.imageFill.height * scaleY);
893
1.44k
          }
894
1.44k
          propList.insert("draw:fill-image-ref-point", "bottom-left");
895
1.44k
          if (m_currentFillStyle.imageFill.isRelative)
896
0
          {
897
0
            if (!CDR_ALMOST_ZERO(m_currentFillStyle.imageFill.xOffset) && !CDR_ALMOST_ZERO(m_currentFillStyle.imageFill.xOffset))
898
0
              propList.insert("draw:fill-image-ref-point-x", m_currentFillStyle.imageFill.xOffset, librevenge::RVNG_PERCENT);
899
0
            if (!CDR_ALMOST_ZERO(m_currentFillStyle.imageFill.yOffset) && !CDR_ALMOST_ZERO(m_currentFillStyle.imageFill.yOffset))
900
0
              propList.insert("draw:fill-image-ref-point-y", m_currentFillStyle.imageFill.yOffset, librevenge::RVNG_PERCENT);
901
0
          }
902
1.44k
          else
903
1.44k
          {
904
1.44k
            if (!CDR_ALMOST_ZERO(m_fillTransforms.getTranslateX()))
905
0
            {
906
0
              double xOffset = m_fillTransforms.getTranslateX() / m_currentFillStyle.imageFill.width;
907
0
              while (xOffset < 0.0)
908
0
                xOffset += 1.0;
909
0
              while (xOffset > 1.0)
910
0
                xOffset -= 1.0;
911
0
              propList.insert("draw:fill-image-ref-point-x", xOffset, librevenge::RVNG_PERCENT);
912
0
            }
913
1.44k
            if (!CDR_ALMOST_ZERO(m_fillTransforms.getTranslateY()))
914
0
            {
915
0
              double yOffset = m_fillTransforms.getTranslateY() / m_currentFillStyle.imageFill.width;
916
0
              while (yOffset < 0.0)
917
0
                yOffset += 1.0;
918
0
              while (yOffset > 1.0)
919
0
                yOffset -= 1.0;
920
0
              propList.insert("draw:fill-image-ref-point-y", 1.0 - yOffset, librevenge::RVNG_PERCENT);
921
0
            }
922
1.44k
          }
923
1.44k
        }
924
1.57k
        else
925
1.57k
          propList.insert("draw:fill", "none");
926
3.01k
      }
927
3.01k
      break;
928
327
      case 10: // Full color
929
327
      {
930
327
        auto iterVect = m_ps.m_vects.find(m_currentFillStyle.imageFill.id);
931
327
        if (iterVect != m_ps.m_vects.end())
932
0
        {
933
0
          propList.insert("draw:fill", "bitmap");
934
0
          propList.insert("librevenge:mime-type", "image/svg+xml");
935
0
          propList.insert("draw:fill-image", iterVect->second);
936
0
          propList.insert("style:repeat", "repeat");
937
0
          if (m_currentFillStyle.imageFill.isRelative)
938
0
          {
939
0
            propList.insert("svg:width", m_currentFillStyle.imageFill.width, librevenge::RVNG_PERCENT);
940
0
            propList.insert("svg:height", m_currentFillStyle.imageFill.height, librevenge::RVNG_PERCENT);
941
0
          }
942
0
          else
943
0
          {
944
0
            double scaleX = 1.0;
945
0
            double scaleY = 1.0;
946
0
            if (m_currentFillStyle.imageFill.flags & 0x04) // scale fill with image
947
0
            {
948
0
              scaleX = m_currentTransforms.getScaleX();
949
0
              scaleY = m_currentTransforms.getScaleY();
950
0
            }
951
0
            propList.insert("svg:width", m_currentFillStyle.imageFill.width * scaleX);
952
0
            propList.insert("svg:height", m_currentFillStyle.imageFill.height * scaleY);
953
0
          }
954
0
          propList.insert("draw:fill-image-ref-point", "bottom-left");
955
0
          if (m_currentFillStyle.imageFill.isRelative)
956
0
          {
957
0
            if (!CDR_ALMOST_ZERO(m_currentFillStyle.imageFill.xOffset) && !CDR_ALMOST_ZERO(m_currentFillStyle.imageFill.xOffset))
958
0
              propList.insert("draw:fill-image-ref-point-x", m_currentFillStyle.imageFill.xOffset, librevenge::RVNG_PERCENT);
959
0
            if (!CDR_ALMOST_ZERO(m_currentFillStyle.imageFill.yOffset) && !CDR_ALMOST_ZERO(m_currentFillStyle.imageFill.yOffset))
960
0
              propList.insert("draw:fill-image-ref-point-y", m_currentFillStyle.imageFill.yOffset, librevenge::RVNG_PERCENT);
961
0
          }
962
0
          else
963
0
          {
964
0
            if (!CDR_ALMOST_ZERO(m_fillTransforms.getTranslateX()))
965
0
            {
966
0
              double xOffset = m_fillTransforms.getTranslateX() / m_currentFillStyle.imageFill.width;
967
0
              while (xOffset < 0.0)
968
0
                xOffset += 1.0;
969
0
              while (xOffset > 1.0)
970
0
                xOffset -= 1.0;
971
0
              propList.insert("draw:fill-image-ref-point-x", xOffset, librevenge::RVNG_PERCENT);
972
0
            }
973
0
            if (!CDR_ALMOST_ZERO(m_fillTransforms.getTranslateY()))
974
0
            {
975
0
              double yOffset = m_fillTransforms.getTranslateY() / m_currentFillStyle.imageFill.width;
976
0
              while (yOffset < 0.0)
977
0
                yOffset += 1.0;
978
0
              while (yOffset > 1.0)
979
0
                yOffset -= 1.0;
980
0
              propList.insert("draw:fill-image-ref-point-y", 1.0 - yOffset, librevenge::RVNG_PERCENT);
981
0
            }
982
0
          }
983
0
        }
984
327
        else
985
327
          propList.insert("draw:fill", "none");
986
327
      }
987
327
      break;
988
1.01k
      default:
989
1.01k
        propList.insert("draw:fill", "none");
990
1.01k
        break;
991
50.0k
      }
992
50.0k
    }
993
61.9k
  }
994
66.6k
}
995
996
void libcdr::CDRContentCollector::_lineProperties(librevenge::RVNGPropertyList &propList)
997
66.6k
{
998
66.6k
  if (m_currentLineStyle.lineType == (unsigned short)-1 && m_currentStyleId)
999
7.92k
  {
1000
7.92k
    CDRStyle tmpStyle;
1001
7.92k
    m_ps.getRecursedStyle(tmpStyle, m_currentStyleId);
1002
7.92k
    m_currentLineStyle = tmpStyle.m_lineStyle;
1003
7.92k
  }
1004
1005
66.6k
  if (m_currentLineStyle.lineType == (unsigned short)-1)
1006
    /* No line style specified and also no line style from the style id,
1007
       the shape has no outline then. */
1008
13.0k
    propList.insert("draw:stroke", "none");
1009
53.5k
  else
1010
53.5k
  {
1011
53.5k
    if (m_currentLineStyle.lineType & 0x1)
1012
145
      propList.insert("draw:stroke", "none");
1013
53.3k
    else if (m_currentLineStyle.lineType & 0x6)
1014
51.4k
    {
1015
51.4k
      if (m_currentLineStyle.dashArray.size() && (m_currentLineStyle.lineType & 0x4))
1016
3.30k
        propList.insert("draw:stroke", "dash");
1017
48.1k
      else
1018
48.1k
        propList.insert("draw:stroke", "solid");
1019
51.4k
      double scale = 1.0;
1020
51.4k
      if (m_currentLineStyle.lineType & 0x20) // scale line with image
1021
3.65k
      {
1022
3.65k
        scale = m_currentTransforms.getScaleX();
1023
3.65k
        double scaleY = m_currentTransforms.getScaleY();
1024
3.65k
        if (scaleY > scale)
1025
33
          scale = scaleY;
1026
3.65k
      }
1027
51.4k
      scale *= m_currentLineStyle.stretch;
1028
51.4k
      propList.insert("svg:stroke-width", m_currentLineStyle.lineWidth * scale);
1029
51.4k
      propList.insert("svg:stroke-color", m_ps.getRGBColorString(m_currentLineStyle.color));
1030
1031
51.4k
      switch (m_currentLineStyle.capsType)
1032
51.4k
      {
1033
27
      case 1:
1034
27
        propList.insert("svg:stroke-linecap", "round");
1035
27
        break;
1036
18
      case 2:
1037
18
        propList.insert("svg:stroke-linecap", "square");
1038
18
        break;
1039
51.4k
      default:
1040
51.4k
        propList.insert("svg:stroke-linecap", "butt");
1041
51.4k
      }
1042
1043
51.4k
      switch (m_currentLineStyle.joinType)
1044
51.4k
      {
1045
168
      case 1:
1046
168
        propList.insert("svg:stroke-linejoin", "round");
1047
168
        break;
1048
24
      case 2:
1049
24
        propList.insert("svg:stroke-linejoin", "bevel");
1050
24
        break;
1051
51.2k
      default:
1052
51.2k
        propList.insert("svg:stroke-linejoin", "miter");
1053
51.4k
      }
1054
1055
51.4k
      if (m_currentLineStyle.dashArray.size())
1056
46.5k
      {
1057
46.5k
        int dots1 = 0;
1058
46.5k
        int dots2 = 0;
1059
46.5k
        unsigned dots1len = 0;
1060
46.5k
        unsigned dots2len = 0;
1061
46.5k
        unsigned gap = 0;
1062
1063
46.5k
        if (m_currentLineStyle.dashArray.size() >= 2)
1064
45.6k
        {
1065
45.6k
          dots1len = m_currentLineStyle.dashArray[0];
1066
45.6k
          gap = m_currentLineStyle.dashArray[1];
1067
45.6k
        }
1068
1069
46.5k
        unsigned long count = m_currentLineStyle.dashArray.size() / 2;
1070
46.5k
        unsigned i = 0;
1071
95.3k
        for (; i < count;)
1072
51.6k
        {
1073
51.6k
          if (dots1len == m_currentLineStyle.dashArray[2*i])
1074
48.7k
            dots1++;
1075
2.86k
          else
1076
2.86k
            break;
1077
48.7k
          gap = gap < m_currentLineStyle.dashArray[2*i+1] ?  m_currentLineStyle.dashArray[2*i+1] : gap;
1078
48.7k
          i++;
1079
48.7k
        }
1080
46.5k
        if (i < count)
1081
2.86k
        {
1082
2.86k
          dots2len = m_currentLineStyle.dashArray[2*i];
1083
2.86k
          gap = gap < m_currentLineStyle.dashArray[2*i+1] ? m_currentLineStyle.dashArray[2*i+1] : gap;
1084
2.86k
        }
1085
50.7k
        for (; i < count;)
1086
6.26k
        {
1087
6.26k
          if (dots2len == m_currentLineStyle.dashArray[2*i])
1088
4.21k
            dots2++;
1089
2.04k
          else
1090
2.04k
            break;
1091
4.21k
          gap = gap < m_currentLineStyle.dashArray[2*i+1] ? m_currentLineStyle.dashArray[2*i+1] : gap;
1092
4.21k
          i++;
1093
4.21k
        }
1094
46.5k
        if (!dots2)
1095
43.7k
        {
1096
43.7k
          dots2 = dots1;
1097
43.7k
          dots2len = dots1len;
1098
43.7k
        }
1099
46.5k
        propList.insert("draw:dots1", dots1);
1100
46.5k
        propList.insert("draw:dots1-length", 72.0*(m_currentLineStyle.lineWidth * scale)*dots1len, librevenge::RVNG_POINT);
1101
46.5k
        propList.insert("draw:dots2", dots2);
1102
46.5k
        propList.insert("draw:dots2-length", 72.0*(m_currentLineStyle.lineWidth * scale)*dots2len, librevenge::RVNG_POINT);
1103
46.5k
        propList.insert("draw:distance", 72.0*(m_currentLineStyle.lineWidth * scale)*gap, librevenge::RVNG_POINT);
1104
46.5k
      }
1105
51.4k
    }
1106
1.93k
    else
1107
1.93k
    {
1108
1.93k
      propList.insert("draw:stroke", "solid");
1109
1.93k
      propList.insert("svg:stroke-width", 0.0);
1110
1.93k
      propList.insert("svg:stroke-color", "#000000");
1111
1.93k
    }
1112
53.5k
  }
1113
1114
  // Deal with line markers (arrows, etc.)
1115
66.6k
  if (!m_currentLineStyle.startMarker.empty())
1116
2.28k
  {
1117
2.28k
    CDRPath startMarker(m_currentLineStyle.startMarker);
1118
2.28k
    startMarker.transform(m_currentTransforms);
1119
2.28k
    if (!m_groupTransforms.empty())
1120
3
      startMarker.transform(m_groupTransforms.top());
1121
2.28k
    CDRTransform tmpTrafo = CDRTransform(1.0, 0.0, 0.0, 0.0, -1.0, 0);
1122
2.28k
    startMarker.transform(tmpTrafo);
1123
2.28k
    librevenge::RVNGString path, viewBox;
1124
2.28k
    double width;
1125
2.28k
    startMarker.writeOut(path, viewBox, width);
1126
2.28k
    propList.insert("draw:marker-start-viewbox", viewBox);
1127
2.28k
    propList.insert("draw:marker-start-path", path);
1128
    // propList.insert("draw:marker-start-width", width);
1129
2.28k
  }
1130
66.6k
  if (!m_currentLineStyle.endMarker.empty())
1131
2.50k
  {
1132
2.50k
    CDRPath endMarker(m_currentLineStyle.endMarker);
1133
2.50k
    endMarker.transform(m_currentTransforms);
1134
2.50k
    if (!m_groupTransforms.empty())
1135
1
      endMarker.transform(m_groupTransforms.top());
1136
2.50k
    CDRTransform tmpTrafo = CDRTransform(-1.0, 0.0, 0.0, 0.0, -1.0, 0);
1137
2.50k
    endMarker.transform(tmpTrafo);
1138
2.50k
    librevenge::RVNGString path, viewBox;
1139
2.50k
    double width;
1140
2.50k
    endMarker.writeOut(path, viewBox, width);
1141
2.50k
    propList.insert("draw:marker-end-viewbox", viewBox);
1142
2.50k
    propList.insert("draw:marker-end-path", path);
1143
    // propList.insert("draw:marker-end-width", width);
1144
2.50k
  }
1145
1146
1147
1148
66.6k
}
1149
1150
void libcdr::CDRContentCollector::_generateBitmapFromPattern(librevenge::RVNGBinaryData &bitmap, const CDRPattern &pattern, const CDRColor &fgColor, const CDRColor &bgColor)
1151
323
{
1152
323
  unsigned height = pattern.height;
1153
323
  unsigned width = pattern.width;
1154
323
  auto tmpPixelSize = (unsigned)(height * width);
1155
323
  if (tmpPixelSize < (unsigned)height) // overflow
1156
0
    return;
1157
1158
323
  unsigned tmpDIBImageSize = tmpPixelSize * 4;
1159
323
  if (tmpPixelSize > tmpDIBImageSize) // overflow !!!
1160
0
    return;
1161
1162
323
  unsigned tmpDIBOffsetBits = 14 + 40;
1163
323
  unsigned tmpDIBFileSize = tmpDIBOffsetBits + tmpDIBImageSize;
1164
323
  if (tmpDIBImageSize > tmpDIBFileSize) // overflow !!!
1165
0
    return;
1166
1167
  // Create DIB file header
1168
323
  writeU16(bitmap, 0x4D42);  // Type
1169
323
  writeU32(bitmap, (int)tmpDIBFileSize); // Size
1170
323
  writeU16(bitmap, 0); // Reserved1
1171
323
  writeU16(bitmap, 0); // Reserved2
1172
323
  writeU32(bitmap, (int)tmpDIBOffsetBits); // OffsetBits
1173
1174
  // Create DIB Info header
1175
323
  writeU32(bitmap, 40); // Size
1176
1177
323
  writeU32(bitmap, (int)width);  // Width
1178
323
  writeU32(bitmap, (int)height); // Height
1179
1180
323
  writeU16(bitmap, 1); // Planes
1181
323
  writeU16(bitmap, 32); // BitCount
1182
323
  writeU32(bitmap, 0); // Compression
1183
323
  writeU32(bitmap, (int)tmpDIBImageSize); // SizeImage
1184
323
  writeU32(bitmap, 0); // XPelsPerMeter
1185
323
  writeU32(bitmap, 0); // YPelsPerMeter
1186
323
  writeU32(bitmap, 0); // ColorsUsed
1187
323
  writeU32(bitmap, 0); // ColorsImportant
1188
1189
  // The Bitmaps in CDR are padded to 32bit border
1190
323
  unsigned lineWidth = (width + 7) / 8;
1191
1192
323
  unsigned foreground = m_ps._getRGBColor(fgColor);
1193
323
  unsigned background = m_ps._getRGBColor(bgColor);
1194
1195
899
  for (unsigned j = height; j > 0; --j)
1196
576
  {
1197
576
    unsigned i = 0;
1198
576
    unsigned k = 0;
1199
17.2k
    while (i <lineWidth && k < width)
1200
16.6k
    {
1201
16.6k
      unsigned l = 0;
1202
16.6k
      unsigned char c = 0;
1203
16.6k
      const unsigned index = (j-1)*lineWidth+i;
1204
16.6k
      if (index < pattern.pattern.size())
1205
16.6k
        c = pattern.pattern[index];
1206
16.6k
      i++;
1207
149k
      while (k < width && l < 8)
1208
133k
      {
1209
133k
        if (c & 0x80)
1210
101k
          writeU32(bitmap, (int)background);
1211
32.0k
        else
1212
32.0k
          writeU32(bitmap, (int)foreground);
1213
133k
        c <<= 1;
1214
133k
        l++;
1215
133k
        k++;
1216
133k
      }
1217
16.6k
    }
1218
576
  }
1219
323
}
1220
1221
void libcdr::CDRContentCollector::collectBitmap(unsigned imageId, double x1, double x2, double y1, double y2)
1222
8.11k
{
1223
8.11k
  auto iter = m_ps.m_bmps.find(imageId);
1224
8.11k
  if (iter != m_ps.m_bmps.end())
1225
4.75k
    m_currentImage = CDRImage(iter->second, x1, x2, y1, y2);
1226
8.11k
}
1227
1228
void libcdr::CDRContentCollector::collectPpdt(const std::vector<std::pair<double, double> > &points, const std::vector<unsigned> &knotVector)
1229
1.97k
{
1230
1.97k
  m_splineData = CDRSplineData(points, knotVector);
1231
1.97k
}
1232
1233
void libcdr::CDRContentCollector::collectFillOpacity(double opacity)
1234
180
{
1235
180
  m_fillOpacity = opacity;
1236
180
}
1237
1238
void libcdr::CDRContentCollector::collectBBox(double x0, double y0, double x1, double y1)
1239
8.11k
{
1240
8.11k
  CDRBox bBox(x0, y0, x1, y1);
1241
8.11k
  if (m_currentVectLevel && CDR_ALMOST_ZERO(m_page.width) && CDR_ALMOST_ZERO(m_page.height))
1242
162
  {
1243
162
    m_page.width = bBox.getWidth();
1244
162
    m_page.height = bBox.getHeight();
1245
162
    m_page.offsetX = bBox.getMinX();
1246
162
    m_page.offsetY = bBox.getMinY();
1247
162
  }
1248
8.11k
  m_currentBBox = bBox;
1249
8.11k
}
1250
1251
void libcdr::CDRContentCollector::collectSpnd(unsigned spnd)
1252
5.56k
{
1253
5.56k
  if (m_currentVectLevel && !m_spnd)
1254
125
    m_spnd = spnd;
1255
5.43k
  else if (!m_currentVectLevel)
1256
5.38k
    m_spnd = spnd;
1257
5.56k
}
1258
1259
void libcdr::CDRContentCollector::collectVectorPattern(unsigned id, const librevenge::RVNGBinaryData &data)
1260
20.5k
{
1261
20.5k
  librevenge::RVNGInputStream *input = data.getDataStream();
1262
20.5k
  if (!input)
1263
0
    return;
1264
1265
20.5k
  input->seek(0, librevenge::RVNG_SEEK_SET);
1266
20.5k
  if (!libcdr::CMXDocument::isSupported(input))
1267
2.46k
    return;
1268
18.0k
  input->seek(0, librevenge::RVNG_SEEK_SET);
1269
18.0k
  librevenge::RVNGStringVector svgOutput;
1270
18.0k
  librevenge::RVNGSVGDrawingGenerator generator(svgOutput, "");
1271
18.0k
  if (!libcdr::CMXDocument::parse(input, &generator))
1272
18.0k
    return;
1273
0
  if (!svgOutput.empty())
1274
0
  {
1275
0
    const char *header =
1276
0
      "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
1277
0
    librevenge::RVNGBinaryData output((const unsigned char *)header, strlen(header));
1278
0
    output.append((const unsigned char *)svgOutput[0].cstr(), strlen(svgOutput[0].cstr()));
1279
0
    m_ps.m_vects[id] = output;
1280
0
  }
1281
#if DUMP_VECT
1282
  librevenge::RVNGString filename;
1283
  filename.sprintf("vect%.8x.cmx", id);
1284
  FILE *f = fopen(filename.cstr(), "wb");
1285
  if (f)
1286
  {
1287
    const unsigned char *tmpBuffer = data.getDataBuffer();
1288
    for (unsigned long k = 0; k < data.size(); k++)
1289
      fprintf(f, "%c",tmpBuffer[k]);
1290
    fclose(f);
1291
  }
1292
  filename.sprintf("vect%.8x.svg", id);
1293
  f = fopen(filename.cstr(), "wb");
1294
  if (f)
1295
  {
1296
    const unsigned char *tmpBuffer = m_ps.m_vects[id].getDataBuffer();
1297
    for (unsigned long k = 0; k < m_ps.m_vects[id].size(); k++)
1298
      fprintf(f, "%c",tmpBuffer[k]);
1299
    fclose(f);
1300
  }
1301
#endif
1302
0
}
1303
1304
void libcdr::CDRContentCollector::collectArtisticText(double x, double y)
1305
3.02k
{
1306
3.02k
  m_currentTextBox = CDRBox(x, y, x, y);
1307
3.02k
  m_currentBBox.m_w *= 2.0;
1308
3.02k
  std::map<unsigned, std::vector<CDRTextLine> >::const_iterator iter = m_ps.m_texts.find(m_spnd);
1309
3.02k
  if (iter != m_ps.m_texts.end())
1310
2.68k
    m_currentText = &(iter->second);
1311
3.02k
}
1312
1313
void libcdr::CDRContentCollector::collectParagraphText(double x, double y, double width, double height)
1314
2.92k
{
1315
2.92k
  m_currentTextBox.m_x = x;
1316
2.92k
  m_currentTextBox.m_y = y;
1317
2.92k
  m_currentTextBox.m_w = width;
1318
2.92k
  m_currentTextBox.m_h = height;
1319
2.92k
  std::map<unsigned, std::vector<CDRTextLine> >::const_iterator iter = m_ps.m_texts.find(m_spnd);
1320
2.92k
  if (iter != m_ps.m_texts.end())
1321
2.36k
    m_currentText = &(iter->second);
1322
2.92k
}
1323
1324
void libcdr::CDRContentCollector::collectStyleId(unsigned styleId)
1325
9.18k
{
1326
9.18k
  m_currentStyleId = styleId;
1327
9.18k
}
1328
1329
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */