Coverage Report

Created: 2026-09-14 06:37

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.99k
  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
894k
  for (i.rewind(); i.next();)
127
764k
  {
128
764k
    if (!i()["librevenge:path-action"])
129
0
      continue;
130
764k
    if (i()["svg:x"] && i()["svg:y"])
131
764k
    {
132
764k
      bool ignoreM = false;
133
764k
      x = i()["svg:x"]->getDouble();
134
764k
      y = i()["svg:y"]->getDouble();
135
764k
      if (firstPoint)
136
129k
      {
137
129k
        initialX = x;
138
129k
        initialY = y;
139
129k
        firstPoint = false;
140
129k
        wasMove = true;
141
129k
      }
142
635k
      else if (i()["librevenge:path-action"]->getStr() == "M")
143
307
      {
144
307
        if (FH_ALMOST_ZERO(previousX - x) && FH_ALMOST_ZERO(previousY - y))
145
17
          ignoreM = true;
146
290
        else
147
290
        {
148
290
          if (!tmpPath.empty())
149
290
          {
150
290
            if (!wasMove)
151
290
            {
152
290
              if ((FH_ALMOST_ZERO(initialX - previousX) && FH_ALMOST_ZERO(initialY - previousY)) || isClosed)
153
263
              {
154
263
                librevenge::RVNGPropertyList node;
155
263
                node.insert("librevenge:path-action", "Z");
156
263
                tmpPath.push_back(node);
157
263
              }
158
290
            }
159
0
            else
160
0
              tmpPath.pop_back();
161
290
          }
162
290
        }
163
164
307
        if (!ignoreM)
165
290
        {
166
290
          initialX = x;
167
290
          initialY = y;
168
290
          wasMove = true;
169
290
        }
170
171
307
      }
172
635k
      else
173
635k
        wasMove = false;
174
175
764k
      if (!ignoreM)
176
764k
      {
177
764k
        tmpPath.push_back(i());
178
764k
        previousX = x;
179
764k
        previousY = y;
180
764k
      }
181
182
764k
    }
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
764k
  }
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
71.0k
      {
195
71.0k
        if (tmpPath.back()["librevenge:path-action"] && tmpPath.back()["librevenge:path-action"]->getStr() != "Z")
196
71.0k
        {
197
71.0k
          librevenge::RVNGPropertyList closedPath;
198
71.0k
          closedPath.insert("librevenge:path-action", "Z");
199
71.0k
          tmpPath.push_back(closedPath);
200
71.0k
        }
201
71.0k
      }
202
129k
    }
203
49
    else
204
49
      tmpPath.pop_back();
205
129k
  }
206
129k
  if (!tmpPath.empty())
207
129k
  {
208
129k
    path.clear();
209
965k
    for (std::vector<librevenge::RVNGPropertyList>::const_iterator iter = tmpPath.begin(); iter != tmpPath.end(); ++iter)
210
836k
      path.append(*iter);
211
129k
  }
212
129k
}
213
214
class ObjectRecursionGuard
215
{
216
public:
217
  ObjectRecursionGuard(std::deque<unsigned> &objectStack, const unsigned id)
218
744k
    : m_objectStack(objectStack)
219
744k
    , m_id(id)
220
744k
  {
221
744k
    m_objectStack.push_front(m_id);
222
744k
  }
223
224
  ~ObjectRecursionGuard()
225
744k
  {
226
744k
    assert(!m_objectStack.empty());
227
744k
    assert(m_objectStack.front() == m_id);
228
744k
    m_objectStack.pop_front();
229
744k
  }
230
231
private:
232
  std::deque<unsigned> &m_objectStack;
233
  const unsigned m_id;
234
};
235
236
}
237
238
libfreehand::FHCollector::FHCollector() :
239
8.47k
  m_pageInfo(), m_fhTail(), m_block(), m_transforms(), m_paths(), m_strings(), m_names(), m_lists(),
240
8.47k
  m_layers(), m_groups(), m_clipGroups(), m_currentTransforms(), m_fakeTransforms(), m_compositePaths(),
241
8.47k
  m_pathTexts(), m_tStrings(), m_fonts(), m_tEffects(), m_paragraphs(), m_tabs(), m_textBloks(), m_textObjects(), m_charProperties(),
242
8.47k
  m_paragraphProperties(), m_rgbColors(), m_basicFills(), m_propertyLists(),
243
8.47k
  m_basicLines(), m_customProcs(), m_patternLines(), m_displayTexts(), m_graphicStyles(),
244
8.47k
  m_attributeHolders(), m_data(), m_dataLists(), m_images(), m_multiColorLists(), m_linearFills(),
245
8.47k
  m_tints(), m_lensFills(), m_radialFills(), m_newBlends(), m_filterAttributeHolders(), m_opacityFilters(),
246
8.47k
  m_shadowFilters(), m_glowFilters(), m_tileFills(), m_symbolClasses(), m_symbolInstances(), m_patternFills(),
247
8.47k
  m_linePatterns(), m_arrowPaths(),
248
8.47k
  m_strokeId(0), m_fillId(0), m_contentId(0), m_textBoxNumberId(0), m_visitedObjects()
249
8.47k
{
250
8.47k
}
251
252
libfreehand::FHCollector::~FHCollector()
253
8.47k
{
254
8.47k
}
255
256
void libfreehand::FHCollector::collectPageInfo(const FHPageInfo &pageInfo)
257
4.36k
{
258
4.36k
  m_pageInfo = pageInfo;
259
4.36k
}
260
261
void libfreehand::FHCollector::collectString(unsigned recordId, const librevenge::RVNGString &str)
262
91.5k
{
263
91.5k
  m_strings[recordId] = str;
264
91.5k
}
265
266
void libfreehand::FHCollector::collectName(unsigned recordId, const librevenge::RVNGString &name)
267
51.8k
{
268
51.8k
  m_names[name] = recordId;
269
51.8k
  if (name == "stroke")
270
1.66k
    m_strokeId = recordId;
271
51.8k
  if (name == "fill")
272
1.53k
    m_fillId = recordId;
273
51.8k
  if (name == "contents")
274
1.09k
    m_contentId = recordId;
275
51.8k
}
276
277
void libfreehand::FHCollector::collectPath(unsigned recordId, const libfreehand::FHPath &path)
278
445k
{
279
445k
  m_paths[recordId] = path;
280
445k
}
281
282
void libfreehand::FHCollector::collectXform(unsigned recordId,
283
                                            double m11, double m21, double m12, double m22, double m13, double m23)
284
87.4k
{
285
87.4k
  m_transforms[recordId] = FHTransform(m11, m21, m12, m22, m13, m23);
286
87.4k
}
287
288
void libfreehand::FHCollector::collectFHTail(unsigned /* recordId */, const FHTail &fhTail)
289
3.66k
{
290
3.66k
  m_fhTail = fhTail;
291
3.66k
}
292
293
void libfreehand::FHCollector::collectBlock(unsigned recordId, const libfreehand::FHBlock &block)
294
5.76k
{
295
5.76k
  if (m_block.first && m_block.first != recordId)
296
3.44k
  {
297
3.44k
    FH_DEBUG_MSG(("FHCollector::collectBlock -- WARNING: Several \"Block\" records in the file\n"));
298
3.44k
  }
299
5.76k
  m_block = std::make_pair(recordId, block);
300
5.76k
}
301
302
void libfreehand::FHCollector::collectList(unsigned recordId, const libfreehand::FHList &lst)
303
109k
{
304
109k
  m_lists[recordId] = lst;
305
109k
}
306
307
void libfreehand::FHCollector::collectLayer(unsigned recordId, const libfreehand::FHLayer &layer)
308
13.0k
{
309
13.0k
  m_layers[recordId] = layer;
310
13.0k
}
311
312
void libfreehand::FHCollector::collectGroup(unsigned recordId, const libfreehand::FHGroup &group)
313
33.1k
{
314
33.1k
  m_groups[recordId] = group;
315
33.1k
}
316
317
void libfreehand::FHCollector::collectClipGroup(unsigned recordId, const libfreehand::FHGroup &group)
318
1.15k
{
319
1.15k
  m_clipGroups[recordId] = group;
320
1.15k
}
321
322
void libfreehand::FHCollector::collectCompositePath(unsigned recordId, const libfreehand::FHCompositePath &compositePath)
323
2.76k
{
324
2.76k
  m_compositePaths[recordId] = compositePath;
325
2.76k
}
326
327
void libfreehand::FHCollector::collectPathText(unsigned recordId, const libfreehand::FHPathText &pathText)
328
3.51k
{
329
3.51k
  m_pathTexts[recordId] = pathText;
330
3.51k
}
331
332
void libfreehand::FHCollector::collectTString(unsigned recordId, const std::vector<unsigned> &elements)
333
20.5k
{
334
20.5k
  m_tStrings[recordId] = elements;
335
20.5k
}
336
337
void libfreehand::FHCollector::collectAGDFont(unsigned recordId, const FHAGDFont &font)
338
2.94k
{
339
2.94k
  m_fonts[recordId] = font;
340
2.94k
}
341
342
void libfreehand::FHCollector::collectTEffect(unsigned recordId, const FHTEffect &tEffect)
343
62.6k
{
344
62.6k
  m_tEffects[recordId] = tEffect;
345
62.6k
}
346
347
void libfreehand::FHCollector::collectParagraph(unsigned recordId, const FHParagraph &paragraph)
348
25.9k
{
349
25.9k
  m_paragraphs[recordId] = paragraph;
350
25.9k
}
351
352
void libfreehand::FHCollector::collectTabTable(unsigned recordId, const std::vector<FHTab> &tabs)
353
8.28k
{
354
8.28k
  if (tabs.empty()) return;
355
358
  m_tabs[recordId] = tabs;
356
358
}
357
358
void libfreehand::FHCollector::collectTextBlok(unsigned recordId, const std::vector<unsigned short> &characters)
359
26.2k
{
360
26.2k
  m_textBloks[recordId]  = characters;
361
26.2k
}
362
363
void libfreehand::FHCollector::collectTextObject(unsigned recordId, const FHTextObject &textObject)
364
21.5k
{
365
21.5k
  m_textObjects[recordId] = textObject;
366
21.5k
}
367
368
void libfreehand::FHCollector::collectCharProps(unsigned recordId, const FHCharProperties &charProps)
369
35.4k
{
370
35.4k
  m_charProperties[recordId] = charProps;
371
35.4k
}
372
373
void libfreehand::FHCollector::collectParagraphProps(unsigned recordId, const FHParagraphProperties &paragraphProps)
374
60.5k
{
375
60.5k
  m_paragraphProperties[recordId] = paragraphProps;
376
60.5k
}
377
378
void libfreehand::FHCollector::collectColor(unsigned recordId, const FHRGBColor &color)
379
38.1k
{
380
38.1k
  m_rgbColors[recordId] = color;
381
38.1k
}
382
383
void libfreehand::FHCollector::collectTintColor(unsigned recordId, const FHTintColor &color)
384
2.67k
{
385
2.67k
  m_tints[recordId] = color;
386
2.67k
}
387
388
void libfreehand::FHCollector::collectBasicFill(unsigned recordId, const FHBasicFill &fill)
389
8.77k
{
390
8.77k
  m_basicFills[recordId] = fill;
391
8.77k
}
392
393
void libfreehand::FHCollector::collectBasicLine(unsigned recordId, const FHBasicLine &line)
394
16.1k
{
395
16.1k
  m_basicLines[recordId] = line;
396
16.1k
}
397
398
void libfreehand::FHCollector::collectCustomProc(unsigned recordId, const FHCustomProc &line)
399
2.98k
{
400
2.98k
  m_customProcs[recordId] = line;
401
2.98k
}
402
403
void libfreehand::FHCollector::collectPatternLine(unsigned recordId, const FHPatternLine &line)
404
3.52k
{
405
3.52k
  m_patternLines[recordId] = line;
406
3.52k
}
407
408
void libfreehand::FHCollector::collectTileFill(unsigned recordId, const FHTileFill &fill)
409
990
{
410
990
  m_tileFills[recordId] = fill;
411
990
}
412
413
void libfreehand::FHCollector::collectPatternFill(unsigned recordId, const FHPatternFill &fill)
414
4.00k
{
415
4.00k
  m_patternFills[recordId] = fill;
416
4.00k
}
417
418
void libfreehand::FHCollector::collectLinePattern(unsigned recordId, const FHLinePattern &line)
419
22.7k
{
420
22.7k
  m_linePatterns[recordId] = line;
421
22.7k
}
422
423
void libfreehand::FHCollector::collectArrowPath(unsigned recordId, const FHPath &path)
424
12.9k
{
425
  // osnola: useme
426
12.9k
  m_arrowPaths[recordId] = path;
427
12.9k
}
428
429
void libfreehand::FHCollector::collectPropList(unsigned recordId, const FHPropList &propertyList)
430
243k
{
431
243k
  m_propertyLists[recordId] = propertyList;
432
243k
}
433
434
void libfreehand::FHCollector::collectDisplayText(unsigned recordId, const FHDisplayText &displayText)
435
43.1k
{
436
43.1k
  m_displayTexts[recordId] = displayText;
437
43.1k
}
438
439
void libfreehand::FHCollector::collectGraphicStyle(unsigned recordId, const FHGraphicStyle &graphicStyle)
440
35.4k
{
441
35.4k
  m_graphicStyles[recordId] = graphicStyle;
442
35.4k
}
443
444
void libfreehand::FHCollector::collectAttributeHolder(unsigned recordId, const FHAttributeHolder &attributeHolder)
445
34.5k
{
446
34.5k
  m_attributeHolders[recordId] = attributeHolder;
447
34.5k
}
448
449
void libfreehand::FHCollector::collectFilterAttributeHolder(unsigned recordId, const FHFilterAttributeHolder &filterAttributeHolder)
450
5.20k
{
451
5.20k
  m_filterAttributeHolders[recordId] = filterAttributeHolder;
452
5.20k
}
453
454
void libfreehand::FHCollector::collectData(unsigned recordId, const librevenge::RVNGBinaryData &data)
455
1.13k
{
456
1.13k
  m_data[recordId] = data;
457
1.13k
}
458
459
void libfreehand::FHCollector::collectDataList(unsigned recordId, const FHDataList &list)
460
285
{
461
285
  m_dataLists[recordId] = list;
462
285
}
463
464
void libfreehand::FHCollector::collectImage(unsigned recordId, const FHImageImport &image)
465
1.85k
{
466
1.85k
  m_images[recordId] = image;
467
1.85k
}
468
469
void libfreehand::FHCollector::collectMultiColorList(unsigned recordId, const std::vector<FHColorStop> &colorStops)
470
2.64k
{
471
2.64k
  m_multiColorLists[recordId] = colorStops;
472
2.64k
}
473
474
void libfreehand::FHCollector::collectLinearFill(unsigned recordId, const FHLinearFill &fill)
475
7.53k
{
476
7.53k
  m_linearFills[recordId] = fill;
477
7.53k
}
478
479
void libfreehand::FHCollector::collectLensFill(unsigned recordId, const FHLensFill &fill)
480
663
{
481
663
  m_lensFills[recordId] = fill;
482
663
}
483
484
void libfreehand::FHCollector::collectRadialFill(unsigned recordId, const FHRadialFill &fill)
485
7.52k
{
486
7.52k
  m_radialFills[recordId] = fill;
487
7.52k
}
488
489
void libfreehand::FHCollector::collectNewBlend(unsigned recordId, const FHNewBlend &newBlend)
490
3.77k
{
491
3.77k
  m_newBlends[recordId] = newBlend;
492
3.77k
}
493
494
void libfreehand::FHCollector::collectOpacityFilter(unsigned recordId, double opacity)
495
3.15k
{
496
3.15k
  m_opacityFilters[recordId] = opacity;
497
3.15k
}
498
499
void libfreehand::FHCollector::collectFWShadowFilter(unsigned recordId, const FWShadowFilter &filter)
500
3.14k
{
501
3.14k
  m_shadowFilters[recordId] = filter;
502
3.14k
}
503
504
void libfreehand::FHCollector::collectFWGlowFilter(unsigned recordId, const FWGlowFilter &filter)
505
2.84k
{
506
2.84k
  m_glowFilters[recordId] = filter;
507
2.84k
}
508
509
void libfreehand::FHCollector::collectSymbolClass(unsigned recordId, const FHSymbolClass &symbolClass)
510
72.8k
{
511
72.8k
  m_symbolClasses[recordId] = symbolClass;
512
72.8k
}
513
514
void libfreehand::FHCollector::collectSymbolInstance(unsigned recordId, const FHSymbolInstance &symbolInstance)
515
17.8k
{
516
17.8k
  m_symbolInstances[recordId] = symbolInstance;
517
17.8k
}
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
176k
{
527
176k
  FHTransform trafo(1.0, 0.0, 0.0, -1.0, - m_pageInfo.m_minX, m_pageInfo.m_maxY);
528
176k
  trafo.applyToPoint(x, y);
529
176k
}
530
531
void libfreehand::FHCollector::_getBBofPath(const FHPath *path, libfreehand::FHBoundingBox &bBox)
532
3.44k
{
533
3.44k
  if (!path || path->empty())
534
1.75k
    return;
535
536
1.69k
  FHPath fhPath(*path);
537
1.69k
  unsigned short xform = fhPath.getXFormId();
538
539
1.69k
  if (xform)
540
395
  {
541
395
    const FHTransform *trafo = _findTransform(xform);
542
395
    if (trafo)
543
0
      fhPath.transform(*trafo);
544
395
  }
545
1.69k
  std::stack<FHTransform> groupTransforms(m_currentTransforms);
546
3.59k
  while (!groupTransforms.empty())
547
1.90k
  {
548
1.90k
    fhPath.transform(groupTransforms.top());
549
1.90k
    groupTransforms.pop();
550
1.90k
  }
551
1.69k
  _normalizePath(fhPath);
552
2.17k
  for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
553
487
  {
554
487
    fhPath.transform(*iter);
555
487
  }
556
557
1.69k
  FHBoundingBox tmpBBox;
558
1.69k
  fhPath.getBoundingBox(tmpBBox.m_xmin, tmpBBox.m_ymin, tmpBBox.m_xmax, tmpBBox.m_ymax);
559
1.69k
  bBox.merge(tmpBBox);
560
1.69k
}
561
562
void libfreehand::FHCollector::_getBBofGroup(const FHGroup *group, libfreehand::FHBoundingBox &bBox)
563
3.44k
{
564
3.44k
  if (!group)
565
3.44k
    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
3.44k
{
598
3.44k
  if (!group)
599
3.44k
    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
3.44k
{
630
3.44k
  if (!compositePath)
631
3.44k
    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
3.44k
{
664
3.44k
  if (!pathText)
665
3.44k
    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
3.44k
{
672
3.44k
  if (!textObject)
673
1.85k
    return;
674
675
1.58k
  double xa = textObject->m_startX;
676
1.58k
  double ya = textObject->m_startY;
677
1.58k
  double xb = textObject->m_startX + textObject->m_width;
678
1.58k
  double yb = textObject->m_startY + textObject->m_height;
679
1.58k
  double xc = xa;
680
1.58k
  double yc = yb;
681
1.58k
  double xd = xb;
682
1.58k
  double yd = ya;
683
1.58k
  unsigned xFormId = textObject->m_xFormId;
684
1.58k
  if (xFormId)
685
1.40k
  {
686
1.40k
    const FHTransform *trafo = _findTransform(xFormId);
687
1.40k
    if (trafo)
688
1.20k
    {
689
1.20k
      trafo->applyToPoint(xa, ya);
690
1.20k
      trafo->applyToPoint(xb, yb);
691
1.20k
      trafo->applyToPoint(xc, yc);
692
1.20k
      trafo->applyToPoint(xd, yd);
693
1.20k
    }
694
1.40k
  }
695
1.58k
  std::stack<FHTransform> groupTransforms(m_currentTransforms);
696
3.16k
  while (!groupTransforms.empty())
697
1.58k
  {
698
1.58k
    groupTransforms.top().applyToPoint(xa, ya);
699
1.58k
    groupTransforms.top().applyToPoint(xb, yb);
700
1.58k
    groupTransforms.top().applyToPoint(xc, yc);
701
1.58k
    groupTransforms.top().applyToPoint(xd, yd);
702
1.58k
    groupTransforms.pop();
703
1.58k
  }
704
1.58k
  _normalizePoint(xa, ya);
705
1.58k
  _normalizePoint(xb, yb);
706
1.58k
  _normalizePoint(xc, yc);
707
1.58k
  _normalizePoint(xd, yd);
708
709
1.58k
  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
1.58k
  FHBoundingBox tmpBBox;
718
1.58k
  if (xa < tmpBBox.m_xmin) tmpBBox.m_xmin = xa;
719
1.58k
  if (xb < tmpBBox.m_xmin) tmpBBox.m_xmin = xb;
720
1.58k
  if (xc < tmpBBox.m_xmin) tmpBBox.m_xmin = xc;
721
1.58k
  if (xd < tmpBBox.m_xmin) tmpBBox.m_xmin = xd;
722
723
1.58k
  if (xa > tmpBBox.m_xmax) tmpBBox.m_xmax = xa;
724
1.58k
  if (xb > tmpBBox.m_xmax) tmpBBox.m_xmax = xb;
725
1.58k
  if (xc > tmpBBox.m_xmax) tmpBBox.m_xmax = xc;
726
1.58k
  if (xd > tmpBBox.m_xmax) tmpBBox.m_xmax = xd;
727
728
1.58k
  if (ya < tmpBBox.m_ymin) tmpBBox.m_ymin = ya;
729
1.58k
  if (yb < tmpBBox.m_ymin) tmpBBox.m_ymin = yb;
730
1.58k
  if (yc < tmpBBox.m_ymin) tmpBBox.m_ymin = yc;
731
1.58k
  if (yd < tmpBBox.m_ymin) tmpBBox.m_ymin = yd;
732
733
1.58k
  if (ya > tmpBBox.m_ymax) tmpBBox.m_ymax = ya;
734
1.58k
  if (yb > tmpBBox.m_ymax) tmpBBox.m_ymax = yb;
735
1.58k
  if (yc > tmpBBox.m_ymax) tmpBBox.m_ymax = yc;
736
1.58k
  if (yd > tmpBBox.m_ymax) tmpBBox.m_ymax = yd;
737
1.58k
  bBox.merge(tmpBBox);
738
1.58k
}
739
740
void libfreehand::FHCollector::_getBBofDisplayText(const FHDisplayText *displayText, libfreehand::FHBoundingBox &bBox)
741
3.44k
{
742
3.44k
  if (!displayText)
743
3.44k
    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
3.44k
{
812
3.44k
  if (!image)
813
3.44k
    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
3.44k
{
882
3.44k
}
883
884
void libfreehand::FHCollector::_getBBofSymbolInstance(const FHSymbolInstance *symbolInstance, libfreehand::FHBoundingBox &bBox)
885
3.44k
{
886
3.44k
  if (!symbolInstance)
887
3.44k
    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
3.44k
{
905
3.44k
  if (!somethingId)
906
0
    return;
907
908
3.44k
  FHBoundingBox tmpBBox;
909
3.44k
  _getBBofGroup(_findGroup(somethingId), tmpBBox);
910
3.44k
  _getBBofClipGroup(_findClipGroup(somethingId), tmpBBox);
911
3.44k
  _getBBofPathText(_findPathText(somethingId), tmpBBox);
912
3.44k
  _getBBofPath(_findPath(somethingId), tmpBBox);
913
3.44k
  _getBBofCompositePath(_findCompositePath(somethingId), tmpBBox);
914
3.44k
  _getBBofTextObject(_findTextObject(somethingId), tmpBBox);
915
3.44k
  _getBBofDisplayText(_findDisplayText(somethingId), tmpBBox);
916
3.44k
  _getBBofImageImport(_findImageImport(somethingId), tmpBBox);
917
3.44k
  _getBBofNewBlend(_findNewBlend(somethingId), tmpBBox);
918
3.44k
  _getBBofSymbolInstance(_findSymbolInstance(somethingId), tmpBBox);
919
3.44k
  bBox.merge(tmpBBox);
920
3.44k
}
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.3k
    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.9k
  {
940
18.9k
    const FHTransform *trafo = _findTransform(xform);
941
18.9k
    if (trafo)
942
17.3k
      fhPath.transform(*trafo);
943
18.9k
  }
944
129k
  std::stack<FHTransform> groupTransforms(m_currentTransforms);
945
320k
  while (!groupTransforms.empty())
946
191k
  {
947
191k
    fhPath.transform(groupTransforms.top());
948
191k
    groupTransforms.pop();
949
191k
  }
950
129k
  _normalizePath(fhPath);
951
952
132k
  for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
953
3.37k
  {
954
3.37k
    fhPath.transform(*iter);
955
3.37k
  }
956
957
129k
  librevenge::RVNGPropertyListVector propVec;
958
129k
  fhPath.writeOut(propVec);
959
129k
  if (propList["draw:fill"] && propList["draw:fill"]->getStr() != "none")
960
10.0k
    _composePath(propVec, true);
961
118k
  else
962
118k
    _composePath(propVec, fhPath.isClosed());
963
129k
  librevenge::RVNGPropertyList pList;
964
129k
  pList.insert("svg:d", propVec);
965
129k
  if (contentId)
966
10.9k
    painter->openGroup(librevenge::RVNGPropertyList());
967
129k
  painter->setStyle(propList);
968
129k
  painter->drawPath(pList);
969
129k
  if (contentId)
970
10.9k
  {
971
10.9k
    FHBoundingBox bBox;
972
10.9k
    fhPath.getBoundingBox(bBox.m_xmin, bBox.m_ymin, bBox.m_xmax, bBox.m_ymax);
973
10.9k
    FHTransform trafo(1.0, 0.0, 0.0, 1.0, - bBox.m_xmin, - bBox.m_ymin);
974
10.9k
    m_fakeTransforms.push_back(trafo);
975
10.9k
    librevenge::RVNGStringVector svgOutput;
976
10.9k
    librevenge::RVNGSVGDrawingGenerator generator(svgOutput, "");
977
10.9k
    propList.clear();
978
10.9k
    propList.insert("svg:width", bBox.m_xmax - bBox.m_xmin);
979
10.9k
    propList.insert("svg:height", bBox.m_ymax - bBox.m_ymin);
980
10.9k
    generator.startPage(propList);
981
10.9k
    _outputSomething(contentId, &generator);
982
10.9k
    generator.endPage();
983
10.9k
    if (!svgOutput.empty() && svgOutput[0].size() > 140) // basically empty svg if it is not fullfilled
984
10.9k
    {
985
10.9k
      const char *header =
986
10.9k
        "<?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.9k
      librevenge::RVNGBinaryData output((const unsigned char *)header, strlen(header));
988
10.9k
      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.9k
      propList.clear();
1004
10.9k
      propList.insert("draw:stroke", "none");
1005
10.9k
      propList.insert("draw:fill", "bitmap");
1006
10.9k
      propList.insert("librevenge:mime-type", "image/svg+xml");
1007
10.9k
      propList.insert("style:repeat", "stretch");
1008
10.9k
      propList.insert("draw:fill-image", output);
1009
10.9k
      painter->setStyle(propList);
1010
10.9k
      painter->drawPath(pList);
1011
10.9k
    }
1012
10.9k
    if (!m_fakeTransforms.empty())
1013
10.9k
      m_fakeTransforms.pop_back();
1014
10.9k
    painter->closeGroup();
1015
10.9k
  }
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
229k
{
1036
229k
  if (!painter || !somethingId)
1037
5.80k
    return;
1038
223k
  if (find(m_visitedObjects.begin(), m_visitedObjects.end(), somethingId) != m_visitedObjects.end())
1039
579
    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
204k
    return;
1059
1060
18.6k
  if (group->m_xFormId)
1061
94
  {
1062
94
    const FHTransform *trafo = _findTransform(group->m_xFormId);
1063
94
    if (trafo)
1064
4
      m_currentTransforms.push(*trafo);
1065
90
    else
1066
90
      m_currentTransforms.push(libfreehand::FHTransform());
1067
94
  }
1068
18.5k
  else
1069
18.5k
    m_currentTransforms.push(libfreehand::FHTransform());
1070
1071
18.6k
  const std::vector<unsigned> *elements = _findListElements(group->m_elementsId);
1072
18.6k
  if (!elements)
1073
154
  {
1074
154
    FH_DEBUG_MSG(("ERROR: The pointed element list does not exist\n"));
1075
154
    return;
1076
154
  }
1077
1078
18.5k
  if (!elements->empty())
1079
18.5k
  {
1080
18.5k
    painter->openGroup(librevenge::RVNGPropertyList());
1081
18.5k
    for (unsigned int element : *elements)
1082
170k
      _outputSomething(element, painter);
1083
18.5k
    painter->closeGroup();
1084
18.5k
  }
1085
1086
18.5k
  if (!m_currentTransforms.empty())
1087
18.5k
    m_currentTransforms.pop();
1088
18.5k
}
1089
1090
void libfreehand::FHCollector::_outputClipGroup(const libfreehand::FHGroup *group, librevenge::RVNGDrawingInterface *painter)
1091
223k
{
1092
223k
  if (!painter || !group)
1093
222k
    return;
1094
1095
112
  const std::vector<unsigned> *elements = _findListElements(group->m_elementsId);
1096
112
  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
112
  if (!elements->empty())
1103
112
  {
1104
112
    auto iter = elements->begin();
1105
112
    const FHPath *path = _findPath(*iter);
1106
112
    if (!path)
1107
0
      _outputGroup(group, painter);
1108
112
    else
1109
112
    {
1110
112
      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
112
      else
1119
112
        m_currentTransforms.push(libfreehand::FHTransform());
1120
1121
112
      librevenge::RVNGPropertyList propList;
1122
112
      FHPath fhPath(*path);
1123
112
      _appendStrokeProperties(propList, fhPath.getGraphicStyleId());
1124
112
      _appendFillProperties(propList, fhPath.getGraphicStyleId());
1125
112
      if (fhPath.getEvenOdd())
1126
0
        propList.insert("svg:fill-rule", "evenodd");
1127
112
      unsigned short xform = fhPath.getXFormId();
1128
1129
112
      if (xform)
1130
0
      {
1131
0
        const FHTransform *trafo = _findTransform(xform);
1132
0
        if (trafo)
1133
0
          fhPath.transform(*trafo);
1134
0
      }
1135
112
      std::stack<FHTransform> groupTransforms(m_currentTransforms);
1136
476
      while (!groupTransforms.empty())
1137
364
      {
1138
364
        fhPath.transform(groupTransforms.top());
1139
364
        groupTransforms.pop();
1140
364
      }
1141
112
      _normalizePath(fhPath);
1142
1143
208
      for (std::vector<FHTransform>::const_iterator iterVec = m_fakeTransforms.begin(); iterVec != m_fakeTransforms.end(); ++iterVec)
1144
96
      {
1145
96
        fhPath.transform(*iterVec);
1146
96
      }
1147
1148
112
      if (!m_currentTransforms.empty())
1149
112
        m_currentTransforms.pop();
1150
1151
112
      librevenge::RVNGPropertyListVector propVec;
1152
112
      fhPath.writeOut(propVec);
1153
112
      _composePath(propVec, true);
1154
112
      librevenge::RVNGPropertyList pList;
1155
112
      pList.insert("svg:d", propVec);
1156
1157
1158
112
      FHBoundingBox bBox;
1159
112
      fhPath.getBoundingBox(bBox.m_xmin, bBox.m_ymin, bBox.m_xmax, bBox.m_ymax);
1160
112
      FHTransform trafo(1.0, 0.0, 0.0, 1.0, - bBox.m_xmin, - bBox.m_ymin);
1161
112
      m_fakeTransforms.push_back(trafo);
1162
112
      librevenge::RVNGStringVector svgOutput;
1163
112
      librevenge::RVNGSVGDrawingGenerator generator(svgOutput, "");
1164
112
      propList.clear();
1165
112
      propList.insert("svg:width", bBox.m_xmax - bBox.m_xmin);
1166
112
      propList.insert("svg:height", bBox.m_ymax - bBox.m_ymin);
1167
112
      generator.startPage(propList);
1168
112
      _outputGroup(group, &generator);
1169
112
      generator.endPage();
1170
112
      if (!svgOutput.empty() && svgOutput[0].size() > 140) // basically empty svg if it is not fullfilled
1171
112
      {
1172
112
        const char *header =
1173
112
          "<?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
112
        librevenge::RVNGBinaryData output((const unsigned char *)header, strlen(header));
1175
112
        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
112
        propList.insert("draw:stroke", "none");
1191
112
        propList.insert("draw:fill", "bitmap");
1192
112
        propList.insert("librevenge:mime-type", "image/svg+xml");
1193
112
        propList.insert("style:repeat", "stretch");
1194
112
        propList.insert("draw:fill-image", output);
1195
112
        painter->setStyle(propList);
1196
112
        painter->drawPath(pList);
1197
112
      }
1198
112
      if (!m_fakeTransforms.empty())
1199
112
        m_fakeTransforms.pop_back();
1200
112
    }
1201
112
  }
1202
112
}
1203
1204
void libfreehand::FHCollector::_outputPathText(const libfreehand::FHPathText *pathText, librevenge::RVNGDrawingInterface *painter)
1205
223k
{
1206
223k
  if (!painter || !pathText)
1207
222k
    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
222k
    return;
1216
1217
16
  m_currentTransforms.push(libfreehand::FHTransform());
1218
1219
16
  painter->openGroup(librevenge::RVNGPropertyList());
1220
16
  const std::vector<unsigned> *elements1 = _findListElements(newBlend->m_list1Id);
1221
16
  if (elements1 && !elements1->empty())
1222
16
  {
1223
16
    for (unsigned int iterVec : *elements1)
1224
32
      _outputSomething(iterVec, painter);
1225
16
  }
1226
16
  const std::vector<unsigned> *elements2 = _findListElements(newBlend->m_list2Id);
1227
16
  if (elements2 && !elements2->empty())
1228
16
  {
1229
16
    for (unsigned int iterVec : *elements2)
1230
16
      _outputSomething(iterVec, painter);
1231
16
  }
1232
16
  const std::vector<unsigned> *elements3 = _findListElements(newBlend->m_list3Id);
1233
16
  if (elements3 && !elements3->empty())
1234
16
  {
1235
16
    for (unsigned int iterVec : *elements3)
1236
16
      _outputSomething(iterVec, painter);
1237
16
  }
1238
16
  painter->closeGroup();
1239
1240
16
  if (!m_currentTransforms.empty())
1241
16
    m_currentTransforms.pop();
1242
16
}
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.36k
{
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.36k
  if (!painter)
1282
0
    return;
1283
1284
4.36k
  if (!m_fhTail.m_blockId || m_fhTail.m_blockId != m_block.first)
1285
3.47k
  {
1286
3.47k
    FH_DEBUG_MSG(("WARNING: FHTail points to an invalid Block ID\n"));
1287
3.47k
    m_fhTail.m_blockId = m_block.first;
1288
3.47k
  }
1289
4.36k
  if (!m_fhTail.m_blockId)
1290
2.36k
  {
1291
2.36k
    FH_DEBUG_MSG(("ERROR: Block record is absent from this file\n"));
1292
2.36k
    return;
1293
2.36k
  }
1294
1295
1.99k
  if (FH_UNINITIALIZED(m_pageInfo))
1296
1.59k
    m_pageInfo = m_fhTail.m_pageInfo;
1297
1298
1.99k
  painter->startDocument(librevenge::RVNGPropertyList());
1299
1.99k
  librevenge::RVNGPropertyList propList;
1300
1.99k
  propList.insert("svg:height", m_pageInfo.m_maxY - m_pageInfo.m_minY);
1301
1.99k
  propList.insert("svg:width", m_pageInfo.m_maxX - m_pageInfo.m_minX);
1302
1.99k
  painter->startPage(propList);
1303
1304
1.99k
  unsigned layerListId = m_block.second.m_layerListId;
1305
1306
1.99k
  const std::vector<unsigned> *elements = _findListElements(layerListId);
1307
1.99k
  if (elements)
1308
1.82k
  {
1309
1.82k
    for (unsigned int element : *elements)
1310
9.82k
    {
1311
9.82k
      _outputLayer(element, painter);
1312
9.82k
    }
1313
1.82k
  }
1314
1.99k
  painter->endPage();
1315
1.99k
  painter->endDocument();
1316
1.99k
}
1317
1318
void libfreehand::FHCollector::_outputLayer(unsigned layerId, librevenge::RVNGDrawingInterface *painter)
1319
9.82k
{
1320
9.82k
  if (!painter)
1321
0
    return;
1322
1323
9.82k
  std::map<unsigned, FHLayer>::const_iterator layerIter = m_layers.find(layerId);
1324
9.82k
  if (layerIter == m_layers.end())
1325
5.16k
  {
1326
5.16k
    FH_DEBUG_MSG(("ERROR: Could not find the referenced layer\n"));
1327
5.16k
    return;
1328
5.16k
  }
1329
1330
4.66k
  if (layerIter->second.m_visibility != 3)
1331
2.52k
    return;
1332
1333
2.13k
  unsigned layerElementsListId = layerIter->second.m_elementsId;
1334
2.13k
  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.13k
  const std::vector<unsigned> *elements = _findListElements(layerElementsListId);
1341
2.13k
  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
2.11k
  for (unsigned int element : *elements)
1348
44.5k
    _outputSomething(element, painter);
1349
2.11k
}
1350
1351
void libfreehand::FHCollector::_outputCompositePath(const libfreehand::FHCompositePath *compositePath, librevenge::RVNGDrawingInterface *painter)
1352
223k
{
1353
223k
  if (!painter || !compositePath)
1354
222k
    return;
1355
1356
369
  const std::vector<unsigned> *elements = _findListElements(compositePath->m_elementsId);
1357
369
  if (elements && !elements->empty())
1358
314
  {
1359
314
    libfreehand::FHPath fhPath;
1360
314
    auto iter = elements->begin();
1361
314
    const libfreehand::FHPath *path = _findPath(*(iter++));
1362
314
    if (path)
1363
245
    {
1364
245
      fhPath = *path;
1365
245
      if (!fhPath.getGraphicStyleId())
1366
0
        fhPath.setGraphicStyleId(compositePath->m_graphicStyleId);
1367
245
    }
1368
1369
2.69k
    for (; iter != elements->end(); ++iter)
1370
2.38k
    {
1371
2.38k
      path = _findPath(*iter);
1372
2.38k
      if (path)
1373
336
      {
1374
336
        fhPath.appendPath(*path);
1375
336
        if (!fhPath.getGraphicStyleId())
1376
38
          fhPath.setGraphicStyleId(compositePath->m_graphicStyleId);
1377
336
      }
1378
2.38k
    }
1379
314
    _outputPath(&fhPath, painter);
1380
314
  }
1381
369
}
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.4k
  double width=textObject->m_width;
1389
19.4k
  double height=textObject->m_height;
1390
19.4k
  unsigned num[]= {textObject->m_colNum,textObject->m_rowNum};
1391
19.4k
  double decalX[]= {width+textObject->m_colSep,0};
1392
19.4k
  double decalY[]= {0, height+textObject->m_rowSep};
1393
19.4k
  if (textObject->m_rowBreakFirst)
1394
169
  {
1395
169
    std::swap(num[0],num[1]);
1396
169
    std::swap(decalX[0],decalX[1]);
1397
169
    std::swap(decalY[0],decalY[1]);
1398
169
  }
1399
19.4k
  for (unsigned int &i : num)
1400
38.8k
  {
1401
38.8k
    if (i<=0 || i>10)
1402
1.81k
    {
1403
1.81k
      FH_DEBUG_MSG(("libfreehand::FHCollector::_outputTextObject: the number of row/col seems bad\n"));
1404
1.81k
      i=1;
1405
1.81k
    }
1406
38.8k
  }
1407
19.4k
  ++m_textBoxNumberId;
1408
19.4k
  for (unsigned dim0=0; dim0<num[0]; ++dim0)
1409
19.4k
  {
1410
19.4k
    for (unsigned dim1=0; dim1<num[1]; ++dim1)
1411
19.4k
    {
1412
19.4k
      unsigned id = dim0*num[1]+dim1;
1413
19.4k
      double rotation = 0, finalHeight = 0, finalWidth = 0, xmid=0, ymid=0;
1414
19.4k
      bool useShapeBox=false;
1415
19.4k
      if ((width<=0 || height<=0) && textObject->m_pathId)
1416
295
      {
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
295
        FHBoundingBox bbox;
1422
295
        _getBBofSomething(textObject->m_pathId, bbox);
1423
295
        useShapeBox=true;
1424
295
        xmid=0.5*(bbox.m_xmin+bbox.m_xmax);
1425
295
        ymid=0.5*(bbox.m_ymin+bbox.m_ymax);
1426
295
        width=finalWidth=(bbox.m_xmax-bbox.m_xmin);
1427
295
        height=finalHeight=(bbox.m_ymax-bbox.m_ymin);
1428
295
      }
1429
19.4k
      if (!useShapeBox)
1430
19.1k
      {
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.1k
        double startX=textObject->m_startX;
1441
19.1k
        double startY=textObject->m_startY;
1442
19.1k
        width += (num[0]-1)*decalX[0]+(num[1]-1)*decalX[1];
1443
19.1k
        height += (num[0]-1)*decalY[0]+(num[1]-1)*decalY[1];
1444
19.1k
#endif
1445
19.1k
        double xa = startX;
1446
19.1k
        double ya = startY;
1447
19.1k
        double xb = startX + width;
1448
19.1k
        double yb = startY + height;
1449
19.1k
        double xc = xa;
1450
19.1k
        double yc = yb;
1451
19.1k
        unsigned xFormId = textObject->m_xFormId;
1452
19.1k
        if (xFormId)
1453
19.0k
        {
1454
19.0k
          const FHTransform *trafo = _findTransform(xFormId);
1455
19.0k
          if (trafo)
1456
18.7k
          {
1457
18.7k
            trafo->applyToPoint(xa, ya);
1458
18.7k
            trafo->applyToPoint(xb, yb);
1459
18.7k
            trafo->applyToPoint(xc, yc);
1460
18.7k
          }
1461
19.0k
        }
1462
19.1k
        std::stack<FHTransform> groupTransforms(m_currentTransforms);
1463
78.7k
        while (!groupTransforms.empty())
1464
59.6k
        {
1465
59.6k
          groupTransforms.top().applyToPoint(xa, ya);
1466
59.6k
          groupTransforms.top().applyToPoint(xb, yb);
1467
59.6k
          groupTransforms.top().applyToPoint(xc, yc);
1468
59.6k
          groupTransforms.pop();
1469
59.6k
        }
1470
19.1k
        _normalizePoint(xa, ya);
1471
19.1k
        _normalizePoint(xb, yb);
1472
19.1k
        _normalizePoint(xc, yc);
1473
1474
20.6k
        for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
1475
1.50k
        {
1476
1.50k
          iter->applyToPoint(xa, ya);
1477
1.50k
          iter->applyToPoint(xb, yb);
1478
1.50k
          iter->applyToPoint(xc, yc);
1479
1.50k
        }
1480
1481
19.1k
        rotation = atan2(yb-yc, xb-xc);
1482
19.1k
        finalHeight = sqrt((xc-xa)*(xc-xa) + (yc-ya)*(yc-ya));
1483
19.1k
        finalWidth = sqrt((xc-xb)*(xc-xb) + (yc-yb)*(yc-yb));
1484
19.1k
        xmid = (xa + xb) / 2.0;
1485
19.1k
        ymid = (ya + yb) / 2.0;
1486
19.1k
      }
1487
1488
19.4k
      librevenge::RVNGPropertyList textObjectProps;
1489
19.4k
      textObjectProps.insert("svg:x", xmid - width / 2.0);
1490
19.4k
      textObjectProps.insert("svg:y", ymid + height / 2.0);
1491
19.4k
      textObjectProps.insert("svg:height", finalHeight);
1492
19.4k
      textObjectProps.insert("svg:width", finalWidth);
1493
19.4k
      if (!FH_ALMOST_ZERO(rotation))
1494
847
      {
1495
847
        textObjectProps.insert("librevenge:rotate", rotation * 180.0 / M_PI);
1496
847
        textObjectProps.insert("librevenge:rotate-cx",xmid);
1497
847
        textObjectProps.insert("librevenge:rotate-cy",ymid);
1498
847
      }
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.4k
      painter->startTextObject(textObjectProps);
1514
1515
19.4k
      if (id==0)
1516
19.4k
      {
1517
19.4k
        const std::vector<unsigned> *elements = _findTStringElements(textObject->m_tStringId);
1518
19.4k
        unsigned actPos=0;
1519
19.4k
        if (elements && !elements->empty())
1520
19.0k
        {
1521
19.0k
          for (unsigned int element : *elements)
1522
71.0k
            _outputParagraph(_findParagraph(element), painter, actPos, textObject->m_beginPos, textObject->m_endPos);
1523
19.0k
        }
1524
19.4k
      }
1525
19.4k
      painter->endTextObject();
1526
1527
19.4k
#if !defined(HAVE_CHAINED_TEXTBOX)
1528
19.4k
      break;
1529
19.4k
#endif
1530
19.4k
    }
1531
19.4k
#if !defined(HAVE_CHAINED_TEXTBOX)
1532
19.4k
    break;
1533
19.4k
#endif
1534
19.4k
  }
1535
19.4k
}
1536
1537
void libfreehand::FHCollector::_outputParagraph(const libfreehand::FHParagraph *paragraph, librevenge::RVNGDrawingInterface *painter, unsigned &actPos, unsigned minPos, unsigned maxPos)
1538
71.0k
{
1539
71.0k
  if (!painter || !paragraph)
1540
45.7k
    return;
1541
25.2k
  bool paragraphOpened=false;
1542
25.2k
  std::map<unsigned, std::vector<unsigned short> >::const_iterator iter = m_textBloks.find(paragraph->m_textBlokId);
1543
25.2k
  if (iter != m_textBloks.end())
1544
24.8k
  {
1545
1546
54.2k
    for (std::vector<std::pair<unsigned, unsigned> >::size_type i = 0; i < paragraph->m_charStyleIds.size(); ++i)
1547
32.0k
    {
1548
32.0k
      if (actPos>=maxPos) break;
1549
29.4k
      unsigned lastChar=i+1 < paragraph->m_charStyleIds.size() ? paragraph->m_charStyleIds[i+1].first : iter->second.size();
1550
29.4k
      unsigned numChar=lastChar-paragraph->m_charStyleIds[i].first;
1551
29.4k
      unsigned nextPos=actPos+numChar;
1552
29.4k
      if (nextPos<minPos)
1553
926
      {
1554
926
        actPos=nextPos;
1555
926
        continue;
1556
926
      }
1557
28.5k
      if (!paragraphOpened)
1558
21.9k
      {
1559
21.9k
        librevenge::RVNGPropertyList propList;
1560
21.9k
        _appendParagraphProperties(propList, paragraph->m_paraStyleId);
1561
21.9k
        painter->openParagraph(propList);
1562
21.9k
        paragraphOpened=true;
1563
21.9k
      }
1564
28.5k
      unsigned fChar=paragraph->m_charStyleIds[i].first + (actPos<minPos ? minPos-actPos : 0);
1565
28.5k
      numChar=lastChar-fChar;
1566
28.5k
      if (actPos+numChar>maxPos) numChar=maxPos-actPos;
1567
28.5k
      _outputTextRun(&(iter->second), fChar, numChar, paragraph->m_charStyleIds[i].second, painter);
1568
28.5k
      actPos=nextPos;
1569
28.5k
    }
1570
24.8k
  }
1571
25.2k
  ++actPos; // EOL
1572
25.2k
  if (paragraphOpened)
1573
21.9k
    painter->closeParagraph();
1574
25.2k
}
1575
1576
void libfreehand::FHCollector::_appendCharacterProperties(librevenge::RVNGPropertyList &propList, unsigned charPropsId)
1577
27.9k
{
1578
27.9k
  std::map<unsigned, FHCharProperties>::const_iterator iter = m_charProperties.find(charPropsId);
1579
27.9k
  if (iter == m_charProperties.end())
1580
1.84k
    return;
1581
26.0k
  const FHCharProperties &charProps = iter->second;
1582
26.0k
  if (charProps.m_fontNameId)
1583
38
  {
1584
38
    std::map<unsigned, librevenge::RVNGString>::const_iterator iterString = m_strings.find(charProps.m_fontNameId);
1585
38
    if (iterString != m_strings.end())
1586
38
      propList.insert("style:font-name", iterString->second);
1587
38
  }
1588
26.0k
  propList.insert("fo:font-size", charProps.m_fontSize, librevenge::RVNG_POINT);
1589
26.0k
  if (charProps.m_fontId)
1590
25.8k
    _appendFontProperties(propList, charProps.m_fontId);
1591
26.0k
  if (charProps.m_textColorId)
1592
519
  {
1593
519
    std::map<unsigned, FHBasicFill>::const_iterator iterBasicFill = m_basicFills.find(charProps.m_textColorId);
1594
519
    if (iterBasicFill != m_basicFills.end() && iterBasicFill->second.m_colorId)
1595
131
    {
1596
131
      librevenge::RVNGString color = getColorString(iterBasicFill->second.m_colorId);
1597
131
      if (!color.empty())
1598
131
        propList.insert("fo:color", color);
1599
131
    }
1600
519
  }
1601
26.0k
  FHTEffect const *eff=_findTEffect(charProps.m_tEffectId);
1602
26.0k
  if (eff && eff->m_nameId)
1603
699
  {
1604
699
    std::map<unsigned, librevenge::RVNGString>::const_iterator iterString = m_strings.find(eff->m_nameId);
1605
699
    if (iterString != m_strings.end())
1606
664
    {
1607
664
      librevenge::RVNGString const &type=iterString->second;
1608
664
      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
177
          propList.insert("fo:color", color);
1614
185
      }
1615
479
      else if (type=="ShadowEffect")
1616
185
        propList.insert("fo:text-shadow", "1pt 1pt");
1617
294
      else if (type=="ZoomEffect")
1618
173
      {
1619
173
        propList.insert("style:font-relief", "embossed");
1620
173
        propList.insert("fo:text-shadow", "1pt -1pt");
1621
173
        librevenge::RVNGString color = getColorString(eff->m_colorId[0]);
1622
173
        if (!color.empty())
1623
170
          propList.insert("fo:color", color);
1624
173
      }
1625
121
      else
1626
121
      {
1627
121
        FH_DEBUG_MSG(("libfreehand::FHCollector::_appendCharacterProperties: find unknown effect %s\n",type.cstr()));
1628
121
      }
1629
664
    }
1630
699
  }
1631
103k
  for (auto it=charProps.m_idToDoubleMap.begin(); it!=charProps.m_idToDoubleMap.end(); ++it)
1632
77.5k
  {
1633
77.5k
    switch (it->first)
1634
77.5k
    {
1635
25.7k
    case FH_BASELN_SHIFT:
1636
25.7k
    {
1637
25.7k
      if (it->second<=0 && it->second>=0) break;
1638
3.09k
      librevenge::RVNGString value;
1639
3.09k
      double fontSize=(charProps.m_fontSize>0) ? charProps.m_fontSize : 24.;
1640
3.09k
      value.sprintf("%g%%",100.*it->second/fontSize);
1641
3.09k
      propList.insert("style:text-position", value);
1642
3.09k
      break;
1643
25.7k
    }
1644
26.0k
    case FH_HOR_SCALE:
1645
26.0k
      if (it->second<=1 && it->second>=1) break;
1646
517
      propList.insert("style:text-scale", it->second, librevenge::RVNG_PERCENT);
1647
517
      break;
1648
25.8k
    case FH_RNG_KERN:
1649
25.8k
      if (it->second<=0 && it->second>=0) break;
1650
340
      propList.insert("fo:letter-spacing", it->second*charProps.m_fontSize, librevenge::RVNG_POINT);
1651
340
      break;
1652
0
    default:
1653
0
      break;
1654
77.5k
    }
1655
77.5k
  }
1656
26.0k
}
1657
1658
void libfreehand::FHCollector::_appendCharacterProperties(librevenge::RVNGPropertyList &propList, const FH3CharProperties &charProps)
1659
202k
{
1660
202k
  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
202k
  propList.insert("fo:font-size", charProps.m_fontSize, librevenge::RVNG_POINT);
1667
202k
  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
202k
  if (charProps.m_fontStyle & 1)
1674
2.17k
    propList.insert("fo:font-weight", "bold");
1675
202k
  if (charProps.m_fontStyle & 2)
1676
2.62k
    propList.insert("fo:font-style", "italic");
1677
202k
  if (charProps.m_letterSpacing<0 || charProps.m_letterSpacing>0)
1678
7.80k
    propList.insert("fo:letter-spacing", charProps.m_letterSpacing, librevenge::RVNG_POINT);
1679
202k
  if (charProps.m_horizontalScale<1 || charProps.m_horizontalScale>1)
1680
6.38k
    propList.insert("style:text-scale", charProps.m_horizontalScale, librevenge::RVNG_PERCENT);
1681
202k
  if (charProps.m_baselineShift<0 || charProps.m_baselineShift>0)
1682
6.36k
  {
1683
6.36k
    librevenge::RVNGString value;
1684
6.36k
    double fontSize=(charProps.m_fontSize>0) ? charProps.m_fontSize : 24.;
1685
6.36k
    value.sprintf("%g%%",100.*charProps.m_baselineShift/fontSize);
1686
6.36k
    propList.insert("style:text-position", value);
1687
6.36k
  }
1688
202k
  FHTEffect const *eff=_findTEffect(charProps.m_textEffsId);
1689
202k
  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
202k
}
1720
1721
void libfreehand::FHCollector::_appendTabProperties(librevenge::RVNGPropertyList &propList, const libfreehand::FHTab &tab)
1722
49.8k
{
1723
49.8k
  switch (tab.m_type)
1724
49.8k
  {
1725
3.81k
  case 0:
1726
7.41k
  case 4: // unsure, look like a left tab
1727
7.69k
  default:
1728
7.69k
    break;
1729
34.8k
  case 1:
1730
34.8k
    propList.insert("style:type", "right");
1731
34.8k
    break;
1732
3.65k
  case 2:
1733
3.65k
    propList.insert("style:type", "center");
1734
3.65k
    break;
1735
3.60k
  case 3:
1736
3.60k
    propList.insert("style:type", "char");
1737
3.60k
    propList.insert("style:char", "."); // checkme
1738
3.60k
    break;
1739
49.8k
  }
1740
49.8k
  propList.insert("style:position", tab.m_position, librevenge::RVNG_POINT);
1741
49.8k
}
1742
1743
void libfreehand::FHCollector::_appendParagraphProperties(librevenge::RVNGPropertyList & /* propList */, const FH3ParaProperties & /* paraProps */)
1744
37.3k
{
1745
37.3k
}
1746
1747
void libfreehand::FHCollector::_appendParagraphProperties(librevenge::RVNGPropertyList &propList, unsigned paragraphPropsId)
1748
21.9k
{
1749
21.9k
  std::map<unsigned, FHParagraphProperties>::const_iterator iter = m_paragraphProperties.find(paragraphPropsId);
1750
21.9k
  if (iter == m_paragraphProperties.end())
1751
233
    return;
1752
21.7k
  FHParagraphProperties const &para=iter->second;
1753
21.7k
  for (const auto &it : para.m_idToZoneIdMap)
1754
21.4k
  {
1755
21.4k
    switch (it.first)
1756
21.4k
    {
1757
21.4k
    case FH_PARA_TAB_TABLE_ID:
1758
21.4k
      if (m_tabs.find(it.second)!=m_tabs.end())
1759
19.3k
      {
1760
19.3k
        std::vector<FHTab> const &tabs=m_tabs.find(it.second)->second;
1761
19.3k
        if (tabs.empty())
1762
0
          break;
1763
19.3k
        librevenge::RVNGPropertyListVector tabVect;
1764
19.3k
        for (const auto &tab : tabs)
1765
49.8k
        {
1766
49.8k
          librevenge::RVNGPropertyList tabList;
1767
49.8k
          _appendTabProperties(tabList, tab);
1768
49.8k
          tabVect.append(tabList);
1769
49.8k
        }
1770
19.3k
        propList.insert("style:tab-stops", tabVect);
1771
19.3k
      }
1772
21.4k
      break;
1773
21.4k
    default:
1774
0
      break;
1775
21.4k
    }
1776
21.4k
  }
1777
129k
  for (auto it=para.m_idToDoubleMap.begin(); it!=para.m_idToDoubleMap.end(); ++it)
1778
107k
  {
1779
107k
    switch (it->first)
1780
107k
    {
1781
21.5k
    case FH_PARA_LEFT_INDENT:
1782
21.5k
      propList.insert("fo:margin-left", it->second, librevenge::RVNG_POINT);
1783
21.5k
      break;
1784
21.1k
    case FH_PARA_RIGHT_INDENT:
1785
21.1k
      propList.insert("fo:margin-right", it->second, librevenge::RVNG_POINT);
1786
21.1k
      break;
1787
21.5k
    case FH_PARA_TEXT_INDENT:
1788
21.5k
      propList.insert("fo:text-indent", it->second, librevenge::RVNG_POINT);
1789
21.5k
      break;
1790
21.5k
    case FH_PARA_SPC_ABOVE:
1791
21.5k
      propList.insert("fo:margin-top", it->second, librevenge::RVNG_POINT);
1792
21.5k
      break;
1793
21.5k
    case FH_PARA_SPC_BELLOW:
1794
21.5k
      propList.insert("fo:margin-bottom", it->second, librevenge::RVNG_POINT);
1795
21.5k
      break;
1796
109
    case FH_PARA_LEADING:
1797
109
      if (it->second<=0 && it->second>=0) break;
1798
81
      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
59
      switch (para.m_idToIntMap.find(FH_PARA_LEADING_TYPE)->second)
1804
59
      {
1805
17
      case 0: // delta in point
1806
17
        propList.insert("fo:line-height",1.+it->second/(it->second>0 ? 12 : 24), librevenge::RVNG_PERCENT);
1807
17
        break;
1808
10
      case 1:
1809
10
        propList.insert("fo:line-height",it->second, librevenge::RVNG_POINT);
1810
10
        break;
1811
22
      case 2:
1812
22
        propList.insert("fo:line-height",it->second, librevenge::RVNG_PERCENT);
1813
22
        break;
1814
10
      default:
1815
10
        break;
1816
59
      }
1817
59
      break;
1818
59
    default:
1819
0
      break;
1820
107k
    }
1821
107k
  }
1822
21.7k
  for (const auto &it : para.m_idToIntMap)
1823
43.3k
  {
1824
43.3k
    switch (it.first)
1825
43.3k
    {
1826
21.5k
    case FH_PARA_TEXT_ALIGN:
1827
21.5k
      switch (it.second)
1828
21.5k
      {
1829
19.4k
      case 0:
1830
19.4k
        propList.insert("fo:text-align", "left");
1831
19.4k
        break;
1832
102
      case 1:
1833
102
        propList.insert("fo:text-align", "end");
1834
102
        break;
1835
376
      case 2:
1836
376
        propList.insert("fo:text-align", "center");
1837
376
        break;
1838
1.36k
      case 3:
1839
1.36k
        propList.insert("fo:text-align", "justify");
1840
1.36k
        break;
1841
218
      default:
1842
218
        break;
1843
21.5k
      }
1844
21.5k
      break;
1845
21.5k
    case FH_PARA_KEEP_SAME_LINE:
1846
124
      if (it.second==1)
1847
9
        propList.insert("fo:keep-together", "always");
1848
124
      break;
1849
118
    case FH_PARA_LEADING_TYPE: // done with FH_PARA_LEADING
1850
21.6k
    default:
1851
21.6k
      break;
1852
43.3k
    }
1853
43.3k
  }
1854
21.7k
}
1855
1856
void libfreehand::FHCollector::_outputDisplayText(const libfreehand::FHDisplayText *displayText, librevenge::RVNGDrawingInterface *painter)
1857
223k
{
1858
223k
  if (!painter || !displayText)
1859
185k
    return;
1860
1861
37.3k
  double xa = displayText->m_startX;
1862
37.3k
  double ya = displayText->m_startY;
1863
37.3k
  double xb = displayText->m_startX + displayText->m_width;
1864
37.3k
  double yb = displayText->m_startY + displayText->m_height;
1865
37.3k
  double xc = xa;
1866
37.3k
  double yc = yb;
1867
37.3k
  unsigned xFormId = displayText->m_xFormId;
1868
37.3k
  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.3k
  std::stack<FHTransform> groupTransforms(m_currentTransforms);
1879
73.2k
  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.3k
  _normalizePoint(xa, ya);
1887
37.3k
  _normalizePoint(xb, yb);
1888
37.3k
  _normalizePoint(xc, yc);
1889
1890
37.5k
  for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
1891
189
  {
1892
189
    iter->applyToPoint(xa, ya);
1893
189
    iter->applyToPoint(xb, yb);
1894
189
    iter->applyToPoint(xc, yc);
1895
189
  }
1896
1897
37.3k
  double rotation = atan2(yb-yc, xb-xc);
1898
37.3k
  double height = sqrt((xc-xa)*(xc-xa) + (yc-ya)*(yc-ya));
1899
37.3k
  double width = sqrt((xc-xb)*(xc-xb) + (yc-yb)*(yc-yb));
1900
37.3k
  double xmid = (xa + xb) / 2.0;
1901
37.3k
  double ymid = (ya + yb) / 2.0;
1902
1903
37.3k
  librevenge::RVNGPropertyList textObjectProps;
1904
37.3k
  textObjectProps.insert("svg:x", xmid - displayText->m_width / 2.0);
1905
37.3k
  textObjectProps.insert("svg:y", ymid + displayText->m_height / 2.0);
1906
37.3k
  textObjectProps.insert("svg:height", height);
1907
37.3k
  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.3k
  if (!FH_ALMOST_ZERO(rotation))
1914
238
  {
1915
238
    textObjectProps.insert("librevenge:rotate", rotation * 180.0 / M_PI);
1916
238
    textObjectProps.insert("librevenge:rotate-cx",xmid);
1917
238
    textObjectProps.insert("librevenge:rotate-cy",ymid);
1918
238
  }
1919
37.3k
  if (displayText->m_justify==4)  // top-down: checkme do nothing
1920
9
    textObjectProps.insert("style:writing-mode", "tb-lr");
1921
37.3k
  painter->startTextObject(textObjectProps);
1922
1923
37.3k
  auto iterPara = displayText->m_paraProps.begin();
1924
37.3k
  auto iterChar = displayText->m_charProps.begin();
1925
1926
37.3k
  FH3ParaProperties paraProps = *iterPara++;
1927
37.3k
  FH3CharProperties charProps = *iterChar++;
1928
37.3k
  librevenge::RVNGString text;
1929
37.3k
  std::vector<unsigned char>::size_type i = 0;
1930
1931
37.3k
  librevenge::RVNGPropertyList paraPropList;
1932
37.3k
  _appendParagraphProperties(paraPropList, paraProps);
1933
37.3k
  switch (displayText->m_justify)
1934
37.3k
  {
1935
37.1k
  case 0: // left
1936
37.1k
    break;
1937
38
  case 1:
1938
38
    paraPropList.insert("fo:text-align", "center");
1939
38
    break;
1940
107
  case 2:
1941
107
    paraPropList.insert("fo:text-align", "end");
1942
107
    break;
1943
9
  case 3:
1944
9
    paraPropList.insert("fo:text-align", "justify");
1945
9
    break;
1946
9
  case 4:
1947
36
  default:
1948
36
    break;
1949
37.3k
  }
1950
37.3k
  if (charProps.m_leading>0)
1951
1.35k
    paraPropList.insert("fo:line-height",charProps.m_leading,librevenge::RVNG_POINT);
1952
36.0k
  else
1953
36.0k
    paraPropList.insert("fo:line-height",1.,librevenge::RVNG_PERCENT);
1954
37.3k
  painter->openParagraph(paraPropList);
1955
37.3k
  bool isParagraphOpened = true;
1956
1957
37.3k
  librevenge::RVNGPropertyList charPropList;
1958
37.3k
  _appendCharacterProperties(charPropList, charProps);
1959
37.3k
  painter->openSpan(charPropList);
1960
37.3k
  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.6k
        painter->insertText(text);
1989
200k
      text.clear();
1990
200k
      if (isSpanOpened)
1991
58.6k
      {
1992
58.6k
        painter->closeSpan();
1993
58.6k
        isSpanOpened = false;
1994
58.6k
      }
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.3k
      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.25k
        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.1k
    {
2024
58.1k
      charPropList.clear();
2025
58.1k
      _appendCharacterProperties(charPropList, charProps);
2026
58.1k
      painter->openSpan(charPropList);
2027
58.1k
      isSpanOpened = true;
2028
58.1k
    }
2029
1.02M
  }
2030
37.3k
  if (!text.empty())
2031
92
    painter->insertText(text);
2032
37.3k
  if (isSpanOpened)
2033
92
    painter->closeSpan();
2034
37.3k
  if (isParagraphOpened)
2035
587
    painter->closeParagraph();
2036
2037
37.3k
  painter->endTextObject();
2038
37.3k
}
2039
2040
void libfreehand::FHCollector::_outputImageImport(const FHImageImport *image, librevenge::RVNGDrawingInterface *painter)
2041
223k
{
2042
223k
  if (!painter || !image)
2043
222k
    return;
2044
2045
110
  librevenge::RVNGPropertyList propList;
2046
110
  _appendStrokeProperties(propList, image->m_graphicStyleId);
2047
110
  _appendFillProperties(propList, image->m_graphicStyleId);
2048
110
  double xa = image->m_startX;
2049
110
  double ya = image->m_startY;
2050
110
  double xb = image->m_startX + image->m_width;
2051
110
  double yb = image->m_startY + image->m_height;
2052
110
  double xc = xa;
2053
110
  double yc = yb;
2054
110
  unsigned xFormId = image->m_xFormId;
2055
110
  if (xFormId)
2056
82
  {
2057
82
    const FHTransform *trafo = _findTransform(xFormId);
2058
82
    if (trafo)
2059
6
    {
2060
6
      trafo->applyToPoint(xa, ya);
2061
6
      trafo->applyToPoint(xb, yb);
2062
6
      trafo->applyToPoint(xc, yc);
2063
6
    }
2064
82
  }
2065
110
  std::stack<FHTransform> groupTransforms(m_currentTransforms);
2066
110
  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
110
  _normalizePoint(xa, ya);
2074
110
  _normalizePoint(xb, yb);
2075
110
  _normalizePoint(xc, yc);
2076
2077
240
  for (std::vector<FHTransform>::const_iterator iter = m_fakeTransforms.begin(); iter != m_fakeTransforms.end(); ++iter)
2078
130
  {
2079
130
    iter->applyToPoint(xa, ya);
2080
130
    iter->applyToPoint(xb, yb);
2081
130
    iter->applyToPoint(xc, yc);
2082
130
  }
2083
2084
110
  double rotation = atan2(yb-yc, xb-xc);
2085
110
  double height = sqrt((xc-xa)*(xc-xa) + (yc-ya)*(yc-ya));
2086
110
  double width = sqrt((xc-xb)*(xc-xb) + (yc-yb)*(yc-yb));
2087
110
  double xmid = (xa + xb) / 2.0;
2088
110
  double ymid = (ya + yb) / 2.0;
2089
2090
110
  librevenge::RVNGPropertyList imageProps;
2091
110
  imageProps.insert("svg:x", xmid - width / 2.0);
2092
110
  imageProps.insert("svg:y", ymid - height / 2.0);
2093
110
  imageProps.insert("svg:height", height);
2094
110
  imageProps.insert("svg:width", width);
2095
110
  if (!FH_ALMOST_ZERO(rotation))
2096
75
    imageProps.insert("librevenge:rotate", rotation * 180.0 / M_PI);
2097
2098
110
  imageProps.insert("librevenge:mime-type", "whatever");
2099
110
  librevenge::RVNGBinaryData data = getImageData(image->m_dataListId);
2100
2101
110
  if (data.empty())
2102
110
    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
28.5k
{
2123
28.5k
  if (!painter || !characters || characters->empty())
2124
623
    return;
2125
27.9k
  librevenge::RVNGPropertyList propList;
2126
27.9k
  _appendCharacterProperties(propList, charStyleId);
2127
27.9k
  painter->openSpan(propList);
2128
27.9k
  std::vector<unsigned short> tmpChars;
2129
27.9k
  bool lastIsSpace=false;
2130
743k
  for (unsigned i = offset; i < length+offset && i < characters->size(); ++i)
2131
715k
  {
2132
715k
    unsigned c=(*characters)[i];
2133
715k
    if (c=='\t' || (c==' ' && lastIsSpace))
2134
14.5k
    {
2135
14.5k
      if (!tmpChars.empty())
2136
5.02k
      {
2137
5.02k
        librevenge::RVNGString text;
2138
5.02k
        _appendUTF16(text, tmpChars);
2139
5.02k
        painter->insertText(text);
2140
5.02k
        tmpChars.clear();
2141
5.02k
      }
2142
14.5k
      if (c=='\t')
2143
14.5k
        painter->insertTab();
2144
6
      else
2145
6
        painter->insertSpace();
2146
14.5k
      continue;
2147
14.5k
    }
2148
700k
    else
2149
700k
    {
2150
700k
      if (c<=0x1f)
2151
266k
      {
2152
266k
        switch (c)
2153
266k
        {
2154
677
        case 0xb: // end of column
2155
677
          break;
2156
215
        case 0x1f: // optional hyphen
2157
215
          break;
2158
265k
        default:
2159
265k
          FH_DEBUG_MSG(("libfreehand::FHCollector::_outputTextRun: find character %x\n", c));
2160
265k
          break;
2161
266k
        }
2162
266k
      }
2163
434k
      else
2164
434k
        tmpChars.push_back(c);
2165
700k
    }
2166
700k
    lastIsSpace=c==' ';
2167
700k
  }
2168
27.9k
  if (!tmpChars.empty())
2169
25.4k
  {
2170
25.4k
    librevenge::RVNGString text;
2171
25.4k
    _appendUTF16(text, tmpChars);
2172
25.4k
    painter->insertText(text);
2173
25.4k
  }
2174
27.9k
  painter->closeSpan();
2175
27.9k
}
2176
2177
const std::vector<unsigned> *libfreehand::FHCollector::_findListElements(unsigned id)
2178
23.3k
{
2179
23.3k
  std::map<unsigned, FHList>::const_iterator iter = m_lists.find(id);
2180
23.3k
  if (iter != m_lists.end())
2181
22.9k
    return &(iter->second.m_elements);
2182
387
  return nullptr;
2183
23.3k
}
2184
2185
2186
void libfreehand::FHCollector::_appendFontProperties(librevenge::RVNGPropertyList &propList, unsigned agdFontId)
2187
25.8k
{
2188
25.8k
  std::map<unsigned, FHAGDFont>::const_iterator iter = m_fonts.find(agdFontId);
2189
25.8k
  if (iter == m_fonts.end())
2190
240
    return;
2191
25.6k
  const FHAGDFont &font = iter->second;
2192
25.6k
  if (font.m_fontNameId)
2193
25.4k
  {
2194
25.4k
    std::map<unsigned, librevenge::RVNGString>::const_iterator iterString = m_strings.find(font.m_fontNameId);
2195
25.4k
    if (iterString != m_strings.end())
2196
24.5k
      propList.insert("style:font-name", iterString->second);
2197
25.4k
  }
2198
25.6k
  propList.insert("fo:font-size", font.m_fontSize, librevenge::RVNG_POINT);
2199
25.6k
  if (font.m_fontStyle & 1)
2200
1.63k
    propList.insert("fo:font-weight", "bold");
2201
25.6k
  if (font.m_fontStyle & 2)
2202
1.51k
    propList.insert("fo:font-style", "italic");
2203
25.6k
}
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
260k
  {
2211
260k
    const ObjectRecursionGuard guard(m_visitedObjects, graphicStyleId);
2212
260k
    const FHPropList *propertyList = _findPropList(graphicStyleId);
2213
260k
    if (propertyList)
2214
247k
    {
2215
247k
      if (propertyList->m_parentId)
2216
133k
        _appendFillProperties(propList, propertyList->m_parentId);
2217
247k
      auto iter = propertyList->m_elements.find(m_fillId);
2218
247k
      if (iter != propertyList->m_elements.end())
2219
15.9k
      {
2220
15.9k
        _appendBasicFill(propList, _findBasicFill(iter->second));
2221
15.9k
        _appendLinearFill(propList, _findLinearFill(iter->second));
2222
15.9k
        _appendLensFill(propList, _findLensFill(iter->second));
2223
15.9k
        _appendRadialFill(propList, _findRadialFill(iter->second));
2224
15.9k
        _appendTileFill(propList, _findTileFill(iter->second));
2225
15.9k
        _appendPatternFill(propList, _findPatternFill(iter->second));
2226
15.9k
        _appendCustomProcFill(propList, _findCustomProc(iter->second));
2227
15.9k
      }
2228
247k
    }
2229
13.3k
    else
2230
13.3k
    {
2231
13.3k
      const FHGraphicStyle *graphicStyle = _findGraphicStyle(graphicStyleId);
2232
13.3k
      if (graphicStyle)
2233
5.88k
      {
2234
5.88k
        if (graphicStyle->m_parentId)
2235
3.28k
          _appendFillProperties(propList, graphicStyle->m_parentId);
2236
5.88k
        unsigned fillId = _findFillId(*graphicStyle);;
2237
5.88k
        if (fillId)
2238
1.38k
        {
2239
1.38k
          _appendBasicFill(propList, _findBasicFill(fillId));
2240
1.38k
          _appendLinearFill(propList, _findLinearFill(fillId));
2241
1.38k
          _appendLensFill(propList, _findLensFill(fillId));
2242
1.38k
          _appendRadialFill(propList, _findRadialFill(fillId));
2243
1.38k
          _appendTileFill(propList, _findTileFill(fillId));
2244
1.38k
          _appendPatternFill(propList, _findPatternFill(fillId));
2245
1.38k
          _appendCustomProcFill(propList, _findCustomProc(fillId));
2246
1.38k
        }
2247
4.49k
        else
2248
4.49k
        {
2249
4.49k
          const FHFilterAttributeHolder *filterAttributeHolder = _findFilterAttributeHolder(*graphicStyle);
2250
4.49k
          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.49k
        }
2258
5.88k
      }
2259
13.3k
    }
2260
260k
  }
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
260k
  {
2269
260k
    const ObjectRecursionGuard guard(m_visitedObjects, graphicStyleId);
2270
260k
    const FHPropList *propertyList = _findPropList(graphicStyleId);
2271
260k
    if (propertyList)
2272
247k
    {
2273
247k
      if (propertyList->m_parentId)
2274
133k
        _appendStrokeProperties(propList, propertyList->m_parentId);
2275
247k
      auto iter = propertyList->m_elements.find(m_strokeId);
2276
247k
      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
247k
    }
2283
13.3k
    else
2284
13.3k
    {
2285
13.3k
      const FHGraphicStyle *graphicStyle = _findGraphicStyle(graphicStyleId);
2286
13.3k
      if (graphicStyle)
2287
5.88k
      {
2288
5.88k
        if (graphicStyle->m_parentId)
2289
3.28k
          _appendStrokeProperties(propList, graphicStyle->m_parentId);
2290
5.88k
        unsigned strokeId = _findStrokeId(*graphicStyle);
2291
5.88k
        if (strokeId)
2292
2.96k
        {
2293
2.96k
          _appendBasicLine(propList, _findBasicLine(strokeId));
2294
2.96k
          _appendPatternLine(propList, _findPatternLine(strokeId));
2295
2.96k
          _appendCustomProcLine(propList, _findCustomProc(strokeId));
2296
2.96k
        }
2297
2.91k
        else
2298
2.91k
        {
2299
2.91k
          const FHFilterAttributeHolder *filterAttributeHolder = _findFilterAttributeHolder(*graphicStyle);
2300
2.91k
          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.91k
        }
2308
5.88k
      }
2309
13.3k
    }
2310
260k
  }
2311
265k
}
2312
2313
void libfreehand::FHCollector::_appendBasicFill(librevenge::RVNGPropertyList &propList, const libfreehand::FHBasicFill *basicFill)
2314
17.3k
{
2315
17.3k
  if (!basicFill)
2316
13.9k
    return;
2317
3.35k
  propList.insert("draw:fill", "solid");
2318
3.35k
  librevenge::RVNGString color = getColorString(basicFill->m_colorId);
2319
3.35k
  if (!color.empty())
2320
2.87k
    propList.insert("draw:fill-color", color);
2321
474
  else
2322
474
    propList.insert("draw:fill-color", "#000000");
2323
3.35k
}
2324
2325
void libfreehand::FHCollector::_appendCustomProcFill(librevenge::RVNGPropertyList &propList, const libfreehand::FHCustomProc *fill)
2326
17.3k
{
2327
17.3k
  if (!fill || fill->m_ids.empty())
2328
16.0k
    return;
2329
1.29k
  propList.insert("draw:fill", "solid");
2330
1.29k
  librevenge::RVNGString color = getColorString(fill->m_ids[0]);
2331
1.29k
  if (!color.empty())
2332
1.21k
    propList.insert("draw:fill-color", color);
2333
79
  else
2334
79
    propList.insert("draw:fill-color", "#000000");
2335
1.29k
}
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.9k
        return iter->second;
2347
123k
    }
2348
5.33k
    else
2349
5.33k
    {
2350
5.33k
      const FHGraphicStyle *graphicStyle = _findGraphicStyle(graphicStyleId);
2351
5.33k
      if (graphicStyle)
2352
2.96k
      {
2353
2.96k
        auto iter = graphicStyle->m_elements.find(m_contentId);
2354
2.96k
        if (iter != graphicStyle->m_elements.end())
2355
0
          return iter->second;
2356
2.96k
      }
2357
5.33k
    }
2358
128k
  }
2359
118k
  return 0;
2360
129k
}
2361
2362
void libfreehand::FHCollector::_appendLinearFill(librevenge::RVNGPropertyList &propList, const libfreehand::FHLinearFill *linearFill)
2363
17.3k
{
2364
17.3k
  if (!linearFill)
2365
16.4k
    return;
2366
866
  propList.insert("draw:fill", "gradient");
2367
866
  propList.insert("draw:style", "linear");
2368
866
  double angle = 90.0 - linearFill->m_angle;
2369
2.77k
  while (angle < 0.0)
2370
1.90k
    angle += 360.0;
2371
6.90k
  while (angle > 360.0)
2372
6.03k
    angle -= 360.0;
2373
866
  propList.insert("draw:angle", angle, librevenge::RVNG_GENERIC);
2374
2375
866
  const std::vector<FHColorStop> *multiColorList = _findMultiColorList(linearFill->m_multiColorListId);
2376
866
  if (multiColorList && multiColorList->size() > 1)
2377
96
  {
2378
96
    librevenge::RVNGString color = getColorString((*multiColorList)[0].m_colorId);
2379
96
    if (!color.empty())
2380
96
      propList.insert("draw:start-color", color);
2381
96
    color = getColorString((*multiColorList)[1].m_colorId);
2382
96
    if (!color.empty())
2383
96
      propList.insert("draw:end-color", color);
2384
96
  }
2385
770
  else
2386
770
  {
2387
770
    librevenge::RVNGString color = getColorString(linearFill->m_color1Id);
2388
770
    if (!color.empty())
2389
622
      propList.insert("draw:start-color", color);
2390
770
    color = getColorString(linearFill->m_color2Id);
2391
770
    if (!color.empty())
2392
571
      propList.insert("draw:end-color", color);
2393
770
  }
2394
866
}
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
17.3k
{
2437
17.3k
  if (!lensFill)
2438
17.3k
    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
17.3k
{
2484
17.3k
  if (!radialFill)
2485
15.0k
    return;
2486
2487
2.29k
  propList.insert("draw:fill", "gradient");
2488
2.29k
  propList.insert("draw:style", "radial");
2489
2.29k
  propList.insert("svg:cx", radialFill->m_cx, librevenge::RVNG_PERCENT);
2490
2.29k
  propList.insert("svg:cy", radialFill->m_cy, librevenge::RVNG_PERCENT);
2491
2492
2.29k
  const std::vector<FHColorStop> *multiColorList = _findMultiColorList(radialFill->m_multiColorListId);
2493
2.29k
  if (multiColorList && multiColorList->size() > 1)
2494
83
  {
2495
83
    librevenge::RVNGString color = getColorString((*multiColorList)[0].m_colorId);
2496
83
    if (!color.empty())
2497
83
      propList.insert("draw:start-color", color);
2498
83
    color = getColorString((*multiColorList)[1].m_colorId);
2499
83
    if (!color.empty())
2500
83
      propList.insert("draw:end-color", color);
2501
83
  }
2502
2.21k
  else
2503
2.21k
  {
2504
2.21k
    librevenge::RVNGString color = getColorString(radialFill->m_color1Id);
2505
2.21k
    if (!color.empty())
2506
1.31k
      propList.insert("draw:start-color", color);
2507
2.21k
    color = getColorString(radialFill->m_color2Id);
2508
2.21k
    if (!color.empty())
2509
1.42k
      propList.insert("draw:end-color", color);
2510
2.21k
  }
2511
2.29k
}
2512
2513
void libfreehand::FHCollector::_appendTileFill(librevenge::RVNGPropertyList &propList, const libfreehand::FHTileFill *tileFill)
2514
17.3k
{
2515
17.3k
  if (!tileFill || !(tileFill->m_groupId))
2516
14.1k
    return;
2517
2518
3.14k
  const FHTransform *trafo = _findTransform(tileFill->m_xFormId);
2519
3.14k
  if (trafo)
2520
2.61k
    m_currentTransforms.push(*trafo);
2521
531
  else
2522
531
    m_currentTransforms.push(FHTransform());
2523
2524
3.14k
  FHBoundingBox bBox;
2525
3.14k
  _getBBofSomething(tileFill->m_groupId, bBox);
2526
3.14k
  if (bBox.isValid() && !FH_ALMOST_ZERO(bBox.m_xmax - bBox.m_xmin) && !FH_ALMOST_ZERO(bBox.m_ymax - bBox.m_ymin))
2527
2.87k
  {
2528
2.87k
    FHTransform fakeTrafo(tileFill->m_scaleX, 0.0, 0.0, tileFill->m_scaleY, - bBox.m_xmin, -bBox.m_ymin);
2529
2.87k
    m_fakeTransforms.push_back(fakeTrafo);
2530
2531
2.87k
    librevenge::RVNGStringVector svgOutput;
2532
2.87k
    librevenge::RVNGSVGDrawingGenerator generator(svgOutput, "");
2533
2.87k
    librevenge::RVNGPropertyList pList;
2534
2.87k
    pList.insert("svg:width", tileFill->m_scaleX * (bBox.m_xmax - bBox.m_xmin));
2535
2.87k
    pList.insert("svg:height", tileFill->m_scaleY * (bBox.m_ymax - bBox.m_ymin));
2536
2.87k
    generator.startPage(pList);
2537
2538
2.87k
    _outputSomething(tileFill->m_groupId, &generator);
2539
2.87k
    generator.endPage();
2540
2.87k
    if (!svgOutput.empty() && svgOutput[0].size() > 140) // basically empty svg if it is not fullfilled
2541
2.87k
    {
2542
2.87k
      const char *header =
2543
2.87k
        "<?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
2.87k
      librevenge::RVNGBinaryData output((const unsigned char *)header, strlen(header));
2545
2.87k
      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
2.87k
      propList.insert("draw:fill", "bitmap");
2561
2.87k
      propList.insert("draw:fill-image", output);
2562
2.87k
      propList.insert("draw:fill-image-width", tileFill->m_scaleX * (bBox.m_xmax - bBox.m_xmin));
2563
2.87k
      propList.insert("draw:fill-image-height", tileFill->m_scaleY * (bBox.m_ymax - bBox.m_ymin));
2564
2.87k
      propList.insert("librevenge:mime-type", "image/svg+xml");
2565
2.87k
      propList.insert("style:repeat", "repeat");
2566
2.87k
    }
2567
2568
2.87k
    if (!m_fakeTransforms.empty())
2569
2.87k
      m_fakeTransforms.pop_back();
2570
2.87k
  }
2571
3.14k
  if (!m_currentTransforms.empty())
2572
3.14k
    m_currentTransforms.pop();
2573
3.14k
}
2574
2575
void libfreehand::FHCollector::_appendPatternFill(librevenge::RVNGPropertyList &propList, const libfreehand::FHPatternFill *patternFill)
2576
17.3k
{
2577
17.3k
  if (!patternFill)
2578
16.1k
    return;
2579
1.15k
  librevenge::RVNGBinaryData output;
2580
1.15k
  _generateBitmapFromPattern(output, patternFill->m_colorId, patternFill->m_pattern);
2581
1.15k
  propList.insert("draw:fill", "bitmap");
2582
1.15k
  propList.insert("draw:fill-image", output);
2583
1.15k
  propList.insert("librevenge:mime-type", "image/bmp");
2584
1.15k
  propList.insert("style:repeat", "repeat");
2585
1.15k
}
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
2.18k
  int nDots1=0, nDots2=0;
2592
2.18k
  double size1=0, size2=0, totalGap=0.0;
2593
6.26k
  for (size_t c=0; c+1 < linePattern->m_dashes.size();)
2594
4.38k
  {
2595
4.38k
    double sz=linePattern->m_dashes[c++];
2596
4.38k
    if (nDots2 && (sz<size2||sz>size2))
2597
301
    {
2598
301
      static bool first=true;
2599
301
      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
301
      break;
2605
301
    }
2606
4.08k
    if (nDots2)
2607
709
      nDots2++;
2608
3.37k
    else if (!nDots1 || (sz>=size1 && sz <= size1))
2609
2.41k
    {
2610
2.41k
      nDots1++;
2611
2.41k
      size1=sz;
2612
2.41k
    }
2613
959
    else
2614
959
    {
2615
959
      nDots2=1;
2616
959
      size2=sz;
2617
959
    }
2618
4.08k
    totalGap += linePattern->m_dashes[c++];
2619
4.08k
  }
2620
2.18k
  propList.insert("draw:stroke", "dash");
2621
2.18k
  propList.insert("draw:dots1", nDots1);
2622
2.18k
  propList.insert("draw:dots1-length", double(size1), librevenge::RVNG_POINT);
2623
2.18k
  if (nDots2)
2624
959
  {
2625
959
    propList.insert("draw:dots2", nDots2);
2626
959
    propList.insert("draw:dots2-length", double(size2), librevenge::RVNG_POINT);
2627
959
  }
2628
2.18k
  const double distance = ((nDots1 + nDots2) > 0) ? double(totalGap)/double(nDots1+nDots2) : double(totalGap);
2629
2.18k
  propList.insert("draw:distance", distance, librevenge::RVNG_POINT);;
2630
2.18k
}
2631
2632
void libfreehand::FHCollector::_appendArrowPath(librevenge::RVNGPropertyList &propList, const FHPath *arrow, bool startArrow)
2633
236k
{
2634
236k
  if (!arrow)
2635
222k
    return;
2636
2637
13.7k
  FHPath path=*arrow;
2638
13.7k
  path.transform(FHTransform(0,-1,1,0,0,0));
2639
13.7k
  std::string pString=path.getPathString();
2640
13.7k
  if (pString.empty()) return;
2641
13.7k
  std::string wh(startArrow ? "start" : "end");
2642
13.7k
  propList.insert((std::string("draw:marker-")+wh+"-path").c_str(), pString.c_str());
2643
13.7k
  FHBoundingBox box;
2644
13.7k
  path.getBoundingBox(box.m_xmin, box.m_ymin, box.m_xmax, box.m_ymax);
2645
13.7k
  librevenge::RVNGString boxString;
2646
13.7k
  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.7k
  propList.insert((std::string("draw:marker-")+wh+"-viewbox").c_str(), boxString);
2648
13.7k
  propList.insert((std::string("draw:marker-")+wh+"-width").c_str(), 10, librevenge::RVNG_POINT); // change me
2649
13.7k
}
2650
2651
void libfreehand::FHCollector::_appendBasicLine(librevenge::RVNGPropertyList &propList, const libfreehand::FHBasicLine *basicLine)
2652
130k
{
2653
130k
  if (!basicLine)
2654
11.9k
    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
14.0k
  else if (!propList["svg:stroke-color"]) // set to default
2661
12.5k
    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
804
  propList.insert("draw:stroke", "solid");
2673
804
  librevenge::RVNGString color;
2674
804
  if (!customProc->m_ids.empty())
2675
702
    color= getColorString(customProc->m_ids[0]);
2676
804
  if (!color.empty())
2677
619
    propList.insert("svg:stroke-color", color);
2678
804
  if (!customProc->m_widths.empty())
2679
731
    propList.insert("svg:stroke-width", customProc->m_widths[0], librevenge::RVNG_POINT);
2680
804
}
2681
2682
void libfreehand::FHCollector::_appendPatternLine(librevenge::RVNGPropertyList &propList, const libfreehand::FHPatternLine *patternLine)
2683
130k
{
2684
130k
  if (!patternLine)
2685
128k
    return;
2686
1.39k
  propList.insert("draw:stroke", "solid");
2687
1.39k
  librevenge::RVNGString color = getColorString(patternLine->m_colorId, patternLine->m_percentPattern);
2688
1.39k
  if (!color.empty())
2689
1.22k
    propList.insert("svg:stroke-color", color);
2690
169
  else if (!propList["svg:stroke-color"]) // set to default
2691
68
    propList.insert("svg:stroke-color", "#000000");
2692
1.39k
  propList.insert("svg:stroke-width", patternLine->m_width);
2693
1.39k
}
2694
2695
const libfreehand::FHPath *libfreehand::FHCollector::_findPath(unsigned id)
2696
229k
{
2697
229k
  if (!id)
2698
312
    return nullptr;
2699
228k
  std::map<unsigned, FHPath>::const_iterator iter = m_paths.find(id);
2700
228k
  if (iter != m_paths.end())
2701
131k
    return &(iter->second);
2702
97.8k
  return nullptr;
2703
228k
}
2704
2705
const libfreehand::FHNewBlend *libfreehand::FHCollector::_findNewBlend(unsigned id)
2706
226k
{
2707
226k
  if (!id)
2708
0
    return nullptr;
2709
226k
  std::map<unsigned, FHNewBlend>::const_iterator iter = m_newBlends.find(id);
2710
226k
  if (iter != m_newBlends.end())
2711
16
    return &(iter->second);
2712
226k
  return nullptr;
2713
226k
}
2714
2715
const libfreehand::FHGroup *libfreehand::FHCollector::_findGroup(unsigned id)
2716
226k
{
2717
226k
  if (!id)
2718
0
    return nullptr;
2719
226k
  std::map<unsigned, FHGroup>::const_iterator iter = m_groups.find(id);
2720
226k
  if (iter != m_groups.end())
2721
18.5k
    return &(iter->second);
2722
207k
  return nullptr;
2723
226k
}
2724
2725
const libfreehand::FHGroup *libfreehand::FHCollector::_findClipGroup(unsigned id)
2726
226k
{
2727
226k
  if (!id)
2728
0
    return nullptr;
2729
226k
  std::map<unsigned, FHGroup>::const_iterator iter = m_clipGroups.find(id);
2730
226k
  if (iter != m_clipGroups.end())
2731
112
    return &(iter->second);
2732
226k
  return nullptr;
2733
226k
}
2734
2735
const libfreehand::FHCompositePath *libfreehand::FHCollector::_findCompositePath(unsigned id)
2736
226k
{
2737
226k
  if (!id)
2738
0
    return nullptr;
2739
226k
  std::map<unsigned, FHCompositePath>::const_iterator iter = m_compositePaths.find(id);
2740
226k
  if (iter != m_compositePaths.end())
2741
369
    return &(iter->second);
2742
226k
  return nullptr;
2743
226k
}
2744
2745
const libfreehand::FHPathText *libfreehand::FHCollector::_findPathText(unsigned id)
2746
226k
{
2747
226k
  if (!id)
2748
0
    return nullptr;
2749
226k
  std::map<unsigned, FHPathText>::const_iterator iter = m_pathTexts.find(id);
2750
226k
  if (iter != m_pathTexts.end())
2751
189
    return &(iter->second);
2752
226k
  return nullptr;
2753
226k
}
2754
2755
const libfreehand::FHTextObject *libfreehand::FHCollector::_findTextObject(unsigned id)
2756
226k
{
2757
226k
  if (!id)
2758
0
    return nullptr;
2759
226k
  std::map<unsigned, FHTextObject>::const_iterator iter = m_textObjects.find(id);
2760
226k
  if (iter != m_textObjects.end())
2761
21.0k
    return &(iter->second);
2762
205k
  return nullptr;
2763
226k
}
2764
2765
const libfreehand::FHTransform *libfreehand::FHCollector::_findTransform(unsigned id)
2766
77.9k
{
2767
77.9k
  if (!id)
2768
192
    return nullptr;
2769
77.7k
  std::map<unsigned, FHTransform>::const_iterator iter = m_transforms.find(id);
2770
77.7k
  if (iter != m_transforms.end())
2771
74.5k
    return &(iter->second);
2772
3.22k
  return nullptr;
2773
77.7k
}
2774
2775
const libfreehand::FHTEffect *libfreehand::FHCollector::_findTEffect(unsigned id)
2776
228k
{
2777
228k
  if (!id)
2778
66.9k
    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
3.24k
  return nullptr;
2783
161k
}
2784
2785
const libfreehand::FHParagraph *libfreehand::FHCollector::_findParagraph(unsigned id)
2786
71.0k
{
2787
71.0k
  if (!id)
2788
18.8k
    return nullptr;
2789
52.1k
  std::map<unsigned, FHParagraph>::const_iterator iter = m_paragraphs.find(id);
2790
52.1k
  if (iter != m_paragraphs.end())
2791
25.2k
    return &(iter->second);
2792
26.8k
  return nullptr;
2793
52.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.4k
{
2807
19.4k
  if (!id)
2808
96
    return nullptr;
2809
19.3k
  std::map<unsigned, std::vector<unsigned> >::const_iterator iter = m_tStrings.find(id);
2810
19.3k
  if (iter != m_tStrings.end())
2811
19.0k
    return &(iter->second);
2812
338
  return nullptr;
2813
19.3k
}
2814
2815
const libfreehand::FHPropList *libfreehand::FHCollector::_findPropList(unsigned id)
2816
649k
{
2817
649k
  if (!id)
2818
0
    return nullptr;
2819
649k
  std::map<unsigned, FHPropList>::const_iterator iter = m_propertyLists.find(id);
2820
649k
  if (iter != m_propertyLists.end())
2821
617k
    return &(iter->second);
2822
32.0k
  return nullptr;
2823
649k
}
2824
2825
const libfreehand::FHGraphicStyle *libfreehand::FHCollector::_findGraphicStyle(unsigned id)
2826
32.0k
{
2827
32.0k
  if (!id)
2828
0
    return nullptr;
2829
32.0k
  std::map<unsigned, FHGraphicStyle>::const_iterator iter = m_graphicStyles.find(id);
2830
32.0k
  if (iter != m_graphicStyles.end())
2831
14.7k
    return &(iter->second);
2832
17.3k
  return nullptr;
2833
32.0k
}
2834
2835
const libfreehand::FHBasicFill *libfreehand::FHCollector::_findBasicFill(unsigned id)
2836
27.3k
{
2837
27.3k
  if (!id)
2838
4.96k
    return nullptr;
2839
22.3k
  std::map<unsigned, FHBasicFill>::const_iterator iter = m_basicFills.find(id);
2840
22.3k
  if (iter != m_basicFills.end())
2841
4.55k
    return &(iter->second);
2842
17.8k
  return nullptr;
2843
22.3k
}
2844
2845
const libfreehand::FHLinearFill *libfreehand::FHCollector::_findLinearFill(unsigned id)
2846
26.1k
{
2847
26.1k
  if (!id)
2848
4.96k
    return nullptr;
2849
21.1k
  std::map<unsigned, FHLinearFill>::const_iterator iter = m_linearFills.find(id);
2850
21.1k
  if (iter != m_linearFills.end())
2851
962
    return &(iter->second);
2852
20.2k
  return nullptr;
2853
21.1k
}
2854
2855
const libfreehand::FHLensFill *libfreehand::FHCollector::_findLensFill(unsigned id)
2856
26.0k
{
2857
26.0k
  if (!id)
2858
4.96k
    return nullptr;
2859
21.0k
  std::map<unsigned, FHLensFill>::const_iterator iter = m_lensFills.find(id);
2860
21.0k
  if (iter != m_lensFills.end())
2861
0
    return &(iter->second);
2862
21.0k
  return nullptr;
2863
21.0k
}
2864
2865
const libfreehand::FHRadialFill *libfreehand::FHCollector::_findRadialFill(unsigned id)
2866
26.0k
{
2867
26.0k
  if (!id)
2868
4.96k
    return nullptr;
2869
21.0k
  std::map<unsigned, FHRadialFill>::const_iterator iter = m_radialFills.find(id);
2870
21.0k
  if (iter != m_radialFills.end())
2871
2.37k
    return &(iter->second);
2872
18.7k
  return nullptr;
2873
21.0k
}
2874
2875
const libfreehand::FHTileFill *libfreehand::FHCollector::_findTileFill(unsigned id)
2876
25.9k
{
2877
25.9k
  if (!id)
2878
4.96k
    return nullptr;
2879
20.9k
  std::map<unsigned, FHTileFill>::const_iterator iter = m_tileFills.find(id);
2880
20.9k
  if (iter != m_tileFills.end())
2881
3.64k
    return &(iter->second);
2882
17.3k
  return nullptr;
2883
20.9k
}
2884
2885
const libfreehand::FHPatternFill *libfreehand::FHCollector::_findPatternFill(unsigned id)
2886
25.9k
{
2887
25.9k
  if (!id)
2888
4.96k
    return nullptr;
2889
20.9k
  std::map<unsigned, FHPatternFill>::const_iterator iter = m_patternFills.find(id);
2890
20.9k
  if (iter != m_patternFills.end())
2891
1.15k
    return &(iter->second);
2892
19.8k
  return nullptr;
2893
20.9k
}
2894
2895
const libfreehand::FHLinePattern *libfreehand::FHCollector::_findLinePattern(unsigned id)
2896
118k
{
2897
118k
  if (!id)
2898
109k
    return nullptr;
2899
8.69k
  std::map<unsigned, FHLinePattern>::const_iterator iter = m_linePatterns.find(id);
2900
8.69k
  if (iter != m_linePatterns.end())
2901
2.39k
    return &(iter->second);
2902
6.29k
  return nullptr;
2903
8.69k
}
2904
2905
const libfreehand::FHPath *libfreehand::FHCollector::_findArrowPath(unsigned id)
2906
236k
{
2907
236k
  if (!id)
2908
213k
    return nullptr;
2909
23.3k
  std::map<unsigned, FHPath>::const_iterator iter = m_arrowPaths.find(id);
2910
23.3k
  if (iter != m_arrowPaths.end())
2911
13.7k
    return &(iter->second);
2912
9.60k
  return nullptr;
2913
23.3k
}
2914
2915
const libfreehand::FHBasicLine *libfreehand::FHCollector::_findBasicLine(unsigned id)
2916
140k
{
2917
140k
  if (!id)
2918
4.96k
    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
14.0k
  return nullptr;
2923
135k
}
2924
2925
const libfreehand::FHCustomProc *libfreehand::FHCollector::_findCustomProc(unsigned id)
2926
156k
{
2927
156k
  if (!id)
2928
4.96k
    return nullptr;
2929
151k
  std::map<unsigned, FHCustomProc>::const_iterator iter = m_customProcs.find(id);
2930
151k
  if (iter != m_customProcs.end())
2931
2.21k
    return &(iter->second);
2932
149k
  return nullptr;
2933
151k
}
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.39k
    return &(iter->second);
2942
128k
  return nullptr;
2943
130k
}
2944
2945
const libfreehand::FHRGBColor *libfreehand::FHCollector::_findRGBColor(unsigned id)
2946
366k
{
2947
366k
  if (!id)
2948
4.99k
    return nullptr;
2949
361k
  std::map<unsigned, FHRGBColor>::const_iterator iter = m_rgbColors.find(id);
2950
361k
  if (iter != m_rgbColors.end())
2951
345k
    return &(iter->second);
2952
15.6k
  return nullptr;
2953
361k
}
2954
2955
const libfreehand::FHTintColor *libfreehand::FHCollector::_findTintColor(unsigned id)
2956
20.5k
{
2957
20.5k
  if (!id)
2958
4.99k
    return nullptr;
2959
15.5k
  std::map<unsigned, FHTintColor>::const_iterator iter = m_tints.find(id);
2960
15.5k
  if (iter != m_tints.end())
2961
141
    return &(iter->second);
2962
15.4k
  return nullptr;
2963
15.5k
}
2964
2965
const libfreehand::FHDisplayText *libfreehand::FHCollector::_findDisplayText(unsigned id)
2966
226k
{
2967
226k
  if (!id)
2968
29
    return nullptr;
2969
226k
  std::map<unsigned, FHDisplayText>::const_iterator iter = m_displayTexts.find(id);
2970
226k
  if (iter != m_displayTexts.end())
2971
37.3k
    return &(iter->second);
2972
189k
  return nullptr;
2973
226k
}
2974
2975
const libfreehand::FHImageImport *libfreehand::FHCollector::_findImageImport(unsigned id)
2976
226k
{
2977
226k
  if (!id)
2978
0
    return nullptr;
2979
226k
  std::map<unsigned, FHImageImport>::const_iterator iter = m_images.find(id);
2980
226k
  if (iter != m_images.end())
2981
110
    return &(iter->second);
2982
226k
  return nullptr;
2983
226k
}
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
226k
{
3007
226k
  if (!id)
3008
0
    return nullptr;
3009
226k
  std::map<unsigned, FHSymbolInstance>::const_iterator iter = m_symbolInstances.find(id);
3010
226k
  if (iter != m_symbolInstances.end())
3011
0
    return &(iter->second);
3012
226k
  return nullptr;
3013
226k
}
3014
3015
const libfreehand::FHFilterAttributeHolder *libfreehand::FHCollector::_findFilterAttributeHolder(unsigned id)
3016
13.7k
{
3017
13.7k
  if (!id)
3018
412
    return nullptr;
3019
13.3k
  std::map<unsigned, FHFilterAttributeHolder>::const_iterator iter = m_filterAttributeHolders.find(id);
3020
13.3k
  if (iter != m_filterAttributeHolders.end())
3021
0
    return &(iter->second);
3022
13.3k
  return nullptr;
3023
13.3k
}
3024
3025
const std::vector<libfreehand::FHColorStop> *libfreehand::FHCollector::_findMultiColorList(unsigned id)
3026
3.15k
{
3027
3.15k
  if (!id)
3028
2.98k
    return nullptr;
3029
179
  std::map<unsigned, std::vector<libfreehand::FHColorStop> >::const_iterator iter = m_multiColorLists.find(id);
3030
179
  if (iter != m_multiColorLists.end())
3031
179
    return &(iter->second);
3032
0
  return nullptr;
3033
179
}
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.88k
{
3067
5.88k
  unsigned listId = graphicStyle.m_attrId;
3068
5.88k
  if (!listId)
3069
113
    return 0;
3070
5.76k
  std::map<unsigned, FHList>::const_iterator iter = m_lists.find(listId);
3071
5.76k
  if (iter == m_lists.end())
3072
436
    return 0;
3073
5.33k
  unsigned strokeId = 0;
3074
5.33k
  for (unsigned int element : iter->second.m_elements)
3075
10.0k
  {
3076
10.0k
    unsigned valueId = _findValueFromAttribute(element);
3077
10.0k
    if (_findBasicLine(valueId))
3078
2.96k
      strokeId = valueId;
3079
10.0k
  }
3080
5.33k
  return strokeId;
3081
5.76k
}
3082
3083
unsigned libfreehand::FHCollector::_findFillId(const libfreehand::FHGraphicStyle &graphicStyle)
3084
5.88k
{
3085
5.88k
  unsigned listId = graphicStyle.m_attrId;
3086
5.88k
  if (!listId)
3087
113
    return 0;
3088
5.76k
  std::map<unsigned, FHList>::const_iterator iter = m_lists.find(listId);
3089
5.76k
  if (iter == m_lists.end())
3090
436
    return 0;
3091
5.33k
  unsigned fillId = 0;
3092
5.33k
  for (unsigned int element : iter->second.m_elements)
3093
10.0k
  {
3094
10.0k
    unsigned valueId = _findValueFromAttribute(element);
3095
    // Add other fills if we support them
3096
10.0k
    if (_findBasicFill(valueId) || _findLinearFill(valueId)
3097
8.70k
        || _findLensFill(valueId) || _findRadialFill(valueId)
3098
8.62k
        || _findTileFill(valueId) || _findPatternFill(valueId) || _findCustomProc(valueId))
3099
1.38k
      fillId = valueId;
3100
10.0k
  }
3101
5.33k
  return fillId;
3102
5.76k
}
3103
3104
const libfreehand::FHFilterAttributeHolder *libfreehand::FHCollector::_findFilterAttributeHolder(const libfreehand::FHGraphicStyle &graphicStyle)
3105
7.41k
{
3106
7.41k
  unsigned listId = graphicStyle.m_attrId;
3107
7.41k
  if (!listId)
3108
226
    return nullptr;
3109
7.18k
  std::map<unsigned, FHList>::const_iterator iter = m_lists.find(listId);
3110
7.18k
  if (iter == m_lists.end())
3111
872
    return nullptr;
3112
6.31k
  for (unsigned int element : iter->second.m_elements)
3113
13.7k
  {
3114
13.7k
    const FHFilterAttributeHolder *attributeHolder = _findFilterAttributeHolder(element);
3115
3116
13.7k
    if (attributeHolder)
3117
0
      return attributeHolder;
3118
13.7k
  }
3119
6.31k
  return nullptr;
3120
6.31k
}
3121
3122
3123
unsigned libfreehand::FHCollector::_findValueFromAttribute(unsigned id)
3124
22.3k
{
3125
22.3k
  if (!id)
3126
416
    return 0;
3127
21.8k
  std::map<unsigned, FHAttributeHolder>::const_iterator iter = m_attributeHolders.find(id);
3128
21.8k
  if (iter == m_attributeHolders.end())
3129
10.1k
    return 0;
3130
11.7k
  unsigned value = 0;
3131
11.7k
  if (iter->second.m_parentId)
3132
2.28k
    value = _findValueFromAttribute(iter->second.m_parentId);
3133
11.7k
  if (iter->second.m_attrId)
3134
10.9k
    value = iter->second.m_attrId;
3135
11.7k
  return value;
3136
21.8k
}
3137
3138
librevenge::RVNGBinaryData libfreehand::FHCollector::getImageData(unsigned id)
3139
110
{
3140
110
  std::map<unsigned, FHDataList>::const_iterator iter = m_dataLists.find(id);
3141
110
  librevenge::RVNGBinaryData data;
3142
110
  if (iter == m_dataLists.end())
3143
110
    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
110
}
3152
3153
librevenge::RVNGString libfreehand::FHCollector::getColorString(unsigned id, double tintVal)
3154
365k
{
3155
365k
  FHRGBColor col;
3156
365k
  const FHRGBColor *color = _findRGBColor(id);
3157
365k
  if (color)
3158
345k
    col=*color;
3159
20.0k
  else
3160
20.0k
  {
3161
20.0k
    const FHTintColor *tint = _findTintColor(id);
3162
20.0k
    if (!tint)
3163
19.9k
      return librevenge::RVNGString();
3164
141
    col=getRGBFromTint(*tint);
3165
141
  }
3166
345k
  if (tintVal<=0 || tintVal>=1)
3167
344k
    return _getColorString(col);
3168
3169
1.22k
  FHRGBColor finalColor;
3170
1.22k
  finalColor.m_red = col.m_red * tintVal + (1 - tintVal) * 65536;
3171
1.22k
  finalColor.m_green = col.m_green * tintVal + (1 - tintVal) * 65536;
3172
1.22k
  finalColor.m_blue = col.m_blue * tintVal + (1 - tintVal) * 65536;
3173
1.22k
  return _getColorString(finalColor);
3174
345k
}
3175
3176
libfreehand::FHRGBColor libfreehand::FHCollector::getRGBFromTint(const FHTintColor &tint)
3177
141
{
3178
141
  if (!tint.m_baseColorId)
3179
35
    return FHRGBColor();
3180
106
  const FHRGBColor *rgbColor = _findRGBColor(tint.m_baseColorId);
3181
106
  if (!rgbColor)
3182
64
    return FHRGBColor();
3183
42
  unsigned red = rgbColor->m_red * tint.m_tint + ((65536 - tint.m_tint) << 16);
3184
42
  unsigned green = rgbColor->m_green * tint.m_tint + ((65536 - tint.m_tint) << 16);
3185
42
  unsigned blue = rgbColor->m_blue * tint.m_tint + ((65536 - tint.m_tint) << 16);
3186
42
  FHRGBColor color;
3187
42
  color.m_red = (red >> 16);
3188
42
  color.m_green = (green >> 16);
3189
42
  color.m_blue = (blue >> 16);
3190
42
  return color;
3191
106
}
3192
3193
void libfreehand::FHCollector::_generateBitmapFromPattern(librevenge::RVNGBinaryData &bitmap, unsigned colorId, const std::vector<unsigned char> &pattern)
3194
1.15k
{
3195
1.15k
  unsigned height = 8;
3196
1.15k
  unsigned width = 8;
3197
1.15k
  auto tmpPixelSize = (unsigned)(height * width);
3198
3199
1.15k
  unsigned tmpDIBImageSize = tmpPixelSize * 4;
3200
3201
1.15k
  unsigned tmpDIBOffsetBits = 14 + 40;
3202
1.15k
  unsigned tmpDIBFileSize = tmpDIBOffsetBits + tmpDIBImageSize;
3203
3204
  // Create DIB file header
3205
1.15k
  writeU16(bitmap, 0x4D42);  // Type
3206
1.15k
  writeU32(bitmap, (int)tmpDIBFileSize); // Size
3207
1.15k
  writeU16(bitmap, 0); // Reserved1
3208
1.15k
  writeU16(bitmap, 0); // Reserved2
3209
1.15k
  writeU32(bitmap, (int)tmpDIBOffsetBits); // OffsetBits
3210
3211
  // Create DIB Info header
3212
1.15k
  writeU32(bitmap, 40); // Size
3213
3214
1.15k
  writeU32(bitmap, (int)width);  // Width
3215
1.15k
  writeU32(bitmap, (int)height); // Height
3216
3217
1.15k
  writeU16(bitmap, 1); // Planes
3218
1.15k
  writeU16(bitmap, 32); // BitCount
3219
1.15k
  writeU32(bitmap, 0); // Compression
3220
1.15k
  writeU32(bitmap, (int)tmpDIBImageSize); // SizeImage
3221
1.15k
  writeU32(bitmap, 0); // XPelsPerMeter
3222
1.15k
  writeU32(bitmap, 0); // YPelsPerMeter
3223
1.15k
  writeU32(bitmap, 0); // ColorsUsed
3224
1.15k
  writeU32(bitmap, 0); // ColorsImportant
3225
3226
1.15k
  unsigned foreground = 0x000000; // Initialize to black and override after
3227
1.15k
  unsigned background = 0xffffff; // Initialize to white, since that is Freehand behaviour even if overlapping other colors
3228
3229
1.15k
  const FHRGBColor *color = _findRGBColor(colorId);
3230
1.15k
  if (color)
3231
638
    foreground = ((unsigned)(color->m_red & 0xff00) << 8)|((unsigned)color->m_green & 0xff00)|((unsigned)color->m_blue >> 8);
3232
521
  else
3233
521
  {
3234
521
    const FHTintColor *tintColor = _findTintColor(colorId);
3235
521
    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
521
  }
3241
10.4k
  for (unsigned j = height; j > 0; --j)
3242
9.27k
  {
3243
9.27k
    unsigned char c(pattern[j-1]);
3244
83.4k
    for (unsigned i = 0; i < width; ++i)
3245
74.1k
    {
3246
74.1k
      if (c & 0x80)
3247
18.8k
        writeU32(bitmap, foreground);
3248
55.3k
      else
3249
55.3k
        writeU32(bitmap, background);
3250
74.1k
      c <<= 1;
3251
74.1k
    }
3252
9.27k
  }
3253
1.15k
}
3254
3255
3256
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */