Coverage Report

Created: 2026-04-29 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/librevenge/src/lib/RVNGSVGDrawingGenerator.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* librevenge
3
 * Version: MPL 2.0 / LGPLv2.1+
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * Major Contributor(s):
10
 * Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
11
 * Copyright (C) 2005 Fridrich Strba (fridrich.strba@bluewin.ch)
12
 *
13
 * For minor contributions see the git repository.
14
 *
15
 * Alternatively, the contents of this file may be used under the terms
16
 * of the GNU Lesser General Public License Version 2.1 or later
17
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
18
 * applicable instead of those above.
19
 */
20
21
#include <librevenge-generators/librevenge-generators.h>
22
23
#include <stdio.h>
24
25
#include <iostream>
26
#include <map>
27
#include <memory>
28
#include <sstream>
29
#include <string>
30
#include <vector>
31
32
#include "librevenge_internal.h"
33
34
namespace librevenge
35
{
36
37
namespace DrawingSVG
38
{
39
40
static double getInchValue(librevenge::RVNGProperty const &prop)
41
155k
{
42
155k
  double value=prop.getDouble();
43
155k
  switch (prop.getUnit())
44
155k
  {
45
0
  case librevenge::RVNG_GENERIC: // assume inch
46
154k
  case librevenge::RVNG_INCH:
47
154k
    return value;
48
206
  case librevenge::RVNG_POINT:
49
206
    value /= 72.;
50
206
    return value;
51
0
  case librevenge::RVNG_TWIP:
52
0
    value /= 1440.;
53
0
    return value;
54
0
  case librevenge::RVNG_PERCENT:
55
0
  case librevenge::RVNG_UNIT_ERROR:
56
0
  default:
57
0
  {
58
0
    static bool first=true;
59
0
    if (first)
60
0
    {
61
0
      RVNG_DEBUG_MSG(("librevenge::getInchValue: call with no double value\n"));
62
0
      first=false;
63
0
    }
64
0
    break;
65
0
  }
66
155k
  }
67
0
  return value;
68
155k
}
69
70
static std::string doubleToString(const double value)
71
157k
{
72
157k
  std::unique_ptr<RVNGProperty> prop{RVNGPropertyFactory::newDoubleProp(value)};
73
157k
  std::string retVal = prop->getStr().cstr();
74
157k
  return retVal;
75
157k
}
76
77
static unsigned stringToColour(const RVNGString &s)
78
0
{
79
0
  std::string str(s.cstr());
80
0
  if (str[0] == '#')
81
0
  {
82
0
    if (str.length() != 7)
83
0
      return 0;
84
0
    else
85
0
      str.erase(str.begin());
86
0
  }
87
0
  else
88
0
    return 0;
89
90
0
  std::istringstream istr(str);
91
0
  unsigned val = 0;
92
0
  istr >> std::hex >> val;
93
0
  return val;
94
0
}
95
96
//! basic class used to store table information
97
struct Table
98
{
99
  //! constructor
100
0
  Table(const RVNGPropertyList &propList) : m_column(0), m_row(0), m_x(0), m_y(0), m_columnsDistanceFromOrigin(), m_rowsDistanceFromOrigin()
101
0
  {
102
0
    if (propList["svg:x"])
103
0
      m_x=getInchValue(*propList["svg:x"]);
104
0
    if (propList["svg:y"])
105
0
      m_y=getInchValue(*propList["svg:y"]);
106
    // we do not actually use height/width, so...
107
108
0
    m_columnsDistanceFromOrigin.push_back(0);
109
0
    m_rowsDistanceFromOrigin.push_back(0);
110
111
0
    const librevenge::RVNGPropertyListVector *columns = propList.child("librevenge:table-columns");
112
0
    if (columns)
113
0
    {
114
0
      double actDist=0;
115
0
      for (unsigned long i=0; i<columns->count(); ++i)
116
0
      {
117
0
        if ((*columns)[i]["style:column-width"])
118
0
          actDist+=getInchValue(*(*columns)[i]["style:column-width"]);
119
0
        m_columnsDistanceFromOrigin.push_back(actDist);
120
0
      }
121
0
    }
122
0
    else
123
0
    {
124
0
      RVNG_DEBUG_MSG(("librevenge::DrawingSVG::Table::Table: can not find any columns\n"));
125
0
    }
126
0
  }
127
  //! calls to open a row
128
  void openRow(const RVNGPropertyList &propList)
129
0
  {
130
0
    double height=0;
131
0
    if (propList["style:row-height"])
132
0
      height=getInchValue(*propList["style:row-height"]);
133
    // changeme
134
0
    else if (propList["style:min-row-height"])
135
0
      height=getInchValue(*propList["style:min-row-height"]);
136
0
    else
137
0
    {
138
0
      RVNG_DEBUG_MSG(("librevenge::DrawingSVG::Table::openRow: can not determine row height\n"));
139
0
    }
140
0
    m_rowsDistanceFromOrigin.push_back(m_rowsDistanceFromOrigin.back()+height);
141
0
  }
142
  //! call to close a row
143
  void closeRow()
144
0
  {
145
0
    ++m_row;
146
0
  }
147
  //! returns the position of a cellule
148
  bool getPosition(int column, int row, double &x, double &y) const
149
0
  {
150
0
    bool ok=true;
151
0
    if (column>=0 && column <int(m_columnsDistanceFromOrigin.size()))
152
0
      x=m_x+m_columnsDistanceFromOrigin[size_t(column)];
153
0
    else
154
0
    {
155
0
      ok=false;
156
0
      RVNG_DEBUG_MSG(("librevenge::DrawingSVG::Table::getPosition: the column %d seems bad\n", column));
157
0
      x=(column<0 || m_columnsDistanceFromOrigin.empty()) ? m_x : m_x + m_columnsDistanceFromOrigin.back();
158
0
    }
159
0
    if (row>=0 && row <int(m_rowsDistanceFromOrigin.size()))
160
0
      y=m_y+m_rowsDistanceFromOrigin[size_t(row)];
161
0
    else
162
0
    {
163
0
      ok=false;
164
0
      RVNG_DEBUG_MSG(("librevenge::DrawingSVG::Table::getPosition: the row %d seems bad\n", row));
165
0
      y=(row<0 || m_rowsDistanceFromOrigin.empty()) ? m_y : m_y + m_rowsDistanceFromOrigin.back();
166
0
    }
167
0
    return ok;
168
0
  }
169
  //! the actual column
170
  int m_column;
171
  //! the actual row
172
  int m_row;
173
  //! the origin table position in inches
174
  double m_x, m_y;
175
  //! the distance of each begin column in inches from origin
176
  std::vector<double> m_columnsDistanceFromOrigin;
177
  //! the distance of each begin row in inches from origin
178
  std::vector<double> m_rowsDistanceFromOrigin;
179
};
180
181
} // DrawingSVG namespace
182
183
using namespace DrawingSVG;
184
185
struct RVNGSVGDrawingGeneratorPrivate
186
{
187
  RVNGSVGDrawingGeneratorPrivate(RVNGStringVector &vec, const RVNGString &nmSpace);
188
189
  void setStyle(const RVNGPropertyList &propList);
190
  void writeStyle(bool isClosed=true);
191
  void drawPolySomething(const RVNGPropertyListVector &vertices, bool isClosed);
192
193
  //! return the namespace and the delimiter
194
  std::string const &getNamespaceAndDelim() const
195
45.7k
  {
196
45.7k
    return m_nmSpaceAndDelim;
197
45.7k
  }
198
199
  std::map<int, RVNGPropertyList> m_idSpanMap;
200
201
  RVNGPropertyListVector m_gradient;
202
  RVNGPropertyList m_style;
203
  int m_gradientIndex, m_shadowIndex;
204
  //! index uses when fill=bitmap
205
  int m_patternIndex;
206
  int m_arrowStartIndex /** start arrow index*/, m_arrowEndIndex /** end arrow index */;
207
  //! groupId used if svg:id is not defined when calling openGroup
208
  int m_groupId;
209
  //! layerId used if svg:id is not defined when calling startLayer
210
  int m_layerId;
211
  //! a prefix used to define the svg namespace
212
  std::string m_nmSpace;
213
  //! a prefix used to define the svg namespace with delimiter
214
  std::string m_nmSpaceAndDelim;
215
  std::ostringstream m_outputSink;
216
  RVNGStringVector &m_vec;
217
  //! the actual master name
218
  RVNGString m_masterName;
219
  //! a map master name to master content
220
  std::map<RVNGString, std::string> m_masterNameToContentMap;
221
  //! the actual opened table
222
  std::shared_ptr<Table> m_table;
223
};
224
225
RVNGSVGDrawingGeneratorPrivate::RVNGSVGDrawingGeneratorPrivate(RVNGStringVector &vec, const RVNGString &nmSpace) :
226
10.8k
  m_idSpanMap(),
227
10.8k
  m_gradient(),
228
10.8k
  m_style(),
229
10.8k
  m_gradientIndex(1),
230
10.8k
  m_shadowIndex(1),
231
10.8k
  m_patternIndex(1),
232
10.8k
  m_arrowStartIndex(1),
233
10.8k
  m_arrowEndIndex(1),
234
10.8k
  m_groupId(1000), m_layerId(1000),
235
10.8k
  m_nmSpace(nmSpace.cstr()),
236
10.8k
  m_nmSpaceAndDelim(""),
237
10.8k
  m_outputSink(),
238
10.8k
  m_vec(vec),
239
10.8k
  m_masterName(), m_masterNameToContentMap(),
240
10.8k
  m_table()
241
10.8k
{
242
10.8k
  if (!m_nmSpace.empty())
243
0
    m_nmSpaceAndDelim = m_nmSpace+":";
244
10.8k
}
245
246
void RVNGSVGDrawingGeneratorPrivate::drawPolySomething(const RVNGPropertyListVector &vertices, bool isClosed)
247
0
{
248
0
  if (vertices.count() < 2)
249
0
    return;
250
251
0
  if (vertices.count() == 2)
252
0
  {
253
0
    if (!vertices[0]["svg:x"]||!vertices[0]["svg:y"]||!vertices[1]["svg:x"]||!vertices[1]["svg:y"])
254
0
      return;
255
0
    m_outputSink << "<" << getNamespaceAndDelim() << "line ";
256
0
    m_outputSink << "x1=\"" << doubleToString(72*getInchValue(*vertices[0]["svg:x"])) << "\"  y1=\"" << doubleToString(72*getInchValue(*vertices[0]["svg:y"])) << "\" ";
257
0
    m_outputSink << "x2=\"" << doubleToString(72*getInchValue(*vertices[1]["svg:x"])) << "\"  y2=\"" << doubleToString(72*getInchValue(*vertices[1]["svg:y"])) << "\"\n";
258
0
    writeStyle();
259
0
    m_outputSink << "/>\n";
260
0
  }
261
0
  else
262
0
  {
263
0
    if (isClosed)
264
0
      m_outputSink << "<" << getNamespaceAndDelim() << "polygon ";
265
0
    else
266
0
      m_outputSink << "<" << getNamespaceAndDelim() << "polyline ";
267
268
0
    m_outputSink << "points=\"";
269
0
    for (unsigned long i = 0; i < vertices.count(); i++)
270
0
    {
271
0
      if (!vertices[i]["svg:x"]||!vertices[i]["svg:y"])
272
0
        continue;
273
0
      m_outputSink << doubleToString(72*getInchValue(*vertices[i]["svg:x"])) << " " << doubleToString(72*getInchValue(*vertices[i]["svg:y"]));
274
0
      if (i < vertices.count()-1)
275
0
        m_outputSink << ", ";
276
0
    }
277
0
    m_outputSink << "\"\n";
278
0
    writeStyle(isClosed);
279
0
    m_outputSink << "/>\n";
280
0
  }
281
0
}
282
283
void RVNGSVGDrawingGeneratorPrivate::setStyle(const RVNGPropertyList &propList)
284
3.17k
{
285
3.17k
  m_style.clear();
286
3.17k
  m_style = propList;
287
288
3.17k
  const librevenge::RVNGPropertyListVector *gradient = propList.child("svg:linearGradient");
289
3.17k
  if (!gradient)
290
3.17k
    gradient = propList.child("svg:radialGradient");
291
3.17k
  m_gradient = gradient ? *gradient : librevenge::RVNGPropertyListVector();
292
3.17k
  if (m_style["draw:shadow"] && m_style["draw:shadow"]->getStr() == "visible" && m_style["draw:shadow-opacity"])
293
0
  {
294
0
    double shadowRed = 0.0;
295
0
    double shadowGreen = 0.0;
296
0
    double shadowBlue = 0.0;
297
0
    if (m_style["draw:shadow-color"])
298
0
    {
299
0
      unsigned shadowColour = stringToColour(m_style["draw:shadow-color"]->getStr());
300
0
      shadowRed = (double)((shadowColour & 0x00ff0000) >> 16)/255.0;
301
0
      shadowGreen = (double)((shadowColour & 0x0000ff00) >> 8)/255.0;
302
0
      shadowBlue = (double)(shadowColour & 0x000000ff)/255.0;
303
0
    }
304
0
    m_outputSink << "<" << getNamespaceAndDelim() << "defs>\n";
305
0
    m_outputSink << "<" << getNamespaceAndDelim() << "filter filterUnits=\"userSpaceOnUse\" id=\"shadow" << m_shadowIndex++ << "\">";
306
0
    m_outputSink << "<" << getNamespaceAndDelim() << "feOffset in=\"SourceGraphic\" result=\"offset\" ";
307
0
    if (m_style["draw:shadow-offset-x"])
308
0
      m_outputSink << "dx=\"" << doubleToString(72*getInchValue(*m_style["draw:shadow-offset-x"])) << "\" ";
309
0
    if (m_style["draw:shadow-offset-y"])
310
0
      m_outputSink << "dy=\"" << doubleToString(72*getInchValue(*m_style["draw:shadow-offset-y"])) << "\" ";
311
0
    m_outputSink << "/>";
312
0
    m_outputSink << "<" << getNamespaceAndDelim() << "feColorMatrix in=\"offset\" result=\"offset-color\" type=\"matrix\" values=\"";
313
0
    m_outputSink << "0 0 0 0 " << doubleToString(shadowRed) ;
314
0
    m_outputSink << " 0 0 0 0 " << doubleToString(shadowGreen);
315
0
    m_outputSink << " 0 0 0 0 " << doubleToString(shadowBlue);
316
0
    if (m_style["draw:opacity"] && m_style["draw:opacity"]->getDouble() < 1)
317
0
      m_outputSink << " 0 0 0 "   << doubleToString(m_style["draw:shadow-opacity"]->getDouble()/m_style["draw:opacity"]->getDouble()) << " 0\"/>";
318
0
    else
319
0
      m_outputSink << " 0 0 0 "   << doubleToString(m_style["draw:shadow-opacity"]->getDouble()) << " 0\"/>";
320
321
0
    m_outputSink << "<" << getNamespaceAndDelim() << "feMerge>";
322
0
    m_outputSink << "<" << getNamespaceAndDelim() << "feMergeNode in=\"offset-color\" />";
323
0
    m_outputSink << "<" << getNamespaceAndDelim() << "feMergeNode in=\"SourceGraphic\" />";
324
0
    m_outputSink << "</" << getNamespaceAndDelim() << "feMerge>";
325
0
    m_outputSink << "</" << getNamespaceAndDelim() << "filter>";
326
0
    m_outputSink << "</" << getNamespaceAndDelim() << "defs>";
327
0
  }
328
329
3.17k
  if (m_style["draw:fill"] && m_style["draw:fill"]->getStr() == "gradient")
330
333
  {
331
333
    double angle = (m_style["draw:angle"] ? m_style["draw:angle"]->getDouble() : 0.0);
332
333
    angle *= -1.0;
333
424
    while (angle < 0)
334
91
      angle += 360;
335
333
    while (angle > 360)
336
0
      angle -= 360;
337
338
333
    if (m_style["draw:style"] && (m_style["draw:style"]->getStr() == "radial" || m_style["draw:style"]->getStr() == "rectangular" ||
339
91
                                  m_style["draw:style"]->getStr() == "square" || m_style["draw:style"]->getStr() == "ellipsoid"))
340
242
    {
341
242
      m_outputSink << "<" << getNamespaceAndDelim() << "defs>\n";
342
242
      m_outputSink << "  <" << getNamespaceAndDelim() << "radialGradient id=\"grad" << m_gradientIndex++ << "\"";
343
344
242
      if (m_style["svg:cx"])
345
242
        m_outputSink << " cx=\"" << m_style["svg:cx"]->getStr().cstr() << "\"";
346
0
      else if (m_style["draw:cx"])
347
0
        m_outputSink << " cx=\"" << m_style["draw:cx"]->getStr().cstr() << "\"";
348
349
242
      if (m_style["svg:cy"])
350
242
        m_outputSink << " cy=\"" << m_style["svg:cy"]->getStr().cstr() << "\"";
351
0
      else if (m_style["draw:cy"])
352
0
        m_outputSink << " cy=\"" << m_style["draw:cy"]->getStr().cstr() << "\"";
353
242
      if (m_style["svg:r"])
354
0
        m_outputSink << " r=\"" << m_style["svg:r"]->getStr().cstr() << "\"";
355
242
      else if (m_style["draw:border"])
356
0
        m_outputSink << " r=\"" << doubleToString((1 - m_style["draw:border"]->getDouble())*100.0) << "%\"";
357
242
      else
358
242
        m_outputSink << " r=\"100%\"";
359
242
      m_outputSink << " >\n";
360
242
      if (m_gradient.count())
361
0
      {
362
0
        for (unsigned long c = 0; c < m_gradient.count(); c++)
363
0
        {
364
0
          RVNGPropertyList const &grad=m_gradient[c];
365
0
          m_outputSink << "    <" << getNamespaceAndDelim() << "stop";
366
0
          if (grad["svg:offset"])
367
0
            m_outputSink << " offset=\"" << grad["svg:offset"]->getStr().cstr() << "\"";
368
0
          if (grad["svg:stop-color"])
369
0
            m_outputSink << " stop-color=\"" << grad["svg:stop-color"]->getStr().cstr() << "\"";
370
0
          if (grad["svg:stop-opacity"])
371
0
            m_outputSink << " stop-opacity=\"" << doubleToString(grad["svg:stop-opacity"]->getDouble()) << "\"";
372
0
          m_outputSink << "/>" << std::endl;
373
0
        }
374
0
      }
375
242
      else if (m_style["draw:start-color"] && m_style["draw:end-color"])
376
130
      {
377
130
        m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"0%\"";
378
130
        m_outputSink << " stop-color=\"" << m_style["draw:end-color"]->getStr().cstr() << "\"";
379
130
        m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:end-opacity"] ? m_style["librevenge:end-opacity"]->getDouble() : 1) << "\" />" << std::endl;
380
381
130
        m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"100%\"";
382
130
        m_outputSink << " stop-color=\"" << m_style["draw:start-color"]->getStr().cstr() << "\"";
383
130
        m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:start-opacity"] ? m_style["librevenge:start-opacity"]->getDouble() : 1) << "\" />" << std::endl;
384
130
      }
385
242
      m_outputSink << "  </" << getNamespaceAndDelim() << "radialGradient>\n";
386
242
      m_outputSink << "</" << getNamespaceAndDelim() << "defs>\n";
387
242
    }
388
91
    else if (!m_style["draw:style"] || m_style["draw:style"]->getStr() == "linear" || m_style["draw:style"]->getStr() == "axial")
389
91
    {
390
91
      m_outputSink << "<" << getNamespaceAndDelim() << "defs>\n";
391
91
      m_outputSink << "  <" << getNamespaceAndDelim() << "linearGradient id=\"grad" << m_gradientIndex++ << "\" >\n";
392
393
91
      if (m_gradient.count())
394
0
      {
395
0
        bool canBuildAxial = false;
396
0
        if (m_style["draw:style"] && m_style["draw:style"]->getStr() == "axial")
397
0
        {
398
          // check if we can reconstruct the linear offset, ie. if each offset is a valid percent%
399
0
          canBuildAxial = true;
400
0
          for (unsigned long c = 0; c < m_gradient.count(); ++c)
401
0
          {
402
0
            RVNGPropertyList const &grad=m_gradient[c];
403
0
            if (!grad["svg:offset"] || grad["svg:offset"]->getDouble()<0 || grad["svg:offset"]->getDouble() > 1)
404
0
            {
405
0
              canBuildAxial=false;
406
0
              break;
407
0
            }
408
0
            RVNGString str=grad["svg:offset"]->getStr();
409
0
            int len=str.len();
410
0
            if (len<1 || str.cstr()[len-1]!='%')
411
0
            {
412
0
              canBuildAxial=false;
413
0
              break;
414
0
            }
415
0
          }
416
0
        }
417
0
        if (canBuildAxial)
418
0
        {
419
0
          for (unsigned long c = m_gradient.count(); c>0 ;)
420
0
          {
421
0
            RVNGPropertyList const &grad=m_gradient[--c];
422
0
            m_outputSink << "    <" << getNamespaceAndDelim() << "stop ";
423
0
            if (grad["svg:offset"])
424
0
              m_outputSink << "offset=\"" << doubleToString(50.-50.*grad["svg:offset"]->getDouble()) << "%\"";
425
0
            if (grad["svg:stop-color"])
426
0
              m_outputSink << " stop-color=\"" << grad["svg:stop-color"]->getStr().cstr() << "\"";
427
0
            if (grad["svg:stop-opacity"])
428
0
              m_outputSink << " stop-opacity=\"" << doubleToString(grad["svg:stop-opacity"]->getDouble()) << "\"";
429
0
            m_outputSink << "/>" << std::endl;
430
0
          }
431
0
          for (unsigned long c = 0; c < m_gradient.count(); ++c)
432
0
          {
433
0
            RVNGPropertyList const &grad=m_gradient[c];
434
0
            if (c==0 && grad["svg:offset"] && grad["svg:offset"]->getDouble() <= 0)
435
0
              continue;
436
0
            m_outputSink << "    <" << getNamespaceAndDelim() << "stop ";
437
0
            if (grad["svg:offset"])
438
0
              m_outputSink << "offset=\"" << doubleToString(50.+50.*grad["svg:offset"]->getDouble()) << "%\"";
439
0
            if (grad["svg:stop-color"])
440
0
              m_outputSink << " stop-color=\"" << grad["svg:stop-color"]->getStr().cstr() << "\"";
441
0
            if (grad["svg:stop-opacity"])
442
0
              m_outputSink << " stop-opacity=\"" << doubleToString(grad["svg:stop-opacity"]->getDouble()) << "\"";
443
0
            m_outputSink << "/>" << std::endl;
444
0
          }
445
0
        }
446
0
        else
447
0
        {
448
0
          for (unsigned long c = 0; c < m_gradient.count(); c++)
449
0
          {
450
0
            RVNGPropertyList const &grad=m_gradient[c];
451
0
            m_outputSink << "    <" << getNamespaceAndDelim() << "stop";
452
0
            if (grad["svg:offset"])
453
0
              m_outputSink << " offset=\"" << grad["svg:offset"]->getStr().cstr() << "\"";
454
0
            if (grad["svg:stop-color"])
455
0
              m_outputSink << " stop-color=\"" << grad["svg:stop-color"]->getStr().cstr() << "\"";
456
0
            if (grad["svg:stop-opacity"])
457
0
              m_outputSink << " stop-opacity=\"" << doubleToString(grad["svg:stop-opacity"]->getDouble()) << "\"";
458
0
            m_outputSink << "/>" << std::endl;
459
0
          }
460
0
        }
461
0
      }
462
91
      else if (m_style["draw:start-color"] && m_style["draw:end-color"])
463
69
      {
464
69
        if (!m_style["draw:style"] || m_style["draw:style"]->getStr() == "linear")
465
69
        {
466
69
          m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"0%\"";
467
69
          m_outputSink << " stop-color=\"" << m_style["draw:start-color"]->getStr().cstr() << "\"";
468
69
          m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:start-opacity"] ? m_style["librevenge:start-opacity"]->getDouble() : 1) << "\" />" << std::endl;
469
470
69
          m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"100%\"";
471
69
          m_outputSink << " stop-color=\"" << m_style["draw:end-color"]->getStr().cstr() << "\"";
472
69
          m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:end-opacity"] ? m_style["librevenge:end-opacity"]->getDouble() : 1) << "\" />" << std::endl;
473
69
        }
474
0
        else
475
0
        {
476
0
          m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"0%\"";
477
0
          m_outputSink << " stop-color=\"" << m_style["draw:end-color"]->getStr().cstr() << "\"";
478
0
          m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:end-opacity"] ? m_style["librevenge:end-opacity"]->getDouble() : 1) << "\" />" << std::endl;
479
480
0
          m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"50%\"";
481
0
          m_outputSink << " stop-color=\"" << m_style["draw:start-color"]->getStr().cstr() << "\"";
482
0
          m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:start-opacity"] ? m_style["librevenge:start-opacity"]->getDouble() : 1) << "\" />" << std::endl;
483
484
0
          m_outputSink << "    <" << getNamespaceAndDelim() << "stop offset=\"100%\"";
485
0
          m_outputSink << " stop-color=\"" << m_style["draw:end-color"]->getStr().cstr() << "\"";
486
0
          m_outputSink << " stop-opacity=\"" << doubleToString(m_style["librevenge:end-opacity"] ? m_style["librevenge:end-opacity"]->getDouble() : 1) << "\" />" << std::endl;
487
0
        }
488
69
      }
489
91
      m_outputSink << "  </" << getNamespaceAndDelim() << "linearGradient>\n";
490
491
      // not a simple horizontal gradient
492
91
      if (angle<270 || angle>270)
493
85
      {
494
85
        m_outputSink << "  <" << getNamespaceAndDelim() << "linearGradient xlink:href=\"#grad" << m_gradientIndex-1 << "\"";
495
85
        m_outputSink << " id=\"grad" << m_gradientIndex++ << "\" ";
496
85
        m_outputSink << "x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\" ";
497
85
        m_outputSink << "gradientTransform=\"rotate(" << angle << " .5 .5)\" ";
498
85
        m_outputSink << "gradientUnits=\"objectBoundingBox\" >\n";
499
85
        m_outputSink << "  </" << getNamespaceAndDelim() << "linearGradient>\n";
500
85
      }
501
502
91
      m_outputSink << "</" << getNamespaceAndDelim() << "defs>\n";
503
91
    }
504
333
  }
505
2.84k
  else if (m_style["draw:fill"] && m_style["draw:fill"]->getStr() == "bitmap" && m_style["draw:fill-image"] && m_style["librevenge:mime-type"])
506
1.42k
  {
507
1.42k
    m_outputSink << "<" << getNamespaceAndDelim() << "defs>\n";
508
1.42k
    m_outputSink << "  <" << getNamespaceAndDelim() << "pattern id=\"img" << m_patternIndex++ << "\" patternUnits=\"userSpaceOnUse\" ";
509
1.42k
    if (m_style["svg:width"])
510
48
      m_outputSink << "width=\"" << doubleToString(72*getInchValue(*m_style["svg:width"])) << "\" ";
511
1.37k
    else
512
1.37k
      m_outputSink << "width=\"100\" ";
513
514
1.42k
    if (m_style["svg:height"])
515
48
      m_outputSink << "height=\"" << doubleToString(72*getInchValue(*m_style["svg:height"])) << "\">" << std::endl;
516
1.37k
    else
517
1.37k
      m_outputSink << "height=\"100\">" << std::endl;
518
1.42k
    m_outputSink << "<" << getNamespaceAndDelim() << "image ";
519
520
1.42k
    if (m_style["svg:x"])
521
0
      m_outputSink << "x=\"" << doubleToString(72*getInchValue(*m_style["svg:x"])) << "\" ";
522
1.42k
    else
523
1.42k
      m_outputSink << "x=\"0\" ";
524
525
1.42k
    if (m_style["svg:y"])
526
0
      m_outputSink << "y=\"" << doubleToString(72*getInchValue(*m_style["svg:y"])) << "\" ";
527
1.42k
    else
528
1.42k
      m_outputSink << "y=\"0\" ";
529
530
1.42k
    if (m_style["svg:width"])
531
48
      m_outputSink << "width=\"" << doubleToString(72*getInchValue(*m_style["svg:width"])) << "\" ";
532
1.37k
    else
533
1.37k
      m_outputSink << "width=\"100\" ";
534
535
1.42k
    if (m_style["svg:height"])
536
48
      m_outputSink << "height=\"" << doubleToString(72*getInchValue(*m_style["svg:height"])) << "\" ";
537
1.37k
    else
538
1.37k
      m_outputSink << "height=\"100\" ";
539
540
1.42k
    m_outputSink << "xlink:href=\"data:" << m_style["librevenge:mime-type"]->getStr().cstr() << ";base64,";
541
1.42k
    m_outputSink << m_style["draw:fill-image"]->getStr().cstr();
542
1.42k
    m_outputSink << "\" />\n";
543
1.42k
    m_outputSink << "  </" << getNamespaceAndDelim() << "pattern>\n";
544
1.42k
    m_outputSink << "</" << getNamespaceAndDelim() << "defs>\n";
545
1.42k
  }
546
547
  // check for arrow and if find some, define a basic arrow
548
3.17k
  if (m_style["draw:marker-start-path"])
549
340
  {
550
340
    m_outputSink << "<" << getNamespaceAndDelim() << "defs>\n";
551
340
    m_outputSink << "<" << getNamespaceAndDelim() << "marker id=\"startMarker" << m_arrowStartIndex++ << "\" ";
552
340
    m_outputSink << " markerUnits=\"strokeWidth\" orient=\"auto\" markerWidth=\"8\" markerHeight=\"6\"\n";
553
340
    m_outputSink << " viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\">\n";
554
340
    m_outputSink << "<" << getNamespaceAndDelim() << "polyline points=\"10,0 0,5 10,10 9,5\" fill=\"solid\" />\n";
555
340
    m_outputSink << "</" << getNamespaceAndDelim() << "marker>\n";
556
340
    m_outputSink << "</" << getNamespaceAndDelim() << "defs>\n";
557
340
  }
558
3.17k
  if (m_style["draw:marker-end-path"])
559
348
  {
560
348
    m_outputSink << "<" << getNamespaceAndDelim() << "defs>\n";
561
348
    m_outputSink << "<" << getNamespaceAndDelim() << "marker id=\"endMarker" << m_arrowEndIndex++ << "\" ";
562
348
    m_outputSink << " markerUnits=\"strokeWidth\" orient=\"auto\" markerWidth=\"8\" markerHeight=\"6\"\n";
563
348
    m_outputSink << " viewBox=\"0 0 10 10\" refX=\"1\" refY=\"5\">\n";
564
348
    m_outputSink << "<" << getNamespaceAndDelim() << "polyline points=\"0,0 10,5 0,10 1,5\" fill=\"solid\" />\n";
565
348
    m_outputSink << "</" << getNamespaceAndDelim() << "marker>\n";
566
348
    m_outputSink << "</" << getNamespaceAndDelim() << "defs>\n";
567
348
  }
568
3.17k
}
569
570
// create "style" attribute based on current pen and brush
571
void RVNGSVGDrawingGeneratorPrivate::writeStyle(bool /* isClosed */)
572
3.17k
{
573
3.17k
  m_outputSink << "style=\"";
574
575
3.17k
  double width = 1.0 / 72.0;
576
3.17k
  if (m_style["svg:stroke-width"])
577
1.15k
  {
578
1.15k
    width = getInchValue(*m_style["svg:stroke-width"]);
579
#if 0
580
    // add me in libmspub and libcdr
581
    if (width <= 0.0 && m_style["draw:stroke"] && m_style["draw:stroke"]->getStr() != "none")
582
      width = 0.2 / 72.0; // reasonable hairline
583
#endif
584
1.15k
    m_outputSink << "stroke-width: " << doubleToString(72*width) << "; ";
585
1.15k
  }
586
587
3.17k
  if (m_style["draw:stroke"] && m_style["draw:stroke"]->getStr() != "none")
588
1.18k
  {
589
1.18k
    if (m_style["svg:stroke-color"])
590
1.14k
      m_outputSink << "stroke: " << m_style["svg:stroke-color"]->getStr().cstr()  << "; ";
591
1.18k
    if (m_style["svg:stroke-opacity"] &&  m_style["svg:stroke-opacity"]->getInt()!= 1)
592
0
      m_outputSink << "stroke-opacity: " << doubleToString(m_style["svg:stroke-opacity"]->getDouble()) << "; ";
593
1.18k
  }
594
595
3.17k
  if (m_style["draw:stroke"] && m_style["draw:stroke"]->getStr() == "solid")
596
1.12k
    m_outputSink << "stroke-dasharray: none; ";
597
2.05k
  else if (m_style["draw:stroke"] && m_style["draw:stroke"]->getStr() == "dash")
598
59
  {
599
59
    int dots1 = m_style["draw:dots1"] ? m_style["draw:dots1"]->getInt() : 0;
600
59
    int dots2 = m_style["draw:dots2"] ? m_style["draw:dots2"]->getInt() : 0;
601
59
    double dots1len = 72.*width, dots2len = 72.*width, gap = 72.*width;
602
59
    if (m_style["draw:dots1-length"])
603
59
    {
604
59
      if (m_style["draw:dots1-length"]->getUnit()==librevenge::RVNG_PERCENT)
605
0
        dots1len=72*m_style["draw:dots1-length"]->getDouble()*width;
606
59
      else
607
59
        dots1len=72*getInchValue(*m_style["draw:dots1-length"]);
608
59
    }
609
59
    if (m_style["draw:dots2-length"])
610
47
    {
611
47
      if (m_style["draw:dots2-length"]->getUnit()==librevenge::RVNG_PERCENT)
612
0
        dots2len=72*m_style["draw:dots2-length"]->getDouble()*width;
613
47
      else
614
47
        dots2len=72*getInchValue(*m_style["draw:dots2-length"]);
615
47
    }
616
59
    if (m_style["draw:distance"])
617
59
    {
618
59
      if (m_style["draw:distance"]->getUnit()==librevenge::RVNG_PERCENT)
619
0
        gap=72*m_style["draw:distance"]->getDouble()*width;
620
59
      else
621
59
        gap=72*getInchValue(*m_style["draw:distance"]);
622
59
    }
623
59
    m_outputSink << "stroke-dasharray: ";
624
147
    for (int i = 0; i < dots1; i++)
625
88
    {
626
88
      if (i)
627
29
        m_outputSink << ", ";
628
88
      m_outputSink << doubleToString(dots1len);
629
88
      m_outputSink << ", ";
630
88
      m_outputSink << doubleToString(gap);
631
88
    }
632
117
    for (int j = 0; j < dots2; j++)
633
58
    {
634
58
      m_outputSink << ", ";
635
58
      m_outputSink << doubleToString(dots2len);
636
58
      m_outputSink << ", ";
637
58
      m_outputSink << doubleToString(gap);
638
58
    }
639
59
    m_outputSink << "; ";
640
59
  }
641
642
3.17k
  if (m_style["svg:stroke-linecap"])
643
0
    m_outputSink << "stroke-linecap: " << m_style["svg:stroke-linecap"]->getStr().cstr() << "; ";
644
645
3.17k
  if (m_style["svg:stroke-linejoin"])
646
0
    m_outputSink << "stroke-linejoin: " << m_style["svg:stroke-linejoin"]->getStr().cstr() << "; ";
647
648
3.17k
  if (m_style["draw:fill"] && m_style["draw:fill"]->getStr() == "none")
649
961
    m_outputSink << "fill: none; ";
650
2.21k
  else if (m_style["svg:fill-rule"])
651
0
    m_outputSink << "fill-rule: " << m_style["svg:fill-rule"]->getStr().cstr() << "; ";
652
653
3.17k
  if (m_style["draw:fill"] && m_style["draw:fill"]->getStr() == "gradient")
654
333
    m_outputSink << "fill: url(#grad" << m_gradientIndex-1 << "); ";
655
2.84k
  else if (m_style["draw:fill"] && m_style["draw:fill"]->getStr() == "bitmap")
656
1.42k
    m_outputSink << "fill: url(#img" << m_patternIndex-1 << "); ";
657
658
3.17k
  if (m_style["draw:shadow"] && m_style["draw:shadow"]->getStr() == "visible")
659
0
    m_outputSink << "filter:url(#shadow" << m_shadowIndex-1 << "); ";
660
661
3.17k
  if (m_style["draw:fill"] && m_style["draw:fill"]->getStr() == "solid")
662
460
    if (m_style["draw:fill-color"])
663
460
      m_outputSink << "fill: " << m_style["draw:fill-color"]->getStr().cstr() << "; ";
664
3.17k
  if (m_style["draw:opacity"] && m_style["draw:opacity"]->getDouble() < 1)
665
0
    m_outputSink << "fill-opacity: " << doubleToString(m_style["draw:opacity"]->getDouble()) << "; ";
666
667
3.17k
  if (m_style["draw:marker-start-path"])
668
340
    m_outputSink << "marker-start: url(#startMarker" << m_arrowStartIndex-1 << "); ";
669
3.17k
  if (m_style["draw:marker-end-path"])
670
348
    m_outputSink << "marker-end: url(#endMarker" << m_arrowEndIndex-1 << "); ";
671
672
3.17k
  m_outputSink << "\""; // style
673
3.17k
}
674
675
676
RVNGSVGDrawingGenerator::RVNGSVGDrawingGenerator(RVNGStringVector &vec, const RVNGString &nmSpace) :
677
10.8k
  m_pImpl(new RVNGSVGDrawingGeneratorPrivate(vec, nmSpace))
678
10.8k
{
679
10.8k
}
680
681
RVNGSVGDrawingGenerator::~RVNGSVGDrawingGenerator()
682
10.8k
{
683
10.8k
  delete m_pImpl;
684
10.8k
}
685
686
0
void RVNGSVGDrawingGenerator::startDocument(const RVNGPropertyList & /*propList*/) {}
687
0
void RVNGSVGDrawingGenerator::endDocument() {}
688
0
void RVNGSVGDrawingGenerator::setDocumentMetaData(const RVNGPropertyList & /*propList*/) {}
689
0
void RVNGSVGDrawingGenerator::defineEmbeddedFont(const RVNGPropertyList & /*propList*/) {}
690
691
void RVNGSVGDrawingGenerator::startPage(const RVNGPropertyList &propList)
692
10.7k
{
693
10.7k
  if (propList["librevenge:master-page-name"])
694
0
  {
695
0
    if (m_pImpl->m_masterNameToContentMap.find(propList["librevenge:master-page-name"]->getStr())!=
696
0
            m_pImpl->m_masterNameToContentMap.end())
697
0
    {
698
0
      m_pImpl->m_outputSink << m_pImpl->m_masterNameToContentMap.find(propList["librevenge:master-page-name"]->getStr())->second;
699
0
      return;
700
0
    }
701
0
    RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::startPage: can not find page with given master name\n"));
702
0
  }
703
704
10.7k
  m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "svg version=\"1.1\" xmlns";
705
10.7k
  m_pImpl->m_outputSink << (m_pImpl->m_nmSpace.empty() ? "" : ":") << m_pImpl->m_nmSpace << "=\"http://www.w3.org/2000/svg\" ";
706
10.7k
  m_pImpl->m_outputSink << "xmlns:xlink=\"http://www.w3.org/1999/xlink\" ";
707
10.7k
  if (propList["svg:width"])
708
10.7k
    m_pImpl->m_outputSink << "width=\"" << doubleToString(getInchValue(*propList["svg:width"])) << "in\" ";
709
10.7k
  if (propList["svg:height"])
710
10.7k
    m_pImpl->m_outputSink << "height=\"" << doubleToString(getInchValue(*propList["svg:height"])) << "in\" ";
711
10.7k
  if (propList["svg:width"] && propList["svg:height"])
712
10.7k
  {
713
10.7k
    m_pImpl->m_outputSink << "viewBox=\"0 0 " << doubleToString(72*getInchValue(*propList["svg:width"]));
714
10.7k
    m_pImpl->m_outputSink << " " << doubleToString(72*getInchValue(*propList["svg:height"])) << "\"";
715
10.7k
  }
716
10.7k
  m_pImpl->m_outputSink << " >\n";
717
10.7k
}
718
719
void RVNGSVGDrawingGenerator::endPage()
720
10.7k
{
721
10.7k
  m_pImpl->m_outputSink << "</" << m_pImpl->getNamespaceAndDelim() << "svg>\n";
722
10.7k
  m_pImpl->m_vec.append(m_pImpl->m_outputSink.str().c_str());
723
10.7k
  m_pImpl->m_outputSink.str("");
724
10.7k
}
725
726
void RVNGSVGDrawingGenerator::startMasterPage(const RVNGPropertyList &propList)
727
0
{
728
0
  if (!m_pImpl->m_masterName.empty())
729
0
  {
730
0
    RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::startMasterPage: a master page is already opened\n"));
731
0
  }
732
0
  else if (propList["librevenge:master-page-name"])
733
0
  {
734
0
    m_pImpl->m_masterName=propList["librevenge:master-page-name"]->getStr();
735
0
    RVNGPropertyList pList(propList);
736
0
    pList.remove("librevenge:master-page-name");
737
0
    startPage(pList);
738
0
  }
739
0
  else
740
0
  {
741
0
    RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::startMasterPage: can not find the master name\n"));
742
0
  }
743
0
}
744
745
void RVNGSVGDrawingGenerator::endMasterPage()
746
0
{
747
0
  if (m_pImpl->m_masterName.empty())
748
0
  {
749
0
    RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::endMasterPage: no master pages are opened\n"));
750
0
  }
751
0
  else
752
0
  {
753
0
    if (m_pImpl->m_masterNameToContentMap.find(m_pImpl->m_masterName)!=m_pImpl->m_masterNameToContentMap.end())
754
0
    {
755
0
      RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::endMasterPage: a master page with name %s already exists\n",
756
0
                      m_pImpl->m_masterName.cstr()));
757
0
    }
758
    // no need to close the page, this will be done when the master page will be used
759
0
    m_pImpl->m_masterNameToContentMap[m_pImpl->m_masterName]=m_pImpl->m_outputSink.str();
760
0
    m_pImpl->m_masterName.clear();
761
0
  }
762
  // reset the content
763
0
  m_pImpl->m_outputSink.str("");
764
0
}
765
766
void RVNGSVGDrawingGenerator::startLayer(const RVNGPropertyList &propList)
767
0
{
768
0
  m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "g";
769
0
  librevenge::RVNGString layer("Layer");
770
0
  if (propList["draw:layer"])
771
0
    layer.append(propList["draw:layer"]->getStr());
772
0
  else if (propList["svg:id"])
773
0
    layer.append(propList["svg:id"]->getStr());
774
0
  else
775
0
    layer.sprintf("Layer%d", m_pImpl->m_layerId++);
776
0
  librevenge::RVNGString finalName("");
777
0
  finalName.appendEscapedXML(layer);
778
0
  m_pImpl->m_outputSink << " id=\"" << finalName.cstr() << "\"";
779
0
  if (propList["svg:fill-rule"])
780
0
    m_pImpl->m_outputSink << " fill-rule=\"" << propList["svg:fill-rule"]->getStr().cstr() << "\"";
781
0
  m_pImpl->m_outputSink << " >\n";
782
0
}
783
784
void RVNGSVGDrawingGenerator::endLayer()
785
0
{
786
0
  m_pImpl->m_outputSink << "</" << m_pImpl->getNamespaceAndDelim() << "g>\n";
787
0
}
788
789
0
void RVNGSVGDrawingGenerator::startEmbeddedGraphics(const RVNGPropertyList & /*propList*/) {}
790
0
void RVNGSVGDrawingGenerator::endEmbeddedGraphics() {}
791
792
void RVNGSVGDrawingGenerator::openGroup(const RVNGPropertyList & /*propList*/)
793
1.27k
{
794
1.27k
  m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "g";
795
1.27k
  librevenge::RVNGString group;
796
1.27k
  group.sprintf("Group%d", m_pImpl->m_groupId++);
797
1.27k
  m_pImpl->m_outputSink << " id=\"" << group.cstr() << "\"";
798
1.27k
  m_pImpl->m_outputSink << " >\n";
799
1.27k
}
800
801
void RVNGSVGDrawingGenerator::closeGroup()
802
1.27k
{
803
1.27k
  m_pImpl->m_outputSink << "</" << m_pImpl->getNamespaceAndDelim() << "g>\n";
804
1.27k
}
805
806
void RVNGSVGDrawingGenerator::setStyle(const RVNGPropertyList &propList)
807
3.17k
{
808
3.17k
  m_pImpl->setStyle(propList);
809
3.17k
}
810
811
void RVNGSVGDrawingGenerator::drawRectangle(const RVNGPropertyList &propList)
812
0
{
813
0
  if (!propList["svg:x"] || !propList["svg:y"] || !propList["svg:width"] || !propList["svg:height"])
814
0
    return;
815
0
  m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "rect ";
816
0
  m_pImpl->m_outputSink << "x=\"" << doubleToString(72*getInchValue(*propList["svg:x"])) << "\" y=\"" << doubleToString(72*getInchValue(*propList["svg:y"])) << "\" ";
817
0
  m_pImpl->m_outputSink << "width=\"" << doubleToString(72*getInchValue(*propList["svg:width"])) << "\" height=\"" << doubleToString(72*getInchValue(*propList["svg:height"])) << "\" ";
818
0
  if (propList["svg:rx"] && propList["svg:rx"]->getDouble() > 0 && propList["svg:ry"] && propList["svg:ry"]->getDouble()>0)
819
0
    m_pImpl->m_outputSink << "rx=\"" << doubleToString(72*getInchValue(*propList["svg:rx"])) << "\" ry=\"" << doubleToString(72*getInchValue(*propList["svg:ry"])) << "\" ";
820
0
  m_pImpl->writeStyle();
821
0
  m_pImpl->m_outputSink << "/>\n";
822
0
}
823
824
void RVNGSVGDrawingGenerator::drawEllipse(const RVNGPropertyList &propList)
825
0
{
826
0
  if (!propList["svg:cx"] || !propList["svg:cy"] || !propList["svg:rx"] || !propList["svg:ry"])
827
0
    return;
828
0
  m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "ellipse ";
829
0
  m_pImpl->m_outputSink << "cx=\"" << doubleToString(72*getInchValue(*propList["svg:cx"])) << "\" cy=\"" << doubleToString(72*getInchValue(*propList["svg:cy"])) << "\" ";
830
0
  m_pImpl->m_outputSink << "rx=\"" << doubleToString(72*getInchValue(*propList["svg:rx"])) << "\" ry=\"" << doubleToString(72*getInchValue(*propList["svg:ry"])) << "\" ";
831
0
  m_pImpl->writeStyle();
832
0
  if (propList["librevenge:rotate"] && (propList["librevenge:rotate"]->getDouble()<0||propList["librevenge:rotate"]->getDouble()>0))
833
0
    m_pImpl->m_outputSink << " transform=\" rotate(" << doubleToString(-propList["librevenge:rotate"]->getDouble())
834
0
                          << ", " << doubleToString(72*getInchValue(*propList["svg:cx"]))
835
0
                          << ", " << doubleToString(72*getInchValue(*propList["svg:cy"]))
836
0
                          << ")\" ";
837
0
  m_pImpl->m_outputSink << "/>\n";
838
0
}
839
840
void RVNGSVGDrawingGenerator::drawPolyline(const RVNGPropertyList &propList)
841
0
{
842
0
  const RVNGPropertyListVector *vertices = propList.child("svg:points");
843
0
  if (vertices && vertices->count())
844
0
    m_pImpl->drawPolySomething(*vertices, false);
845
0
}
846
847
void RVNGSVGDrawingGenerator::drawPolygon(const RVNGPropertyList &propList)
848
0
{
849
0
  const RVNGPropertyListVector *vertices = propList.child("svg:points");
850
0
  if (vertices && vertices->count())
851
0
    m_pImpl->drawPolySomething(*vertices, true);
852
0
}
853
854
void RVNGSVGDrawingGenerator::drawPath(const RVNGPropertyList &propList)
855
3.17k
{
856
3.17k
  const RVNGPropertyListVector *path = propList.child("svg:d");
857
3.17k
  if (!path)
858
0
    return;
859
3.17k
  m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "path d=\" ";
860
3.17k
  bool isClosed = false;
861
3.17k
  unsigned long i=0;
862
33.1k
  for (i=0; i < path->count(); i++)
863
29.9k
  {
864
29.9k
    RVNGPropertyList pList((*path)[i]);
865
29.9k
    if (!pList["librevenge:path-action"]) continue;
866
29.9k
    std::string action=pList["librevenge:path-action"]->getStr().cstr();
867
29.9k
    if (action.length()!=1) continue;
868
29.9k
    bool coordOk=pList["svg:x"]&&pList["svg:y"];
869
29.9k
    bool coord1Ok=coordOk && pList["svg:x1"]&&pList["svg:y1"];
870
29.9k
    bool coord2Ok=coord1Ok && pList["svg:x2"]&&pList["svg:y2"];
871
29.9k
    if (pList["svg:x"] && action[0] == 'H')
872
0
      m_pImpl->m_outputSink << "\nH" << doubleToString(72*getInchValue(*pList["svg:x"]));
873
29.9k
    else if (pList["svg:y"] && action[0] == 'V')
874
0
      m_pImpl->m_outputSink << "\nV" << doubleToString(72*getInchValue(*pList["svg:y"]));
875
29.9k
    else if (coordOk && (action[0] == 'M' || action[0] == 'L' || action[0] == 'T'))
876
12.1k
    {
877
12.1k
      m_pImpl->m_outputSink << "\n" << action;
878
12.1k
      m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x"])) << "," << doubleToString(72*getInchValue(*pList["svg:y"]));
879
12.1k
    }
880
17.7k
    else if (coord1Ok && (action[0] == 'Q' || action[0] == 'S'))
881
2.81k
    {
882
2.81k
      m_pImpl->m_outputSink << "\n" << action;
883
2.81k
      m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x1"])) << "," << doubleToString(72*getInchValue(*pList["svg:y1"])) << " ";
884
2.81k
      m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x"])) << "," << doubleToString(72*getInchValue(*pList["svg:y"]));
885
2.81k
    }
886
14.9k
    else if (coord2Ok && action[0] == 'C')
887
12.1k
    {
888
12.1k
      m_pImpl->m_outputSink << "\nC";
889
12.1k
      m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x1"])) << "," << doubleToString(72*getInchValue(*pList["svg:y1"])) << " ";
890
12.1k
      m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x2"])) << "," << doubleToString(72*getInchValue(*pList["svg:y2"])) << " ";
891
12.1k
      m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x"])) << "," << doubleToString(72*getInchValue(*pList["svg:y"]));
892
12.1k
    }
893
2.80k
    else if (coordOk && pList["svg:rx"] && pList["svg:ry"] && action[0] == 'A')
894
0
    {
895
0
      m_pImpl->m_outputSink << "\nA";
896
0
      m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:rx"])) << "," << doubleToString(72*getInchValue(*pList["svg:ry"])) << " ";
897
0
      m_pImpl->m_outputSink << doubleToString(pList["librevenge:rotate"] ? pList["librevenge:rotate"]->getDouble() : 0) << " ";
898
0
      m_pImpl->m_outputSink << (pList["librevenge:large-arc"] ? pList["librevenge:large-arc"]->getInt() : 1) << ",";
899
0
      m_pImpl->m_outputSink << (pList["librevenge:sweep"] ? pList["librevenge:sweep"]->getInt() : 1) << " ";
900
0
      m_pImpl->m_outputSink << doubleToString(72*getInchValue(*pList["svg:x"])) << "," << doubleToString(72*getInchValue(*pList["svg:y"]));
901
0
    }
902
2.80k
    else if (action[0] == 'Z')
903
2.80k
    {
904
2.80k
      isClosed = true;
905
2.80k
      m_pImpl->m_outputSink << "\nZ";
906
2.80k
    }
907
29.9k
  }
908
909
3.17k
  m_pImpl->m_outputSink << "\" \n";
910
3.17k
  m_pImpl->writeStyle(isClosed);
911
3.17k
  m_pImpl->m_outputSink << "/>\n";
912
3.17k
}
913
914
void RVNGSVGDrawingGenerator::drawGraphicObject(const RVNGPropertyList &propList)
915
0
{
916
0
  if (!propList["librevenge:mime-type"] || propList["librevenge:mime-type"]->getStr().len() <= 0)
917
0
    return;
918
0
  if (!propList["office:binary-data"])
919
0
    return;
920
0
  m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "image ";
921
0
  if (propList["svg:x"] && propList["svg:y"] && propList["svg:width"] && propList["svg:height"])
922
0
  {
923
0
    double x=getInchValue(*propList["svg:x"]);
924
0
    double y=getInchValue(*propList["svg:y"]);
925
0
    double width=getInchValue(*propList["svg:width"]);
926
0
    double height=getInchValue(*propList["svg:height"]);
927
0
    bool flipX(propList["draw:mirror-horizontal"] && propList["draw:mirror-horizontal"]->getInt());
928
0
    bool flipY(propList["draw:mirror-vertical"] && propList["draw:mirror-vertical"]->getInt());
929
930
0
    m_pImpl->m_outputSink << "x=\"" << doubleToString(72*x) << "\" y=\"" << doubleToString(72*y) << "\" ";
931
0
    m_pImpl->m_outputSink << "width=\"" << doubleToString(72*width) << "\" height=\"" << doubleToString(72*height) << "\" ";
932
0
    if (flipX || flipY || propList["librevenge:rotate"])
933
0
    {
934
0
      double xmiddle = x + width / 2.0;
935
0
      double ymiddle = y + height / 2.0;
936
0
      m_pImpl->m_outputSink << "transform=\"";
937
0
      m_pImpl->m_outputSink << " translate(" << doubleToString(72*xmiddle) << ", " << doubleToString(72*ymiddle) << ") ";
938
0
      m_pImpl->m_outputSink << " scale(" << (flipX ? "-1" : "1") << ", " << (flipY ? "-1" : "1") << ") ";
939
      // rotation is around the center of the object's bounding box
940
0
      if (propList["librevenge:rotate"])
941
0
      {
942
0
        double angle(propList["librevenge:rotate"]->getDouble());
943
0
        while (angle > 180.0)
944
0
          angle -= 360.0;
945
0
        while (angle < -180.0)
946
0
          angle += 360.0;
947
0
        m_pImpl->m_outputSink << " rotate(" << doubleToString(angle) << ") ";
948
0
      }
949
0
      m_pImpl->m_outputSink << " translate(" << doubleToString(-72*xmiddle) << ", " << doubleToString(-72*ymiddle) << ") ";
950
0
      m_pImpl->m_outputSink << "\" ";
951
0
    }
952
0
  }
953
0
  m_pImpl->m_outputSink << "xlink:href=\"data:" << propList["librevenge:mime-type"]->getStr().cstr() << ";base64,";
954
0
  m_pImpl->m_outputSink << propList["office:binary-data"]->getStr().cstr();
955
0
  m_pImpl->m_outputSink << "\" />\n";
956
0
}
957
958
void RVNGSVGDrawingGenerator::drawConnector(const RVNGPropertyList &/*propList*/)
959
0
{
960
  // TODO: implement me
961
0
}
962
963
void RVNGSVGDrawingGenerator::startTextObject(const RVNGPropertyList &propList)
964
504
{
965
504
  double x = 0.0;
966
504
  double y = 0.0;
967
504
  double height = 0.0;
968
504
  m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "text ";
969
504
  if (propList["svg:x"] && propList["svg:y"])
970
504
  {
971
504
    x = getInchValue(*propList["svg:x"]);
972
504
    y = getInchValue(*propList["svg:y"]);
973
504
  }
974
975
504
  double xmiddle = x;
976
504
  double ymiddle = y;
977
978
504
  if (propList["svg:width"])
979
504
  {
980
504
    double width = getInchValue(*propList["svg:width"]);
981
504
    xmiddle += width / 2.0;
982
504
  }
983
984
504
  if (propList["svg:height"])
985
504
  {
986
504
    height = getInchValue(*propList["svg:height"]);
987
504
    ymiddle += height / 2.0;
988
504
  }
989
990
504
  if (propList["draw:textarea-vertical-align"])
991
0
  {
992
0
    if (propList["draw:textarea-vertical-align"]->getStr() == "middle")
993
0
      y = ymiddle;
994
0
    if (propList["draw:textarea-vertical-align"]->getStr() == "bottom")
995
0
    {
996
0
      y += height;
997
0
      if (propList["fo:padding-bottom"])
998
0
        y -= propList["fo:padding-bottom"]->getDouble();
999
0
    }
1000
0
  }
1001
504
  else
1002
504
    y += height;
1003
1004
504
  if (propList["fo:padding-left"])
1005
111
    x += propList["fo:padding-left"]->getDouble();
1006
1007
504
  m_pImpl->m_outputSink << "x=\"" << doubleToString(72*x) << "\" y=\"" << doubleToString(72*y) << "\"";
1008
1009
  // rotation is around the center of the object's bounding box
1010
504
  if (propList["librevenge:rotate"] && (propList["librevenge:rotate"]->getDouble()<0||propList["librevenge:rotate"]->getDouble()>0))
1011
184
  {
1012
184
    double angle(propList["librevenge:rotate"]->getDouble());
1013
184
    while (angle > 180.0)
1014
0
      angle -= 360.0;
1015
184
    while (angle < -180.0)
1016
0
      angle += 360.0;
1017
184
    m_pImpl->m_outputSink << " transform=\"rotate(" << doubleToString(angle) << ", " << doubleToString(72*xmiddle) << ", " << doubleToString(72*ymiddle) << ")\" ";
1018
184
  }
1019
504
  m_pImpl->m_outputSink << ">\n";
1020
1021
504
}
1022
1023
void RVNGSVGDrawingGenerator::endTextObject()
1024
504
{
1025
504
  m_pImpl->m_outputSink << "</" << m_pImpl->getNamespaceAndDelim() << "text>\n";
1026
504
}
1027
1028
0
void RVNGSVGDrawingGenerator::openOrderedListLevel(const RVNGPropertyList & /*propList*/) {}
1029
0
void RVNGSVGDrawingGenerator::closeOrderedListLevel() {}
1030
1031
0
void RVNGSVGDrawingGenerator::openUnorderedListLevel(const RVNGPropertyList & /*propList*/) {}
1032
0
void RVNGSVGDrawingGenerator::closeUnorderedListLevel() {}
1033
1034
0
void RVNGSVGDrawingGenerator::openListElement(const RVNGPropertyList & /*propList*/) {}
1035
0
void RVNGSVGDrawingGenerator::closeListElement() {}
1036
1037
0
void RVNGSVGDrawingGenerator::defineParagraphStyle(const RVNGPropertyList & /*propList*/) {}
1038
1.16k
void RVNGSVGDrawingGenerator::openParagraph(const RVNGPropertyList & /*propList*/) {}
1039
1.16k
void RVNGSVGDrawingGenerator::closeParagraph() {}
1040
1041
void RVNGSVGDrawingGenerator::defineCharacterStyle(const RVNGPropertyList &propList)
1042
0
{
1043
0
  if (!propList["librevenge:span-id"])
1044
0
  {
1045
0
    RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::defineCharacterStyle: can not find the span-id\n"));
1046
0
    return;
1047
0
  }
1048
0
  m_pImpl->m_idSpanMap[propList["librevenge:span-id"]->getInt()]=propList;
1049
0
}
1050
1051
void RVNGSVGDrawingGenerator::openSpan(const RVNGPropertyList &propList)
1052
2.51k
{
1053
2.51k
  RVNGPropertyList pList(propList);
1054
2.51k
  if (propList["librevenge:span-id"] &&
1055
0
          m_pImpl->m_idSpanMap.find(propList["librevenge:span-id"]->getInt())!=m_pImpl->m_idSpanMap.end())
1056
0
    pList=m_pImpl->m_idSpanMap.find(propList["librevenge:span-id"]->getInt())->second;
1057
1058
2.51k
  m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "tspan ";
1059
2.51k
  if (pList["style:font-name"])
1060
2.03k
    m_pImpl->m_outputSink << "font-family=\"" << pList["style:font-name"]->getStr().cstr() << "\" ";
1061
2.51k
  if (pList["fo:font-style"])
1062
115
    m_pImpl->m_outputSink << "font-style=\"" << pList["fo:font-style"]->getStr().cstr() << "\" ";
1063
2.51k
  if (pList["fo:font-weight"])
1064
420
    m_pImpl->m_outputSink << "font-weight=\"" << pList["fo:font-weight"]->getStr().cstr() << "\" ";
1065
2.51k
  if (pList["fo:font-variant"])
1066
0
    m_pImpl->m_outputSink << "font-variant=\"" << pList["fo:font-variant"]->getStr().cstr() << "\" ";
1067
2.51k
  if (pList["fo:font-size"])
1068
2.23k
    m_pImpl->m_outputSink << "font-size=\"" << doubleToString(pList["fo:font-size"]->getDouble()) << "\" ";
1069
2.51k
  if (pList["fo:color"])
1070
174
    m_pImpl->m_outputSink << "fill=\"" << pList["fo:color"]->getStr().cstr() << "\" ";
1071
2.51k
  if (pList["fo:text-transform"])
1072
0
    m_pImpl->m_outputSink << "text-transform=\"" << pList["fo:text-transform"]->getStr().cstr() << "\" ";
1073
2.51k
  if (pList["svg:fill-opacity"])
1074
0
    m_pImpl->m_outputSink << "fill-opacity=\"" << doubleToString(pList["svg:fill-opacity"]->getDouble()) << "\" ";
1075
2.51k
  if (pList["svg:stroke-opacity"])
1076
0
    m_pImpl->m_outputSink << "stroke-opacity=\"" << doubleToString(pList["svg:stroke-opacity"]->getDouble()) << "\" ";
1077
2.51k
  m_pImpl->m_outputSink << ">\n";
1078
2.51k
}
1079
1080
void RVNGSVGDrawingGenerator::closeSpan()
1081
2.51k
{
1082
2.51k
  m_pImpl->m_outputSink << "</" << m_pImpl->getNamespaceAndDelim() << "tspan>\n";
1083
2.51k
}
1084
1085
0
void RVNGSVGDrawingGenerator::openLink(const RVNGPropertyList & /*propList*/) {}
1086
0
void RVNGSVGDrawingGenerator::closeLink() {}
1087
1088
void RVNGSVGDrawingGenerator::insertText(const RVNGString &str)
1089
5.53k
{
1090
5.53k
  m_pImpl->m_outputSink << RVNGString::escapeXML(str).cstr();
1091
5.53k
}
1092
1093
void RVNGSVGDrawingGenerator::insertTab()
1094
6.84k
{
1095
6.84k
  m_pImpl->m_outputSink << "\t";
1096
6.84k
}
1097
1098
void RVNGSVGDrawingGenerator::insertSpace()
1099
0
{
1100
0
  m_pImpl->m_outputSink << " ";
1101
0
}
1102
1103
void RVNGSVGDrawingGenerator::insertLineBreak()
1104
0
{
1105
0
  m_pImpl->m_outputSink << "\n";
1106
0
}
1107
1108
0
void RVNGSVGDrawingGenerator::insertField(const RVNGPropertyList & /*propList*/) {}
1109
1110
void RVNGSVGDrawingGenerator::startTableObject(const RVNGPropertyList &propList)
1111
0
{
1112
0
  if (m_pImpl->m_table)
1113
0
  {
1114
0
    RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::startTableObject: a table is already opened\n"));
1115
0
    return;
1116
0
  }
1117
0
  m_pImpl->m_table.reset(new Table(propList));
1118
0
}
1119
1120
void RVNGSVGDrawingGenerator::openTableRow(const RVNGPropertyList &propList)
1121
0
{
1122
0
  if (!m_pImpl->m_table) return;
1123
0
  m_pImpl->m_table->openRow(propList);
1124
0
}
1125
1126
void RVNGSVGDrawingGenerator::closeTableRow()
1127
0
{
1128
0
  if (!m_pImpl->m_table) return;
1129
0
  m_pImpl->m_table->closeRow();
1130
0
}
1131
1132
void RVNGSVGDrawingGenerator::openTableCell(const RVNGPropertyList &propList)
1133
0
{
1134
0
  if (!m_pImpl->m_table) return;
1135
1136
0
  if (propList["librevenge:column"])
1137
0
    m_pImpl->m_table->m_column=propList["librevenge:column"]->getInt();
1138
0
  if (propList["librevenge:row"])
1139
0
    m_pImpl->m_table->m_row=propList["librevenge:row"]->getInt();
1140
1141
0
  double x = 0, y=0;
1142
0
  m_pImpl->m_table->getPosition(m_pImpl->m_table->m_column, m_pImpl->m_table->m_row, x, y);
1143
0
  m_pImpl->m_outputSink << "<" << m_pImpl->getNamespaceAndDelim() << "text ";
1144
0
  m_pImpl->m_outputSink << "x=\"" << doubleToString(72*x) << "\" y=\"" << doubleToString(72*y) << "\"";
1145
0
  m_pImpl->m_outputSink << ">\n";
1146
1147
  // time to update the next cell's column
1148
0
  if (propList["table:number-columns-spanned"])
1149
0
    m_pImpl->m_table->m_column += propList["librevenge:column"]->getInt();
1150
0
  else
1151
0
    ++m_pImpl->m_table->m_column;
1152
0
}
1153
1154
void RVNGSVGDrawingGenerator::closeTableCell()
1155
0
{
1156
0
  if (!m_pImpl->m_table) return;
1157
0
  m_pImpl->m_outputSink << "</" << m_pImpl->getNamespaceAndDelim() << "text>\n";
1158
0
}
1159
1160
void RVNGSVGDrawingGenerator::insertCoveredTableCell(const RVNGPropertyList &/*propList*/)
1161
0
{
1162
0
  if (!m_pImpl->m_table) return;
1163
  // TODO: implement me
1164
0
}
1165
1166
void RVNGSVGDrawingGenerator::endTableObject()
1167
0
{
1168
0
  if (!m_pImpl->m_table)
1169
0
  {
1170
0
    RVNG_DEBUG_MSG(("RVNGSVGDrawingGenerator::endTableObject: no table is already opened\n"));
1171
0
    return;
1172
0
  }
1173
0
  m_pImpl->m_table.reset();
1174
0
}
1175
1176
}
1177
1178
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */