Coverage Report

Created: 2026-04-29 07:28

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