Coverage Report

Created: 2026-07-20 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libfreehand/src/lib/FHCollector.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 libfreehand 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 <algorithm>
11
#include <cassert>
12
#include <string.h>
13
#include <librevenge/librevenge.h>
14
#include "FHCollector.h"
15
#include "FHConstants.h"
16
#include "libfreehand_utils.h"
17
18
#ifndef M_PI
19
#define M_PI 3.14159265358979323846
20
#endif
21
22
#ifndef DUMP_CONTENTS
23
#define DUMP_CONTENTS 0
24
#endif
25
#ifndef DUMP_BINARY_OBJECTS
26
#define DUMP_BINARY_OBJECTS 0
27
#endif
28
#ifndef DEBUG_BOUNDING_BOX
29
#define DEBUG_BOUNDING_BOX 0
30
#endif
31
#ifndef DUMP_TILE_FILLS
32
#define DUMP_TILE_FILLS 0
33
#endif
34
#ifndef DUMP_CLIP_GROUPS
35
#define DUMP_CLIP_GROUPS 0
36
#endif
37
38
#define FH_UNINITIALIZED(pI) \
39
1.90k
  FH_ALMOST_ZERO(pI.m_minX) && FH_ALMOST_ZERO(pI.m_minY) && FH_ALMOST_ZERO(pI.m_maxY) && FH_ALMOST_ZERO(pI.m_maxX)
40
41
namespace
42
{
43
bool isTiff(const unsigned char *buffer, unsigned long size)
44
0
{
45
0
  if (size < 4)
46
0
    return false;
47
0
  if (buffer[0] == 0x49 && buffer[1] == 0x49 && buffer[2] == 0x2a && buffer[3] == 0x00)
48
0
    return true;
49
0
  if (buffer[0] == 0x4d && buffer[1] == 0x4d && buffer[2] == 0x00 && buffer[3] == 0x2a)
50
0
    return true;
51
0
  return false;
52
0
}
53
54
bool isBmp(const unsigned char *buffer, unsigned long size)
55
0
{
56
0
  if (size < 6)
57
0
    return false;
58
0
  if (buffer[0] != 0x42)
59
0
    return false;
60
0
  if (buffer[1] != 0x4d)
61
0
    return false;
62
0
  unsigned bufferSize = ((unsigned long)buffer[2] + ((unsigned long)buffer[3] << 8) + ((unsigned long)buffer[4] << 8) + ((unsigned long)buffer[5] << 8));
63
0
  if (bufferSize != size)
64
0
    return false;
65
0
  return true;
66
0
}
67
68
bool isJpeg(const unsigned char *buffer, unsigned long size)
69
0
{
70
0
  if (size < 4)
71
0
    return false;
72
0
  if (buffer[0] != 0xff)
73
0
    return false;
74
0
  if (buffer[1] != 0xd8)
75
0
    return false;
76
0
  if (buffer[size-2] != 0xff)
77
0
    return false;
78
0
  if (buffer[size-1] != 0xd9)
79
0
    return false;
80
0
  return true;
81
0
}
82
83
bool isPng(const unsigned char *buffer, unsigned long size)
84
0
{
85
0
  if (size < 8)
86
0
    return false;
87
0
  if (buffer[0] != 0x89)
88
0
    return false;
89
0
  if (buffer[1] != 0x50)
90
0
    return false;
91
0
  if (buffer[2] != 0x4e)
92
0
    return false;
93
0
  if (buffer[3] != 0x47)
94
0
    return false;
95
0
  if (buffer[4] != 0x0d)
96
0
    return false;
97
0
  if (buffer[5] != 0x0a)
98
0
    return false;
99
0
  if (buffer[6] != 0x1a)
100
0
    return false;
101
0
  if (buffer[7] != 0x0a)
102
0
    return false;
103
0
  return true;
104
0
}
105
106
librevenge::RVNGString _getColorString(const libfreehand::FHRGBColor &color)
107
345k
{
108
345k
  librevenge::RVNGString colorString;
109
345k
  colorString.sprintf("#%.2x%.2x%.2x", color.m_red >> 8, color.m_green >> 8, color.m_blue >> 8);
110
345k
  return colorString;
111
345k
}
112
113
static void _composePath(librevenge::RVNGPropertyListVector &path, bool isClosed)
114
129k
{
115
129k
  bool firstPoint = true;
116
129k
  bool wasMove = false;
117
129k
  double initialX = 0.0;
118
129k
  double initialY = 0.0;
119
129k
  double previousX = 0.0;
120
129k
  double previousY = 0.0;
121
129k
  double x = 0.0;
122
129k
  double y = 0.0;
123
129k
  std::vector<librevenge::RVNGPropertyList> tmpPath;
124
125
129k
  librevenge::RVNGPropertyListVector::Iter i(path);
126
900k
  for (i.rewind(); i.next();)
127
771k
  {
128
771k
    if (!i()["librevenge:path-action"])
129
0
      continue;
130
771k
    if (i()["svg:x"] && i()["svg:y"])
131
771k
    {
132
771k
      bool ignoreM = false;
133
771k
      x = i()["svg:x"]->getDouble();
134
771k
      y = i()["svg:y"]->getDouble();
135
771k
      if (firstPoint)
136
129k
      {
137
129k
        initialX = x;
138
129k
        initialY = y;
139
129k
        firstPoint = false;
140
129k
        wasMove = true;
141
129k
      }
142
641k
      else if (i()["librevenge:path-action"]->getStr() == "M")
143
263
      {
144
263
        if (FH_ALMOST_ZERO(previousX - x) && FH_ALMOST_ZERO(previousY - y))
145
13
          ignoreM = true;
146
250
        else
147
250
        {
148
250
          if (!tmpPath.empty())
149
250
          {
150
250
            if (!wasMove)
151
250
            {
152
250
              if ((FH_ALMOST_ZERO(initialX - previousX) && FH_ALMOST_ZERO(initialY - previousY)) || isClosed)
153
225
              {
154
225
                librevenge::RVNGPropertyList node;
155
225
                node.insert("librevenge:path-action", "Z");
156
225
                tmpPath.push_back(node);
157
225
              }
158
250
            }
159
0
            else
160
0
              tmpPath.pop_back();
161
250
          }
162
250
        }
163
164
263
        if (!ignoreM)
165
250
        {
166
250
          initialX = x;
167
250
          initialY = y;
168
250
          wasMove = true;
169
250
        }
170
171
263
      }
172
641k
      else
173
641k
        wasMove = false;
174
175
771k
      if (!ignoreM)
176
771k
      {
177
771k
        tmpPath.push_back(i());
178
771k
        previousX = x;
179
771k
        previousY = y;
180
771k
      }
181
182
771k
    }
183
0
    else if (i()["librevenge:path-action"]->getStr() == "Z")
184
0
    {
185
0
      if (tmpPath.back()["librevenge:path-action"] && tmpPath.back()["librevenge:path-action"]->getStr() != "Z")
186
0
        tmpPath.push_back(i());
187
0
    }
188
771k
  }
189
129k
  if (!tmpPath.empty())
190
129k
  {
191
129k
    if (!wasMove)
192
129k
    {
193
129k
      if ((FH_ALMOST_ZERO(initialX - previousX) && FH_ALMOST_ZERO(initialY - previousY)) || isClosed)
194
70.7k
      {
195
70.7k
        if (tmpPath.back()["librevenge:path-action"] && tmpPath.back()["librevenge:path-action"]->getStr() != "Z")
196
70.7k
        {
197
70.7k
          librevenge::RVNGPropertyList closedPath;
198
70.7k
          closedPath.insert("librevenge:path-action", "Z");
199
70.7k
          tmpPath.push_back(closedPath);
200
70.7k
        }
201
70.7k
      }
202
129k
    }
203
49
    else
204
49
      tmpPath.pop_back();
205
129k
  }
206
129k
  if (!tmpPath.empty())
207
129k
  {
208
129k
    path.clear();
209
971k
    for (std::vector<librevenge::RVNGPropertyList>::const_iterator iter = tmpPath.begin(); iter != tmpPath.end(); ++iter)
210
841k
      path.append(*iter);
211
129k
  }
212
129k
}
213
214
class ObjectRecursionGuard
215
{
216
public:
217
  ObjectRecursionGuard(std::deque<unsigned> &objectStack, const unsigned id)
218
745k
    : m_objectStack(objectStack)
219
745k
    , m_id(id)
220
745k
  {
221
745k
    m_objectStack.push_front(m_id);
222
745k
  }
223
224
  ~ObjectRecursionGuard()
225
745k
  {
226
745k
    assert(!m_objectStack.empty());
227
745k
    assert(m_objectStack.front() == m_id);
228
745k
    m_objectStack.pop_front();
229
745k
  }
230
231
private:
232
  std::deque<unsigned> &m_objectStack;
233
  const unsigned m_id;
234
};
235
236
}
237
238
libfreehand::FHCollector::FHCollector() :
239
8.17k
  m_pageInfo(), m_fhTail(), m_block(), m_transforms(), m_paths(), m_strings(), m_names(), m_lists(),
240
8.17k
  m_layers(), m_groups(), m_clipGroups(), m_currentTransforms(), m_fakeTransforms(), m_compositePaths(),
241
8.17k
  m_pathTexts(), m_tStrings(), m_fonts(), m_tEffects(), m_paragraphs(), m_tabs(), m_textBloks(), m_textObjects(), m_charProperties(),
242
8.17k
  m_paragraphProperties(), m_rgbColors(), m_basicFills(), m_propertyLists(),
243
8.17k
  m_basicLines(), m_customProcs(), m_patternLines(), m_displayTexts(), m_graphicStyles(),
244
8.17k
  m_attributeHolders(), m_data(), m_dataLists(), m_images(), m_multiColorLists(), m_linearFills(),
245
8.17k
  m_tints(), m_lensFills(), m_radialFills(), m_newBlends(), m_filterAttributeHolders(), m_opacityFilters(),
246
8.17k
  m_shadowFilters(), m_glowFilters(), m_tileFills(), m_symbolClasses(), m_symbolInstances(), m_patternFills(),
247
8.17k
  m_linePatterns(), m_arrowPaths(),
248
8.17k
  m_strokeId(0), m_fillId(0), m_contentId(0), m_textBoxNumberId(0), m_visitedObjects()
249
8.17k
{
250
8.17k
}
251
252
libfreehand::FHCollector::~FHCollector()
253
8.17k
{
254
8.17k
}
255
256
void libfreehand::FHCollector::collectPageInfo(const FHPageInfo &pageInfo)
257
4.20k
{
258
4.20k
  m_pageInfo = pageInfo;
259
4.20k
}
260
261
void libfreehand::FHCollector::collectString(unsigned recordId, const librevenge::RVNGString &str)
262
88.1k
{
263
88.1k
  m_strings[recordId] = str;
264
88.1k
}
265
266
void libfreehand::FHCollector::collectName(unsigned recordId, const librevenge::RVNGString &name)
267
49.5k
{
268
49.5k
  m_names[name] = recordId;
269
49.5k
  if (name == "stroke")
270
1.58k
    m_strokeId = recordId;
271
49.5k
  if (name == "fill")
272
1.46k
    m_fillId = recordId;
273
49.5k
  if (name == "contents")
274
1.05k
    m_contentId = recordId;
275
49.5k
}
276
277
void libfreehand::FHCollector::collectPath(unsigned recordId, const libfreehand::FHPath &path)
278
443k
{
279
443k
  m_paths[recordId] = path;
280
443k
}
281
282
void libfreehand::FHCollector::collectXform(unsigned recordId,
283
                                            double m11, double m21, double m12, double m22, double m13, double m23)
284
86.5k
{
285
86.5k
  m_transforms[recordId] = FHTransform(m11, m21, m12, m22, m13, m23);
286
86.5k
}
287
288
void libfreehand::FHCollector::collectFHTail(unsigned /* recordId */, const FHTail &fhTail)
289
3.54k
{
290
3.54k
  m_fhTail = fhTail;
291
3.54k
}
292
293
void libfreehand::FHCollector::collectBlock(unsigned recordId, const libfreehand::FHBlock &block)
294
5.65k
{
295
5.65k
  if (m_block.first && m_block.first != recordId)
296
3.43k
  {
297
3.43k
    FH_DEBUG_MSG(("FHCollector::collectBlock -- WARNING: Several \"Block\" records in the file\n"));
298
3.43k
  }
299
5.65k
  m_block = std::make_pair(recordId, block);
300
5.65k
}
301
302
void libfreehand::FHCollector::collectList(unsigned recordId, const libfreehand::FHList &lst)
303
108k
{
304
108k
  m_lists[recordId] = lst;
305
108k
}
306
307
void libfreehand::FHCollector::collectLayer(unsigned recordId, const libfreehand::FHLayer &layer)
308
13.4k
{
309
13.4k
  m_layers[recordId] = layer;
310
13.4k
}
311
312
void libfreehand::FHCollector::collectGroup(unsigned recordId, const libfreehand::FHGroup &group)
313
34.2k
{
314
34.2k
  m_groups[recordId] = group;
315
34.2k
}
316
317
void libfreehand::FHCollector::collectClipGroup(unsigned recordId, const libfreehand::FHGroup &group)
318
1.14k
{
319
1.14k
  m_clipGroups[recordId] = group;
320
1.14k
}
321
322
void libfreehand::FHCollector::collectCompositePath(unsigned recordId, const libfreehand::FHCompositePath &compositePath)
323
2.99k
{
324
2.99k
  m_compositePaths[recordId] = compositePath;
325
2.99k
}
326
327
void libfreehand::FHCollector::collectPathText(unsigned recordId, const libfreehand::FHPathText &pathText)
328
3.53k
{
329
3.53k
  m_pathTexts[recordId] = pathText;
330
3.53k
}
331
332
void libfreehand::FHCollector::collectTString(unsigned recordId, const std::vector<unsigned> &elements)
333
21.3k
{
334
21.3k
  m_tStrings[recordId] = elements;
335
21.3k
}
336
337
void libfreehand::FHCollector::collectAGDFont(unsigned recordId, const FHAGDFont &font)
338
2.79k
{
339
2.79k
  m_fonts[recordId] = font;
340
2.79k
}
341
342
void libfreehand::FHCollector::collectTEffect(unsigned recordId, const FHTEffect &tEffect)
343
61.3k
{
344
61.3k
  m_tEffects[recordId] = tEffect;
345
61.3k
}
346
347
void libfreehand::FHCollector::collectParagraph(unsigned recordId, const FHParagraph &paragraph)
348
26.2k
{
349
26.2k
  m_paragraphs[recordId] = paragraph;
350
26.2k
}
351
352
void libfreehand::FHCollector::collectTabTable(unsigned recordId, const std::vector<FHTab> &tabs)
353
7.68k
{
354
7.68k
  if (tabs.empty()) return;
355
321
  m_tabs[recordId] = tabs;
356
321
}
357
358
void libfreehand::FHCollector::collectTextBlok(unsigned recordId, const std::vector<unsigned short> &characters)
359
26.5k
{
360
26.5k
  m_textBloks[recordId]  = characters;
361
26.5k
}
362
363
void libfreehand::FHCollector::collectTextObject(unsigned recordId, const FHTextObject &textObject)
364
22.2k
{
365
22.2k
  m_textObjects[recordId] = textObject;
366
22.2k
}
367
368
void libfreehand::FHCollector::collectCharProps(unsigned recordId, const FHCharProperties &charProps)
369
34.7k
{
370
34.7k
  m_charProperties[recordId] = charProps;
371
34.7k
}
372
373
void libfreehand::FHCollector::collectParagraphProps(unsigned recordId, const FHParagraphProperties &paragraphProps)
374
60.1k
{
375
60.1k
  m_paragraphProperties[recordId] = paragraphProps;
376
60.1k
}
377
378
void libfreehand::FHCollector::collectColor(unsigned recordId, const FHRGBColor &color)
379
36.6k
{
380
36.6k
  m_rgbColors[recordId] = color;
381
36.6k
}
382
383
void libfreehand::FHCollector::collectTintColor(unsigned recordId, const FHTintColor &color)
384
2.28k
{
385
2.28k
  m_tints[recordId] = color;
386
2.28k
}
387
388
void libfreehand::FHCollector::collectBasicFill(unsigned recordId, const FHBasicFill &fill)
389
8.63k
{
390
8.63k
  m_basicFills[recordId] = fill;
391
8.63k
}
392
393
void libfreehand::FHCollector::collectBasicLine(unsigned recordId, const FHBasicLine &line)
394
15.5k
{
395
15.5k
  m_basicLines[recordId] = line;
396
15.5k
}
397
398
void libfreehand::FHCollector::collectCustomProc(unsigned recordId, const FHCustomProc &line)
399
2.79k
{
400
2.79k
  m_customProcs[recordId] = line;
401
2.79k
}
402
403
void libfreehand::FHCollector::collectPatternLine(unsigned recordId, const FHPatternLine &line)
404
2.69k
{
405
2.69k
  m_patternLines[recordId] = line;
406
2.69k
}
407
408
void libfreehand::FHCollector::collectTileFill(unsigned recordId, const FHTileFill &fill)
409
935
{
410
935
  m_tileFills[recordId] = fill;
411
935
}
412
413
void libfreehand::FHCollector::collectPatternFill(unsigned recordId, const FHPatternFill &fill)
414
3.91k
{
415
3.91k
  m_patternFills[recordId] = fill;
416
3.91k
}
417
418
void libfreehand::FHCollector::collectLinePattern(unsigned recordId, const FHLinePattern &line)
419
21.9k
{
420
21.9k
  m_linePatterns[recordId] = line;
421
21.9k
}
422
423
void libfreehand::FHCollector::collectArrowPath(unsigned recordId, const FHPath &path)
424
12.4k
{
425
  // osnola: useme
426
12.4k
  m_arrowPaths[recordId] = path;
427
12.4k
}
428
429
void libfreehand::FHCollector::collectPropList(unsigned recordId, const FHPropList &propertyList)
430
241k
{
431
241k
  m_propertyLists[recordId] = propertyList;
432
241k
}
433
434
void libfreehand::FHCollector::collectDisplayText(unsigned recordId, const FHDisplayText &displayText)
435
42.2k
{
436
42.2k
  m_displayTexts[recordId] = displayText;
437
42.2k
}
438
439
void libfreehand::FHCollector::collectGraphicStyle(unsigned recordId, const FHGraphicStyle &graphicStyle)
440
34.7k
{
441
34.7k
  m_graphicStyles[recordId] = graphicStyle;
442
34.7k
}
443
444
void libfreehand::FHCollector::collectAttributeHolder(unsigned recordId, const FHAttributeHolder &attributeHolder)
445
33.5k
{
446
33.5k
  m_attributeHolders[recordId] = attributeHolder;
447
33.5k
}
448
449
void libfreehand::FHCollector::collectFilterAttributeHolder(unsigned recordId, const FHFilterAttributeHolder &filterAttributeHolder)
450
5.39k
{
451
5.39k
  m_filterAttributeHolders[recordId] = filterAttributeHolder;
452
5.39k
}
453
454
void libfreehand::FHCollector::collectData(unsigned recordId, const librevenge::RVNGBinaryData &data)
455
1.08k
{
456
1.08k
  m_data[recordId] = data;
457
1.08k
}
458
459
void libfreehand::FHCollector::collectDataList(unsigned recordId, const FHDataList &list)
460
271
{
461
271
  m_dataLists[recordId] = list;
462
271
}
463
464
void libfreehand::FHCollector::collectImage(unsigned recordId, const FHImageImport &image)
465
1.80k
{
466
1.80k
  m_images[recordId] = image;
467
1.80k
}
468
469
void libfreehand::FHCollector::collectMultiColorList(unsigned recordId, const std::vector<FHColorStop> &colorStops)
470
2.58k
{
471
2.58k
  m_multiColorLists[recordId] = colorStops;
472
2.58k
}
473
474
void libfreehand::FHCollector::collectLinearFill(unsigned recordId, const FHLinearFill &fill)
475
7.96k
{
476
7.96k
  m_linearFills[recordId] = fill;
477
7.96k
}
478
479
void libfreehand::FHCollector::collectLensFill(unsigned recordId, const FHLensFill &fill)
480
630
{
481
630
  m_lensFills[recordId] = fill;
482
630
}
483
484
void libfreehand::FHCollector::collectRadialFill(unsigned recordId, const FHRadialFill &fill)
485
7.45k
{
486
7.45k
  m_radialFills[recordId] = fill;
487
7.45k
}
488
489
void libfreehand::FHCollector::collectNewBlend(unsigned recordId, const FHNewBlend &newBlend)
490
2.99k
{
491
2.99k
  m_newBlends[recordId] = newBlend;
492
2.99k
}
493
494
void libfreehand::FHCollector::collectOpacityFilter(unsigned recordId, double opacity)
495
3.71k
{
496
3.71k
  m_opacityFilters[recordId] = opacity;
497
3.71k
}
498
499
void libfreehand::FHCollector::collectFWShadowFilter(unsigned recordId, const FWShadowFilter &filter)
500
3.43k
{
501
3.43k
  m_shadowFilters[recordId] = filter;
502
3.43k
}
503
504
void libfreehand::FHCollector::collectFWGlowFilter(unsigned recordId, const FWGlowFilter &filter)
505
3.44k
{
506
3.44k
  m_glowFilters[recordId] = filter;
507
3.44k
}
508
509
void libfreehand::FHCollector::collectSymbolClass(unsigned recordId, const FHSymbolClass &symbolClass)
510
73.1k
{
511
73.1k
  m_symbolClasses[recordId] = symbolClass;
512
73.1k
}
513
514
void libfreehand::FHCollector::collectSymbolInstance(unsigned recordId, const FHSymbolInstance &symbolInstance)
515
14.1k
{
516
14.1k
  m_symbolInstances[recordId] = symbolInstance;
517
14.1k
}
518
519
void libfreehand::FHCollector::_normalizePath(libfreehand::FHPath &path)
520
130k
{
521
130k
  FHTransform trafo(1.0, 0.0, 0.0, -1.0, - m_pageInfo.m_minX, m_pageInfo.m_maxY);
522
130k
  path.transform(trafo);
523
130k
}
524
525
void libfreehand::FHCollector::_normalizePoint(double &x, double &y)
526
172k
{
527
172k
  FHTransform trafo(1.0, 0.0, 0.0, -1.0, - m_pageInfo.m_minX, m_pageInfo.m_maxY);
528
172k
  trafo.applyToPoint(x, y);
529
172k
}
530
531
void libfreehand::FHCollector::_getBBofPath(const FHPath *path, libfreehand::FHBoundingBox &bBox)
532
2.24k
{
533
2.24k
  if (!path || path->empty())
534
893
    return;
535
536
1.35k
  FHPath fhPath(*path);
537
1.35k
  unsigned short xform = fhPath.getXFormId();
538
539
1.35k
  if (xform)
540
242
  {
541
242
    const FHTransform *trafo = _findTransform(xform);
542
242
    if (trafo)
543
0
      fhPath.transform(*trafo);
544
242
  }
545
1.35k
  std::stack<FHTransform> groupTransforms(m_currentTransforms);
546
2.86k
  while (!groupTransforms.empty())
547
1.51k
  {
548
1.51k
    fhPath.transform(groupTransforms.top());
549
1.51k
    groupTransforms.pop();
550
1.51k
  }
551
1.35k
  _normalizePath(fhPath);
552
1.75k
  for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
553
403
  {
554
403
    fhPath.transform(*iter);
555
403
  }
556
557
1.35k
  FHBoundingBox tmpBBox;
558
1.35k
  fhPath.getBoundingBox(tmpBBox.m_xmin, tmpBBox.m_ymin, tmpBBox.m_xmax, tmpBBox.m_ymax);
559
1.35k
  bBox.merge(tmpBBox);
560
1.35k
}
561
562
void libfreehand::FHCollector::_getBBofGroup(const FHGroup *group, libfreehand::FHBoundingBox &bBox)
563
2.24k
{
564
2.24k
  if (!group)
565
2.24k
    return;
566
567
0
  if (group->m_xFormId)
568
0
  {
569
0
    const FHTransform *trafo = _findTransform(group->m_xFormId);
570
0
    if (trafo)
571
0
      m_currentTransforms.push(*trafo);
572
0
    else
573
0
      m_currentTransforms.push(libfreehand::FHTransform());
574
0
  }
575
0
  else
576
0
    m_currentTransforms.push(libfreehand::FHTransform());
577
578
0
  const std::vector<unsigned> *elements = _findListElements(group->m_elementsId);
579
0
  if (!elements)
580
0
  {
581
0
    FH_DEBUG_MSG(("ERROR: The pointed element list does not exist\n"));
582
0
    return;
583
0
  }
584
585
0
  for (unsigned int element : *elements)
586
0
  {
587
0
    FHBoundingBox tmpBBox;
588
0
    _getBBofSomething(element, tmpBBox);
589
0
    bBox.merge(tmpBBox);
590
0
  }
591
592
0
  if (!m_currentTransforms.empty())
593
0
    m_currentTransforms.pop();
594
0
}
595
596
void libfreehand::FHCollector::_getBBofClipGroup(const FHGroup *group, libfreehand::FHBoundingBox &bBox)
597
2.24k
{
598
2.24k
  if (!group)
599
2.24k
    return;
600
601
0
  if (group->m_xFormId)
602
0
  {
603
0
    const FHTransform *trafo = _findTransform(group->m_xFormId);
604
0
    if (trafo)
605
0
      m_currentTransforms.push(*trafo);
606
0
    else
607
0
      m_currentTransforms.push(libfreehand::FHTransform());
608
0
  }
609
0
  else
610
0
    m_currentTransforms.push(libfreehand::FHTransform());
611
612
0
  const std::vector<unsigned> *elements = _findListElements(group->m_elementsId);
613
0
  if (!elements)
614
0
  {
615
0
    FH_DEBUG_MSG(("ERROR: The pointed element list does not exist\n"));
616
0
    return;
617
0
  }
618
619
0
  auto iterVec = elements->begin();
620
0
  FHBoundingBox tmpBBox;
621
0
  _getBBofSomething(*iterVec, tmpBBox);
622
0
  bBox.merge(tmpBBox);
623
624
0
  if (!m_currentTransforms.empty())
625
0
    m_currentTransforms.pop();
626
0
}
627
628
void libfreehand::FHCollector::_getBBofCompositePath(const FHCompositePath *compositePath, libfreehand::FHBoundingBox &bBox)
629
2.24k
{
630
2.24k
  if (!compositePath)
631
2.24k
    return;
632
633
0
  const std::vector<unsigned> *elements = _findListElements(compositePath->m_elementsId);
634
0
  if (elements && !elements->empty())
635
0
  {
636
0
    libfreehand::FHPath fhPath;
637
0
    auto iter = elements->begin();
638
0
    const libfreehand::FHPath *path = _findPath(*(iter++));
639
0
    if (path)
640
0
    {
641
0
      fhPath = *path;
642
0
      if (!fhPath.getGraphicStyleId())
643
0
        fhPath.setGraphicStyleId(compositePath->m_graphicStyleId);
644
0
    }
645
646
0
    for (; iter != elements->end(); ++iter)
647
0
    {
648
0
      path = _findPath(*iter);
649
0
      if (path)
650
0
      {
651
0
        fhPath.appendPath(*path);
652
0
        if (!fhPath.getGraphicStyleId())
653
0
          fhPath.setGraphicStyleId(compositePath->m_graphicStyleId);
654
0
      }
655
0
    }
656
0
    FHBoundingBox tmpBBox;
657
0
    _getBBofPath(&fhPath, tmpBBox);
658
0
    bBox.merge(tmpBBox);
659
0
  }
660
0
}
661
662
void libfreehand::FHCollector::_getBBofPathText(const FHPathText *pathText, libfreehand::FHBoundingBox &bBox)
663
2.24k
{
664
2.24k
  if (!pathText)
665
2.24k
    return;
666
667
0
  _getBBofDisplayText(_findDisplayText(pathText->m_displayTextId),bBox);
668
0
}
669
670
void libfreehand::FHCollector::_getBBofTextObject(const FHTextObject *textObject, libfreehand::FHBoundingBox &bBox)
671
2.24k
{
672
2.24k
  if (!textObject)
673
1.52k
    return;
674
675
723
  double xa = textObject->m_startX;
676
723
  double ya = textObject->m_startY;
677
723
  double xb = textObject->m_startX + textObject->m_width;
678
723
  double yb = textObject->m_startY + textObject->m_height;
679
723
  double xc = xa;
680
723
  double yc = yb;
681
723
  double xd = xb;
682
723
  double yd = ya;
683
723
  unsigned xFormId = textObject->m_xFormId;
684
723
  if (xFormId)
685
692
  {
686
692
    const FHTransform *trafo = _findTransform(xFormId);
687
692
    if (trafo)
688
610
    {
689
610
      trafo->applyToPoint(xa, ya);
690
610
      trafo->applyToPoint(xb, yb);
691
610
      trafo->applyToPoint(xc, yc);
692
610
      trafo->applyToPoint(xd, yd);
693
610
    }
694
692
  }
695
723
  std::stack<FHTransform> groupTransforms(m_currentTransforms);
696
1.44k
  while (!groupTransforms.empty())
697
721
  {
698
721
    groupTransforms.top().applyToPoint(xa, ya);
699
721
    groupTransforms.top().applyToPoint(xb, yb);
700
721
    groupTransforms.top().applyToPoint(xc, yc);
701
721
    groupTransforms.top().applyToPoint(xd, yd);
702
721
    groupTransforms.pop();
703
721
  }
704
723
  _normalizePoint(xa, ya);
705
723
  _normalizePoint(xb, yb);
706
723
  _normalizePoint(xc, yc);
707
723
  _normalizePoint(xd, yd);
708
709
723
  for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
710
0
  {
711
0
    iter->applyToPoint(xa, ya);
712
0
    iter->applyToPoint(xb, yb);
713
0
    iter->applyToPoint(xc, yc);
714
0
    iter->applyToPoint(xd, yd);
715
0
  }
716
717
723
  FHBoundingBox tmpBBox;
718
723
  if (xa < tmpBBox.m_xmin) tmpBBox.m_xmin = xa;
719
723
  if (xb < tmpBBox.m_xmin) tmpBBox.m_xmin = xb;
720
723
  if (xc < tmpBBox.m_xmin) tmpBBox.m_xmin = xc;
721
723
  if (xd < tmpBBox.m_xmin) tmpBBox.m_xmin = xd;
722
723
723
  if (xa > tmpBBox.m_xmax) tmpBBox.m_xmax = xa;
724
723
  if (xb > tmpBBox.m_xmax) tmpBBox.m_xmax = xb;
725
723
  if (xc > tmpBBox.m_xmax) tmpBBox.m_xmax = xc;
726
723
  if (xd > tmpBBox.m_xmax) tmpBBox.m_xmax = xd;
727
728
723
  if (ya < tmpBBox.m_ymin) tmpBBox.m_ymin = ya;
729
723
  if (yb < tmpBBox.m_ymin) tmpBBox.m_ymin = yb;
730
723
  if (yc < tmpBBox.m_ymin) tmpBBox.m_ymin = yc;
731
723
  if (yd < tmpBBox.m_ymin) tmpBBox.m_ymin = yd;
732
733
723
  if (ya > tmpBBox.m_ymax) tmpBBox.m_ymax = ya;
734
723
  if (yb > tmpBBox.m_ymax) tmpBBox.m_ymax = yb;
735
723
  if (yc > tmpBBox.m_ymax) tmpBBox.m_ymax = yc;
736
723
  if (yd > tmpBBox.m_ymax) tmpBBox.m_ymax = yd;
737
723
  bBox.merge(tmpBBox);
738
723
}
739
740
void libfreehand::FHCollector::_getBBofDisplayText(const FHDisplayText *displayText, libfreehand::FHBoundingBox &bBox)
741
2.24k
{
742
2.24k
  if (!displayText)
743
2.24k
    return;
744
745
0
  double xa = displayText->m_startX;
746
0
  double ya = displayText->m_startY;
747
0
  double xb = displayText->m_startX + displayText->m_width;
748
0
  double yb = displayText->m_startY + displayText->m_height;
749
0
  double xc = xa;
750
0
  double yc = yb;
751
0
  double xd = xb;
752
0
  double yd = ya;
753
0
  unsigned xFormId = displayText->m_xFormId;
754
0
  if (xFormId)
755
0
  {
756
0
    const FHTransform *trafo = _findTransform(xFormId);
757
0
    if (trafo)
758
0
    {
759
0
      trafo->applyToPoint(xa, ya);
760
0
      trafo->applyToPoint(xb, yb);
761
0
      trafo->applyToPoint(xc, yc);
762
0
      trafo->applyToPoint(xd, yd);
763
0
    }
764
0
  }
765
0
  std::stack<FHTransform> groupTransforms(m_currentTransforms);
766
0
  while (!groupTransforms.empty())
767
0
  {
768
0
    groupTransforms.top().applyToPoint(xa, ya);
769
0
    groupTransforms.top().applyToPoint(xb, yb);
770
0
    groupTransforms.top().applyToPoint(xc, yc);
771
0
    groupTransforms.top().applyToPoint(xd, yd);
772
0
    groupTransforms.pop();
773
0
  }
774
0
  _normalizePoint(xa, ya);
775
0
  _normalizePoint(xb, yb);
776
0
  _normalizePoint(xc, yc);
777
0
  _normalizePoint(xd, yd);
778
779
0
  for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
780
0
  {
781
0
    iter->applyToPoint(xa, ya);
782
0
    iter->applyToPoint(xb, yb);
783
0
    iter->applyToPoint(xc, yc);
784
0
    iter->applyToPoint(xd, yd);
785
0
  }
786
787
0
  FHBoundingBox tmpBBox;
788
0
  if (xa < tmpBBox.m_xmin) tmpBBox.m_xmin = xa;
789
0
  if (xb < tmpBBox.m_xmin) tmpBBox.m_xmin = xb;
790
0
  if (xc < tmpBBox.m_xmin) tmpBBox.m_xmin = xc;
791
0
  if (xd < tmpBBox.m_xmin) tmpBBox.m_xmin = xd;
792
793
0
  if (xa > tmpBBox.m_xmax) tmpBBox.m_xmax = xa;
794
0
  if (xb > tmpBBox.m_xmax) tmpBBox.m_xmax = xb;
795
0
  if (xc > tmpBBox.m_xmax) tmpBBox.m_xmax = xc;
796
0
  if (xd > tmpBBox.m_xmax) tmpBBox.m_xmax = xd;
797
798
0
  if (ya < tmpBBox.m_ymin) tmpBBox.m_ymin = ya;
799
0
  if (yb < tmpBBox.m_ymin) tmpBBox.m_ymin = yb;
800
0
  if (yc < tmpBBox.m_ymin) tmpBBox.m_ymin = yc;
801
0
  if (yd < tmpBBox.m_ymin) tmpBBox.m_ymin = yd;
802
803
0
  if (ya > tmpBBox.m_ymax) tmpBBox.m_ymax = ya;
804
0
  if (yb > tmpBBox.m_ymax) tmpBBox.m_ymax = yb;
805
0
  if (yc > tmpBBox.m_ymax) tmpBBox.m_ymax = yc;
806
0
  if (yd > tmpBBox.m_ymax) tmpBBox.m_ymax = yd;
807
0
  bBox.merge(tmpBBox);
808
0
}
809
810
void libfreehand::FHCollector::_getBBofImageImport(const FHImageImport *image, libfreehand::FHBoundingBox &bBox)
811
2.24k
{
812
2.24k
  if (!image)
813
2.24k
    return;
814
815
0
  double xa = image->m_startX;
816
0
  double ya = image->m_startY;
817
0
  double xb = image->m_startX + image->m_width;
818
0
  double yb = image->m_startY + image->m_height;
819
0
  double xc = xa;
820
0
  double yc = yb;
821
0
  double xd = xb;
822
0
  double yd = ya;
823
0
  unsigned xFormId = image->m_xFormId;
824
0
  if (xFormId)
825
0
  {
826
0
    const FHTransform *trafo = _findTransform(xFormId);
827
0
    if (trafo)
828
0
    {
829
0
      trafo->applyToPoint(xa, ya);
830
0
      trafo->applyToPoint(xb, yb);
831
0
      trafo->applyToPoint(xc, yc);
832
0
      trafo->applyToPoint(xd, yd);
833
0
    }
834
0
  }
835
0
  std::stack<FHTransform> groupTransforms(m_currentTransforms);
836
0
  while (!groupTransforms.empty())
837
0
  {
838
0
    groupTransforms.top().applyToPoint(xa, ya);
839
0
    groupTransforms.top().applyToPoint(xb, yb);
840
0
    groupTransforms.top().applyToPoint(xc, yc);
841
0
    groupTransforms.top().applyToPoint(xd, yd);
842
0
    groupTransforms.pop();
843
0
  }
844
0
  _normalizePoint(xa, ya);
845
0
  _normalizePoint(xb, yb);
846
0
  _normalizePoint(xc, yc);
847
0
  _normalizePoint(xd, yd);
848
849
0
  for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
850
0
  {
851
0
    iter->applyToPoint(xa, ya);
852
0
    iter->applyToPoint(xb, yb);
853
0
    iter->applyToPoint(xc, yc);
854
0
    iter->applyToPoint(xd, yd);
855
0
  }
856
857
0
  FHBoundingBox tmpBBox;
858
0
  if (xa < tmpBBox.m_xmin) tmpBBox.m_xmin = xa;
859
0
  if (xb < tmpBBox.m_xmin) tmpBBox.m_xmin = xb;
860
0
  if (xc < tmpBBox.m_xmin) tmpBBox.m_xmin = xc;
861
0
  if (xd < tmpBBox.m_xmin) tmpBBox.m_xmin = xd;
862
863
0
  if (xa > tmpBBox.m_xmax) tmpBBox.m_xmax = xa;
864
0
  if (xb > tmpBBox.m_xmax) tmpBBox.m_xmax = xb;
865
0
  if (xc > tmpBBox.m_xmax) tmpBBox.m_xmax = xc;
866
0
  if (xd > tmpBBox.m_xmax) tmpBBox.m_xmax = xd;
867
868
0
  if (ya < tmpBBox.m_ymin) tmpBBox.m_ymin = ya;
869
0
  if (yb < tmpBBox.m_ymin) tmpBBox.m_ymin = yb;
870
0
  if (yc < tmpBBox.m_ymin) tmpBBox.m_ymin = yc;
871
0
  if (yd < tmpBBox.m_ymin) tmpBBox.m_ymin = yd;
872
873
0
  if (ya > tmpBBox.m_ymax) tmpBBox.m_ymax = ya;
874
0
  if (yb > tmpBBox.m_ymax) tmpBBox.m_ymax = yb;
875
0
  if (yc > tmpBBox.m_ymax) tmpBBox.m_ymax = yc;
876
0
  if (yd > tmpBBox.m_ymax) tmpBBox.m_ymax = yd;
877
0
  bBox.merge(tmpBBox);
878
0
}
879
880
void libfreehand::FHCollector::_getBBofNewBlend(const FHNewBlend * /* newBlend */, libfreehand::FHBoundingBox & /* bBox */)
881
2.24k
{
882
2.24k
}
883
884
void libfreehand::FHCollector::_getBBofSymbolInstance(const FHSymbolInstance *symbolInstance, libfreehand::FHBoundingBox &bBox)
885
2.24k
{
886
2.24k
  if (!symbolInstance)
887
2.24k
    return;
888
889
0
  m_currentTransforms.push(symbolInstance->m_xForm);
890
891
0
  const FHSymbolClass *symbolClass = _findSymbolClass(symbolInstance->m_symbolClassId);
892
0
  if (symbolClass)
893
0
  {
894
0
    FHBoundingBox tmpBBox;
895
0
    _getBBofSomething(symbolClass->m_groupId, tmpBBox);
896
0
    bBox.merge(tmpBBox);
897
0
  }
898
899
0
  if (!m_currentTransforms.empty())
900
0
    m_currentTransforms.pop();
901
0
}
902
903
void libfreehand::FHCollector::_getBBofSomething(unsigned somethingId, libfreehand::FHBoundingBox &bBox)
904
2.24k
{
905
2.24k
  if (!somethingId)
906
0
    return;
907
908
2.24k
  FHBoundingBox tmpBBox;
909
2.24k
  _getBBofGroup(_findGroup(somethingId), tmpBBox);
910
2.24k
  _getBBofClipGroup(_findClipGroup(somethingId), tmpBBox);
911
2.24k
  _getBBofPathText(_findPathText(somethingId), tmpBBox);
912
2.24k
  _getBBofPath(_findPath(somethingId), tmpBBox);
913
2.24k
  _getBBofCompositePath(_findCompositePath(somethingId), tmpBBox);
914
2.24k
  _getBBofTextObject(_findTextObject(somethingId), tmpBBox);
915
2.24k
  _getBBofDisplayText(_findDisplayText(somethingId), tmpBBox);
916
2.24k
  _getBBofImageImport(_findImageImport(somethingId), tmpBBox);
917
2.24k
  _getBBofNewBlend(_findNewBlend(somethingId), tmpBBox);
918
2.24k
  _getBBofSymbolInstance(_findSymbolInstance(somethingId), tmpBBox);
919
2.24k
  bBox.merge(tmpBBox);
920
2.24k
}
921
922
923
void libfreehand::FHCollector::_outputPath(const libfreehand::FHPath *path, librevenge::RVNGDrawingInterface *painter)
924
223k
{
925
223k
  if (!painter || !path || path->empty())
926
94.0k
    return;
927
928
129k
  FHPath fhPath(*path);
929
129k
  librevenge::RVNGPropertyList propList;
930
129k
  _appendStrokeProperties(propList, fhPath.getGraphicStyleId());
931
129k
  _appendFillProperties(propList, fhPath.getGraphicStyleId());
932
129k
  unsigned contentId = _findContentId(fhPath.getGraphicStyleId());
933
129k
  if (fhPath.getEvenOdd())
934
0
    propList.insert("svg:fill-rule", "evenodd");
935
936
129k
  unsigned short xform = fhPath.getXFormId();
937
938
129k
  if (xform)
939
18.8k
  {
940
18.8k
    const FHTransform *trafo = _findTransform(xform);
941
18.8k
    if (trafo)
942
17.3k
      fhPath.transform(*trafo);
943
18.8k
  }
944
129k
  std::stack<FHTransform> groupTransforms(m_currentTransforms);
945
325k
  while (!groupTransforms.empty())
946
195k
  {
947
195k
    fhPath.transform(groupTransforms.top());
948
195k
    groupTransforms.pop();
949
195k
  }
950
129k
  _normalizePath(fhPath);
951
952
132k
  for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
953
3.02k
  {
954
3.02k
    fhPath.transform(*iter);
955
3.02k
  }
956
957
129k
  librevenge::RVNGPropertyListVector propVec;
958
129k
  fhPath.writeOut(propVec);
959
129k
  if (propList["draw:fill"] && propList["draw:fill"]->getStr() != "none")
960
9.18k
    _composePath(propVec, true);
961
120k
  else
962
120k
    _composePath(propVec, fhPath.isClosed());
963
129k
  librevenge::RVNGPropertyList pList;
964
129k
  pList.insert("svg:d", propVec);
965
129k
  if (contentId)
966
10.6k
    painter->openGroup(librevenge::RVNGPropertyList());
967
129k
  painter->setStyle(propList);
968
129k
  painter->drawPath(pList);
969
129k
  if (contentId)
970
10.6k
  {
971
10.6k
    FHBoundingBox bBox;
972
10.6k
    fhPath.getBoundingBox(bBox.m_xmin, bBox.m_ymin, bBox.m_xmax, bBox.m_ymax);
973
10.6k
    FHTransform trafo(1.0, 0.0, 0.0, 1.0, - bBox.m_xmin, - bBox.m_ymin);
974
10.6k
    m_fakeTransforms.push_back(trafo);
975
10.6k
    librevenge::RVNGStringVector svgOutput;
976
10.6k
    librevenge::RVNGSVGDrawingGenerator generator(svgOutput, "");
977
10.6k
    propList.clear();
978
10.6k
    propList.insert("svg:width", bBox.m_xmax - bBox.m_xmin);
979
10.6k
    propList.insert("svg:height", bBox.m_ymax - bBox.m_ymin);
980
10.6k
    generator.startPage(propList);
981
10.6k
    _outputSomething(contentId, &generator);
982
10.6k
    generator.endPage();
983
10.6k
    if (!svgOutput.empty() && svgOutput[0].size() > 140) // basically empty svg if it is not fullfilled
984
10.6k
    {
985
10.6k
      const char *header =
986
10.6k
        "<?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";
987
10.6k
      librevenge::RVNGBinaryData output((const unsigned char *)header, strlen(header));
988
10.6k
      output.append((unsigned char *)svgOutput[0].cstr(), strlen(svgOutput[0].cstr()));
989
#if DUMP_CONTENTS
990
      {
991
        librevenge::RVNGString filename;
992
        filename.sprintf("fhcontents%.4x.svg", contentId);
993
        FILE *f = fopen(filename.cstr(), "wb");
994
        if (f)
995
        {
996
          const unsigned char *tmpBuffer = output.getDataBuffer();
997
          for (unsigned long k = 0; k < output.size(); k++)
998
            fprintf(f, "%c",tmpBuffer[k]);
999
          fclose(f);
1000
        }
1001
      }
1002
#endif
1003
10.6k
      propList.clear();
1004
10.6k
      propList.insert("draw:stroke", "none");
1005
10.6k
      propList.insert("draw:fill", "bitmap");
1006
10.6k
      propList.insert("librevenge:mime-type", "image/svg+xml");
1007
10.6k
      propList.insert("style:repeat", "stretch");
1008
10.6k
      propList.insert("draw:fill-image", output);
1009
10.6k
      painter->setStyle(propList);
1010
10.6k
      painter->drawPath(pList);
1011
10.6k
    }
1012
10.6k
    if (!m_fakeTransforms.empty())
1013
10.6k
      m_fakeTransforms.pop_back();
1014
10.6k
    painter->closeGroup();
1015
10.6k
  }
1016
#if DEBUG_BOUNDING_BOX
1017
  {
1018
    librevenge::RVNGPropertyList rectangleProps;
1019
    rectangleProps.insert("draw:fill", "none");
1020
    rectangleProps.insert("draw:stroke", "solid");
1021
    painter->setStyle(rectangleProps);
1022
    double xmin, ymin, xmax, ymax;
1023
    fhPath.getBoundingBox(xmin, ymin, xmax, ymax);
1024
    librevenge::RVNGPropertyList rectangleList;
1025
    rectangleList.insert("svg:x", xmin);
1026
    rectangleList.insert("svg:y", ymin);
1027
    rectangleList.insert("svg:width", xmax - xmin);
1028
    rectangleList.insert("svg:height", ymax - ymin);
1029
    painter->drawRectangle(rectangleList);
1030
  }
1031
#endif
1032
129k
}
1033
1034
void libfreehand::FHCollector::_outputSomething(unsigned somethingId, librevenge::RVNGDrawingInterface *painter)
1035
228k
{
1036
228k
  if (!painter || !somethingId)
1037
5.12k
    return;
1038
223k
  if (find(m_visitedObjects.begin(), m_visitedObjects.end(), somethingId) != m_visitedObjects.end())
1039
482
    return;
1040
1041
223k
  const ObjectRecursionGuard guard(m_visitedObjects, somethingId);
1042
1043
223k
  _outputGroup(_findGroup(somethingId), painter);
1044
223k
  _outputClipGroup(_findClipGroup(somethingId), painter);
1045
223k
  _outputPathText(_findPathText(somethingId), painter);
1046
223k
  _outputPath(_findPath(somethingId), painter);
1047
223k
  _outputCompositePath(_findCompositePath(somethingId), painter);
1048
223k
  _outputTextObject(_findTextObject(somethingId), painter);
1049
223k
  _outputDisplayText(_findDisplayText(somethingId), painter);
1050
223k
  _outputImageImport(_findImageImport(somethingId), painter);
1051
223k
  _outputNewBlend(_findNewBlend(somethingId), painter);
1052
223k
  _outputSymbolInstance(_findSymbolInstance(somethingId), painter);
1053
223k
}
1054
1055
void libfreehand::FHCollector::_outputGroup(const libfreehand::FHGroup *group, librevenge::RVNGDrawingInterface *painter)
1056
223k
{
1057
223k
  if (!painter || !group)
1058
203k
    return;
1059
1060
19.7k
  if (group->m_xFormId)
1061
91
  {
1062
91
    const FHTransform *trafo = _findTransform(group->m_xFormId);
1063
91
    if (trafo)
1064
4
      m_currentTransforms.push(*trafo);
1065
87
    else
1066
87
      m_currentTransforms.push(libfreehand::FHTransform());
1067
91
  }
1068
19.6k
  else
1069
19.6k
    m_currentTransforms.push(libfreehand::FHTransform());
1070
1071
19.7k
  const std::vector<unsigned> *elements = _findListElements(group->m_elementsId);
1072
19.7k
  if (!elements)
1073
151
  {
1074
151
    FH_DEBUG_MSG(("ERROR: The pointed element list does not exist\n"));
1075
151
    return;
1076
151
  }
1077
1078
19.6k
  if (!elements->empty())
1079
19.5k
  {
1080
19.5k
    painter->openGroup(librevenge::RVNGPropertyList());
1081
19.5k
    for (unsigned int element : *elements)
1082
174k
      _outputSomething(element, painter);
1083
19.5k
    painter->closeGroup();
1084
19.5k
  }
1085
1086
19.6k
  if (!m_currentTransforms.empty())
1087
19.6k
    m_currentTransforms.pop();
1088
19.6k
}
1089
1090
void libfreehand::FHCollector::_outputClipGroup(const libfreehand::FHGroup *group, librevenge::RVNGDrawingInterface *painter)
1091
223k
{
1092
223k
  if (!painter || !group)
1093
223k
    return;
1094
1095
84
  const std::vector<unsigned> *elements = _findListElements(group->m_elementsId);
1096
84
  if (!elements)
1097
0
  {
1098
0
    FH_DEBUG_MSG(("ERROR: The pointed element list does not exist\n"));
1099
0
    return;
1100
0
  }
1101
1102
84
  if (!elements->empty())
1103
84
  {
1104
84
    auto iter = elements->begin();
1105
84
    const FHPath *path = _findPath(*iter);
1106
84
    if (!path)
1107
0
      _outputGroup(group, painter);
1108
84
    else
1109
84
    {
1110
84
      if (group->m_xFormId)
1111
0
      {
1112
0
        const FHTransform *trafo = _findTransform(group->m_xFormId);
1113
0
        if (trafo)
1114
0
          m_currentTransforms.push(*trafo);
1115
0
        else
1116
0
          m_currentTransforms.push(libfreehand::FHTransform());
1117
0
      }
1118
84
      else
1119
84
        m_currentTransforms.push(libfreehand::FHTransform());
1120
1121
84
      librevenge::RVNGPropertyList propList;
1122
84
      FHPath fhPath(*path);
1123
84
      _appendStrokeProperties(propList, fhPath.getGraphicStyleId());
1124
84
      _appendFillProperties(propList, fhPath.getGraphicStyleId());
1125
84
      if (fhPath.getEvenOdd())
1126
0
        propList.insert("svg:fill-rule", "evenodd");
1127
84
      unsigned short xform = fhPath.getXFormId();
1128
1129
84
      if (xform)
1130
0
      {
1131
0
        const FHTransform *trafo = _findTransform(xform);
1132
0
        if (trafo)
1133
0
          fhPath.transform(*trafo);
1134
0
      }
1135
84
      std::stack<FHTransform> groupTransforms(m_currentTransforms);
1136
357
      while (!groupTransforms.empty())
1137
273
      {
1138
273
        fhPath.transform(groupTransforms.top());
1139
273
        groupTransforms.pop();
1140
273
      }
1141
84
      _normalizePath(fhPath);
1142
1143
156
      for (std::vector<FHTransform>::const_iterator iterVec = m_fakeTransforms.begin(); iterVec != m_fakeTransforms.end(); ++iterVec)
1144
72
      {
1145
72
        fhPath.transform(*iterVec);
1146
72
      }
1147
1148
84
      if (!m_currentTransforms.empty())
1149
84
        m_currentTransforms.pop();
1150
1151
84
      librevenge::RVNGPropertyListVector propVec;
1152
84
      fhPath.writeOut(propVec);
1153
84
      _composePath(propVec, true);
1154
84
      librevenge::RVNGPropertyList pList;
1155
84
      pList.insert("svg:d", propVec);
1156
1157
1158
84
      FHBoundingBox bBox;
1159
84
      fhPath.getBoundingBox(bBox.m_xmin, bBox.m_ymin, bBox.m_xmax, bBox.m_ymax);
1160
84
      FHTransform trafo(1.0, 0.0, 0.0, 1.0, - bBox.m_xmin, - bBox.m_ymin);
1161
84
      m_fakeTransforms.push_back(trafo);
1162
84
      librevenge::RVNGStringVector svgOutput;
1163
84
      librevenge::RVNGSVGDrawingGenerator generator(svgOutput, "");
1164
84
      propList.clear();
1165
84
      propList.insert("svg:width", bBox.m_xmax - bBox.m_xmin);
1166
84
      propList.insert("svg:height", bBox.m_ymax - bBox.m_ymin);
1167
84
      generator.startPage(propList);
1168
84
      _outputGroup(group, &generator);
1169
84
      generator.endPage();
1170
84
      if (!svgOutput.empty() && svgOutput[0].size() > 140) // basically empty svg if it is not fullfilled
1171
84
      {
1172
84
        const char *header =
1173
84
          "<?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";
1174
84
        librevenge::RVNGBinaryData output((const unsigned char *)header, strlen(header));
1175
84
        output.append((unsigned char *)svgOutput[0].cstr(), strlen(svgOutput[0].cstr()));
1176
#if DUMP_CLIP_GROUPS
1177
        {
1178
          librevenge::RVNGString filename;
1179
          filename.sprintf("freehandclipgroup%.4x.svg", group->m_elementsId);
1180
          FILE *f = fopen(filename.cstr(), "wb");
1181
          if (f)
1182
          {
1183
            const unsigned char *tmpBuffer = output.getDataBuffer();
1184
            for (unsigned long k = 0; k < output.size(); k++)
1185
              fprintf(f, "%c",tmpBuffer[k]);
1186
            fclose(f);
1187
          }
1188
        }
1189
#endif
1190
84
        propList.insert("draw:stroke", "none");
1191
84
        propList.insert("draw:fill", "bitmap");
1192
84
        propList.insert("librevenge:mime-type", "image/svg+xml");
1193
84
        propList.insert("style:repeat", "stretch");
1194
84
        propList.insert("draw:fill-image", output);
1195
84
        painter->setStyle(propList);
1196
84
        painter->drawPath(pList);
1197
84
      }
1198
84
      if (!m_fakeTransforms.empty())
1199
84
        m_fakeTransforms.pop_back();
1200
84
    }
1201
84
  }
1202
84
}
1203
1204
void libfreehand::FHCollector::_outputPathText(const libfreehand::FHPathText *pathText, librevenge::RVNGDrawingInterface *painter)
1205
223k
{
1206
223k
  if (!painter || !pathText)
1207
223k
    return;
1208
1209
189
  _outputDisplayText(_findDisplayText(pathText->m_displayTextId), painter);
1210
189
}
1211
1212
void libfreehand::FHCollector::_outputNewBlend(const libfreehand::FHNewBlend *newBlend, librevenge::RVNGDrawingInterface *painter)
1213
223k
{
1214
223k
  if (!painter || !newBlend)
1215
223k
    return;
1216
1217
12
  m_currentTransforms.push(libfreehand::FHTransform());
1218
1219
12
  painter->openGroup(librevenge::RVNGPropertyList());
1220
12
  const std::vector<unsigned> *elements1 = _findListElements(newBlend->m_list1Id);
1221
12
  if (elements1 && !elements1->empty())
1222
12
  {
1223
12
    for (unsigned int iterVec : *elements1)
1224
24
      _outputSomething(iterVec, painter);
1225
12
  }
1226
12
  const std::vector<unsigned> *elements2 = _findListElements(newBlend->m_list2Id);
1227
12
  if (elements2 && !elements2->empty())
1228
12
  {
1229
12
    for (unsigned int iterVec : *elements2)
1230
12
      _outputSomething(iterVec, painter);
1231
12
  }
1232
12
  const std::vector<unsigned> *elements3 = _findListElements(newBlend->m_list3Id);
1233
12
  if (elements3 && !elements3->empty())
1234
12
  {
1235
12
    for (unsigned int iterVec : *elements3)
1236
12
      _outputSomething(iterVec, painter);
1237
12
  }
1238
12
  painter->closeGroup();
1239
1240
12
  if (!m_currentTransforms.empty())
1241
12
    m_currentTransforms.pop();
1242
12
}
1243
1244
void libfreehand::FHCollector::_outputSymbolInstance(const libfreehand::FHSymbolInstance *symbolInstance, librevenge::RVNGDrawingInterface *painter)
1245
223k
{
1246
223k
  if (!painter || !symbolInstance)
1247
223k
    return;
1248
1249
0
  m_currentTransforms.push(symbolInstance->m_xForm);
1250
1251
0
  const FHSymbolClass *symbolClass = _findSymbolClass(symbolInstance->m_symbolClassId);
1252
0
  if (symbolClass)
1253
0
  {
1254
0
    _outputSomething(symbolClass->m_groupId, painter);
1255
0
  }
1256
1257
0
  if (!m_currentTransforms.empty())
1258
0
    m_currentTransforms.pop();
1259
0
}
1260
1261
void libfreehand::FHCollector::outputDrawing(librevenge::RVNGDrawingInterface *painter)
1262
4.20k
{
1263
1264
#if DUMP_BINARY_OBJECTS
1265
  for (std::map<unsigned, FHImageImport>::const_iterator iterImage = m_images.begin(); iterImage != m_images.end(); ++iterImage)
1266
  {
1267
    librevenge::RVNGBinaryData data = getImageData(iterImage->second.m_dataListId);
1268
    librevenge::RVNGString filename;
1269
    filename.sprintf("freehanddump%.4x.%s", iterImage->first, iterImage->second.m_format.empty() ? "bin" : iterImage->second.m_format.cstr());
1270
    FILE *f = fopen(filename.cstr(), "wb");
1271
    if (f)
1272
    {
1273
      const unsigned char *tmpBuffer = data.getDataBuffer();
1274
      for (unsigned long k = 0; k < data.size(); k++)
1275
        fprintf(f, "%c",tmpBuffer[k]);
1276
      fclose(f);
1277
    }
1278
  }
1279
#endif
1280
1281
4.20k
  if (!painter)
1282
0
    return;
1283
1284
4.20k
  if (!m_fhTail.m_blockId || m_fhTail.m_blockId != m_block.first)
1285
3.34k
  {
1286
3.34k
    FH_DEBUG_MSG(("WARNING: FHTail points to an invalid Block ID\n"));
1287
3.34k
    m_fhTail.m_blockId = m_block.first;
1288
3.34k
  }
1289
4.20k
  if (!m_fhTail.m_blockId)
1290
2.29k
  {
1291
2.29k
    FH_DEBUG_MSG(("ERROR: Block record is absent from this file\n"));
1292
2.29k
    return;
1293
2.29k
  }
1294
1295
1.90k
  if (FH_UNINITIALIZED(m_pageInfo))
1296
1.52k
    m_pageInfo = m_fhTail.m_pageInfo;
1297
1298
1.90k
  painter->startDocument(librevenge::RVNGPropertyList());
1299
1.90k
  librevenge::RVNGPropertyList propList;
1300
1.90k
  propList.insert("svg:height", m_pageInfo.m_maxY - m_pageInfo.m_minY);
1301
1.90k
  propList.insert("svg:width", m_pageInfo.m_maxX - m_pageInfo.m_minX);
1302
1.90k
  painter->startPage(propList);
1303
1304
1.90k
  unsigned layerListId = m_block.second.m_layerListId;
1305
1306
1.90k
  const std::vector<unsigned> *elements = _findListElements(layerListId);
1307
1.90k
  if (elements)
1308
1.73k
  {
1309
1.73k
    for (unsigned int element : *elements)
1310
9.29k
    {
1311
9.29k
      _outputLayer(element, painter);
1312
9.29k
    }
1313
1.73k
  }
1314
1.90k
  painter->endPage();
1315
1.90k
  painter->endDocument();
1316
1.90k
}
1317
1318
void libfreehand::FHCollector::_outputLayer(unsigned layerId, librevenge::RVNGDrawingInterface *painter)
1319
9.29k
{
1320
9.29k
  if (!painter)
1321
0
    return;
1322
1323
9.29k
  std::map<unsigned, FHLayer>::const_iterator layerIter = m_layers.find(layerId);
1324
9.29k
  if (layerIter == m_layers.end())
1325
4.88k
  {
1326
4.88k
    FH_DEBUG_MSG(("ERROR: Could not find the referenced layer\n"));
1327
4.88k
    return;
1328
4.88k
  }
1329
1330
4.41k
  if (layerIter->second.m_visibility != 3)
1331
2.39k
    return;
1332
1333
2.01k
  unsigned layerElementsListId = layerIter->second.m_elementsId;
1334
2.01k
  if (!layerElementsListId)
1335
3
  {
1336
3
    FH_DEBUG_MSG(("ERROR: Layer points to invalid element list\n"));
1337
3
    return;
1338
3
  }
1339
1340
2.01k
  const std::vector<unsigned> *elements = _findListElements(layerElementsListId);
1341
2.01k
  if (!elements)
1342
21
  {
1343
21
    FH_DEBUG_MSG(("ERROR: The pointed element list does not exist\n"));
1344
21
    return;
1345
21
  }
1346
1347
1.99k
  for (unsigned int element : *elements)
1348
41.6k
    _outputSomething(element, painter);
1349
1.99k
}
1350
1351
void libfreehand::FHCollector::_outputCompositePath(const libfreehand::FHCompositePath *compositePath, librevenge::RVNGDrawingInterface *painter)
1352
223k
{
1353
223k
  if (!painter || !compositePath)
1354
223k
    return;
1355
1356
322
  const std::vector<unsigned> *elements = _findListElements(compositePath->m_elementsId);
1357
322
  if (elements && !elements->empty())
1358
267
  {
1359
267
    libfreehand::FHPath fhPath;
1360
267
    auto iter = elements->begin();
1361
267
    const libfreehand::FHPath *path = _findPath(*(iter++));
1362
267
    if (path)
1363
206
    {
1364
206
      fhPath = *path;
1365
206
      if (!fhPath.getGraphicStyleId())
1366
0
        fhPath.setGraphicStyleId(compositePath->m_graphicStyleId);
1367
206
    }
1368
1369
2.48k
    for (; iter != elements->end(); ++iter)
1370
2.22k
    {
1371
2.22k
      path = _findPath(*iter);
1372
2.22k
      if (path)
1373
290
      {
1374
290
        fhPath.appendPath(*path);
1375
290
        if (!fhPath.getGraphicStyleId())
1376
36
          fhPath.setGraphicStyleId(compositePath->m_graphicStyleId);
1377
290
      }
1378
2.22k
    }
1379
267
    _outputPath(&fhPath, painter);
1380
267
  }
1381
322
}
1382
1383
void libfreehand::FHCollector::_outputTextObject(const libfreehand::FHTextObject *textObject, librevenge::RVNGDrawingInterface *painter)
1384
223k
{
1385
223k
  if (!painter || !textObject)
1386
203k
    return;
1387
1388
19.5k
  double width=textObject->m_width;
1389
19.5k
  double height=textObject->m_height;
1390
19.5k
  unsigned num[]= {textObject->m_colNum,textObject->m_rowNum};
1391
19.5k
  double decalX[]= {width+textObject->m_colSep,0};
1392
19.5k
  double decalY[]= {0, height+textObject->m_rowSep};
1393
19.5k
  if (textObject->m_rowBreakFirst)
1394
68
  {
1395
68
    std::swap(num[0],num[1]);
1396
68
    std::swap(decalX[0],decalX[1]);
1397
68
    std::swap(decalY[0],decalY[1]);
1398
68
  }
1399
19.5k
  for (unsigned int &i : num)
1400
39.1k
  {
1401
39.1k
    if (i<=0 || i>10)
1402
1.04k
    {
1403
1.04k
      FH_DEBUG_MSG(("libfreehand::FHCollector::_outputTextObject: the number of row/col seems bad\n"));
1404
1.04k
      i=1;
1405
1.04k
    }
1406
39.1k
  }
1407
19.5k
  ++m_textBoxNumberId;
1408
19.5k
  for (unsigned dim0=0; dim0<num[0]; ++dim0)
1409
19.5k
  {
1410
19.5k
    for (unsigned dim1=0; dim1<num[1]; ++dim1)
1411
19.5k
    {
1412
19.5k
      unsigned id = dim0*num[1]+dim1;
1413
19.5k
      double rotation = 0, finalHeight = 0, finalWidth = 0, xmid=0, ymid=0;
1414
19.5k
      bool useShapeBox=false;
1415
19.5k
      if ((width<=0 || height<=0) && textObject->m_pathId)
1416
266
      {
1417
        /* the position are not set for TFOnPath, so we must look for the shape box
1418
1419
           Note: the width and height seem better, the x,y position are still quite random :-~
1420
        */
1421
266
        FHBoundingBox bbox;
1422
266
        _getBBofSomething(textObject->m_pathId, bbox);
1423
266
        useShapeBox=true;
1424
266
        xmid=0.5*(bbox.m_xmin+bbox.m_xmax);
1425
266
        ymid=0.5*(bbox.m_ymin+bbox.m_ymax);
1426
266
        width=finalWidth=(bbox.m_xmax-bbox.m_xmin);
1427
266
        height=finalHeight=(bbox.m_ymax-bbox.m_ymin);
1428
266
      }
1429
19.5k
      if (!useShapeBox)
1430
19.3k
      {
1431
#ifdef HAVE_CHAINED_TEXTBOX
1432
        // useme when we can chain frames in Draw
1433
        double startX=textObject->m_startX+dim0*decalX[0]+dim1*decalX[1];
1434
        double startY=textObject->m_startY+dim0*decalY[0]+dim1*decalY[1];
1435
#else
1436
        /* if the number of row/column is greater than 1, we have a
1437
           big problem. Let increase the text-box size to contain all
1438
           the chained text-boxes...
1439
         */
1440
19.3k
        double startX=textObject->m_startX;
1441
19.3k
        double startY=textObject->m_startY;
1442
19.3k
        width += (num[0]-1)*decalX[0]+(num[1]-1)*decalX[1];
1443
19.3k
        height += (num[0]-1)*decalY[0]+(num[1]-1)*decalY[1];
1444
19.3k
#endif
1445
19.3k
        double xa = startX;
1446
19.3k
        double ya = startY;
1447
19.3k
        double xb = startX + width;
1448
19.3k
        double yb = startY + height;
1449
19.3k
        double xc = xa;
1450
19.3k
        double yc = yb;
1451
19.3k
        unsigned xFormId = textObject->m_xFormId;
1452
19.3k
        if (xFormId)
1453
19.2k
        {
1454
19.2k
          const FHTransform *trafo = _findTransform(xFormId);
1455
19.2k
          if (trafo)
1456
19.0k
          {
1457
19.0k
            trafo->applyToPoint(xa, ya);
1458
19.0k
            trafo->applyToPoint(xb, yb);
1459
19.0k
            trafo->applyToPoint(xc, yc);
1460
19.0k
          }
1461
19.2k
        }
1462
19.3k
        std::stack<FHTransform> groupTransforms(m_currentTransforms);
1463
80.6k
        while (!groupTransforms.empty())
1464
61.3k
        {
1465
61.3k
          groupTransforms.top().applyToPoint(xa, ya);
1466
61.3k
          groupTransforms.top().applyToPoint(xb, yb);
1467
61.3k
          groupTransforms.top().applyToPoint(xc, yc);
1468
61.3k
          groupTransforms.pop();
1469
61.3k
        }
1470
19.3k
        _normalizePoint(xa, ya);
1471
19.3k
        _normalizePoint(xb, yb);
1472
19.3k
        _normalizePoint(xc, yc);
1473
1474
20.0k
        for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
1475
705
        {
1476
705
          iter->applyToPoint(xa, ya);
1477
705
          iter->applyToPoint(xb, yb);
1478
705
          iter->applyToPoint(xc, yc);
1479
705
        }
1480
1481
19.3k
        rotation = atan2(yb-yc, xb-xc);
1482
19.3k
        finalHeight = sqrt((xc-xa)*(xc-xa) + (yc-ya)*(yc-ya));
1483
19.3k
        finalWidth = sqrt((xc-xb)*(xc-xb) + (yc-yb)*(yc-yb));
1484
19.3k
        xmid = (xa + xb) / 2.0;
1485
19.3k
        ymid = (ya + yb) / 2.0;
1486
19.3k
      }
1487
1488
19.5k
      librevenge::RVNGPropertyList textObjectProps;
1489
19.5k
      textObjectProps.insert("svg:x", xmid - width / 2.0);
1490
19.5k
      textObjectProps.insert("svg:y", ymid + height / 2.0);
1491
19.5k
      textObjectProps.insert("svg:height", finalHeight);
1492
19.5k
      textObjectProps.insert("svg:width", finalWidth);
1493
19.5k
      if (!FH_ALMOST_ZERO(rotation))
1494
513
      {
1495
513
        textObjectProps.insert("librevenge:rotate", rotation * 180.0 / M_PI);
1496
513
        textObjectProps.insert("librevenge:rotate-cx",xmid);
1497
513
        textObjectProps.insert("librevenge:rotate-cy",ymid);
1498
513
      }
1499
#ifdef HAVE_CHAINED_TEXTBOX
1500
      if (id)
1501
      {
1502
        librevenge::RVNGString name;
1503
        name.sprintf("Textbox%d-%d",m_textBoxNumberId,id);
1504
        textObjectProps.insert("librevenge:frame-name",name);
1505
      }
1506
      if (id+1!=num[0]*num[1])
1507
      {
1508
        librevenge::RVNGString name;
1509
        name.sprintf("Textbox%d-%d",m_textBoxNumberId,id+1);
1510
        textObjectProps.insert("librevenge:next-frame-name",name);
1511
      }
1512
#endif
1513
19.5k
      painter->startTextObject(textObjectProps);
1514
1515
19.5k
      if (id==0)
1516
19.5k
      {
1517
19.5k
        const std::vector<unsigned> *elements = _findTStringElements(textObject->m_tStringId);
1518
19.5k
        unsigned actPos=0;
1519
19.5k
        if (elements && !elements->empty())
1520
19.4k
        {
1521
19.4k
          for (unsigned int element : *elements)
1522
80.1k
            _outputParagraph(_findParagraph(element), painter, actPos, textObject->m_beginPos, textObject->m_endPos);
1523
19.4k
        }
1524
19.5k
      }
1525
19.5k
      painter->endTextObject();
1526
1527
19.5k
#if !defined(HAVE_CHAINED_TEXTBOX)
1528
19.5k
      break;
1529
19.5k
#endif
1530
19.5k
    }
1531
19.5k
#if !defined(HAVE_CHAINED_TEXTBOX)
1532
19.5k
    break;
1533
19.5k
#endif
1534
19.5k
  }
1535
19.5k
}
1536
1537
void libfreehand::FHCollector::_outputParagraph(const libfreehand::FHParagraph *paragraph, librevenge::RVNGDrawingInterface *painter, unsigned &actPos, unsigned minPos, unsigned maxPos)
1538
80.1k
{
1539
80.1k
  if (!painter || !paragraph)
1540
55.4k
    return;
1541
24.6k
  bool paragraphOpened=false;
1542
24.6k
  std::map<unsigned, std::vector<unsigned short> >::const_iterator iter = m_textBloks.find(paragraph->m_textBlokId);
1543
24.6k
  if (iter != m_textBloks.end())
1544
24.2k
  {
1545
1546
52.3k
    for (std::vector<std::pair<unsigned, unsigned> >::size_type i = 0; i < paragraph->m_charStyleIds.size(); ++i)
1547
30.1k
    {
1548
30.1k
      if (actPos>=maxPos) break;
1549
28.1k
      unsigned lastChar=i+1 < paragraph->m_charStyleIds.size() ? paragraph->m_charStyleIds[i+1].first : iter->second.size();
1550
28.1k
      unsigned numChar=lastChar-paragraph->m_charStyleIds[i].first;
1551
28.1k
      unsigned nextPos=actPos+numChar;
1552
28.1k
      if (nextPos<minPos)
1553
679
      {
1554
679
        actPos=nextPos;
1555
679
        continue;
1556
679
      }
1557
27.4k
      if (!paragraphOpened)
1558
22.0k
      {
1559
22.0k
        librevenge::RVNGPropertyList propList;
1560
22.0k
        _appendParagraphProperties(propList, paragraph->m_paraStyleId);
1561
22.0k
        painter->openParagraph(propList);
1562
22.0k
        paragraphOpened=true;
1563
22.0k
      }
1564
27.4k
      unsigned fChar=paragraph->m_charStyleIds[i].first + (actPos<minPos ? minPos-actPos : 0);
1565
27.4k
      numChar=lastChar-fChar;
1566
27.4k
      if (actPos+numChar>maxPos) numChar=maxPos-actPos;
1567
27.4k
      _outputTextRun(&(iter->second), fChar, numChar, paragraph->m_charStyleIds[i].second, painter);
1568
27.4k
      actPos=nextPos;
1569
27.4k
    }
1570
24.2k
  }
1571
24.6k
  ++actPos; // EOL
1572
24.6k
  if (paragraphOpened)
1573
22.0k
    painter->closeParagraph();
1574
24.6k
}
1575
1576
void libfreehand::FHCollector::_appendCharacterProperties(librevenge::RVNGPropertyList &propList, unsigned charPropsId)
1577
27.0k
{
1578
27.0k
  std::map<unsigned, FHCharProperties>::const_iterator iter = m_charProperties.find(charPropsId);
1579
27.0k
  if (iter == m_charProperties.end())
1580
1.82k
    return;
1581
25.1k
  const FHCharProperties &charProps = iter->second;
1582
25.1k
  if (charProps.m_fontNameId)
1583
36
  {
1584
36
    std::map<unsigned, librevenge::RVNGString>::const_iterator iterString = m_strings.find(charProps.m_fontNameId);
1585
36
    if (iterString != m_strings.end())
1586
36
      propList.insert("style:font-name", iterString->second);
1587
36
  }
1588
25.1k
  propList.insert("fo:font-size", charProps.m_fontSize, librevenge::RVNG_POINT);
1589
25.1k
  if (charProps.m_fontId)
1590
25.0k
    _appendFontProperties(propList, charProps.m_fontId);
1591
25.1k
  if (charProps.m_textColorId)
1592
510
  {
1593
510
    std::map<unsigned, FHBasicFill>::const_iterator iterBasicFill = m_basicFills.find(charProps.m_textColorId);
1594
510
    if (iterBasicFill != m_basicFills.end() && iterBasicFill->second.m_colorId)
1595
140
    {
1596
140
      librevenge::RVNGString color = getColorString(iterBasicFill->second.m_colorId);
1597
140
      if (!color.empty())
1598
140
        propList.insert("fo:color", color);
1599
140
    }
1600
510
  }
1601
25.1k
  FHTEffect const *eff=_findTEffect(charProps.m_tEffectId);
1602
25.1k
  if (eff && eff->m_nameId)
1603
627
  {
1604
627
    std::map<unsigned, librevenge::RVNGString>::const_iterator iterString = m_strings.find(eff->m_nameId);
1605
627
    if (iterString != m_strings.end())
1606
598
    {
1607
598
      librevenge::RVNGString const &type=iterString->second;
1608
598
      if (type=="InlineEffect")   // inside col1, outside col0
1609
185
      {
1610
185
        propList.insert("fo:font-weight", "bold");
1611
185
        librevenge::RVNGString color = getColorString(eff->m_colorId[1]);
1612
185
        if (!color.empty())
1613
176
          propList.insert("fo:color", color);
1614
185
      }
1615
413
      else if (type=="ShadowEffect")
1616
188
        propList.insert("fo:text-shadow", "1pt 1pt");
1617
225
      else if (type=="ZoomEffect")
1618
162
      {
1619
162
        propList.insert("style:font-relief", "embossed");
1620
162
        propList.insert("fo:text-shadow", "1pt -1pt");
1621
162
        librevenge::RVNGString color = getColorString(eff->m_colorId[0]);
1622
162
        if (!color.empty())
1623
158
          propList.insert("fo:color", color);
1624
162
      }
1625
63
      else
1626
63
      {
1627
63
        FH_DEBUG_MSG(("libfreehand::FHCollector::_appendCharacterProperties: find unknown effect %s\n",type.cstr()));
1628
63
      }
1629
598
    }
1630
627
  }
1631
100k
  for (auto it=charProps.m_idToDoubleMap.begin(); it!=charProps.m_idToDoubleMap.end(); ++it)
1632
74.9k
  {
1633
74.9k
    switch (it->first)
1634
74.9k
    {
1635
24.8k
    case FH_BASELN_SHIFT:
1636
24.8k
    {
1637
24.8k
      if (it->second<=0 && it->second>=0) break;
1638
2.61k
      librevenge::RVNGString value;
1639
2.61k
      double fontSize=(charProps.m_fontSize>0) ? charProps.m_fontSize : 24.;
1640
2.61k
      value.sprintf("%g%%",100.*it->second/fontSize);
1641
2.61k
      propList.insert("style:text-position", value);
1642
2.61k
      break;
1643
24.8k
    }
1644
25.1k
    case FH_HOR_SCALE:
1645
25.1k
      if (it->second<=1 && it->second>=1) break;
1646
429
      propList.insert("style:text-scale", it->second, librevenge::RVNG_PERCENT);
1647
429
      break;
1648
24.9k
    case FH_RNG_KERN:
1649
24.9k
      if (it->second<=0 && it->second>=0) break;
1650
309
      propList.insert("fo:letter-spacing", it->second*charProps.m_fontSize, librevenge::RVNG_POINT);
1651
309
      break;
1652
0
    default:
1653
0
      break;
1654
74.9k
    }
1655
74.9k
  }
1656
25.1k
}
1657
1658
void libfreehand::FHCollector::_appendCharacterProperties(librevenge::RVNGPropertyList &propList, const FH3CharProperties &charProps)
1659
201k
{
1660
201k
  if (charProps.m_fontNameId)
1661
199k
  {
1662
199k
    std::map<unsigned, librevenge::RVNGString>::const_iterator iterString = m_strings.find(charProps.m_fontNameId);
1663
199k
    if (iterString != m_strings.end())
1664
197k
      propList.insert("style:font-name", iterString->second);
1665
199k
  }
1666
201k
  propList.insert("fo:font-size", charProps.m_fontSize, librevenge::RVNG_POINT);
1667
201k
  if (charProps.m_fontColorId)
1668
198k
  {
1669
198k
    librevenge::RVNGString color = getColorString(charProps.m_fontColorId);
1670
198k
    if (!color.empty())
1671
195k
      propList.insert("fo:color", color);
1672
198k
  }
1673
201k
  if (charProps.m_fontStyle & 1)
1674
2.14k
    propList.insert("fo:font-weight", "bold");
1675
201k
  if (charProps.m_fontStyle & 2)
1676
2.57k
    propList.insert("fo:font-style", "italic");
1677
201k
  if (charProps.m_letterSpacing<0 || charProps.m_letterSpacing>0)
1678
7.57k
    propList.insert("fo:letter-spacing", charProps.m_letterSpacing, librevenge::RVNG_POINT);
1679
201k
  if (charProps.m_horizontalScale<1 || charProps.m_horizontalScale>1)
1680
6.15k
    propList.insert("style:text-scale", charProps.m_horizontalScale, librevenge::RVNG_PERCENT);
1681
201k
  if (charProps.m_baselineShift<0 || charProps.m_baselineShift>0)
1682
6.23k
  {
1683
6.23k
    librevenge::RVNGString value;
1684
6.23k
    double fontSize=(charProps.m_fontSize>0) ? charProps.m_fontSize : 24.;
1685
6.23k
    value.sprintf("%g%%",100.*charProps.m_baselineShift/fontSize);
1686
6.23k
    propList.insert("style:text-position", value);
1687
6.23k
  }
1688
201k
  FHTEffect const *eff=_findTEffect(charProps.m_textEffsId);
1689
201k
  if (eff && eff->m_shortNameId)
1690
156k
  {
1691
156k
    std::map<unsigned, librevenge::RVNGString>::const_iterator iterString = m_strings.find(eff->m_shortNameId);
1692
156k
    if (iterString != m_strings.end())
1693
154k
    {
1694
154k
      librevenge::RVNGString const &type=iterString->second;
1695
154k
      if (type=="inlin")   // inside col1, outside col0
1696
33.9k
        propList.insert("fo:font-weight", "bold");
1697
120k
      else if (type=="otw stol")
1698
27.2k
        propList.insert("style:text-outline", "true");
1699
92.9k
      else if (type=="stob")
1700
17.4k
        propList.insert("fo:font-style", "italic");
1701
75.5k
      else if (type=="stsh")
1702
16.4k
        propList.insert("fo:text-shadow", "1pt 1pt");
1703
59.1k
      else if (type=="sthv")
1704
16.8k
        propList.insert("fo:font-weight", "bold");
1705
42.2k
      else if (type=="extrude")
1706
34.7k
      {
1707
34.7k
        propList.insert("style:font-relief", "embossed");
1708
34.7k
        propList.insert("fo:text-shadow", "1pt -1pt");
1709
34.7k
        librevenge::RVNGString color = getColorString(eff->m_colorId[0]);
1710
34.7k
        if (!color.empty())
1711
34.6k
          propList.insert("fo:color", color);
1712
34.7k
      }
1713
7.48k
      else
1714
7.48k
      {
1715
7.48k
        FH_DEBUG_MSG(("libfreehand::FHCollector::_appendCharacterProperties: find unknown effect %s\n",type.cstr()));
1716
7.48k
      }
1717
154k
    }
1718
156k
  }
1719
201k
}
1720
1721
void libfreehand::FHCollector::_appendTabProperties(librevenge::RVNGPropertyList &propList, const libfreehand::FHTab &tab)
1722
47.6k
{
1723
47.6k
  switch (tab.m_type)
1724
47.6k
  {
1725
2.87k
  case 0:
1726
5.67k
  case 4: // unsure, look like a left tab
1727
5.84k
  default:
1728
5.84k
    break;
1729
36.1k
  case 1:
1730
36.1k
    propList.insert("style:type", "right");
1731
36.1k
    break;
1732
2.85k
  case 2:
1733
2.85k
    propList.insert("style:type", "center");
1734
2.85k
    break;
1735
2.80k
  case 3:
1736
2.80k
    propList.insert("style:type", "char");
1737
2.80k
    propList.insert("style:char", "."); // checkme
1738
2.80k
    break;
1739
47.6k
  }
1740
47.6k
  propList.insert("style:position", tab.m_position, librevenge::RVNG_POINT);
1741
47.6k
}
1742
1743
void libfreehand::FHCollector::_appendParagraphProperties(librevenge::RVNGPropertyList & /* propList */, const FH3ParaProperties & /* paraProps */)
1744
37.2k
{
1745
37.2k
}
1746
1747
void libfreehand::FHCollector::_appendParagraphProperties(librevenge::RVNGPropertyList &propList, unsigned paragraphPropsId)
1748
22.0k
{
1749
22.0k
  std::map<unsigned, FHParagraphProperties>::const_iterator iter = m_paragraphProperties.find(paragraphPropsId);
1750
22.0k
  if (iter == m_paragraphProperties.end())
1751
137
    return;
1752
21.8k
  FHParagraphProperties const &para=iter->second;
1753
21.8k
  for (const auto &it : para.m_idToZoneIdMap)
1754
21.6k
  {
1755
21.6k
    switch (it.first)
1756
21.6k
    {
1757
21.6k
    case FH_PARA_TAB_TABLE_ID:
1758
21.6k
      if (m_tabs.find(it.second)!=m_tabs.end())
1759
19.5k
      {
1760
19.5k
        std::vector<FHTab> const &tabs=m_tabs.find(it.second)->second;
1761
19.5k
        if (tabs.empty())
1762
0
          break;
1763
19.5k
        librevenge::RVNGPropertyListVector tabVect;
1764
19.5k
        for (const auto &tab : tabs)
1765
47.6k
        {
1766
47.6k
          librevenge::RVNGPropertyList tabList;
1767
47.6k
          _appendTabProperties(tabList, tab);
1768
47.6k
          tabVect.append(tabList);
1769
47.6k
        }
1770
19.5k
        propList.insert("style:tab-stops", tabVect);
1771
19.5k
      }
1772
21.6k
      break;
1773
21.6k
    default:
1774
0
      break;
1775
21.6k
    }
1776
21.6k
  }
1777
130k
  for (auto it=para.m_idToDoubleMap.begin(); it!=para.m_idToDoubleMap.end(); ++it)
1778
108k
  {
1779
108k
    switch (it->first)
1780
108k
    {
1781
21.7k
    case FH_PARA_LEFT_INDENT:
1782
21.7k
      propList.insert("fo:margin-left", it->second, librevenge::RVNG_POINT);
1783
21.7k
      break;
1784
21.2k
    case FH_PARA_RIGHT_INDENT:
1785
21.2k
      propList.insert("fo:margin-right", it->second, librevenge::RVNG_POINT);
1786
21.2k
      break;
1787
21.7k
    case FH_PARA_TEXT_INDENT:
1788
21.7k
      propList.insert("fo:text-indent", it->second, librevenge::RVNG_POINT);
1789
21.7k
      break;
1790
21.7k
    case FH_PARA_SPC_ABOVE:
1791
21.7k
      propList.insert("fo:margin-top", it->second, librevenge::RVNG_POINT);
1792
21.7k
      break;
1793
21.6k
    case FH_PARA_SPC_BELLOW:
1794
21.6k
      propList.insert("fo:margin-bottom", it->second, librevenge::RVNG_POINT);
1795
21.6k
      break;
1796
89
    case FH_PARA_LEADING:
1797
89
      if (it->second<=0 && it->second>=0) break;
1798
62
      if (para.m_idToIntMap.find(FH_PARA_LEADING_TYPE)==para.m_idToIntMap.end())
1799
22
      {
1800
22
        FH_DEBUG_MSG(("libfreehand::FHCollector::_appendParagraphProperties: can not find the leading type\n"));
1801
22
        break;
1802
22
      }
1803
40
      switch (para.m_idToIntMap.find(FH_PARA_LEADING_TYPE)->second)
1804
40
      {
1805
16
      case 0: // delta in point
1806
16
        propList.insert("fo:line-height",1.+it->second/(it->second>0 ? 12 : 24), librevenge::RVNG_PERCENT);
1807
16
        break;
1808
10
      case 1:
1809
10
        propList.insert("fo:line-height",it->second, librevenge::RVNG_POINT);
1810
10
        break;
1811
4
      case 2:
1812
4
        propList.insert("fo:line-height",it->second, librevenge::RVNG_PERCENT);
1813
4
        break;
1814
10
      default:
1815
10
        break;
1816
40
      }
1817
40
      break;
1818
40
    default:
1819
0
      break;
1820
108k
    }
1821
108k
  }
1822
21.8k
  for (const auto &it : para.m_idToIntMap)
1823
43.5k
  {
1824
43.5k
    switch (it.first)
1825
43.5k
    {
1826
21.6k
    case FH_PARA_TEXT_ALIGN:
1827
21.6k
      switch (it.second)
1828
21.6k
      {
1829
19.9k
      case 0:
1830
19.9k
        propList.insert("fo:text-align", "left");
1831
19.9k
        break;
1832
75
      case 1:
1833
75
        propList.insert("fo:text-align", "end");
1834
75
        break;
1835
320
      case 2:
1836
320
        propList.insert("fo:text-align", "center");
1837
320
        break;
1838
1.15k
      case 3:
1839
1.15k
        propList.insert("fo:text-align", "justify");
1840
1.15k
        break;
1841
139
      default:
1842
139
        break;
1843
21.6k
      }
1844
21.6k
      break;
1845
21.6k
    case FH_PARA_KEEP_SAME_LINE:
1846
83
      if (it.second==1)
1847
8
        propList.insert("fo:keep-together", "always");
1848
83
      break;
1849
77
    case FH_PARA_LEADING_TYPE: // done with FH_PARA_LEADING
1850
21.7k
    default:
1851
21.7k
      break;
1852
43.5k
    }
1853
43.5k
  }
1854
21.8k
}
1855
1856
void libfreehand::FHCollector::_outputDisplayText(const libfreehand::FHDisplayText *displayText, librevenge::RVNGDrawingInterface *painter)
1857
223k
{
1858
223k
  if (!painter || !displayText)
1859
186k
    return;
1860
1861
37.2k
  double xa = displayText->m_startX;
1862
37.2k
  double ya = displayText->m_startY;
1863
37.2k
  double xb = displayText->m_startX + displayText->m_width;
1864
37.2k
  double yb = displayText->m_startY + displayText->m_height;
1865
37.2k
  double xc = xa;
1866
37.2k
  double yc = yb;
1867
37.2k
  unsigned xFormId = displayText->m_xFormId;
1868
37.2k
  if (xFormId)
1869
34.8k
  {
1870
34.8k
    const FHTransform *trafo = _findTransform(xFormId);
1871
34.8k
    if (trafo)
1872
34.6k
    {
1873
34.6k
      trafo->applyToPoint(xa, ya);
1874
34.6k
      trafo->applyToPoint(xb, yb);
1875
34.6k
      trafo->applyToPoint(xc, yc);
1876
34.6k
    }
1877
34.8k
  }
1878
37.2k
  std::stack<FHTransform> groupTransforms(m_currentTransforms);
1879
73.1k
  while (!groupTransforms.empty())
1880
35.9k
  {
1881
35.9k
    groupTransforms.top().applyToPoint(xa, ya);
1882
35.9k
    groupTransforms.top().applyToPoint(xb, yb);
1883
35.9k
    groupTransforms.top().applyToPoint(xc, yc);
1884
35.9k
    groupTransforms.pop();
1885
35.9k
  }
1886
37.2k
  _normalizePoint(xa, ya);
1887
37.2k
  _normalizePoint(xb, yb);
1888
37.2k
  _normalizePoint(xc, yc);
1889
1890
37.4k
  for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
1891
191
  {
1892
191
    iter->applyToPoint(xa, ya);
1893
191
    iter->applyToPoint(xb, yb);
1894
191
    iter->applyToPoint(xc, yc);
1895
191
  }
1896
1897
37.2k
  double rotation = atan2(yb-yc, xb-xc);
1898
37.2k
  double height = sqrt((xc-xa)*(xc-xa) + (yc-ya)*(yc-ya));
1899
37.2k
  double width = sqrt((xc-xb)*(xc-xb) + (yc-yb)*(yc-yb));
1900
37.2k
  double xmid = (xa + xb) / 2.0;
1901
37.2k
  double ymid = (ya + yb) / 2.0;
1902
1903
37.2k
  librevenge::RVNGPropertyList textObjectProps;
1904
37.2k
  textObjectProps.insert("svg:x", xmid - displayText->m_width / 2.0);
1905
37.2k
  textObjectProps.insert("svg:y", ymid + displayText->m_height / 2.0);
1906
37.2k
  textObjectProps.insert("svg:height", height);
1907
37.2k
  textObjectProps.insert("svg:width", width);
1908
186k
  for (int i=0; i<4; ++i) // osnola: let assume that there is no padding
1909
149k
  {
1910
149k
    char const *padding[] = {"fo:padding-left","fo:padding-right","fo:padding-top","fo:padding-bottom"};
1911
149k
    textObjectProps.insert(padding[i],0,librevenge::RVNG_POINT);
1912
149k
  }
1913
37.2k
  if (!FH_ALMOST_ZERO(rotation))
1914
243
  {
1915
243
    textObjectProps.insert("librevenge:rotate", rotation * 180.0 / M_PI);
1916
243
    textObjectProps.insert("librevenge:rotate-cx",xmid);
1917
243
    textObjectProps.insert("librevenge:rotate-cy",ymid);
1918
243
  }
1919
37.2k
  if (displayText->m_justify==4)  // top-down: checkme do nothing
1920
9
    textObjectProps.insert("style:writing-mode", "tb-lr");
1921
37.2k
  painter->startTextObject(textObjectProps);
1922
1923
37.2k
  auto iterPara = displayText->m_paraProps.begin();
1924
37.2k
  auto iterChar = displayText->m_charProps.begin();
1925
1926
37.2k
  FH3ParaProperties paraProps = *iterPara++;
1927
37.2k
  FH3CharProperties charProps = *iterChar++;
1928
37.2k
  librevenge::RVNGString text;
1929
37.2k
  std::vector<unsigned char>::size_type i = 0;
1930
1931
37.2k
  librevenge::RVNGPropertyList paraPropList;
1932
37.2k
  _appendParagraphProperties(paraPropList, paraProps);
1933
37.2k
  switch (displayText->m_justify)
1934
37.2k
  {
1935
37.1k
  case 0: // left
1936
37.1k
    break;
1937
35
  case 1:
1938
35
    paraPropList.insert("fo:text-align", "center");
1939
35
    break;
1940
101
  case 2:
1941
101
    paraPropList.insert("fo:text-align", "end");
1942
101
    break;
1943
9
  case 3:
1944
9
    paraPropList.insert("fo:text-align", "justify");
1945
9
    break;
1946
9
  case 4:
1947
35
  default:
1948
35
    break;
1949
37.2k
  }
1950
37.2k
  if (charProps.m_leading>0)
1951
1.30k
    paraPropList.insert("fo:line-height",charProps.m_leading,librevenge::RVNG_POINT);
1952
35.9k
  else
1953
35.9k
    paraPropList.insert("fo:line-height",1.,librevenge::RVNG_PERCENT);
1954
37.2k
  painter->openParagraph(paraPropList);
1955
37.2k
  bool isParagraphOpened = true;
1956
1957
37.2k
  librevenge::RVNGPropertyList charPropList;
1958
37.2k
  _appendCharacterProperties(charPropList, charProps);
1959
37.2k
  painter->openSpan(charPropList);
1960
37.2k
  bool isSpanOpened = true;
1961
1962
1.06M
  while (i < displayText->m_characters.size())
1963
1.06M
  {
1964
1.06M
    _appendMacRoman(text, displayText->m_characters[i++]);
1965
1.06M
    if (i > paraProps.m_offset)
1966
143k
    {
1967
143k
      if (!text.empty())
1968
143k
        painter->insertText(text);
1969
143k
      text.clear();
1970
143k
      if (isParagraphOpened)
1971
143k
      {
1972
143k
        if (isSpanOpened)
1973
143k
        {
1974
143k
          painter->closeSpan();
1975
143k
          isSpanOpened = false;
1976
143k
        }
1977
143k
        painter->closeParagraph();
1978
143k
        isParagraphOpened = false;
1979
143k
      }
1980
143k
      if (iterPara != displayText->m_paraProps.end())
1981
106k
      {
1982
106k
        paraProps = *iterPara++;
1983
106k
      }
1984
143k
    }
1985
1.06M
    if (i > charProps.m_offset)
1986
200k
    {
1987
200k
      if (!text.empty())
1988
58.4k
        painter->insertText(text);
1989
200k
      text.clear();
1990
200k
      if (isSpanOpened)
1991
58.4k
      {
1992
58.4k
        painter->closeSpan();
1993
58.4k
        isSpanOpened = false;
1994
58.4k
      }
1995
200k
      if (iterChar != displayText->m_charProps.end())
1996
163k
      {
1997
163k
        charProps = *iterChar++;
1998
163k
      }
1999
200k
    }
2000
1.06M
    if (i >= displayText->m_characters.size())
2001
37.2k
      break;
2002
1.02M
    if (!isParagraphOpened)
2003
106k
    {
2004
#if 0
2005
      paraPropList.clear();
2006
      _appendParagraphProperties(paraPropList, paraProps);
2007
#endif
2008
106k
      if (charProps.m_leading>0)
2009
1.20k
        paraPropList.insert("fo:line-height",charProps.m_leading,librevenge::RVNG_POINT);
2010
105k
      else
2011
105k
        paraPropList.insert("fo:line-height",1.,librevenge::RVNG_PERCENT);
2012
106k
      painter->openParagraph(paraPropList);
2013
106k
      isParagraphOpened = true;
2014
106k
      if (!isSpanOpened)
2015
106k
      {
2016
106k
        charPropList.clear();
2017
106k
        _appendCharacterProperties(charPropList, charProps);
2018
106k
        painter->openSpan(charPropList);
2019
106k
        isSpanOpened = true;
2020
106k
      }
2021
106k
    }
2022
1.02M
    if (!isSpanOpened)
2023
58.0k
    {
2024
58.0k
      charPropList.clear();
2025
58.0k
      _appendCharacterProperties(charPropList, charProps);
2026
58.0k
      painter->openSpan(charPropList);
2027
58.0k
      isSpanOpened = true;
2028
58.0k
    }
2029
1.02M
  }
2030
37.2k
  if (!text.empty())
2031
80
    painter->insertText(text);
2032
37.2k
  if (isSpanOpened)
2033
80
    painter->closeSpan();
2034
37.2k
  if (isParagraphOpened)
2035
547
    painter->closeParagraph();
2036
2037
37.2k
  painter->endTextObject();
2038
37.2k
}
2039
2040
void libfreehand::FHCollector::_outputImageImport(const FHImageImport *image, librevenge::RVNGDrawingInterface *painter)
2041
223k
{
2042
223k
  if (!painter || !image)
2043
223k
    return;
2044
2045
65
  librevenge::RVNGPropertyList propList;
2046
65
  _appendStrokeProperties(propList, image->m_graphicStyleId);
2047
65
  _appendFillProperties(propList, image->m_graphicStyleId);
2048
65
  double xa = image->m_startX;
2049
65
  double ya = image->m_startY;
2050
65
  double xb = image->m_startX + image->m_width;
2051
65
  double yb = image->m_startY + image->m_height;
2052
65
  double xc = xa;
2053
65
  double yc = yb;
2054
65
  unsigned xFormId = image->m_xFormId;
2055
65
  if (xFormId)
2056
54
  {
2057
54
    const FHTransform *trafo = _findTransform(xFormId);
2058
54
    if (trafo)
2059
0
    {
2060
0
      trafo->applyToPoint(xa, ya);
2061
0
      trafo->applyToPoint(xb, yb);
2062
0
      trafo->applyToPoint(xc, yc);
2063
0
    }
2064
54
  }
2065
65
  std::stack<FHTransform> groupTransforms(m_currentTransforms);
2066
65
  while (!groupTransforms.empty())
2067
0
  {
2068
0
    groupTransforms.top().applyToPoint(xa, ya);
2069
0
    groupTransforms.top().applyToPoint(xb, yb);
2070
0
    groupTransforms.top().applyToPoint(xc, yc);
2071
0
    groupTransforms.pop();
2072
0
  }
2073
65
  _normalizePoint(xa, ya);
2074
65
  _normalizePoint(xb, yb);
2075
65
  _normalizePoint(xc, yc);
2076
2077
136
  for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
2078
71
  {
2079
71
    iter->applyToPoint(xa, ya);
2080
71
    iter->applyToPoint(xb, yb);
2081
71
    iter->applyToPoint(xc, yc);
2082
71
  }
2083
2084
65
  double rotation = atan2(yb-yc, xb-xc);
2085
65
  double height = sqrt((xc-xa)*(xc-xa) + (yc-ya)*(yc-ya));
2086
65
  double width = sqrt((xc-xb)*(xc-xb) + (yc-yb)*(yc-yb));
2087
65
  double xmid = (xa + xb) / 2.0;
2088
65
  double ymid = (ya + yb) / 2.0;
2089
2090
65
  librevenge::RVNGPropertyList imageProps;
2091
65
  imageProps.insert("svg:x", xmid - width / 2.0);
2092
65
  imageProps.insert("svg:y", ymid - height / 2.0);
2093
65
  imageProps.insert("svg:height", height);
2094
65
  imageProps.insert("svg:width", width);
2095
65
  if (!FH_ALMOST_ZERO(rotation))
2096
42
    imageProps.insert("librevenge:rotate", rotation * 180.0 / M_PI);
2097
2098
65
  imageProps.insert("librevenge:mime-type", "whatever");
2099
65
  librevenge::RVNGBinaryData data = getImageData(image->m_dataListId);
2100
2101
65
  if (data.empty())
2102
65
    return;
2103
2104
0
  const unsigned char *buffer = data.getDataBuffer();
2105
0
  unsigned long size = data.size();
2106
0
  if (isTiff(buffer, size))
2107
0
    imageProps.insert("librevenge:mime-type", "image/tiff");
2108
0
  else if (isBmp(buffer, size))
2109
0
    imageProps.insert("librevenge:mime-type", "image/bmp");
2110
0
  else if (isJpeg(buffer, size))
2111
0
    imageProps.insert("librevenge:mime-type", "image/jpeg");
2112
0
  else if (isPng(buffer, size))
2113
0
    imageProps.insert("librevenge:mime-type", "image/png");
2114
2115
0
  imageProps.insert("office:binary-data", data);
2116
0
  painter->setStyle(propList);
2117
0
  painter->drawGraphicObject(imageProps);
2118
0
}
2119
2120
void libfreehand::FHCollector::_outputTextRun(const std::vector<unsigned short> *characters, unsigned offset, unsigned length,
2121
                                              unsigned charStyleId, librevenge::RVNGDrawingInterface *painter)
2122
27.4k
{
2123
27.4k
  if (!painter || !characters || characters->empty())
2124
449
    return;
2125
27.0k
  librevenge::RVNGPropertyList propList;
2126
27.0k
  _appendCharacterProperties(propList, charStyleId);
2127
27.0k
  painter->openSpan(propList);
2128
27.0k
  std::vector<unsigned short> tmpChars;
2129
27.0k
  bool lastIsSpace=false;
2130
678k
  for (unsigned i = offset; i < length+offset && i < characters->size(); ++i)
2131
651k
  {
2132
651k
    unsigned c=(*characters)[i];
2133
651k
    if (c=='\t' || (c==' ' && lastIsSpace))
2134
10.0k
    {
2135
10.0k
      if (!tmpChars.empty())
2136
4.79k
      {
2137
4.79k
        librevenge::RVNGString text;
2138
4.79k
        _appendUTF16(text, tmpChars);
2139
4.79k
        painter->insertText(text);
2140
4.79k
        tmpChars.clear();
2141
4.79k
      }
2142
10.0k
      if (c=='\t')
2143
10.0k
        painter->insertTab();
2144
5
      else
2145
5
        painter->insertSpace();
2146
10.0k
      continue;
2147
10.0k
    }
2148
641k
    else
2149
641k
    {
2150
641k
      if (c<=0x1f)
2151
232k
      {
2152
232k
        switch (c)
2153
232k
        {
2154
591
        case 0xb: // end of column
2155
591
          break;
2156
192
        case 0x1f: // optional hyphen
2157
192
          break;
2158
231k
        default:
2159
231k
          FH_DEBUG_MSG(("libfreehand::FHCollector::_outputTextRun: find character %x\n", c));
2160
231k
          break;
2161
232k
        }
2162
232k
      }
2163
408k
      else
2164
408k
        tmpChars.push_back(c);
2165
641k
    }
2166
641k
    lastIsSpace=c==' ';
2167
641k
  }
2168
27.0k
  if (!tmpChars.empty())
2169
24.7k
  {
2170
24.7k
    librevenge::RVNGString text;
2171
24.7k
    _appendUTF16(text, tmpChars);
2172
24.7k
    painter->insertText(text);
2173
24.7k
  }
2174
27.0k
  painter->closeSpan();
2175
27.0k
}
2176
2177
const std::vector<unsigned> *libfreehand::FHCollector::_findListElements(unsigned id)
2178
24.1k
{
2179
24.1k
  std::map<unsigned, FHList>::const_iterator iter = m_lists.find(id);
2180
24.1k
  if (iter != m_lists.end())
2181
23.7k
    return &(iter->second.m_elements);
2182
385
  return nullptr;
2183
24.1k
}
2184
2185
2186
void libfreehand::FHCollector::_appendFontProperties(librevenge::RVNGPropertyList &propList, unsigned agdFontId)
2187
25.0k
{
2188
25.0k
  std::map<unsigned, FHAGDFont>::const_iterator iter = m_fonts.find(agdFontId);
2189
25.0k
  if (iter == m_fonts.end())
2190
206
    return;
2191
24.8k
  const FHAGDFont &font = iter->second;
2192
24.8k
  if (font.m_fontNameId)
2193
24.2k
  {
2194
24.2k
    std::map<unsigned, librevenge::RVNGString>::const_iterator iterString = m_strings.find(font.m_fontNameId);
2195
24.2k
    if (iterString != m_strings.end())
2196
23.4k
      propList.insert("style:font-name", iterString->second);
2197
24.2k
  }
2198
24.8k
  propList.insert("fo:font-size", font.m_fontSize, librevenge::RVNG_POINT);
2199
24.8k
  if (font.m_fontStyle & 1)
2200
1.66k
    propList.insert("fo:font-weight", "bold");
2201
24.8k
  if (font.m_fontStyle & 2)
2202
1.48k
    propList.insert("fo:font-style", "italic");
2203
24.8k
}
2204
2205
void libfreehand::FHCollector::_appendFillProperties(librevenge::RVNGPropertyList &propList, unsigned graphicStyleId)
2206
265k
{
2207
265k
  if (!propList["draw:fill"])
2208
129k
    propList.insert("draw:fill", "none");
2209
265k
  if (graphicStyleId && find(m_visitedObjects.begin(), m_visitedObjects.end(), graphicStyleId) == m_visitedObjects.end())
2210
261k
  {
2211
261k
    const ObjectRecursionGuard guard(m_visitedObjects, graphicStyleId);
2212
261k
    const FHPropList *propertyList = _findPropList(graphicStyleId);
2213
261k
    if (propertyList)
2214
248k
    {
2215
248k
      if (propertyList->m_parentId)
2216
132k
        _appendFillProperties(propList, propertyList->m_parentId);
2217
248k
      auto iter = propertyList->m_elements.find(m_fillId);
2218
248k
      if (iter != propertyList->m_elements.end())
2219
14.4k
      {
2220
14.4k
        _appendBasicFill(propList, _findBasicFill(iter->second));
2221
14.4k
        _appendLinearFill(propList, _findLinearFill(iter->second));
2222
14.4k
        _appendLensFill(propList, _findLensFill(iter->second));
2223
14.4k
        _appendRadialFill(propList, _findRadialFill(iter->second));
2224
14.4k
        _appendTileFill(propList, _findTileFill(iter->second));
2225
14.4k
        _appendPatternFill(propList, _findPatternFill(iter->second));
2226
14.4k
        _appendCustomProcFill(propList, _findCustomProc(iter->second));
2227
14.4k
      }
2228
248k
    }
2229
12.5k
    else
2230
12.5k
    {
2231
12.5k
      const FHGraphicStyle *graphicStyle = _findGraphicStyle(graphicStyleId);
2232
12.5k
      if (graphicStyle)
2233
5.64k
      {
2234
5.64k
        if (graphicStyle->m_parentId)
2235
3.16k
          _appendFillProperties(propList, graphicStyle->m_parentId);
2236
5.64k
        unsigned fillId = _findFillId(*graphicStyle);;
2237
5.64k
        if (fillId)
2238
1.21k
        {
2239
1.21k
          _appendBasicFill(propList, _findBasicFill(fillId));
2240
1.21k
          _appendLinearFill(propList, _findLinearFill(fillId));
2241
1.21k
          _appendLensFill(propList, _findLensFill(fillId));
2242
1.21k
          _appendRadialFill(propList, _findRadialFill(fillId));
2243
1.21k
          _appendTileFill(propList, _findTileFill(fillId));
2244
1.21k
          _appendPatternFill(propList, _findPatternFill(fillId));
2245
1.21k
          _appendCustomProcFill(propList, _findCustomProc(fillId));
2246
1.21k
        }
2247
4.42k
        else
2248
4.42k
        {
2249
4.42k
          const FHFilterAttributeHolder *filterAttributeHolder = _findFilterAttributeHolder(*graphicStyle);
2250
4.42k
          if (filterAttributeHolder)
2251
0
          {
2252
0
            if (filterAttributeHolder->m_graphicStyleId)
2253
0
              _appendFillProperties(propList, filterAttributeHolder->m_graphicStyleId);
2254
0
            if (filterAttributeHolder->m_filterId)
2255
0
              _applyFilter(propList, filterAttributeHolder->m_filterId);
2256
0
          }
2257
4.42k
        }
2258
5.64k
      }
2259
12.5k
    }
2260
261k
  }
2261
265k
}
2262
2263
void libfreehand::FHCollector::_appendStrokeProperties(librevenge::RVNGPropertyList &propList, unsigned graphicStyleId)
2264
265k
{
2265
265k
  if (!propList["draw:stroke"])
2266
129k
    propList.insert("draw:stroke", "none");
2267
265k
  if (graphicStyleId && find(m_visitedObjects.begin(), m_visitedObjects.end(), graphicStyleId) == m_visitedObjects.end())
2268
261k
  {
2269
261k
    const ObjectRecursionGuard guard(m_visitedObjects, graphicStyleId);
2270
261k
    const FHPropList *propertyList = _findPropList(graphicStyleId);
2271
261k
    if (propertyList)
2272
248k
    {
2273
248k
      if (propertyList->m_parentId)
2274
132k
        _appendStrokeProperties(propList, propertyList->m_parentId);
2275
248k
      auto iter = propertyList->m_elements.find(m_strokeId);
2276
248k
      if (iter != propertyList->m_elements.end())
2277
127k
      {
2278
127k
        _appendBasicLine(propList, _findBasicLine(iter->second));
2279
127k
        _appendPatternLine(propList, _findPatternLine(iter->second));
2280
127k
        _appendCustomProcLine(propList, _findCustomProc(iter->second));
2281
127k
      }
2282
248k
    }
2283
12.5k
    else
2284
12.5k
    {
2285
12.5k
      const FHGraphicStyle *graphicStyle = _findGraphicStyle(graphicStyleId);
2286
12.5k
      if (graphicStyle)
2287
5.64k
      {
2288
5.64k
        if (graphicStyle->m_parentId)
2289
3.16k
          _appendStrokeProperties(propList, graphicStyle->m_parentId);
2290
5.64k
        unsigned strokeId = _findStrokeId(*graphicStyle);
2291
5.64k
        if (strokeId)
2292
2.86k
        {
2293
2.86k
          _appendBasicLine(propList, _findBasicLine(strokeId));
2294
2.86k
          _appendPatternLine(propList, _findPatternLine(strokeId));
2295
2.86k
          _appendCustomProcLine(propList, _findCustomProc(strokeId));
2296
2.86k
        }
2297
2.77k
        else
2298
2.77k
        {
2299
2.77k
          const FHFilterAttributeHolder *filterAttributeHolder = _findFilterAttributeHolder(*graphicStyle);
2300
2.77k
          if (filterAttributeHolder)
2301
0
          {
2302
0
            if (filterAttributeHolder->m_graphicStyleId)
2303
0
              _appendFillProperties(propList, filterAttributeHolder->m_graphicStyleId);
2304
0
            if (filterAttributeHolder->m_filterId)
2305
0
              _applyFilter(propList, filterAttributeHolder->m_filterId);
2306
0
          }
2307
2.77k
        }
2308
5.64k
      }
2309
12.5k
    }
2310
261k
  }
2311
265k
}
2312
2313
void libfreehand::FHCollector::_appendBasicFill(librevenge::RVNGPropertyList &propList, const libfreehand::FHBasicFill *basicFill)
2314
15.6k
{
2315
15.6k
  if (!basicFill)
2316
12.5k
    return;
2317
3.13k
  propList.insert("draw:fill", "solid");
2318
3.13k
  librevenge::RVNGString color = getColorString(basicFill->m_colorId);
2319
3.13k
  if (!color.empty())
2320
2.67k
    propList.insert("draw:fill-color", color);
2321
458
  else
2322
458
    propList.insert("draw:fill-color", "#000000");
2323
3.13k
}
2324
2325
void libfreehand::FHCollector::_appendCustomProcFill(librevenge::RVNGPropertyList &propList, const libfreehand::FHCustomProc *fill)
2326
15.6k
{
2327
15.6k
  if (!fill || fill->m_ids.empty())
2328
14.4k
    return;
2329
1.22k
  propList.insert("draw:fill", "solid");
2330
1.22k
  librevenge::RVNGString color = getColorString(fill->m_ids[0]);
2331
1.22k
  if (!color.empty())
2332
1.14k
    propList.insert("draw:fill-color", color);
2333
80
  else
2334
80
    propList.insert("draw:fill-color", "#000000");
2335
1.22k
}
2336
2337
unsigned libfreehand::FHCollector::_findContentId(unsigned graphicStyleId)
2338
129k
{
2339
129k
  if (graphicStyleId)
2340
128k
  {
2341
128k
    const FHPropList *propertyList = _findPropList(graphicStyleId);
2342
128k
    if (propertyList)
2343
123k
    {
2344
123k
      auto iter = propertyList->m_elements.find(m_contentId);
2345
123k
      if (iter != propertyList->m_elements.end())
2346
10.6k
        return iter->second;
2347
123k
    }
2348
5.09k
    else
2349
5.09k
    {
2350
5.09k
      const FHGraphicStyle *graphicStyle = _findGraphicStyle(graphicStyleId);
2351
5.09k
      if (graphicStyle)
2352
2.87k
      {
2353
2.87k
        auto iter = graphicStyle->m_elements.find(m_contentId);
2354
2.87k
        if (iter != graphicStyle->m_elements.end())
2355
0
          return iter->second;
2356
2.87k
      }
2357
5.09k
    }
2358
128k
  }
2359
118k
  return 0;
2360
129k
}
2361
2362
void libfreehand::FHCollector::_appendLinearFill(librevenge::RVNGPropertyList &propList, const libfreehand::FHLinearFill *linearFill)
2363
15.6k
{
2364
15.6k
  if (!linearFill)
2365
14.8k
    return;
2366
797
  propList.insert("draw:fill", "gradient");
2367
797
  propList.insert("draw:style", "linear");
2368
797
  double angle = 90.0 - linearFill->m_angle;
2369
2.93k
  while (angle < 0.0)
2370
2.14k
    angle += 360.0;
2371
6.80k
  while (angle > 360.0)
2372
6.01k
    angle -= 360.0;
2373
797
  propList.insert("draw:angle", angle, librevenge::RVNG_GENERIC);
2374
2375
797
  const std::vector<FHColorStop> *multiColorList = _findMultiColorList(linearFill->m_multiColorListId);
2376
797
  if (multiColorList && multiColorList->size() > 1)
2377
72
  {
2378
72
    librevenge::RVNGString color = getColorString((*multiColorList)[0].m_colorId);
2379
72
    if (!color.empty())
2380
72
      propList.insert("draw:start-color", color);
2381
72
    color = getColorString((*multiColorList)[1].m_colorId);
2382
72
    if (!color.empty())
2383
72
      propList.insert("draw:end-color", color);
2384
72
  }
2385
725
  else
2386
725
  {
2387
725
    librevenge::RVNGString color = getColorString(linearFill->m_color1Id);
2388
725
    if (!color.empty())
2389
575
      propList.insert("draw:start-color", color);
2390
725
    color = getColorString(linearFill->m_color2Id);
2391
725
    if (!color.empty())
2392
526
      propList.insert("draw:end-color", color);
2393
725
  }
2394
797
}
2395
2396
void libfreehand::FHCollector::_applyFilter(librevenge::RVNGPropertyList &propList, unsigned filterId)
2397
0
{
2398
0
  if (!filterId)
2399
0
    return;
2400
0
  _appendOpacity(propList, _findOpacityFilter(filterId));
2401
0
  _appendShadow(propList, _findFWShadowFilter(filterId));
2402
0
  _appendGlow(propList, _findFWGlowFilter(filterId));
2403
0
}
2404
2405
void libfreehand::FHCollector::_appendOpacity(librevenge::RVNGPropertyList &propList, const double *opacity)
2406
0
{
2407
0
  if (!opacity)
2408
0
    return;
2409
0
  if (propList["draw:fill"] && propList["draw:fill"]->getStr() != "none")
2410
0
    propList.insert("draw:opacity", *opacity, librevenge::RVNG_PERCENT);
2411
0
  if (propList["draw:stroke"] && propList["draw:stroke"]->getStr() != "none")
2412
0
    propList.insert("svg:stroke-opacity", *opacity, librevenge::RVNG_PERCENT);
2413
0
}
2414
2415
void libfreehand::FHCollector::_appendShadow(librevenge::RVNGPropertyList &propList, const libfreehand::FWShadowFilter *filter)
2416
0
{
2417
0
  if (!filter)
2418
0
    return;
2419
0
  if (!filter->m_inner)
2420
0
  {
2421
0
    propList.insert("draw:shadow","visible"); // for ODG
2422
0
    propList.insert("draw:shadow-offset-x",filter->m_distribution * cos(M_PI * filter->m_angle / 180.0));
2423
0
    propList.insert("draw:shadow-offset-y",filter->m_distribution * sin(M_PI * filter->m_angle / 180.0));
2424
0
    propList.insert("draw:shadow-color",getColorString(filter->m_colorId));
2425
0
    propList.insert("draw:shadow-opacity",filter->m_opacity, librevenge::RVNG_PERCENT);
2426
0
  }
2427
0
}
2428
2429
void libfreehand::FHCollector::_appendGlow(librevenge::RVNGPropertyList & /* propList */, const libfreehand::FWGlowFilter *filter)
2430
0
{
2431
0
  if (!filter)
2432
0
    return;
2433
0
}
2434
2435
void libfreehand::FHCollector::_appendLensFill(librevenge::RVNGPropertyList &propList, const libfreehand::FHLensFill *lensFill)
2436
15.6k
{
2437
15.6k
  if (!lensFill)
2438
15.6k
    return;
2439
2440
0
  if (lensFill->m_colorId)
2441
0
  {
2442
0
    propList.insert("draw:fill", "solid");
2443
0
    librevenge::RVNGString color = getColorString(lensFill->m_colorId);
2444
0
    if (!color.empty())
2445
0
      propList.insert("draw:fill-color", color);
2446
0
    else
2447
0
      propList.insert("draw:fill", "none");
2448
0
  }
2449
0
  else
2450
0
    propList.insert("draw:fill", "none");
2451
2452
0
  switch (lensFill->m_mode)
2453
0
  {
2454
0
  case FH_LENSFILL_MODE_TRANSPARENCY:
2455
0
    propList.insert("draw:opacity", lensFill->m_value / 100.0, librevenge::RVNG_PERCENT);
2456
0
    break;
2457
0
  case FH_LENSFILL_MODE_MONOCHROME:
2458
0
    propList.insert("draw:fill", "none");
2459
0
    propList.insert("draw:color-mode", "greyscale");
2460
0
    break;
2461
0
  case FH_LENSFILL_MODE_MAGNIFY:
2462
0
    propList.insert("draw:fill", "none");
2463
0
    break;
2464
0
  case FH_LENSFILL_MODE_LIGHTEN: // emulate by semi-transparent white fill
2465
0
    propList.insert("draw:fill", "solid");
2466
0
    propList.insert("draw:fill-color", "#FFFFFF");
2467
0
    propList.insert("draw:opacity", lensFill->m_value / 100.0, librevenge::RVNG_PERCENT);
2468
0
    break;
2469
0
  case FH_LENSFILL_MODE_DARKEN: // emulate by semi-transparent black fill
2470
0
    propList.insert("draw:fill", "solid");
2471
0
    propList.insert("draw:fill-color", "#000000");
2472
0
    propList.insert("draw:opacity", lensFill->m_value / 100.0, librevenge::RVNG_PERCENT);
2473
0
    break;
2474
0
  case FH_LENSFILL_MODE_INVERT:
2475
0
    propList.insert("draw:fill", "none");
2476
0
    break;
2477
0
  default:
2478
0
    break;
2479
0
  }
2480
0
}
2481
2482
void libfreehand::FHCollector::_appendRadialFill(librevenge::RVNGPropertyList &propList, const libfreehand::FHRadialFill *radialFill)
2483
15.6k
{
2484
15.6k
  if (!radialFill)
2485
13.3k
    return;
2486
2487
2.24k
  propList.insert("draw:fill", "gradient");
2488
2.24k
  propList.insert("draw:style", "radial");
2489
2.24k
  propList.insert("svg:cx", radialFill->m_cx, librevenge::RVNG_PERCENT);
2490
2.24k
  propList.insert("svg:cy", radialFill->m_cy, librevenge::RVNG_PERCENT);
2491
2492
2.24k
  const std::vector<FHColorStop> *multiColorList = _findMultiColorList(radialFill->m_multiColorListId);
2493
2.24k
  if (multiColorList && multiColorList->size() > 1)
2494
64
  {
2495
64
    librevenge::RVNGString color = getColorString((*multiColorList)[0].m_colorId);
2496
64
    if (!color.empty())
2497
64
      propList.insert("draw:start-color", color);
2498
64
    color = getColorString((*multiColorList)[1].m_colorId);
2499
64
    if (!color.empty())
2500
64
      propList.insert("draw:end-color", color);
2501
64
  }
2502
2.18k
  else
2503
2.18k
  {
2504
2.18k
    librevenge::RVNGString color = getColorString(radialFill->m_color1Id);
2505
2.18k
    if (!color.empty())
2506
1.27k
      propList.insert("draw:start-color", color);
2507
2.18k
    color = getColorString(radialFill->m_color2Id);
2508
2.18k
    if (!color.empty())
2509
1.34k
      propList.insert("draw:end-color", color);
2510
2.18k
  }
2511
2.24k
}
2512
2513
void libfreehand::FHCollector::_appendTileFill(librevenge::RVNGPropertyList &propList, const libfreehand::FHTileFill *tileFill)
2514
15.6k
{
2515
15.6k
  if (!tileFill || !(tileFill->m_groupId))
2516
13.6k
    return;
2517
2518
1.98k
  const FHTransform *trafo = _findTransform(tileFill->m_xFormId);
2519
1.98k
  if (trafo)
2520
1.59k
    m_currentTransforms.push(*trafo);
2521
383
  else
2522
383
    m_currentTransforms.push(FHTransform());
2523
2524
1.98k
  FHBoundingBox bBox;
2525
1.98k
  _getBBofSomething(tileFill->m_groupId, bBox);
2526
1.98k
  if (bBox.isValid() && !FH_ALMOST_ZERO(bBox.m_xmax - bBox.m_xmin) && !FH_ALMOST_ZERO(bBox.m_ymax - bBox.m_ymin))
2527
1.83k
  {
2528
1.83k
    FHTransform fakeTrafo(tileFill->m_scaleX, 0.0, 0.0, tileFill->m_scaleY, - bBox.m_xmin, -bBox.m_ymin);
2529
1.83k
    m_fakeTransforms.push_back(fakeTrafo);
2530
2531
1.83k
    librevenge::RVNGStringVector svgOutput;
2532
1.83k
    librevenge::RVNGSVGDrawingGenerator generator(svgOutput, "");
2533
1.83k
    librevenge::RVNGPropertyList pList;
2534
1.83k
    pList.insert("svg:width", tileFill->m_scaleX * (bBox.m_xmax - bBox.m_xmin));
2535
1.83k
    pList.insert("svg:height", tileFill->m_scaleY * (bBox.m_ymax - bBox.m_ymin));
2536
1.83k
    generator.startPage(pList);
2537
2538
1.83k
    _outputSomething(tileFill->m_groupId, &generator);
2539
1.83k
    generator.endPage();
2540
1.83k
    if (!svgOutput.empty() && svgOutput[0].size() > 140) // basically empty svg if it is not fullfilled
2541
1.83k
    {
2542
1.83k
      const char *header =
2543
1.83k
        "<?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";
2544
1.83k
      librevenge::RVNGBinaryData output((const unsigned char *)header, strlen(header));
2545
1.83k
      output.append((unsigned char *)svgOutput[0].cstr(), strlen(svgOutput[0].cstr()));
2546
#if DUMP_TILE_FILLS
2547
      {
2548
        librevenge::RVNGString filename;
2549
        filename.sprintf("freehandtilefill%.4x.svg", tileFill->m_groupId);
2550
        FILE *f = fopen(filename.cstr(), "wb");
2551
        if (f)
2552
        {
2553
          const unsigned char *tmpBuffer = output.getDataBuffer();
2554
          for (unsigned long k = 0; k < output.size(); k++)
2555
            fprintf(f, "%c",tmpBuffer[k]);
2556
          fclose(f);
2557
        }
2558
      }
2559
#endif
2560
1.83k
      propList.insert("draw:fill", "bitmap");
2561
1.83k
      propList.insert("draw:fill-image", output);
2562
1.83k
      propList.insert("draw:fill-image-width", tileFill->m_scaleX * (bBox.m_xmax - bBox.m_xmin));
2563
1.83k
      propList.insert("draw:fill-image-height", tileFill->m_scaleY * (bBox.m_ymax - bBox.m_ymin));
2564
1.83k
      propList.insert("librevenge:mime-type", "image/svg+xml");
2565
1.83k
      propList.insert("style:repeat", "repeat");
2566
1.83k
    }
2567
2568
1.83k
    if (!m_fakeTransforms.empty())
2569
1.83k
      m_fakeTransforms.pop_back();
2570
1.83k
  }
2571
1.98k
  if (!m_currentTransforms.empty())
2572
1.98k
    m_currentTransforms.pop();
2573
1.98k
}
2574
2575
void libfreehand::FHCollector::_appendPatternFill(librevenge::RVNGPropertyList &propList, const libfreehand::FHPatternFill *patternFill)
2576
15.6k
{
2577
15.6k
  if (!patternFill)
2578
14.5k
    return;
2579
1.11k
  librevenge::RVNGBinaryData output;
2580
1.11k
  _generateBitmapFromPattern(output, patternFill->m_colorId, patternFill->m_pattern);
2581
1.11k
  propList.insert("draw:fill", "bitmap");
2582
1.11k
  propList.insert("draw:fill-image", output);
2583
1.11k
  propList.insert("librevenge:mime-type", "image/bmp");
2584
1.11k
  propList.insert("style:repeat", "repeat");
2585
1.11k
}
2586
2587
void libfreehand::FHCollector::_appendLinePattern(librevenge::RVNGPropertyList &propList, const libfreehand::FHLinePattern *linePattern)
2588
118k
{
2589
118k
  if (!linePattern || linePattern->m_dashes.size()<=1)
2590
116k
    return;
2591
1.80k
  int nDots1=0, nDots2=0;
2592
1.80k
  double size1=0, size2=0, totalGap=0.0;
2593
4.71k
  for (size_t c=0; c+1 < linePattern->m_dashes.size();)
2594
3.10k
  {
2595
3.10k
    double sz=linePattern->m_dashes[c++];
2596
3.10k
    if (nDots2 && (sz<size2||sz>size2))
2597
191
    {
2598
191
      static bool first=true;
2599
191
      if (first)
2600
1
      {
2601
1
        FH_DEBUG_MSG(("libfreehand::FHCollector::_appendLinePattern: can not set some dash\n"));
2602
1
        first = false;
2603
1
      }
2604
191
      break;
2605
191
    }
2606
2.90k
    if (nDots2)
2607
375
      nDots2++;
2608
2.53k
    else if (!nDots1 || (sz>=size1 && sz <= size1))
2609
1.98k
    {
2610
1.98k
      nDots1++;
2611
1.98k
      size1=sz;
2612
1.98k
    }
2613
547
    else
2614
547
    {
2615
547
      nDots2=1;
2616
547
      size2=sz;
2617
547
    }
2618
2.90k
    totalGap += linePattern->m_dashes[c++];
2619
2.90k
  }
2620
1.80k
  propList.insert("draw:stroke", "dash");
2621
1.80k
  propList.insert("draw:dots1", nDots1);
2622
1.80k
  propList.insert("draw:dots1-length", double(size1), librevenge::RVNG_POINT);
2623
1.80k
  if (nDots2)
2624
547
  {
2625
547
    propList.insert("draw:dots2", nDots2);
2626
547
    propList.insert("draw:dots2-length", double(size2), librevenge::RVNG_POINT);
2627
547
  }
2628
1.80k
  const double distance = ((nDots1 + nDots2) > 0) ? double(totalGap)/double(nDots1+nDots2) : double(totalGap);
2629
1.80k
  propList.insert("draw:distance", distance, librevenge::RVNG_POINT);;
2630
1.80k
}
2631
2632
void libfreehand::FHCollector::_appendArrowPath(librevenge::RVNGPropertyList &propList, const FHPath *arrow, bool startArrow)
2633
237k
{
2634
237k
  if (!arrow)
2635
224k
    return;
2636
2637
13.1k
  FHPath path=*arrow;
2638
13.1k
  path.transform(FHTransform(0,-1,1,0,0,0));
2639
13.1k
  std::string pString=path.getPathString();
2640
13.1k
  if (pString.empty()) return;
2641
13.1k
  std::string wh(startArrow ? "start" : "end");
2642
13.1k
  propList.insert((std::string("draw:marker-")+wh+"-path").c_str(), pString.c_str());
2643
13.1k
  FHBoundingBox box;
2644
13.1k
  path.getBoundingBox(box.m_xmin, box.m_ymin, box.m_xmax, box.m_ymax);
2645
13.1k
  librevenge::RVNGString boxString;
2646
13.1k
  boxString.sprintf("%d %d %d %d", int(box.m_xmin*35), int(box.m_ymin*35), int(35*(box.m_xmax-box.m_xmin)), int(35*(box.m_ymax-box.m_ymin)));
2647
13.1k
  propList.insert((std::string("draw:marker-")+wh+"-viewbox").c_str(), boxString);
2648
13.1k
  propList.insert((std::string("draw:marker-")+wh+"-width").c_str(), 10, librevenge::RVNG_POINT); // change me
2649
13.1k
}
2650
2651
void libfreehand::FHCollector::_appendBasicLine(librevenge::RVNGPropertyList &propList, const libfreehand::FHBasicLine *basicLine)
2652
130k
{
2653
130k
  if (!basicLine)
2654
11.7k
    return;
2655
  // osnola: we do not want to change draw:stroke, if stroke is defined recursively
2656
118k
  propList.insert("draw:stroke", "solid");
2657
118k
  librevenge::RVNGString color = getColorString(basicLine->m_colorId);
2658
118k
  if (!color.empty())
2659
104k
    propList.insert("svg:stroke-color", color);
2660
13.8k
  else if (!propList["svg:stroke-color"]) // set to default
2661
12.3k
    propList.insert("svg:stroke-color", "#000000");
2662
118k
  propList.insert("svg:stroke-width", basicLine->m_width);
2663
118k
  _appendLinePattern(propList, _findLinePattern(basicLine->m_linePatternId));
2664
118k
  _appendArrowPath(propList, _findArrowPath(basicLine->m_startArrowId), true);
2665
118k
  _appendArrowPath(propList, _findArrowPath(basicLine->m_endArrowId), false);
2666
118k
}
2667
2668
void libfreehand::FHCollector::_appendCustomProcLine(librevenge::RVNGPropertyList &propList, const libfreehand::FHCustomProc *customProc)
2669
130k
{
2670
130k
  if (!customProc)
2671
129k
    return;
2672
753
  propList.insert("draw:stroke", "solid");
2673
753
  librevenge::RVNGString color;
2674
753
  if (!customProc->m_ids.empty())
2675
656
    color= getColorString(customProc->m_ids[0]);
2676
753
  if (!color.empty())
2677
553
    propList.insert("svg:stroke-color", color);
2678
753
  if (!customProc->m_widths.empty())
2679
674
    propList.insert("svg:stroke-width", customProc->m_widths[0], librevenge::RVNG_POINT);
2680
753
}
2681
2682
void libfreehand::FHCollector::_appendPatternLine(librevenge::RVNGPropertyList &propList, const libfreehand::FHPatternLine *patternLine)
2683
130k
{
2684
130k
  if (!patternLine)
2685
129k
    return;
2686
1.22k
  propList.insert("draw:stroke", "solid");
2687
1.22k
  librevenge::RVNGString color = getColorString(patternLine->m_colorId, patternLine->m_percentPattern);
2688
1.22k
  if (!color.empty())
2689
1.06k
    propList.insert("svg:stroke-color", color);
2690
157
  else if (!propList["svg:stroke-color"]) // set to default
2691
69
    propList.insert("svg:stroke-color", "#000000");
2692
1.22k
  propList.insert("svg:stroke-width", patternLine->m_width);
2693
1.22k
}
2694
2695
const libfreehand::FHPath *libfreehand::FHCollector::_findPath(unsigned id)
2696
228k
{
2697
228k
  if (!id)
2698
302
    return nullptr;
2699
227k
  std::map<unsigned, FHPath>::const_iterator iter = m_paths.find(id);
2700
227k
  if (iter != m_paths.end())
2701
131k
    return &(iter->second);
2702
96.6k
  return nullptr;
2703
227k
}
2704
2705
const libfreehand::FHNewBlend *libfreehand::FHCollector::_findNewBlend(unsigned id)
2706
225k
{
2707
225k
  if (!id)
2708
0
    return nullptr;
2709
225k
  std::map<unsigned, FHNewBlend>::const_iterator iter = m_newBlends.find(id);
2710
225k
  if (iter != m_newBlends.end())
2711
12
    return &(iter->second);
2712
225k
  return nullptr;
2713
225k
}
2714
2715
const libfreehand::FHGroup *libfreehand::FHCollector::_findGroup(unsigned id)
2716
225k
{
2717
225k
  if (!id)
2718
0
    return nullptr;
2719
225k
  std::map<unsigned, FHGroup>::const_iterator iter = m_groups.find(id);
2720
225k
  if (iter != m_groups.end())
2721
19.6k
    return &(iter->second);
2722
205k
  return nullptr;
2723
225k
}
2724
2725
const libfreehand::FHGroup *libfreehand::FHCollector::_findClipGroup(unsigned id)
2726
225k
{
2727
225k
  if (!id)
2728
0
    return nullptr;
2729
225k
  std::map<unsigned, FHGroup>::const_iterator iter = m_clipGroups.find(id);
2730
225k
  if (iter != m_clipGroups.end())
2731
84
    return &(iter->second);
2732
225k
  return nullptr;
2733
225k
}
2734
2735
const libfreehand::FHCompositePath *libfreehand::FHCollector::_findCompositePath(unsigned id)
2736
225k
{
2737
225k
  if (!id)
2738
0
    return nullptr;
2739
225k
  std::map<unsigned, FHCompositePath>::const_iterator iter = m_compositePaths.find(id);
2740
225k
  if (iter != m_compositePaths.end())
2741
322
    return &(iter->second);
2742
225k
  return nullptr;
2743
225k
}
2744
2745
const libfreehand::FHPathText *libfreehand::FHCollector::_findPathText(unsigned id)
2746
225k
{
2747
225k
  if (!id)
2748
0
    return nullptr;
2749
225k
  std::map<unsigned, FHPathText>::const_iterator iter = m_pathTexts.find(id);
2750
225k
  if (iter != m_pathTexts.end())
2751
189
    return &(iter->second);
2752
225k
  return nullptr;
2753
225k
}
2754
2755
const libfreehand::FHTextObject *libfreehand::FHCollector::_findTextObject(unsigned id)
2756
225k
{
2757
225k
  if (!id)
2758
0
    return nullptr;
2759
225k
  std::map<unsigned, FHTextObject>::const_iterator iter = m_textObjects.find(id);
2760
225k
  if (iter != m_textObjects.end())
2761
20.3k
    return &(iter->second);
2762
205k
  return nullptr;
2763
225k
}
2764
2765
const libfreehand::FHTransform *libfreehand::FHCollector::_findTransform(unsigned id)
2766
76.0k
{
2767
76.0k
  if (!id)
2768
42
    return nullptr;
2769
76.0k
  std::map<unsigned, FHTransform>::const_iterator iter = m_transforms.find(id);
2770
76.0k
  if (iter != m_transforms.end())
2771
73.3k
    return &(iter->second);
2772
2.68k
  return nullptr;
2773
76.0k
}
2774
2775
const libfreehand::FHTEffect *libfreehand::FHCollector::_findTEffect(unsigned id)
2776
227k
{
2777
227k
  if (!id)
2778
66.0k
    return nullptr;
2779
161k
  std::map<unsigned, FHTEffect>::const_iterator iter = m_tEffects.find(id);
2780
161k
  if (iter != m_tEffects.end())
2781
158k
    return &(iter->second);
2782
2.99k
  return nullptr;
2783
161k
}
2784
2785
const libfreehand::FHParagraph *libfreehand::FHCollector::_findParagraph(unsigned id)
2786
80.1k
{
2787
80.1k
  if (!id)
2788
23.9k
    return nullptr;
2789
56.1k
  std::map<unsigned, FHParagraph>::const_iterator iter = m_paragraphs.find(id);
2790
56.1k
  if (iter != m_paragraphs.end())
2791
24.6k
    return &(iter->second);
2792
31.4k
  return nullptr;
2793
56.1k
}
2794
2795
const std::vector<libfreehand::FHTab> *libfreehand::FHCollector::_findTabTable(unsigned id)
2796
0
{
2797
0
  if (!id)
2798
0
    return nullptr;
2799
0
  std::map<unsigned, std::vector<libfreehand::FHTab> >::const_iterator iter = m_tabs.find(id);
2800
0
  if (iter != m_tabs.end())
2801
0
    return &(iter->second);
2802
0
  return nullptr;
2803
0
}
2804
2805
const std::vector<unsigned> *libfreehand::FHCollector::_findTStringElements(unsigned id)
2806
19.5k
{
2807
19.5k
  if (!id)
2808
32
    return nullptr;
2809
19.5k
  std::map<unsigned, std::vector<unsigned> >::const_iterator iter = m_tStrings.find(id);
2810
19.5k
  if (iter != m_tStrings.end())
2811
19.4k
    return &(iter->second);
2812
101
  return nullptr;
2813
19.5k
}
2814
2815
const libfreehand::FHPropList *libfreehand::FHCollector::_findPropList(unsigned id)
2816
651k
{
2817
651k
  if (!id)
2818
0
    return nullptr;
2819
651k
  std::map<unsigned, FHPropList>::const_iterator iter = m_propertyLists.find(id);
2820
651k
  if (iter != m_propertyLists.end())
2821
620k
    return &(iter->second);
2822
30.2k
  return nullptr;
2823
651k
}
2824
2825
const libfreehand::FHGraphicStyle *libfreehand::FHCollector::_findGraphicStyle(unsigned id)
2826
30.2k
{
2827
30.2k
  if (!id)
2828
0
    return nullptr;
2829
30.2k
  std::map<unsigned, FHGraphicStyle>::const_iterator iter = m_graphicStyles.find(id);
2830
30.2k
  if (iter != m_graphicStyles.end())
2831
14.1k
    return &(iter->second);
2832
16.1k
  return nullptr;
2833
30.2k
}
2834
2835
const libfreehand::FHBasicFill *libfreehand::FHCollector::_findBasicFill(unsigned id)
2836
25.2k
{
2837
25.2k
  if (!id)
2838
4.93k
    return nullptr;
2839
20.3k
  std::map<unsigned, FHBasicFill>::const_iterator iter = m_basicFills.find(id);
2840
20.3k
  if (iter != m_basicFills.end())
2841
4.21k
    return &(iter->second);
2842
16.1k
  return nullptr;
2843
20.3k
}
2844
2845
const libfreehand::FHLinearFill *libfreehand::FHCollector::_findLinearFill(unsigned id)
2846
24.2k
{
2847
24.2k
  if (!id)
2848
4.93k
    return nullptr;
2849
19.2k
  std::map<unsigned, FHLinearFill>::const_iterator iter = m_linearFills.find(id);
2850
19.2k
  if (iter != m_linearFills.end())
2851
869
    return &(iter->second);
2852
18.4k
  return nullptr;
2853
19.2k
}
2854
2855
const libfreehand::FHLensFill *libfreehand::FHCollector::_findLensFill(unsigned id)
2856
24.1k
{
2857
24.1k
  if (!id)
2858
4.93k
    return nullptr;
2859
19.1k
  std::map<unsigned, FHLensFill>::const_iterator iter = m_lensFills.find(id);
2860
19.1k
  if (iter != m_lensFills.end())
2861
0
    return &(iter->second);
2862
19.1k
  return nullptr;
2863
19.1k
}
2864
2865
const libfreehand::FHRadialFill *libfreehand::FHCollector::_findRadialFill(unsigned id)
2866
24.1k
{
2867
24.1k
  if (!id)
2868
4.93k
    return nullptr;
2869
19.1k
  std::map<unsigned, FHRadialFill>::const_iterator iter = m_radialFills.find(id);
2870
19.1k
  if (iter != m_radialFills.end())
2871
2.31k
    return &(iter->second);
2872
16.8k
  return nullptr;
2873
19.1k
}
2874
2875
const libfreehand::FHTileFill *libfreehand::FHCollector::_findTileFill(unsigned id)
2876
24.0k
{
2877
24.0k
  if (!id)
2878
4.93k
    return nullptr;
2879
19.1k
  std::map<unsigned, FHTileFill>::const_iterator iter = m_tileFills.find(id);
2880
19.1k
  if (iter != m_tileFills.end())
2881
2.56k
    return &(iter->second);
2882
16.5k
  return nullptr;
2883
19.1k
}
2884
2885
const libfreehand::FHPatternFill *libfreehand::FHCollector::_findPatternFill(unsigned id)
2886
24.0k
{
2887
24.0k
  if (!id)
2888
4.93k
    return nullptr;
2889
19.1k
  std::map<unsigned, FHPatternFill>::const_iterator iter = m_patternFills.find(id);
2890
19.1k
  if (iter != m_patternFills.end())
2891
1.11k
    return &(iter->second);
2892
18.0k
  return nullptr;
2893
19.1k
}
2894
2895
const libfreehand::FHLinePattern *libfreehand::FHCollector::_findLinePattern(unsigned id)
2896
118k
{
2897
118k
  if (!id)
2898
110k
    return nullptr;
2899
8.24k
  std::map<unsigned, FHLinePattern>::const_iterator iter = m_linePatterns.find(id);
2900
8.24k
  if (iter != m_linePatterns.end())
2901
1.97k
    return &(iter->second);
2902
6.27k
  return nullptr;
2903
8.24k
}
2904
2905
const libfreehand::FHPath *libfreehand::FHCollector::_findArrowPath(unsigned id)
2906
237k
{
2907
237k
  if (!id)
2908
215k
    return nullptr;
2909
22.3k
  std::map<unsigned, FHPath>::const_iterator iter = m_arrowPaths.find(id);
2910
22.3k
  if (iter != m_arrowPaths.end())
2911
13.1k
    return &(iter->second);
2912
9.20k
  return nullptr;
2913
22.3k
}
2914
2915
const libfreehand::FHBasicLine *libfreehand::FHCollector::_findBasicLine(unsigned id)
2916
140k
{
2917
140k
  if (!id)
2918
4.93k
    return nullptr;
2919
135k
  std::map<unsigned, FHBasicLine>::const_iterator iter = m_basicLines.find(id);
2920
135k
  if (iter != m_basicLines.end())
2921
121k
    return &(iter->second);
2922
13.6k
  return nullptr;
2923
135k
}
2924
2925
const libfreehand::FHCustomProc *libfreehand::FHCollector::_findCustomProc(unsigned id)
2926
154k
{
2927
154k
  if (!id)
2928
4.93k
    return nullptr;
2929
149k
  std::map<unsigned, FHCustomProc>::const_iterator iter = m_customProcs.find(id);
2930
149k
  if (iter != m_customProcs.end())
2931
2.06k
    return &(iter->second);
2932
147k
  return nullptr;
2933
149k
}
2934
2935
const libfreehand::FHPatternLine *libfreehand::FHCollector::_findPatternLine(unsigned id)
2936
130k
{
2937
130k
  if (!id)
2938
0
    return nullptr;
2939
130k
  std::map<unsigned, FHPatternLine>::const_iterator iter = m_patternLines.find(id);
2940
130k
  if (iter != m_patternLines.end())
2941
1.22k
    return &(iter->second);
2942
129k
  return nullptr;
2943
130k
}
2944
2945
const libfreehand::FHRGBColor *libfreehand::FHCollector::_findRGBColor(unsigned id)
2946
365k
{
2947
365k
  if (!id)
2948
4.95k
    return nullptr;
2949
360k
  std::map<unsigned, FHRGBColor>::const_iterator iter = m_rgbColors.find(id);
2950
360k
  if (iter != m_rgbColors.end())
2951
345k
    return &(iter->second);
2952
15.3k
  return nullptr;
2953
360k
}
2954
2955
const libfreehand::FHTintColor *libfreehand::FHCollector::_findTintColor(unsigned id)
2956
20.2k
{
2957
20.2k
  if (!id)
2958
4.95k
    return nullptr;
2959
15.2k
  std::map<unsigned, FHTintColor>::const_iterator iter = m_tints.find(id);
2960
15.2k
  if (iter != m_tints.end())
2961
124
    return &(iter->second);
2962
15.1k
  return nullptr;
2963
15.2k
}
2964
2965
const libfreehand::FHDisplayText *libfreehand::FHCollector::_findDisplayText(unsigned id)
2966
225k
{
2967
225k
  if (!id)
2968
29
    return nullptr;
2969
225k
  std::map<unsigned, FHDisplayText>::const_iterator iter = m_displayTexts.find(id);
2970
225k
  if (iter != m_displayTexts.end())
2971
37.2k
    return &(iter->second);
2972
188k
  return nullptr;
2973
225k
}
2974
2975
const libfreehand::FHImageImport *libfreehand::FHCollector::_findImageImport(unsigned id)
2976
225k
{
2977
225k
  if (!id)
2978
0
    return nullptr;
2979
225k
  std::map<unsigned, FHImageImport>::const_iterator iter = m_images.find(id);
2980
225k
  if (iter != m_images.end())
2981
65
    return &(iter->second);
2982
225k
  return nullptr;
2983
225k
}
2984
2985
const librevenge::RVNGBinaryData *libfreehand::FHCollector::_findData(unsigned id)
2986
0
{
2987
0
  if (!id)
2988
0
    return nullptr;
2989
0
  std::map<unsigned, librevenge::RVNGBinaryData>::const_iterator iter = m_data.find(id);
2990
0
  if (iter != m_data.end())
2991
0
    return &(iter->second);
2992
0
  return nullptr;
2993
0
}
2994
2995
const libfreehand::FHSymbolClass *libfreehand::FHCollector::_findSymbolClass(unsigned id)
2996
0
{
2997
0
  if (!id)
2998
0
    return nullptr;
2999
0
  std::map<unsigned, FHSymbolClass>::const_iterator iter = m_symbolClasses.find(id);
3000
0
  if (iter != m_symbolClasses.end())
3001
0
    return &(iter->second);
3002
0
  return nullptr;
3003
0
}
3004
3005
const libfreehand::FHSymbolInstance *libfreehand::FHCollector::_findSymbolInstance(unsigned id)
3006
225k
{
3007
225k
  if (!id)
3008
0
    return nullptr;
3009
225k
  std::map<unsigned, FHSymbolInstance>::const_iterator iter = m_symbolInstances.find(id);
3010
225k
  if (iter != m_symbolInstances.end())
3011
0
    return &(iter->second);
3012
225k
  return nullptr;
3013
225k
}
3014
3015
const libfreehand::FHFilterAttributeHolder *libfreehand::FHCollector::_findFilterAttributeHolder(unsigned id)
3016
13.4k
{
3017
13.4k
  if (!id)
3018
404
    return nullptr;
3019
13.0k
  std::map<unsigned, FHFilterAttributeHolder>::const_iterator iter = m_filterAttributeHolders.find(id);
3020
13.0k
  if (iter != m_filterAttributeHolders.end())
3021
0
    return &(iter->second);
3022
13.0k
  return nullptr;
3023
13.0k
}
3024
3025
const std::vector<libfreehand::FHColorStop> *libfreehand::FHCollector::_findMultiColorList(unsigned id)
3026
3.04k
{
3027
3.04k
  if (!id)
3028
2.90k
    return nullptr;
3029
136
  std::map<unsigned, std::vector<libfreehand::FHColorStop> >::const_iterator iter = m_multiColorLists.find(id);
3030
136
  if (iter != m_multiColorLists.end())
3031
136
    return &(iter->second);
3032
0
  return nullptr;
3033
136
}
3034
3035
const double *libfreehand::FHCollector::_findOpacityFilter(unsigned id)
3036
0
{
3037
0
  if (!id)
3038
0
    return nullptr;
3039
0
  std::map<unsigned, double>::const_iterator iter = m_opacityFilters.find(id);
3040
0
  if (iter != m_opacityFilters.end())
3041
0
    return &(iter->second);
3042
0
  return nullptr;
3043
0
}
3044
3045
const libfreehand::FWShadowFilter *libfreehand::FHCollector::_findFWShadowFilter(unsigned id)
3046
0
{
3047
0
  if (!id)
3048
0
    return nullptr;
3049
0
  std::map<unsigned, FWShadowFilter>::const_iterator iter = m_shadowFilters.find(id);
3050
0
  if (iter != m_shadowFilters.end())
3051
0
    return &(iter->second);
3052
0
  return nullptr;
3053
0
}
3054
3055
const libfreehand::FWGlowFilter *libfreehand::FHCollector::_findFWGlowFilter(unsigned id)
3056
0
{
3057
0
  if (!id)
3058
0
    return nullptr;
3059
0
  std::map<unsigned, FWGlowFilter>::const_iterator iter = m_glowFilters.find(id);
3060
0
  if (iter != m_glowFilters.end())
3061
0
    return &(iter->second);
3062
0
  return nullptr;
3063
0
}
3064
3065
unsigned libfreehand::FHCollector::_findStrokeId(const libfreehand::FHGraphicStyle &graphicStyle)
3066
5.64k
{
3067
5.64k
  unsigned listId = graphicStyle.m_attrId;
3068
5.64k
  if (!listId)
3069
111
    return 0;
3070
5.53k
  std::map<unsigned, FHList>::const_iterator iter = m_lists.find(listId);
3071
5.53k
  if (iter == m_lists.end())
3072
402
    return 0;
3073
5.13k
  unsigned strokeId = 0;
3074
5.13k
  for (unsigned int element : iter->second.m_elements)
3075
9.65k
  {
3076
9.65k
    unsigned valueId = _findValueFromAttribute(element);
3077
9.65k
    if (_findBasicLine(valueId))
3078
2.86k
      strokeId = valueId;
3079
9.65k
  }
3080
5.13k
  return strokeId;
3081
5.53k
}
3082
3083
unsigned libfreehand::FHCollector::_findFillId(const libfreehand::FHGraphicStyle &graphicStyle)
3084
5.64k
{
3085
5.64k
  unsigned listId = graphicStyle.m_attrId;
3086
5.64k
  if (!listId)
3087
111
    return 0;
3088
5.53k
  std::map<unsigned, FHList>::const_iterator iter = m_lists.find(listId);
3089
5.53k
  if (iter == m_lists.end())
3090
402
    return 0;
3091
5.13k
  unsigned fillId = 0;
3092
5.13k
  for (unsigned int element : iter->second.m_elements)
3093
9.65k
  {
3094
9.65k
    unsigned valueId = _findValueFromAttribute(element);
3095
    // Add other fills if we support them
3096
9.65k
    if (_findBasicFill(valueId) || _findLinearFill(valueId)
3097
8.49k
        || _findLensFill(valueId) || _findRadialFill(valueId)
3098
8.43k
        || _findTileFill(valueId) || _findPatternFill(valueId) || _findCustomProc(valueId))
3099
1.21k
      fillId = valueId;
3100
9.65k
  }
3101
5.13k
  return fillId;
3102
5.53k
}
3103
3104
const libfreehand::FHFilterAttributeHolder *libfreehand::FHCollector::_findFilterAttributeHolder(const libfreehand::FHGraphicStyle &graphicStyle)
3105
7.20k
{
3106
7.20k
  unsigned listId = graphicStyle.m_attrId;
3107
7.20k
  if (!listId)
3108
222
    return nullptr;
3109
6.98k
  std::map<unsigned, FHList>::const_iterator iter = m_lists.find(listId);
3110
6.98k
  if (iter == m_lists.end())
3111
804
    return nullptr;
3112
6.18k
  for (unsigned int element : iter->second.m_elements)
3113
13.4k
  {
3114
13.4k
    const FHFilterAttributeHolder *attributeHolder = _findFilterAttributeHolder(element);
3115
3116
13.4k
    if (attributeHolder)
3117
0
      return attributeHolder;
3118
13.4k
  }
3119
6.18k
  return nullptr;
3120
6.18k
}
3121
3122
3123
unsigned libfreehand::FHCollector::_findValueFromAttribute(unsigned id)
3124
21.5k
{
3125
21.5k
  if (!id)
3126
408
    return 0;
3127
21.1k
  std::map<unsigned, FHAttributeHolder>::const_iterator iter = m_attributeHolders.find(id);
3128
21.1k
  if (iter == m_attributeHolders.end())
3129
10.0k
    return 0;
3130
11.0k
  unsigned value = 0;
3131
11.0k
  if (iter->second.m_parentId)
3132
2.21k
    value = _findValueFromAttribute(iter->second.m_parentId);
3133
11.0k
  if (iter->second.m_attrId)
3134
10.2k
    value = iter->second.m_attrId;
3135
11.0k
  return value;
3136
21.1k
}
3137
3138
librevenge::RVNGBinaryData libfreehand::FHCollector::getImageData(unsigned id)
3139
65
{
3140
65
  std::map<unsigned, FHDataList>::const_iterator iter = m_dataLists.find(id);
3141
65
  librevenge::RVNGBinaryData data;
3142
65
  if (iter == m_dataLists.end())
3143
65
    return data;
3144
0
  for (unsigned int element : iter->second.m_elements)
3145
0
  {
3146
0
    const librevenge::RVNGBinaryData *pData = _findData(element);
3147
0
    if (pData)
3148
0
      data.append(*pData);
3149
0
  }
3150
0
  return data;
3151
65
}
3152
3153
librevenge::RVNGString libfreehand::FHCollector::getColorString(unsigned id, double tintVal)
3154
364k
{
3155
364k
  FHRGBColor col;
3156
364k
  const FHRGBColor *color = _findRGBColor(id);
3157
364k
  if (color)
3158
345k
    col=*color;
3159
19.6k
  else
3160
19.6k
  {
3161
19.6k
    const FHTintColor *tint = _findTintColor(id);
3162
19.6k
    if (!tint)
3163
19.5k
      return librevenge::RVNGString();
3164
124
    col=getRGBFromTint(*tint);
3165
124
  }
3166
345k
  if (tintVal<=0 || tintVal>=1)
3167
344k
    return _getColorString(col);
3168
3169
1.06k
  FHRGBColor finalColor;
3170
1.06k
  finalColor.m_red = col.m_red * tintVal + (1 - tintVal) * 65536;
3171
1.06k
  finalColor.m_green = col.m_green * tintVal + (1 - tintVal) * 65536;
3172
1.06k
  finalColor.m_blue = col.m_blue * tintVal + (1 - tintVal) * 65536;
3173
1.06k
  return _getColorString(finalColor);
3174
345k
}
3175
3176
libfreehand::FHRGBColor libfreehand::FHCollector::getRGBFromTint(const FHTintColor &tint)
3177
124
{
3178
124
  if (!tint.m_baseColorId)
3179
19
    return FHRGBColor();
3180
105
  const FHRGBColor *rgbColor = _findRGBColor(tint.m_baseColorId);
3181
105
  if (!rgbColor)
3182
64
    return FHRGBColor();
3183
41
  unsigned red = rgbColor->m_red * tint.m_tint + ((65536 - tint.m_tint) << 16);
3184
41
  unsigned green = rgbColor->m_green * tint.m_tint + ((65536 - tint.m_tint) << 16);
3185
41
  unsigned blue = rgbColor->m_blue * tint.m_tint + ((65536 - tint.m_tint) << 16);
3186
41
  FHRGBColor color;
3187
41
  color.m_red = (red >> 16);
3188
41
  color.m_green = (green >> 16);
3189
41
  color.m_blue = (blue >> 16);
3190
41
  return color;
3191
105
}
3192
3193
void libfreehand::FHCollector::_generateBitmapFromPattern(librevenge::RVNGBinaryData &bitmap, unsigned colorId, const std::vector<unsigned char> &pattern)
3194
1.11k
{
3195
1.11k
  unsigned height = 8;
3196
1.11k
  unsigned width = 8;
3197
1.11k
  auto tmpPixelSize = (unsigned)(height * width);
3198
3199
1.11k
  unsigned tmpDIBImageSize = tmpPixelSize * 4;
3200
3201
1.11k
  unsigned tmpDIBOffsetBits = 14 + 40;
3202
1.11k
  unsigned tmpDIBFileSize = tmpDIBOffsetBits + tmpDIBImageSize;
3203
3204
  // Create DIB file header
3205
1.11k
  writeU16(bitmap, 0x4D42);  // Type
3206
1.11k
  writeU32(bitmap, (int)tmpDIBFileSize); // Size
3207
1.11k
  writeU16(bitmap, 0); // Reserved1
3208
1.11k
  writeU16(bitmap, 0); // Reserved2
3209
1.11k
  writeU32(bitmap, (int)tmpDIBOffsetBits); // OffsetBits
3210
3211
  // Create DIB Info header
3212
1.11k
  writeU32(bitmap, 40); // Size
3213
3214
1.11k
  writeU32(bitmap, (int)width);  // Width
3215
1.11k
  writeU32(bitmap, (int)height); // Height
3216
3217
1.11k
  writeU16(bitmap, 1); // Planes
3218
1.11k
  writeU16(bitmap, 32); // BitCount
3219
1.11k
  writeU32(bitmap, 0); // Compression
3220
1.11k
  writeU32(bitmap, (int)tmpDIBImageSize); // SizeImage
3221
1.11k
  writeU32(bitmap, 0); // XPelsPerMeter
3222
1.11k
  writeU32(bitmap, 0); // YPelsPerMeter
3223
1.11k
  writeU32(bitmap, 0); // ColorsUsed
3224
1.11k
  writeU32(bitmap, 0); // ColorsImportant
3225
3226
1.11k
  unsigned foreground = 0x000000; // Initialize to black and override after
3227
1.11k
  unsigned background = 0xffffff; // Initialize to white, since that is Freehand behaviour even if overlapping other colors
3228
3229
1.11k
  const FHRGBColor *color = _findRGBColor(colorId);
3230
1.11k
  if (color)
3231
599
    foreground = ((unsigned)(color->m_red & 0xff00) << 8)|((unsigned)color->m_green & 0xff00)|((unsigned)color->m_blue >> 8);
3232
515
  else
3233
515
  {
3234
515
    const FHTintColor *tintColor = _findTintColor(colorId);
3235
515
    if (tintColor)
3236
0
    {
3237
0
      FHRGBColor rgbColor = getRGBFromTint(*tintColor);
3238
0
      foreground = ((unsigned)(rgbColor.m_red & 0xff00) << 8)|((unsigned)rgbColor.m_green & 0xff00)|((unsigned)rgbColor.m_blue >> 8);
3239
0
    }
3240
515
  }
3241
10.0k
  for (unsigned j = height; j > 0; --j)
3242
8.91k
  {
3243
8.91k
    unsigned char c(pattern[j-1]);
3244
80.2k
    for (unsigned i = 0; i < width; ++i)
3245
71.2k
    {
3246
71.2k
      if (c & 0x80)
3247
18.1k
        writeU32(bitmap, foreground);
3248
53.1k
      else
3249
53.1k
        writeU32(bitmap, background);
3250
71.2k
      c <<= 1;
3251
71.2k
    }
3252
8.91k
  }
3253
1.11k
}
3254
3255
3256
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */