Coverage Report

Created: 2026-10-28 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libabw/src/lib/ABWContentCollector.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 libabw 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
#ifdef HAVE_CONFIG_H
11
#include "config.h"
12
#endif
13
14
#include <cassert>
15
#include <locale>
16
#include <memory>
17
#include <sstream>
18
19
#include <boost/spirit/include/qi.hpp>
20
#include <boost/algorithm/string.hpp>
21
#include <boost/optional.hpp>
22
#include <librevenge/librevenge.h>
23
#include "ABWContentCollector.h"
24
#include "libabw_internal.h"
25
26
445k
#define ABW_EPSILON 1.0E-06
27
103k
#define MAX_LIST_LEVEL 64 // a safeguard against damaged files
28
15.2k
#define MAX_TABLE_ROW (1 << 16) // a safeguard against damaged top-attach
29
1.86k
#define MAX_SECTION_COLUMN 64 // a safeguard against a damaged columns property
30
18.6k
#define MAX_TABLE_COLUMN (1u << 16) // a safeguard against damaged left-attach and right-attach
31
32
using boost::optional;
33
34
namespace libabw
35
{
36
37
namespace
38
{
39
40
static std::string getColor(const std::string &s)
41
598k
{
42
598k
  if (s.empty())
43
562k
    return s;
44
45
36.0k
  if (s[0] == '#')
46
4.29k
  {
47
4.29k
    if (s.length() != 7)
48
4.03k
      return std::string();
49
258
    else
50
258
      return s;
51
4.29k
  }
52
31.7k
  else if (s.length() != 6)
53
13.9k
    return std::string();
54
55
17.7k
  std::string out = ("#");
56
17.7k
  out.append(s);
57
17.7k
  return out;
58
36.0k
}
59
60
static void separateTabsAndInsertText(ABWOutputElements &outputElements, const librevenge::RVNGString &text)
61
182k
{
62
182k
  if (text.empty())
63
3.02k
    return;
64
179k
  librevenge::RVNGString tmpText;
65
179k
  librevenge::RVNGString::Iter i(text);
66
5.66M
  for (i.rewind(); i.next();)
67
5.48M
  {
68
5.48M
    if (*(i()) == '\t')
69
7.15k
    {
70
7.15k
      if (!tmpText.empty())
71
2.43k
      {
72
2.43k
        outputElements.addInsertText(tmpText);
73
2.43k
        tmpText.clear();
74
2.43k
      }
75
7.15k
      outputElements.addInsertTab();
76
7.15k
    }
77
5.48M
    else if (*(i()) == '\n'|| *(i()) == (char)0x0a)
78
116k
    {
79
116k
      if (!tmpText.empty())
80
38.5k
      {
81
38.5k
        outputElements.addInsertText(tmpText);
82
38.5k
        tmpText.clear();
83
38.5k
      }
84
116k
      outputElements.addInsertLineBreak();
85
116k
    }
86
5.36M
    else
87
5.36M
    {
88
5.36M
      tmpText.append(i());
89
5.36M
    }
90
5.48M
  }
91
179k
  if (!tmpText.empty())
92
159k
    outputElements.addInsertText(tmpText);
93
179k
}
94
95
static void separateSpacesAndInsertText(ABWOutputElements &outputElements, const librevenge::RVNGString &text)
96
173k
{
97
173k
  if (text.empty())
98
0
  {
99
0
    outputElements.addInsertText(text);
100
0
    return;
101
0
  }
102
173k
  librevenge::RVNGString tmpText;
103
173k
  int numConsecutiveSpaces = 0;
104
173k
  librevenge::RVNGString::Iter i(text);
105
5.69M
  for (i.rewind(); i.next();)
106
5.52M
  {
107
5.52M
    if (*(i()) == ' ')
108
238k
      numConsecutiveSpaces++;
109
5.28M
    else
110
5.28M
      numConsecutiveSpaces = 0;
111
112
5.52M
    if (numConsecutiveSpaces > 1)
113
35.5k
    {
114
35.5k
      if (!tmpText.empty())
115
9.78k
      {
116
9.78k
        separateTabsAndInsertText(outputElements, tmpText);
117
9.78k
        tmpText.clear();
118
9.78k
      }
119
35.5k
      outputElements.addInsertSpace();
120
35.5k
    }
121
5.48M
    else
122
5.48M
      tmpText.append(i());
123
5.52M
  }
124
173k
  separateTabsAndInsertText(outputElements, tmpText);
125
173k
}
126
127
void parseTableColumns(const std::string &str, librevenge::RVNGPropertyListVector &columns)
128
18.5k
{
129
18.5k
  if (str.empty())
130
17.3k
    return;
131
132
1.25k
  std::string propString(boost::trim_copy_if(str, boost::is_any_of("/ ")));
133
1.25k
  std::vector<std::string> strVec;
134
135
1.25k
  boost::algorithm::split(strVec, propString, boost::is_any_of("/"), boost::token_compress_on);
136
1.25k
  for (auto &i : strVec)
137
551k
  {
138
551k
    ABWUnit unit(ABW_NONE);
139
551k
    double value(0.0);
140
551k
    boost::algorithm::trim(i);
141
551k
    if (findDouble(i, value, unit) || ABW_IN != unit)
142
551k
    {
143
551k
      librevenge::RVNGPropertyList propList;
144
551k
      propList.insert("style:column-width", value);
145
551k
      columns.append(propList);
146
551k
    }
147
551k
  }
148
1.25k
}
149
150
bool parseTabStop(const std::string &str, librevenge::RVNGPropertyList &tabStop)
151
108k
{
152
108k
  if (str.empty())
153
223
    return false;
154
108k
  std::string sTabStop(boost::trim_copy_if(str, boost::is_any_of("/ ")));
155
108k
  std::vector<std::string> strVec;
156
108k
  boost::algorithm::split(strVec, sTabStop, boost::is_any_of("/"), boost::token_compress_on);
157
108k
  if (strVec.size() < 2)
158
40.9k
    return false;
159
67.3k
  boost::algorithm::trim(strVec[0]);
160
67.3k
  ABWUnit unit(ABW_NONE);
161
67.3k
  double value(0.0);
162
67.3k
  if (!findDouble(strVec[0], value, unit) || ABW_IN != unit)
163
38.0k
    return false;
164
29.3k
  tabStop.insert("style:position", value);
165
166
29.3k
  boost::algorithm::trim(strVec[1]);
167
29.3k
  if (!strVec[1].empty())
168
29.0k
  {
169
29.0k
    switch (strVec[1][0])
170
29.0k
    {
171
1.00k
    case 'L':
172
1.00k
      tabStop.insert("style:type", "left");
173
1.00k
      break;
174
760
    case 'C':
175
760
      tabStop.insert("style:type", "center");
176
760
      break;
177
21.9k
    case 'D':
178
21.9k
      tabStop.insert("style:type", "char");
179
21.9k
      break;
180
251
    case 'R':
181
251
      tabStop.insert("style:type", "right");
182
251
      break;
183
5.08k
    default:
184
5.08k
      tabStop.insert("style:type", "left");
185
5.08k
      break;
186
29.0k
    }
187
29.0k
  }
188
189
29.3k
  if (strVec[1].size() > 1)
190
28.4k
  {
191
28.4k
    switch (strVec[1][1])
192
28.4k
    {
193
10.0k
    case '3':
194
10.0k
      tabStop.insert("style:leader-text", "_");
195
10.0k
      break;
196
2.29k
    case '2':
197
2.29k
      tabStop.insert("style:leader-text", "-");
198
2.29k
      break;
199
3.53k
    case '1':
200
3.53k
      tabStop.insert("style:leader-text", ".");
201
3.53k
      break;
202
12.5k
    default:
203
12.5k
      break;
204
28.4k
    }
205
28.4k
  }
206
207
29.3k
  return true;
208
29.3k
}
209
210
void parseTabStops(const std::string &str, librevenge::RVNGPropertyListVector &tabStops)
211
152k
{
212
152k
  if (str.empty())
213
147k
    return;
214
4.16k
  std::string sTabStops(boost::trim_copy_if(str, boost::is_any_of(", ")));
215
4.16k
  std::vector<std::string> strVec;
216
4.16k
  boost::algorithm::split(strVec, sTabStops, boost::is_any_of(","), boost::token_compress_on);
217
4.16k
  for (auto &i : strVec)
218
108k
  {
219
108k
    boost::trim(i);
220
108k
    librevenge::RVNGPropertyList tabStop;
221
108k
    if (parseTabStop(i, tabStop))
222
29.3k
      tabStops.append(tabStop);
223
108k
  }
224
4.16k
}
225
226
void parseLang(const std::string &langStr, optional<std::string> &lang, optional<std::string> &country, optional<std::string> &script)
227
53.9k
{
228
53.9k
  std::vector<std::string> tags;
229
53.9k
  tags.reserve(3);
230
53.9k
  boost::split(tags, langStr, boost::is_any_of("-_"), boost::token_compress_off);
231
232
53.9k
  if ((tags.size() > 0) && boost::all(tags[0], boost::is_lower()) && (2 <= tags[0].size()) && (3 >= tags[0].size()))
233
45.1k
  {
234
45.1k
    lang = tags[0];
235
236
45.1k
    if (tags.size() > 1)
237
44.8k
    {
238
44.8k
      if (boost::all(tags[1], boost::is_upper()) && (2 == tags[1].size()))
239
10.9k
        country = tags[1];
240
33.8k
      else
241
33.8k
        script = tags[1];
242
44.8k
    }
243
244
45.1k
    if ((tags.size() > 2) && bool(script))
245
14.8k
    {
246
14.8k
      if (boost::all(tags[2], boost::is_upper()) && (2 == tags[2].size()))
247
1.72k
        country = tags[2];
248
14.8k
    }
249
45.1k
  }
250
53.9k
}
251
252
static std::string decodeUrl(const std::string &str)
253
8.40k
{
254
8.40k
  using namespace boost::spirit::qi;
255
256
8.40k
  if (str.empty())
257
70
    return str;
258
259
  // look for a hexadecimal number of 2 digits
260
8.33k
  uint_parser<char,16,2,2> urlhex;
261
8.33k
  std::string decoded_string;
262
8.33k
  auto it = str.cbegin();
263
8.33k
  if (parse(it, str.cend(),
264
8.33k
            +(
265
8.33k
              (lit('%') >> (char_('%') | urlhex))
266
8.33k
              | (!lit('%') >> char_)
267
8.33k
            ),
268
8.33k
            decoded_string)
269
7.99k
      && it == str.cend())
270
1.51k
    return decoded_string;
271
272
6.81k
  return str;
273
8.33k
}
274
275
std::string findProperty(const ABWPropertyMap &propMap, const char *const name)
276
8.84M
{
277
8.84M
  if (!name)
278
0
    return std::string();
279
8.84M
  auto iter = propMap.find(name);
280
8.84M
  if (iter != propMap.end())
281
345k
    return iter->second;
282
8.49M
  return std::string();
283
8.84M
}
284
285
} // anonymous namespace
286
287
} // namespace libabw
288
289
libabw::ABWContentTableState::ABWContentTableState() :
290
18.5k
  m_currentTableProperties(),
291
18.5k
  m_currentCellProperties(),
292
293
18.5k
  m_currentTableCol(-1),
294
18.5k
  m_currentTableRow(-1),
295
18.5k
  m_currentTableCellNumberInRow(-1),
296
18.5k
  m_currentTableId(-1),
297
18.5k
  m_isTableRowOpened(false),
298
18.5k
  m_isTableColumnOpened(false),
299
18.5k
  m_isTableCellOpened(false),
300
18.5k
  m_isCellWithoutParagraph(false),
301
18.5k
  m_isRowWithoutCell(false)
302
18.5k
{
303
18.5k
}
304
305
libabw::ABWContentTableState::ABWContentTableState(const ABWContentTableState &ts) :
306
18.5k
  m_currentTableProperties(ts.m_currentTableProperties),
307
18.5k
  m_currentCellProperties(ts.m_currentCellProperties),
308
309
18.5k
  m_currentTableCol(ts.m_currentTableCol),
310
18.5k
  m_currentTableRow(ts.m_currentTableRow),
311
18.5k
  m_currentTableCellNumberInRow(ts.m_currentTableCellNumberInRow),
312
18.5k
  m_currentTableId(ts.m_currentTableId),
313
18.5k
  m_isTableRowOpened(ts.m_isTableRowOpened),
314
18.5k
  m_isTableColumnOpened(ts.m_isTableColumnOpened),
315
18.5k
  m_isTableCellOpened(ts.m_isTableCellOpened),
316
18.5k
  m_isCellWithoutParagraph(ts.m_isCellWithoutParagraph),
317
18.5k
  m_isRowWithoutCell(ts.m_isRowWithoutCell)
318
18.5k
{
319
18.5k
}
320
321
libabw::ABWContentTableState::~ABWContentTableState()
322
37.1k
{
323
37.1k
}
324
325
libabw::ABWContentParsingState::ABWContentParsingState() :
326
42.0k
  m_isDocumentStarted(false),
327
42.0k
  m_isPageSpanOpened(false),
328
42.0k
  m_isSectionOpened(false),
329
42.0k
  m_isHeaderOpened(false),
330
42.0k
  m_isFooterOpened(false),
331
332
42.0k
  m_isPageFrame(false),
333
334
42.0k
  m_isSpanOpened(false),
335
42.0k
  m_isParagraphOpened(false),
336
42.0k
  m_isListElementOpened(false),
337
42.0k
  m_inParagraphOrListElement(false),
338
339
42.0k
  m_currentSectionStyle(),
340
42.0k
  m_currentParagraphStyle(),
341
42.0k
  m_currentCharacterStyle(),
342
343
42.0k
  m_pageWidth(0.0),
344
42.0k
  m_pageHeight(0.0),
345
42.0k
  m_pageMarginTop(0.0),
346
42.0k
  m_pageMarginBottom(0.0),
347
42.0k
  m_pageMarginLeft(0.0),
348
42.0k
  m_pageMarginRight(0.0),
349
42.0k
  m_footerId(-1),
350
42.0k
  m_footerLeftId(-1),
351
42.0k
  m_footerFirstId(-1),
352
42.0k
  m_footerLastId(-1),
353
42.0k
  m_headerId(-1),
354
42.0k
  m_headerLeftId(-1),
355
42.0k
  m_headerFirstId(-1),
356
42.0k
  m_headerLastId(-1),
357
42.0k
  m_currentHeaderFooterId(-1),
358
42.0k
  m_currentHeaderFooterOccurrence(),
359
42.0k
  m_parsingContext(ABW_SECTION),
360
361
42.0k
  m_deferredPageBreak(false),
362
42.0k
  m_deferredColumnBreak(false),
363
364
42.0k
  m_isNote(false),
365
42.0k
  m_currentListLevel(0),
366
42.0k
  m_currentListId(0),
367
42.0k
  m_isFirstTextInListElement(false),
368
369
42.0k
  m_tableStates(),
370
42.0k
  m_listLevels()
371
42.0k
{
372
42.0k
}
373
374
libabw::ABWContentParsingState::ABWContentParsingState(const ABWContentParsingState &ps) :
375
0
  m_isDocumentStarted(ps.m_isDocumentStarted),
376
0
  m_isPageSpanOpened(ps.m_isPageSpanOpened),
377
0
  m_isSectionOpened(ps.m_isSectionOpened),
378
0
  m_isHeaderOpened(ps.m_isHeaderOpened),
379
0
  m_isFooterOpened(ps.m_isFooterOpened),
380
381
0
  m_isPageFrame(ps.m_isPageFrame),
382
383
0
  m_isSpanOpened(ps.m_isSpanOpened),
384
0
  m_isParagraphOpened(ps.m_isParagraphOpened),
385
0
  m_isListElementOpened(ps.m_isListElementOpened),
386
0
  m_inParagraphOrListElement(ps.m_inParagraphOrListElement),
387
388
0
  m_currentSectionStyle(ps.m_currentSectionStyle),
389
0
  m_currentParagraphStyle(ps.m_currentParagraphStyle),
390
0
  m_currentCharacterStyle(ps.m_currentCharacterStyle),
391
392
0
  m_pageWidth(ps.m_pageWidth),
393
0
  m_pageHeight(ps.m_pageHeight),
394
0
  m_pageMarginTop(ps.m_pageMarginTop),
395
0
  m_pageMarginBottom(ps.m_pageMarginBottom),
396
0
  m_pageMarginLeft(ps.m_pageMarginLeft),
397
0
  m_pageMarginRight(ps.m_pageMarginRight),
398
0
  m_footerId(ps.m_footerId),
399
0
  m_footerLeftId(ps.m_footerLeftId),
400
0
  m_footerFirstId(ps.m_footerFirstId),
401
0
  m_footerLastId(ps.m_footerLastId),
402
0
  m_headerId(ps.m_headerId),
403
0
  m_headerLeftId(ps.m_headerLeftId),
404
0
  m_headerFirstId(ps.m_headerFirstId),
405
0
  m_headerLastId(ps.m_headerLastId),
406
0
  m_currentHeaderFooterId(ps.m_currentHeaderFooterId),
407
0
  m_currentHeaderFooterOccurrence(ps.m_currentHeaderFooterOccurrence),
408
0
  m_parsingContext(ps.m_parsingContext),
409
410
0
  m_deferredPageBreak(ps.m_deferredPageBreak),
411
0
  m_deferredColumnBreak(ps.m_deferredColumnBreak),
412
413
0
  m_isNote(ps.m_isNote),
414
0
  m_currentListLevel(ps.m_currentListLevel),
415
0
  m_currentListId(ps.m_currentListId),
416
0
  m_isFirstTextInListElement(ps.m_isFirstTextInListElement),
417
418
0
  m_tableStates(ps.m_tableStates),
419
0
  m_listLevels(ps.m_listLevels)
420
0
{
421
0
}
422
423
libabw::ABWContentParsingState::~ABWContentParsingState()
424
42.0k
{
425
42.0k
}
426
427
libabw::ABWContentCollector::ABWContentCollector(librevenge::RVNGTextInterface *iface, const std::map<int, int> &tableSizes,
428
                                                 const std::map<std::string, ABWData> &data,
429
                                                 const std::map<int, std::shared_ptr<ABWListElement>> &listElements) :
430
36.5k
  m_ps(new ABWContentParsingState),
431
36.5k
  m_iface(iface),
432
36.5k
  m_parsingStates(),
433
36.5k
  m_dontLoop(),
434
36.5k
  m_textStyles(),
435
36.5k
  m_documentStyle(),
436
36.5k
  m_metadata(),
437
36.5k
  m_data(data),
438
36.5k
  m_tableSizes(tableSizes),
439
36.5k
  m_tableCounter(0),
440
36.5k
  m_outputElements(),
441
36.5k
  m_pageOutputElements(),
442
36.5k
  m_listElements(listElements),
443
36.5k
  m_dummyListElements()
444
36.5k
{
445
36.5k
}
446
447
libabw::ABWContentCollector::~ABWContentCollector()
448
36.5k
{
449
36.5k
}
450
451
void libabw::ABWContentCollector::collectTextStyle(const char *name, const char *basedon, const char *followedby, const char *props)
452
12.8k
{
453
12.8k
  ABWStyle style;
454
12.8k
  style.basedon = basedon ? basedon : std::string();
455
12.8k
  style.followedby = followedby ? followedby : std::string();
456
12.8k
  if (props)
457
6.61k
    parsePropString(props, style.properties);
458
12.8k
  if (name)
459
12.2k
    m_textStyles[name] = style;
460
12.8k
}
461
462
void libabw::ABWContentCollector::_recurseTextProperties(const char *name, ABWPropertyMap &styleProps)
463
106k
{
464
106k
  if (name)
465
106k
  {
466
106k
    m_dontLoop.insert(name);
467
106k
    std::map<std::string, ABWStyle>::const_iterator iter = m_textStyles.find(name);
468
106k
    if (iter != m_textStyles.end() && !(iter->second.basedon.empty()) && !m_dontLoop.count(iter->second.basedon))
469
5.44k
      _recurseTextProperties(iter->second.basedon.c_str(), styleProps);
470
106k
    if (iter != m_textStyles.end())
471
19.6k
    {
472
19.6k
      for (const auto &propertie : iter->second.properties)
473
235k
        styleProps[propertie.first] = propertie.second;
474
19.6k
    }
475
476
    // Styles based on "Heading X" style are recognized as headings.
477
106k
    if (boost::starts_with(name, "Heading "))
478
6.16k
    {
479
6.16k
      int level = 0;
480
6.16k
      const std::string levelStr = std::string(name).substr(8);
481
6.16k
      if (findInt(levelStr, level))
482
4.03k
      {
483
        // Abiword only has 4 levels of headings, but allow some more
484
4.03k
        if ((0 < level) && (10 > level))
485
2.84k
          styleProps["libabw:outline-level"] = levelStr;
486
4.03k
      }
487
6.16k
    }
488
106k
  }
489
106k
  if (!m_dontLoop.empty())
490
101k
    m_dontLoop.clear();
491
106k
}
492
493
std::string libabw::ABWContentCollector::_findDocumentProperty(const char *const name)
494
289k
{
495
289k
  return findProperty(m_documentStyle, name);
496
289k
}
497
498
std::string libabw::ABWContentCollector::_findParagraphProperty(const char *name)
499
1.76M
{
500
1.76M
  return findProperty(m_ps->m_currentParagraphStyle, name);
501
1.76M
}
502
503
std::string libabw::ABWContentCollector::_findTableProperty(const char *name)
504
37.1k
{
505
37.1k
  assert(!m_ps->m_tableStates.empty());
506
37.1k
  return findProperty(m_ps->m_tableStates.top().m_currentTableProperties, name);
507
37.1k
}
508
509
std::string libabw::ABWContentCollector::_findCellProperty(const char *name)
510
97.3k
{
511
97.3k
  assert(!m_ps->m_tableStates.empty());
512
97.3k
  return findProperty(m_ps->m_tableStates.top().m_currentCellProperties, name);
513
97.3k
}
514
515
std::string libabw::ABWContentCollector::_findSectionProperty(const char *name)
516
218k
{
517
218k
  return findProperty(m_ps->m_currentSectionStyle, name);
518
218k
}
519
520
std::string libabw::ABWContentCollector::_findCharacterProperty(const char *name)
521
3.17M
{
522
3.17M
  std::string prop = findProperty(m_ps->m_currentCharacterStyle, name);
523
3.17M
  if (prop.empty())
524
3.13M
    prop = findProperty(m_ps->m_currentParagraphStyle, name);
525
3.17M
  return prop;
526
3.17M
}
527
528
std::string libabw::ABWContentCollector::_findMetadataEntry(const char *const name)
529
128k
{
530
128k
  return findProperty(m_metadata, name);
531
128k
}
532
533
void libabw::ABWContentCollector::collectDocumentProperties(const char *const props)
534
10.0k
{
535
10.0k
  if (props)
536
1.41k
    parsePropString(props, m_documentStyle);
537
10.0k
}
538
539
void libabw::ABWContentCollector::_addBorderProperties(const std::map<std::string, std::string> &map, librevenge::RVNGPropertyList &propList, const std::string &defaultUndefBorderProp)
540
164k
{
541
164k
  int setBorders=0;
542
164k
  static char const *odtWh[4]= {"fo:border-left", "fo:border-right", "fo:border-top", "fo:border-bottom"};
543
822k
  for (int i=0, depl=1; i<4; ++i, depl*=2)
544
657k
  {
545
657k
    static char const *wh[4]= {"left", "right", "top", "bot"};
546
657k
    std::string whString(wh[i]);
547
657k
    std::string tmp=whString+"-color";
548
657k
    if (map.find(tmp)==map.end()) continue;
549
9.54k
    std::string color=getColor(map.find(tmp)->second);
550
9.54k
    if (color.empty())
551
2.08k
      continue;
552
7.45k
    int style;
553
7.45k
    tmp=whString+"-style";
554
7.45k
    if (map.find(tmp)==map.end() || !findInt(map.find(tmp)->second, style))
555
5.04k
      style=1;
556
2.41k
    else if (style<=0 || style>=4)
557
1.16k
    {
558
1.16k
      if (style==0) setBorders |= depl;
559
1.16k
      continue;
560
1.16k
    }
561
6.29k
    ABWUnit unit(ABW_NONE);
562
6.29k
    double width(0.0);
563
6.29k
    tmp=whString+"-thickness";
564
6.29k
    if (map.find(tmp)==map.end() || !findDouble(map.find(tmp)->second, width, unit))
565
4.95k
      width=0.01;
566
1.34k
    else if (width<=0 || unit != ABW_IN)
567
367
      continue;
568
5.92k
    std::stringstream s;
569
5.92k
    s.imbue(std::locale("C")); // be sure that we use standart double
570
5.92k
    s << width << "in ";
571
5.92k
    if (style==2) s << "dotted ";
572
5.82k
    else if (style==3) s << "dashed ";
573
5.80k
    else s << "solid ";
574
5.92k
    s << color;
575
5.92k
    propList.insert(odtWh[i], s.str().c_str());
576
5.92k
    setBorders|=depl;
577
5.92k
  }
578
164k
  if (defaultUndefBorderProp.empty()) return;
579
62.4k
  for (int i=0, depl=1; i<4; ++i, depl*=2)
580
49.9k
  {
581
49.9k
    if (setBorders&depl) continue;
582
46.9k
    propList.insert(odtWh[i], defaultUndefBorderProp.c_str());
583
46.9k
  }
584
12.4k
}
585
586
void libabw::ABWContentCollector::collectParagraphProperties(const char *level, const char *listid, const char * /*parentid*/, const char *style, const char *props)
587
100k
{
588
100k
  _closeBlock();
589
100k
  if (!level || !findInt(level, m_ps->m_currentListLevel) || m_ps->m_currentListLevel < 1)
590
87.7k
    m_ps->m_currentListLevel = 0;
591
100k
  if (m_ps->m_currentListLevel > MAX_LIST_LEVEL)
592
3.01k
    m_ps->m_currentListLevel = MAX_LIST_LEVEL;
593
100k
  if (!listid || !findInt(listid, m_ps->m_currentListId) || m_ps->m_currentListId < 0)
594
97.7k
    m_ps->m_currentListId = 0;
595
596
100k
  m_ps->m_currentParagraphStyle.clear();
597
100k
  if (style)
598
20.0k
    _recurseTextProperties(style, m_ps->m_currentParagraphStyle);
599
80.4k
  else
600
80.4k
    _recurseTextProperties("Normal", m_ps->m_currentParagraphStyle);
601
602
100k
  ABWPropertyMap tmpProps;
603
100k
  if (props)
604
18.0k
    parsePropString(props, tmpProps);
605
141k
  for (ABWPropertyMap::const_iterator iter = tmpProps.begin(); iter != tmpProps.end(); ++iter)
606
41.0k
    m_ps->m_currentParagraphStyle[iter->first] = iter->second;
607
100k
  m_ps->m_inParagraphOrListElement = true;
608
100k
}
609
610
void libabw::ABWContentCollector::collectCharacterProperties(const char *style, const char *props)
611
22.6k
{
612
  // We started a new span without closing the last one. That can actually happen, because <p>
613
  // allows mixed content, so there can be text spans not enclosed in <c>.
614
22.6k
  if (m_ps->m_isSpanOpened)
615
2.72k
    _closeSpan();
616
617
22.6k
  m_ps->m_currentCharacterStyle.clear();
618
22.6k
  if (style)
619
1.02k
    _recurseTextProperties(style, m_ps->m_currentCharacterStyle);
620
621
22.6k
  ABWPropertyMap tmpProps;
622
22.6k
  if (props)
623
9.91k
    parsePropString(props, tmpProps);
624
54.2k
  for (ABWPropertyMap::const_iterator iter = tmpProps.begin(); iter != tmpProps.end(); ++iter)
625
31.5k
    m_ps->m_currentCharacterStyle[iter->first] = iter->second;
626
22.6k
}
627
628
void libabw::ABWContentCollector::collectSectionProperties(const char *footer, const char *footerLeft, const char *footerFirst, const char *footerLast,
629
                                                           const char *header, const char *headerLeft, const char *headerFirst, const char *headerLast,
630
                                                           const char *props)
631
37.2k
{
632
37.2k
  _closeHeader();
633
37.2k
  _closeFooter();
634
37.2k
  _closeSection();
635
37.2k
  double pageMarginLeft = m_ps->m_pageMarginLeft;
636
37.2k
  double pageMarginRight = m_ps->m_pageMarginRight;
637
37.2k
  double pageMarginTop = m_ps->m_pageMarginTop;
638
37.2k
  double pageMarginBottom = m_ps->m_pageMarginBottom;
639
37.2k
  int headerId = m_ps->m_headerId;
640
37.2k
  int headerLeftId = m_ps->m_headerLeftId;
641
37.2k
  int headerFirstId = m_ps->m_headerFirstId;
642
37.2k
  int headerLastId = m_ps->m_headerLastId;
643
37.2k
  int footerId = m_ps->m_footerId;
644
37.2k
  int footerLeftId = m_ps->m_footerLeftId;
645
37.2k
  int footerFirstId = m_ps->m_footerFirstId;
646
37.2k
  int footerLastId = m_ps->m_footerLastId;
647
648
37.2k
  m_ps->m_currentSectionStyle.clear();
649
37.2k
  ABWPropertyMap tmpProps;
650
37.2k
  if (props)
651
10.2k
    parsePropString(props, tmpProps);
652
653
37.2k
  ABWUnit unit(ABW_NONE);
654
37.2k
  double value(0.0);
655
62.0k
  for (ABWPropertyMap::const_iterator iter = tmpProps.begin(); iter != tmpProps.end(); ++iter)
656
24.8k
  {
657
24.8k
    if (iter->first == "page-margin-right" && !iter->second.empty() && fabs(m_ps->m_pageMarginRight) < ABW_EPSILON)
658
113
    {
659
113
      if (findDouble(iter->second, value, unit) && unit == ABW_IN && value > 0.0 && fabs(value) > ABW_EPSILON)
660
88
        m_ps->m_pageMarginRight = value;
661
113
    }
662
24.7k
    else if (iter->first == "page-margin-left" && !iter->second.empty() && fabs(m_ps->m_pageMarginLeft) < ABW_EPSILON)
663
152
    {
664
152
      if (findDouble(iter->second, value, unit) && unit == ABW_IN && value > 0.0 && fabs(value) > ABW_EPSILON)
665
105
        m_ps->m_pageMarginLeft = value;
666
152
    }
667
24.5k
    else if (iter->first == "page-margin-top" && !iter->second.empty() && fabs(m_ps->m_pageMarginTop) < ABW_EPSILON)
668
89
    {
669
89
      if (findDouble(iter->second, value, unit) && unit == ABW_IN && value > 0.0 && fabs(value) > ABW_EPSILON)
670
69
        m_ps->m_pageMarginTop = value;
671
89
    }
672
24.4k
    else if (iter->first == "page-margin-bottom" && !iter->second.empty() && fabs(m_ps->m_pageMarginBottom) < ABW_EPSILON)
673
105
    {
674
105
      if (findDouble(iter->second, value, unit) && unit == ABW_IN && value > 0.0 && fabs(value) > ABW_EPSILON)
675
73
        m_ps->m_pageMarginBottom = value;
676
105
    }
677
24.8k
    m_ps->m_currentSectionStyle[iter->first] = iter->second;
678
24.8k
  }
679
680
37.2k
  int intValue(0);
681
37.2k
  if (footer && findInt(footer, intValue) && intValue >= 0)
682
5.86k
    m_ps->m_footerId = intValue;
683
31.3k
  else
684
31.3k
    m_ps->m_footerId = -1;
685
686
37.2k
  if (footerLeft && findInt(footerLeft, intValue) && intValue >= 0)
687
446
    m_ps->m_footerLeftId = intValue;
688
36.8k
  else
689
36.8k
    m_ps->m_footerLeftId = -1;
690
691
37.2k
  if (footerFirst && findInt(footerFirst, intValue) && intValue >= 0)
692
2.37k
    m_ps->m_footerFirstId = intValue;
693
34.8k
  else
694
34.8k
    m_ps->m_footerFirstId = -1;
695
696
37.2k
  if (footerLast && findInt(footerLast, intValue) && intValue >= 0)
697
299
    m_ps->m_footerLastId = intValue;
698
36.9k
  else
699
36.9k
    m_ps->m_footerLastId = -1;
700
701
37.2k
  if (header && findInt(header, intValue) && intValue >= 0)
702
1.65k
    m_ps->m_headerId = intValue;
703
35.6k
  else
704
35.6k
    m_ps->m_headerId = -1;
705
706
37.2k
  if (headerLeft && findInt(headerLeft, intValue) && intValue >= 0)
707
921
    m_ps->m_headerLeftId = intValue;
708
36.3k
  else
709
36.3k
    m_ps->m_headerLeftId = -1;
710
711
37.2k
  if (headerFirst && findInt(headerFirst, intValue) && intValue >= 0)
712
1.19k
    m_ps->m_headerFirstId = intValue;
713
36.0k
  else
714
36.0k
    m_ps->m_headerFirstId = -1;
715
716
37.2k
  if (headerLast && findInt(headerLast, intValue) && intValue >= 0)
717
255
    m_ps->m_headerLastId = intValue;
718
37.0k
  else
719
37.0k
    m_ps->m_headerLastId = -1;
720
721
37.2k
  if (fabs(m_ps->m_pageMarginRight) < ABW_EPSILON)
722
2.76k
    m_ps->m_pageMarginRight = 1.0;
723
37.2k
  if (fabs(m_ps->m_pageMarginLeft) < ABW_EPSILON)
724
2.74k
    m_ps->m_pageMarginLeft = 1.0;
725
37.2k
  if (fabs(m_ps->m_pageMarginTop) < ABW_EPSILON)
726
2.78k
    m_ps->m_pageMarginTop = 1.0;
727
37.2k
  if (fabs(m_ps->m_pageMarginBottom) < ABW_EPSILON)
728
2.77k
    m_ps->m_pageMarginBottom = 1.0;
729
730
37.2k
  if (fabs(pageMarginLeft-m_ps->m_pageMarginLeft) > ABW_EPSILON ||
731
34.4k
      fabs(pageMarginRight-m_ps->m_pageMarginRight) > ABW_EPSILON ||
732
34.4k
      fabs(pageMarginTop-m_ps->m_pageMarginTop) > ABW_EPSILON ||
733
34.4k
      fabs(pageMarginBottom-m_ps->m_pageMarginBottom) > ABW_EPSILON ||
734
34.4k
      footerId != m_ps->m_footerId || footerLeftId != m_ps->m_footerLeftId ||
735
30.7k
      footerFirstId != m_ps->m_footerFirstId || footerLastId != m_ps->m_footerLastId ||
736
28.6k
      headerId != m_ps->m_headerId || headerLeftId != m_ps->m_headerLeftId ||
737
25.4k
      headerFirstId != m_ps->m_headerFirstId || headerLastId != m_ps->m_headerLastId)
738
13.5k
  {
739
13.5k
    _closePageSpan();
740
13.5k
  }
741
37.2k
}
742
743
void libabw::ABWContentCollector::collectHeaderFooter(const char *id, const char *type)
744
8.26k
{
745
8.26k
  if (!id || !findInt(id, m_ps->m_currentHeaderFooterId))
746
5.10k
    m_ps->m_currentHeaderFooterId = -1;
747
748
8.26k
  if (!type)
749
0
    m_ps->m_currentHeaderFooterId = -1;
750
751
8.26k
  std::string sType(type ? type : "");
752
8.26k
  boost::trim(sType);
753
8.26k
  std::vector<std::string> strVec;
754
8.26k
  boost::algorithm::split(strVec, sType, boost::is_any_of("-"), boost::token_compress_on);
755
8.26k
  if (strVec.size() >= 2)
756
1.57k
    m_ps->m_currentHeaderFooterOccurrence = strVec[1].c_str();
757
6.69k
  else
758
6.69k
    m_ps->m_currentHeaderFooterOccurrence = "all";
759
8.26k
  if (!strVec.empty())
760
8.26k
  {
761
8.26k
    if (strVec[0] == "header")
762
2.68k
      m_ps->m_parsingContext = ABW_HEADER;
763
5.57k
    else if (strVec[0] == "footer")
764
3.18k
      m_ps->m_parsingContext = ABW_FOOTER;
765
2.38k
    else
766
2.38k
      m_ps->m_parsingContext = ABW_SECTION;
767
8.26k
  }
768
0
  else
769
0
    m_ps->m_parsingContext = ABW_SECTION;
770
8.26k
}
771
772
void libabw::ABWContentCollector::collectPageSize(const char *width, const char *height, const char *units, const char * /* pageScale */)
773
1.70k
{
774
1.70k
  std::string widthStr(width ? width : "");
775
1.70k
  std::string heightStr(height ? height : "");
776
1.70k
  if (units)
777
837
  {
778
837
    widthStr.append(units);
779
837
    heightStr.append(units);
780
837
  }
781
1.70k
  ABWUnit unit;
782
1.70k
  double value;
783
1.70k
  if (findDouble(widthStr, value, unit) && unit == ABW_IN)
784
312
    m_ps->m_pageWidth = value;
785
1.70k
  if (findDouble(heightStr, value, unit) && unit == ABW_IN)
786
261
    m_ps->m_pageHeight = value;
787
1.70k
}
788
789
void libabw::ABWContentCollector::startDocument()
790
16.1k
{
791
16.1k
  if (!m_ps->m_isNote && m_ps->m_tableStates.empty())
792
16.1k
  {
793
794
16.1k
    if (m_iface && !m_ps->m_isDocumentStarted)
795
16.1k
    {
796
16.1k
      m_iface->startDocument(librevenge::RVNGPropertyList());
797
16.1k
      _setMetadata();
798
16.1k
    }
799
800
16.1k
    m_ps->m_isDocumentStarted = true;
801
16.1k
  }
802
16.1k
}
803
804
void libabw::ABWContentCollector::endDocument()
805
13.1k
{
806
13.1k
  if (!m_ps->m_isNote)
807
13.0k
  {
808
13.0k
    if (!m_ps->m_isPageSpanOpened)
809
1.17k
      _openPageSpan();
810
811
13.0k
    _closeBlock();
812
813
13.0k
    m_ps->m_currentListLevel = 0;
814
13.0k
    _changeList(); // flush the list
815
816
    // close the document nice and tight
817
13.0k
    _closeSection();
818
13.0k
    _closeHeader();
819
13.0k
    _closeFooter();
820
821
13.0k
    _closePageSpan();
822
823
13.0k
    if (m_iface)
824
13.0k
    {
825
13.0k
      m_pageOutputElements.write(m_iface);
826
13.0k
      m_outputElements.write(m_iface);
827
13.0k
      m_iface->endDocument();
828
13.0k
    }
829
13.0k
  }
830
13.1k
}
831
832
void libabw::ABWContentCollector::_setMetadata()
833
16.1k
{
834
16.1k
  librevenge::RVNGPropertyList propList;
835
836
16.1k
  const std::string dcKeys[] = { "language", "publisher", "source", "subject", "title", "type" };
837
838
112k
  for (std::size_t i = 0; i != ABW_NUM_ELEMENTS(dcKeys); ++i)
839
96.6k
  {
840
96.6k
    const std::string abwKey = "dc." + dcKeys[i];
841
96.6k
    const std::string rvngKey = "dc:" + dcKeys[i];
842
96.6k
    const std::string prop = _findMetadataEntry(abwKey.c_str());
843
96.6k
    if (!prop.empty())
844
71
      propList.insert(rvngKey.c_str(), prop.c_str());
845
96.6k
  }
846
847
16.1k
  std::string prop = _findMetadataEntry("abiword.keywords");
848
16.1k
  if (!prop.empty())
849
13
    propList.insert("meta:keyword", prop.c_str());
850
851
16.1k
  prop = _findMetadataEntry("dc.creator");
852
16.1k
  if (!prop.empty())
853
35
    propList.insert("meta:initial-creator", prop.c_str());
854
855
16.1k
#ifdef VERSION
856
16.1k
  const std::string version(VERSION);
857
#else
858
  const std::string version("unknown");
859
#endif
860
16.1k
  std::string generator = "libabw/" + version;
861
16.1k
  propList.insert("meta:generator", generator.c_str());
862
16.1k
  if (m_iface)
863
16.1k
    m_iface->setDocumentMetaData(propList);
864
16.1k
}
865
866
void libabw::ABWContentCollector::endSection()
867
45.5k
{
868
45.5k
  m_ps->m_currentListLevel = 0;
869
45.5k
  _changeList(); // flush the list exterior
870
45.5k
  _closeHeader();
871
45.5k
  _closeFooter();
872
45.5k
  _closeSection();
873
45.5k
}
874
875
void libabw::ABWContentCollector::closeParagraphOrListElement()
876
100k
{
877
  // we have an empty paragraph, insert it
878
100k
  if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
879
72.1k
    _openSpan();
880
100k
  _closeBlock();
881
100k
  m_ps->m_currentParagraphStyle.clear();
882
100k
  m_ps->m_inParagraphOrListElement = false;
883
100k
}
884
885
void libabw::ABWContentCollector::openLink(const char *href)
886
88.8k
{
887
88.8k
  if (m_ps->m_isSpanOpened)
888
52.1k
    _closeSpan();
889
88.8k
  _openBlock();
890
88.8k
  librevenge::RVNGPropertyList propList;
891
88.8k
  if (href)
892
8.40k
    propList.insert("xlink:href", decodeUrl(href).c_str());
893
88.8k
  m_outputElements.addOpenLink(propList);
894
88.8k
  if (!m_ps->m_isSpanOpened)
895
88.8k
    _openSpan();
896
88.8k
}
897
898
void libabw::ABWContentCollector::closeLink()
899
88.8k
{
900
88.8k
  if (m_ps->m_isSpanOpened)
901
85.3k
    _closeSpan();
902
88.8k
  m_outputElements.addCloseLink();
903
88.8k
}
904
905
void libabw::ABWContentCollector::closeSpan()
906
22.6k
{
907
22.6k
  _closeSpan();
908
22.6k
  m_ps->m_currentCharacterStyle.clear();
909
22.6k
}
910
911
void libabw::ABWContentCollector::insertLineBreak()
912
1.99k
{
913
1.99k
  if (!m_ps->m_isSpanOpened)
914
1.22k
    _openSpan();
915
916
1.99k
  m_outputElements.addInsertLineBreak();
917
1.99k
}
918
919
void libabw::ABWContentCollector::insertColumnBreak()
920
22.6k
{
921
22.6k
  _closeBlock();
922
22.6k
  m_ps->m_deferredColumnBreak = true;
923
22.6k
}
924
925
void libabw::ABWContentCollector::insertPageBreak()
926
11.6k
{
927
11.6k
  _closeBlock();
928
11.6k
  m_ps->m_deferredPageBreak = true;
929
11.6k
}
930
931
void libabw::ABWContentCollector::insertText(const char *text)
932
265k
{
933
265k
  if (!m_ps->m_inParagraphOrListElement)
934
92.2k
    return;
935
173k
  if (m_ps->m_isFirstTextInListElement && text && text[0]==' ' && text[1]==0)
936
0
    return;
937
173k
  if (!m_ps->m_isSpanOpened)
938
123k
    _openSpan();
939
173k
  if (!text)
940
0
    return;
941
173k
  if (m_ps->m_isFirstTextInListElement && text[0] == '\t')
942
516
    separateSpacesAndInsertText(m_outputElements, text+1);
943
172k
  else
944
172k
    separateSpacesAndInsertText(m_outputElements, text);
945
173k
  m_ps->m_isFirstTextInListElement = false;
946
173k
}
947
948
void libabw::ABWContentCollector::_openPageSpan()
949
25.4k
{
950
25.4k
  if (!m_ps->m_isPageSpanOpened && !m_ps->m_isNote && m_ps->m_tableStates.empty())
951
25.4k
  {
952
953
25.4k
    if (!m_ps->m_isDocumentStarted)
954
16.1k
      startDocument();
955
956
25.4k
    librevenge::RVNGPropertyList propList;
957
    // assume default page size is A4
958
25.4k
    propList.insert("fo:page-width", m_ps->m_pageWidth>0 ? m_ps->m_pageWidth : 8.27);
959
25.4k
    propList.insert("fo:page-height", m_ps->m_pageHeight>0 ? m_ps->m_pageHeight : 11.7);
960
25.4k
    propList.insert("fo:margin-left", m_ps->m_pageMarginLeft);
961
25.4k
    propList.insert("fo:margin-right", m_ps->m_pageMarginRight);
962
25.4k
    propList.insert("fo:margin-top", m_ps->m_pageMarginTop);
963
25.4k
    propList.insert("fo:margin-bottom", m_ps->m_pageMarginBottom);
964
965
25.4k
    if (!m_ps->m_isPageSpanOpened)
966
25.4k
      m_outputElements.addOpenPageSpan(propList,
967
25.4k
                                       m_ps->m_footerId, m_ps->m_footerLeftId,
968
25.4k
                                       m_ps->m_footerFirstId, m_ps->m_footerLastId,
969
25.4k
                                       m_ps->m_headerId, m_ps->m_headerLeftId,
970
25.4k
                                       m_ps->m_headerFirstId, m_ps->m_headerLastId);
971
25.4k
  }
972
25.4k
  m_ps->m_isPageSpanOpened = true;
973
25.4k
}
974
975
void libabw::ABWContentCollector::_closePageSpan()
976
26.6k
{
977
26.6k
  if (m_ps->m_isPageSpanOpened)
978
22.4k
  {
979
22.4k
    _closeHeader();
980
22.4k
    _closeFooter();
981
22.4k
    _closeSection();
982
983
22.4k
    m_outputElements.addClosePageSpan();
984
22.4k
  }
985
26.6k
  m_ps->m_isPageSpanOpened = false;
986
26.6k
}
987
988
void libabw::ABWContentCollector::_openSection()
989
44.2k
{
990
44.2k
  if (!m_ps->m_isSectionOpened && !m_ps->m_isNote && m_ps->m_tableStates.empty())
991
43.7k
  {
992
43.7k
    if (!m_ps->m_isPageSpanOpened)
993
24.2k
      _openPageSpan();
994
995
43.7k
    librevenge::RVNGPropertyList propList;
996
997
43.7k
    ABWUnit unit(ABW_NONE);
998
43.7k
    double value(0.0);
999
43.7k
    if (findDouble(_findSectionProperty("page-margin-right"), value, unit) && unit == ABW_IN)
1000
359
      propList.insert("fo:margin-right", value - m_ps->m_pageMarginRight);
1001
1002
43.7k
    if (findDouble(_findSectionProperty("page-margin-left"), value, unit) && unit == ABW_IN)
1003
751
      propList.insert("fo:margin-left", value - m_ps->m_pageMarginLeft);
1004
1005
43.7k
    if (findDouble(_findSectionProperty("section-space-after"), value, unit) && unit == ABW_IN)
1006
897
      propList.insert("librevenge:margin-bottom", value);
1007
1008
43.7k
    std::string sValue = _findSectionProperty("dom-dir");
1009
43.7k
    if (sValue.empty()) // try document default
1010
43.5k
      sValue = _findDocumentProperty("dom-dir");
1011
43.7k
    if (sValue == "ltr")
1012
649
      propList.insert("style:writing-mode", "lr-tb");
1013
43.0k
    else if (sValue == "rtl")
1014
94
      propList.insert("style:writing-mode", "rl-tb");
1015
1016
43.7k
    int intValue(0);
1017
43.7k
    if (findInt(_findSectionProperty("columns"), intValue) && intValue > 1)
1018
1.22k
    {
1019
1.22k
      if (intValue > MAX_SECTION_COLUMN)
1020
638
        intValue = MAX_SECTION_COLUMN;
1021
1.22k
      librevenge::RVNGPropertyListVector columns;
1022
44.7k
      for (int i = 0; i < intValue; ++i)
1023
43.4k
      {
1024
43.4k
        librevenge::RVNGPropertyList column;
1025
43.4k
        column.insert("style:rel-width", 1.0 / (double)intValue, librevenge::RVNG_PERCENT);
1026
43.4k
        columns.append(column);
1027
43.4k
      }
1028
1.22k
      if (columns.count())
1029
1.22k
      {
1030
1.22k
        propList.insert("style:columns", columns);
1031
1.22k
        propList.insert("text:dont-balance-text-columns", true);
1032
1.22k
      }
1033
1.22k
    }
1034
43.7k
    m_outputElements.addOpenSection(propList);
1035
43.7k
  }
1036
44.2k
  m_ps->m_isSectionOpened = true;
1037
44.2k
}
1038
1039
void libabw::ABWContentCollector::_openFooter()
1040
5.07k
{
1041
5.07k
  if (!m_ps->m_isFooterOpened && !m_ps->m_isNote && m_ps->m_tableStates.empty())
1042
5.00k
  {
1043
5.00k
    librevenge::RVNGPropertyList propList;
1044
5.00k
    propList.insert("librevenge:occurrence", m_ps->m_currentHeaderFooterOccurrence);
1045
1046
5.00k
    m_outputElements.addOpenFooter(propList, m_ps->m_currentHeaderFooterId);
1047
5.00k
  }
1048
5.07k
  m_ps->m_isFooterOpened = true;
1049
5.07k
}
1050
1051
void libabw::ABWContentCollector::_openHeader()
1052
4.60k
{
1053
4.60k
  if (!m_ps->m_isHeaderOpened && !m_ps->m_isNote && m_ps->m_tableStates.empty())
1054
4.17k
  {
1055
4.17k
    librevenge::RVNGPropertyList propList;
1056
4.17k
    propList.insert("librevenge:occurrence", m_ps->m_currentHeaderFooterOccurrence);
1057
1058
4.17k
    m_outputElements.addOpenHeader(propList, m_ps->m_currentHeaderFooterId);
1059
4.17k
  }
1060
4.60k
  m_ps->m_isHeaderOpened = true;
1061
4.60k
}
1062
1063
void libabw::ABWContentCollector::_fillParagraphProperties(librevenge::RVNGPropertyList &propList,
1064
                                                           bool isListElement)
1065
152k
{
1066
152k
  ABWUnit unit(ABW_NONE);
1067
152k
  double value(0.0);
1068
152k
  int intValue(0);
1069
152k
  std::string sValue;
1070
1071
152k
  if (findDouble(_findParagraphProperty("margin-right"), value, unit) && unit == ABW_IN)
1072
9.65k
    propList.insert("fo:margin-right", value);
1073
1074
152k
  if (findDouble(_findParagraphProperty("margin-top"), value, unit) && unit == ABW_IN)
1075
10.8k
    propList.insert("fo:margin-top", value);
1076
1077
152k
  if (findDouble(_findParagraphProperty("margin-bottom"), value, unit) && unit == ABW_IN)
1078
10.7k
    propList.insert("fo:margin-bottom", value);
1079
1080
152k
  if (!isListElement)
1081
133k
  {
1082
133k
    if (findDouble(_findParagraphProperty("margin-left"), value, unit) && unit == ABW_IN)
1083
12.8k
      propList.insert("fo:margin-left", value);
1084
1085
133k
    if (findDouble(_findParagraphProperty("text-indent"), value, unit) && unit == ABW_IN)
1086
11.9k
      propList.insert("fo:text-indent", value);
1087
1088
    // TODO: Numbered headings should probably not be handled as lists.
1089
    // Just do not make them headings for now.
1090
133k
    sValue = _findParagraphProperty("libabw:outline-level");
1091
133k
    if (!sValue.empty())
1092
1.40k
      propList.insert("text:outline-level", sValue.c_str());
1093
133k
  }
1094
1095
152k
  sValue = _findParagraphProperty("text-align");
1096
152k
  if (!sValue.empty())
1097
14.6k
  {
1098
14.6k
    if (sValue == "left")
1099
10.6k
      propList.insert("fo:text-align", "start");
1100
4.01k
    else if (sValue == "right")
1101
1.59k
      propList.insert("fo:text-align", "end");
1102
2.42k
    else
1103
2.42k
      propList.insert("fo:text-align", sValue.c_str());
1104
14.6k
  }
1105
1106
152k
  sValue = _findParagraphProperty("line-height");
1107
152k
  if (!sValue.empty())
1108
7.78k
  {
1109
7.78k
    std::string propName("fo:line-height");
1110
7.78k
    size_t position = sValue.find_last_of('+');
1111
7.78k
    if (position && position != std::string::npos)
1112
293
    {
1113
293
      propName = "style:line-height-at-least";
1114
293
      sValue.erase(position);
1115
293
    }
1116
7.78k
    if (findDouble(sValue, value, unit))
1117
6.74k
    {
1118
6.74k
      if (ABW_IN == unit)
1119
303
        propList.insert(propName.c_str(), value);
1120
6.44k
      else if (ABW_PERCENT == unit)
1121
6.44k
        propList.insert(propName.c_str(), value, librevenge::RVNG_PERCENT);
1122
6.74k
    }
1123
7.78k
  }
1124
1125
152k
  if (findInt(_findParagraphProperty("orphans"), intValue))
1126
507
    propList.insert("fo:orphans", intValue);
1127
1128
152k
  if (findInt(_findParagraphProperty("widows"), intValue))
1129
11.0k
    propList.insert("fo:widows", intValue);
1130
1131
152k
  librevenge::RVNGPropertyListVector tabStops;
1132
152k
  parseTabStops(_findParagraphProperty("tabstops"), tabStops);
1133
1134
152k
  if (tabStops.count())
1135
3.86k
    propList.insert("style:tab-stops", tabStops);
1136
1137
152k
  sValue = _findParagraphProperty("dom-dir");
1138
152k
  if (sValue == "ltr")
1139
5.59k
    propList.insert("style:writing-mode", "lr-tb");
1140
146k
  else if (sValue == "rtl")
1141
519
    propList.insert("style:writing-mode", "rl-tb");
1142
1143
152k
  if (m_ps->m_deferredPageBreak)
1144
4.30k
    propList.insert("fo:break-before", "page");
1145
147k
  else if (m_ps->m_deferredColumnBreak)
1146
6.82k
    propList.insert("fo:break-before", "column");
1147
152k
  _addBorderProperties(m_ps->m_currentParagraphStyle, propList);
1148
152k
  m_ps->m_deferredPageBreak = false;
1149
152k
  m_ps->m_deferredColumnBreak = false;
1150
152k
}
1151
1152
void libabw::ABWContentCollector::_openBlock()
1153
378k
{
1154
378k
  if (m_ps->m_isParagraphOpened || m_ps->m_isListElementOpened)
1155
226k
    return;
1156
152k
  if (m_ps->m_currentListLevel == 0)
1157
133k
    _openParagraph();
1158
18.3k
  else
1159
18.3k
    _openListElement();
1160
152k
}
1161
1162
void libabw::ABWContentCollector::_closeBlock()
1163
667k
{
1164
667k
  if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
1165
515k
    return;
1166
151k
  if (m_ps->m_isSpanOpened)
1167
137k
    _closeSpan();
1168
151k
  if (m_ps->m_isParagraphOpened)
1169
133k
    _closeParagraph();
1170
151k
  if (m_ps->m_isListElementOpened)
1171
18.3k
    _closeListElement();
1172
151k
}
1173
1174
void libabw::ABWContentCollector::_openParagraph()
1175
133k
{
1176
133k
  if (!m_ps->m_isParagraphOpened)
1177
133k
  {
1178
133k
    switch (m_ps->m_parsingContext)
1179
133k
    {
1180
7.90k
    case ABW_HEADER:
1181
7.90k
      if (!m_ps->m_isHeaderOpened)
1182
2.63k
        _openHeader();
1183
7.90k
      break;
1184
11.2k
    case ABW_FOOTER:
1185
11.2k
      if (!m_ps->m_isFooterOpened)
1186
4.52k
        _openFooter();
1187
11.2k
      break;
1188
113k
    case ABW_SECTION:
1189
113k
    default:
1190
113k
      if (!m_ps->m_isSectionOpened)
1191
39.6k
        _openSection();
1192
113k
      break;
1193
116
    case ABW_FRAME_IMAGE:
1194
116
      ABW_DEBUG_MSG(("libabw::ABWContentCollector::_openParagraph: can not open a paragraph\n"));
1195
116
      m_ps->m_parsingContext=ABW_FRAME_UNKNOWN;
1196
116
      break;
1197
208
    case ABW_FRAME_TEXTBOX:
1198
647
    case ABW_FRAME_UNKNOWN:
1199
647
      break;
1200
133k
    }
1201
1202
133k
    if (!m_ps->m_tableStates.empty() && !m_ps->m_tableStates.top().m_isTableCellOpened)
1203
12.2k
      _openTableCell();
1204
1205
133k
    _changeList();
1206
1207
133k
    librevenge::RVNGPropertyList propList;
1208
133k
    _fillParagraphProperties(propList, false);
1209
1210
133k
    m_ps->m_deferredPageBreak = false;
1211
133k
    m_ps->m_deferredColumnBreak = false;
1212
1213
133k
    m_outputElements.addOpenParagraph(propList);
1214
1215
133k
    m_ps->m_isParagraphOpened = true;
1216
133k
    if (!m_ps->m_tableStates.empty())
1217
16.3k
      m_ps->m_tableStates.top().m_isCellWithoutParagraph = false;
1218
133k
  }
1219
133k
}
1220
1221
void libabw::ABWContentCollector::_openListElement()
1222
18.3k
{
1223
18.3k
  if (!m_ps->m_isListElementOpened)
1224
18.3k
  {
1225
18.3k
    switch (m_ps->m_parsingContext)
1226
18.3k
    {
1227
2.05k
    case ABW_HEADER:
1228
2.05k
      if (!m_ps->m_isHeaderOpened)
1229
650
        _openHeader();
1230
2.05k
      break;
1231
1.26k
    case ABW_FOOTER:
1232
1.26k
      if (!m_ps->m_isFooterOpened)
1233
64
        _openFooter();
1234
1.26k
      break;
1235
15.0k
    case ABW_SECTION:
1236
15.0k
    default:
1237
15.0k
      if (!m_ps->m_isSectionOpened)
1238
1.48k
        _openSection();
1239
15.0k
      break;
1240
1
    case ABW_FRAME_IMAGE:
1241
1
      ABW_DEBUG_MSG(("libabw::ABWContentCollector::_openListElement: can not open a list\n"));
1242
1
      m_ps->m_parsingContext=ABW_FRAME_UNKNOWN;
1243
1
      break;
1244
21
    case ABW_FRAME_TEXTBOX:
1245
40
    case ABW_FRAME_UNKNOWN:
1246
40
      break;
1247
18.3k
    }
1248
1249
18.3k
    if (!m_ps->m_tableStates.empty() && !m_ps->m_tableStates.top().m_isTableCellOpened)
1250
258
      _openTableCell();
1251
1252
18.3k
    _changeList();
1253
1254
18.3k
    librevenge::RVNGPropertyList propList;
1255
18.3k
    _fillParagraphProperties(propList, true);
1256
1257
18.3k
    m_outputElements.addOpenListElement(propList);
1258
1259
18.3k
    m_ps->m_isListElementOpened = true;
1260
18.3k
    if (!m_ps->m_tableStates.empty())
1261
2.65k
      m_ps->m_tableStates.top().m_isCellWithoutParagraph = false;
1262
18.3k
    m_ps->m_isFirstTextInListElement = true;
1263
18.3k
  }
1264
18.3k
}
1265
1266
void libabw::ABWContentCollector::_openSpan()
1267
288k
{
1268
288k
  if (!m_ps->m_isSpanOpened)
1269
288k
  {
1270
288k
    _openBlock();
1271
1272
288k
    librevenge::RVNGPropertyList propList;
1273
288k
    ABWUnit unit(ABW_NONE);
1274
288k
    double value(0.0);
1275
1276
288k
    if (findDouble(_findCharacterProperty("font-size"), value, unit) && unit == ABW_IN)
1277
14.7k
      propList.insert("fo:font-size", value);
1278
1279
288k
    std::string sValue = _findCharacterProperty("font-family");
1280
288k
    if (!sValue.empty())
1281
21.8k
      propList.insert("style:font-name", sValue.c_str());
1282
1283
288k
    sValue = _findCharacterProperty("font-style");
1284
288k
    if (!sValue.empty() && sValue != "normal")
1285
1.89k
      propList.insert("fo:font-style", sValue.c_str());
1286
1287
288k
    sValue = _findCharacterProperty("font-weight");
1288
288k
    if (!sValue.empty() && sValue != "normal")
1289
4.61k
      propList.insert("fo:font-weight", sValue.c_str());
1290
1291
288k
    sValue = _findCharacterProperty("display");
1292
288k
    if (!sValue.empty() && sValue == "none")
1293
403
      propList.insert("text:display", "none");
1294
1295
288k
    sValue = _findCharacterProperty("dir-override");
1296
288k
    if (!sValue.empty() && sValue == "rtl")
1297
530
      propList.insert("style:writing-mode", "rl-tb");
1298
1299
288k
    sValue = _findCharacterProperty("text-decoration");
1300
288k
    std::vector<std::string> listDecorations;
1301
288k
    boost::split(listDecorations, sValue, boost::is_any_of(" "), boost::token_compress_on);
1302
288k
    for (const auto &decoration : listDecorations)
1303
296k
    {
1304
296k
      if (decoration == "underline")
1305
905
      {
1306
905
        propList.insert("style:text-underline-type", "single");
1307
905
        propList.insert("style:text-underline-style", "solid");
1308
905
      }
1309
295k
      else if (decoration == "line-through")
1310
738
      {
1311
738
        propList.insert("style:text-line-through-type", "single");
1312
738
        propList.insert("style:text-line-through-style", "solid");
1313
738
      }
1314
294k
      else if (decoration == "overline")
1315
460
      {
1316
460
        propList.insert("style:text-overline-type", "single");
1317
460
        propList.insert("style:text-overline-style", "solid");
1318
460
      }
1319
296k
    }
1320
288k
    sValue = getColor(_findCharacterProperty("color"));
1321
288k
    if (!sValue.empty())
1322
10.0k
      propList.insert("fo:color", sValue.c_str());
1323
1324
288k
    sValue = getColor(_findCharacterProperty("bgcolor"));
1325
288k
    if (!sValue.empty())
1326
503
      propList.insert("fo:background-color", sValue.c_str());
1327
1328
288k
    sValue = _findCharacterProperty("text-position");
1329
288k
    if (sValue == "subscript")
1330
198
      propList.insert("style:text-position", "sub");
1331
288k
    else if (sValue == "superscript")
1332
213
      propList.insert("style:text-position", "super");
1333
1334
288k
    sValue = _findCharacterProperty("lang");
1335
288k
    if (sValue.empty()) // try document default
1336
245k
      sValue = _findDocumentProperty("lang");
1337
1338
288k
    if (!sValue.empty())
1339
53.9k
    {
1340
53.9k
      optional<std::string> lang;
1341
53.9k
      optional<std::string> country;
1342
53.9k
      optional<std::string> script;
1343
1344
53.9k
      parseLang(sValue, lang, country, script);
1345
1346
53.9k
      if (bool(lang))
1347
45.1k
        propList.insert("fo:language", get(lang).c_str());
1348
53.9k
      if (bool(country))
1349
12.7k
        propList.insert("fo:country", get(country).c_str());
1350
53.9k
      if (bool(script))
1351
33.8k
        propList.insert("fo:script", get(script).c_str());
1352
53.9k
    }
1353
1354
    // do we need to check "font-stretch" here or it is always equal to normal ?
1355
288k
    m_outputElements.addOpenSpan(propList);
1356
288k
  }
1357
288k
  m_ps->m_isSpanOpened = true;
1358
288k
}
1359
1360
void libabw::ABWContentCollector::_closeSection()
1361
118k
{
1362
118k
  if (m_ps->m_isSectionOpened)
1363
41.0k
  {
1364
42.7k
    while (!m_ps->m_tableStates.empty())
1365
1.67k
      _closeTable();
1366
1367
41.0k
    _closeBlock();
1368
41.0k
    m_ps->m_currentListLevel = 0;
1369
41.0k
    _changeList();
1370
1371
41.0k
    m_outputElements.addCloseSection();
1372
1373
41.0k
    m_ps->m_isSectionOpened = false;
1374
41.0k
  }
1375
118k
}
1376
1377
void libabw::ABWContentCollector::_closeHeader()
1378
118k
{
1379
118k
  if (m_ps->m_isHeaderOpened)
1380
4.60k
  {
1381
6.73k
    while (!m_ps->m_tableStates.empty())
1382
2.12k
      _closeTable();
1383
1384
4.60k
    _closeBlock();
1385
4.60k
    m_ps->m_currentListLevel = 0;
1386
4.60k
    _changeList();
1387
1388
4.60k
    m_outputElements.addCloseHeader();
1389
1390
4.60k
    m_ps->m_isHeaderOpened = false;
1391
4.60k
  }
1392
118k
  m_ps->m_currentHeaderFooterId = -1;
1393
118k
  m_ps->m_currentHeaderFooterOccurrence.clear();
1394
118k
}
1395
1396
void libabw::ABWContentCollector::_closeFooter()
1397
118k
{
1398
118k
  if (m_ps->m_isFooterOpened)
1399
5.06k
  {
1400
5.26k
    while (!m_ps->m_tableStates.empty())
1401
198
      _closeTable();
1402
1403
5.06k
    _closeBlock();
1404
5.06k
    m_ps->m_currentListLevel = 0;
1405
5.06k
    _changeList();
1406
1407
5.06k
    m_outputElements.addCloseFooter();
1408
1409
5.06k
    m_ps->m_isFooterOpened = false;
1410
5.06k
  }
1411
118k
  m_ps->m_currentHeaderFooterId = -1;
1412
118k
  m_ps->m_currentHeaderFooterOccurrence.clear();
1413
118k
}
1414
1415
void libabw::ABWContentCollector::_closeParagraph()
1416
133k
{
1417
133k
  if (m_ps->m_isParagraphOpened)
1418
133k
  {
1419
133k
    if (m_ps->m_isSpanOpened)
1420
0
      _closeSpan();
1421
1422
133k
    m_outputElements.addCloseParagraph();
1423
133k
  }
1424
133k
  m_ps->m_isParagraphOpened = false;
1425
133k
}
1426
1427
void libabw::ABWContentCollector::_closeSpan()
1428
305k
{
1429
305k
  if (m_ps->m_isSpanOpened)
1430
288k
    m_outputElements.addCloseSpan();
1431
1432
305k
  m_ps->m_isSpanOpened = false;
1433
305k
}
1434
1435
void libabw::ABWContentCollector::_openTable()
1436
18.5k
{
1437
18.5k
  switch (m_ps->m_parsingContext)
1438
18.5k
  {
1439
6.31k
  case ABW_HEADER:
1440
6.31k
    if (!m_ps->m_isHeaderOpened)
1441
425
      _openHeader();
1442
6.31k
    break;
1443
1.95k
  case ABW_FOOTER:
1444
1.95k
    if (!m_ps->m_isFooterOpened)
1445
26
      _openFooter();
1446
1.95k
    break;
1447
10.1k
  case ABW_SECTION:
1448
10.1k
  default:
1449
10.1k
    if (!m_ps->m_isSectionOpened)
1450
164
      _openSection();
1451
10.1k
    break;
1452
18
  case ABW_FRAME_IMAGE:
1453
49
  case ABW_FRAME_TEXTBOX:
1454
142
  case ABW_FRAME_UNKNOWN:
1455
142
    break;
1456
18.5k
  }
1457
1458
18.5k
  librevenge::RVNGPropertyList propList;
1459
18.5k
  if (m_ps->m_deferredPageBreak)
1460
936
    propList.insert("fo:break-before", "page");
1461
17.6k
  else if (m_ps->m_deferredColumnBreak)
1462
3.96k
    propList.insert("fo:break-before", "column");
1463
18.5k
  m_ps->m_deferredPageBreak = false;
1464
18.5k
  m_ps->m_deferredColumnBreak = false;
1465
1466
18.5k
  librevenge::RVNGPropertyListVector tmpColumns;
1467
18.5k
  parseTableColumns(_findTableProperty("table-column-props"), tmpColumns);
1468
18.5k
  auto numColumns = unsigned(tmpColumns.count());
1469
18.5k
  auto iter = m_tableSizes.find(m_ps->m_tableStates.top().m_currentTableId);
1470
18.5k
  if (iter != m_tableSizes.end())
1471
18.3k
    numColumns = unsigned(iter->second);
1472
18.5k
  if (numColumns > MAX_TABLE_COLUMN)
1473
33
    numColumns = MAX_TABLE_COLUMN;
1474
18.5k
  librevenge::RVNGPropertyListVector columns;
1475
2.27M
  for (unsigned j = 0; j < numColumns; ++j)
1476
2.25M
  {
1477
2.25M
    if (j < tmpColumns.count())
1478
4.25k
      columns.append(tmpColumns[j]);
1479
2.24M
    else
1480
2.24M
      columns.append(librevenge::RVNGPropertyList());
1481
2.25M
  }
1482
18.5k
  if (columns.count())
1483
2.75k
    propList.insert("librevenge:table-columns", columns);
1484
1485
18.5k
  ABWUnit unit(ABW_NONE);
1486
18.5k
  double value(0.0);
1487
18.5k
  if (findDouble(_findTableProperty("table-column-leftpos"), value, unit) && unit == ABW_IN)
1488
75
  {
1489
75
    propList.insert("fo:margin-left", value);
1490
75
    propList.insert("table:align", "margins");
1491
75
  }
1492
18.4k
  else
1493
18.4k
    propList.insert("table:align", "left");
1494
1495
18.5k
  m_outputElements.addOpenTable(propList);
1496
1497
18.5k
  m_ps->m_tableStates.top().m_currentTableRow = (-1);
1498
18.5k
  m_ps->m_tableStates.top().m_currentTableCol = (-1);
1499
18.5k
  m_ps->m_tableStates.top().m_currentTableCellNumberInRow = (-1);
1500
18.5k
}
1501
1502
void libabw::ABWContentCollector::_closeTable()
1503
22.3k
{
1504
22.3k
  if (!m_ps->m_tableStates.empty())
1505
18.5k
  {
1506
18.5k
    if (m_ps->m_tableStates.top().m_isTableRowOpened)
1507
3.95k
      _closeTableRow();
1508
1509
18.5k
    m_outputElements.addCloseTable();
1510
1511
18.5k
    m_ps->m_tableStates.pop();
1512
18.5k
  }
1513
22.3k
}
1514
1515
void libabw::ABWContentCollector::_openTableRow()
1516
793k
{
1517
793k
  if (m_ps->m_tableStates.top().m_isTableRowOpened)
1518
0
    _closeTableRow();
1519
1520
793k
  m_ps->m_tableStates.top().m_currentTableCol = 0;
1521
793k
  m_ps->m_tableStates.top().m_currentTableCellNumberInRow = 0;
1522
1523
793k
  m_outputElements.addOpenTableRow(librevenge::RVNGPropertyList());
1524
1525
793k
  m_ps->m_tableStates.top().m_isTableRowOpened = true;
1526
793k
  m_ps->m_tableStates.top().m_isRowWithoutCell = true;
1527
793k
  m_ps->m_tableStates.top().m_currentTableRow++;
1528
793k
}
1529
1530
void libabw::ABWContentCollector::_closeTableRow()
1531
793k
{
1532
793k
  if (m_ps->m_tableStates.top().m_isTableRowOpened)
1533
793k
  {
1534
793k
    if (m_ps->m_tableStates.top().m_isTableCellOpened)
1535
2.53k
      _closeTableCell();
1536
1537
793k
    if (m_ps->m_tableStates.top().m_isRowWithoutCell)
1538
787k
    {
1539
787k
      m_ps->m_tableStates.top().m_isRowWithoutCell = false;
1540
787k
      m_outputElements.addInsertCoveredTableCell(librevenge::RVNGPropertyList());
1541
787k
    }
1542
793k
    m_outputElements.addCloseTableRow();
1543
793k
  }
1544
793k
  m_ps->m_tableStates.top().m_isTableRowOpened = false;
1545
793k
}
1546
1547
void libabw::ABWContentCollector::_openTableCell()
1548
12.4k
{
1549
12.4k
  librevenge::RVNGPropertyList propList;
1550
12.4k
  propList.insert("librevenge:column", m_ps->m_tableStates.top().m_currentTableCol);
1551
12.4k
  propList.insert("librevenge:row", m_ps->m_tableStates.top().m_currentTableRow);
1552
1553
12.4k
  int rightAttach(0);
1554
12.4k
  if (findInt(_findCellProperty("right-attach"), rightAttach))
1555
3.36k
    propList.insert("table:number-columns-spanned", rightAttach - m_ps->m_tableStates.top().m_currentTableCol);
1556
1557
12.4k
  int botAttach(0);
1558
12.4k
  if (findInt(_findCellProperty("bot-attach"), botAttach))
1559
4.00k
    propList.insert("table:number-rows-spanned", botAttach - m_ps->m_tableStates.top().m_currentTableRow);
1560
1561
12.4k
  std::string bgColor = getColor(_findCellProperty("background-color"));
1562
12.4k
  if (!bgColor.empty())
1563
33
    propList.insert("fo:background-color", bgColor.c_str());
1564
1565
  // by default, table cells have a small border
1566
12.4k
  _addBorderProperties(m_ps->m_tableStates.top().m_currentCellProperties, propList,"0.01in solid #000000");
1567
12.4k
  m_outputElements.addOpenTableCell(propList);
1568
1569
12.4k
  m_ps->m_tableStates.top().m_currentTableCellNumberInRow++;
1570
12.4k
  m_ps->m_tableStates.top().m_isTableCellOpened = true;
1571
12.4k
  m_ps->m_tableStates.top().m_isCellWithoutParagraph = true;
1572
12.4k
  m_ps->m_tableStates.top().m_isRowWithoutCell = false;
1573
12.4k
}
1574
1575
void libabw::ABWContentCollector::_closeTableCell()
1576
16.3k
{
1577
16.3k
  if (m_ps->m_tableStates.top().m_isTableCellOpened)
1578
9.55k
  {
1579
9.55k
    if (m_ps->m_tableStates.top().m_isCellWithoutParagraph)
1580
0
      _openSpan();
1581
9.55k
    _closeBlock();
1582
9.55k
    m_ps->m_currentListLevel = 0;
1583
9.55k
    _changeList();
1584
1585
9.55k
    m_outputElements.addCloseTableCell();
1586
9.55k
  }
1587
16.3k
  m_ps->m_tableStates.top().m_isTableCellOpened = false;
1588
16.3k
}
1589
1590
void libabw::ABWContentCollector::openFoot(const char *id)
1591
2.66k
{
1592
2.66k
  if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
1593
429
    _openSpan();
1594
2.66k
  _closeSpan();
1595
1596
2.66k
  librevenge::RVNGPropertyList propList;
1597
2.66k
  if (id)
1598
78
    propList.insert("librevenge:number", id);
1599
2.66k
  m_outputElements.addOpenFootnote(propList);
1600
1601
2.66k
  m_parsingStates.push(m_ps);
1602
2.66k
  m_ps = std::make_shared<ABWContentParsingState>();
1603
1604
2.66k
  m_ps->m_isNote = true;
1605
2.66k
}
1606
1607
void libabw::ABWContentCollector::closeFoot()
1608
2.66k
{
1609
2.66k
  _closeBlock();
1610
2.66k
  m_ps->m_currentListLevel = 0;
1611
2.66k
  _changeList();
1612
1613
2.66k
  m_outputElements.addCloseFootnote();
1614
1615
2.66k
  if (!m_parsingStates.empty())
1616
2.66k
  {
1617
2.66k
    m_ps = m_parsingStates.top();
1618
2.66k
    m_parsingStates.pop();
1619
2.66k
  }
1620
2.66k
}
1621
1622
void libabw::ABWContentCollector::openEndnote(const char *id)
1623
2.79k
{
1624
2.79k
  if (!m_ps->m_isParagraphOpened && !m_ps->m_isListElementOpened)
1625
366
    _openSpan();
1626
2.79k
  _closeSpan();
1627
1628
2.79k
  librevenge::RVNGPropertyList propList;
1629
2.79k
  if (id)
1630
51
    propList.insert("librevenge:number", id);
1631
2.79k
  m_outputElements.addOpenEndnote(propList);
1632
1633
2.79k
  m_parsingStates.push(m_ps);
1634
2.79k
  m_ps = std::make_shared<ABWContentParsingState>();
1635
1636
2.79k
  m_ps->m_isNote = true;
1637
2.79k
}
1638
1639
void libabw::ABWContentCollector::closeEndnote()
1640
2.78k
{
1641
2.78k
  _closeBlock();
1642
2.78k
  m_ps->m_currentListLevel = 0;
1643
2.78k
  _changeList();
1644
1645
2.78k
  m_outputElements.addCloseEndnote();
1646
1647
2.78k
  if (!m_parsingStates.empty())
1648
2.78k
  {
1649
2.78k
    m_ps = m_parsingStates.top();
1650
2.78k
    m_parsingStates.pop();
1651
2.78k
  }
1652
2.78k
}
1653
1654
void libabw::ABWContentCollector::openField(const char *type, const char * /*id*/)
1655
63.4k
{
1656
63.4k
  if (!type || type[0]==0)
1657
5.18k
  {
1658
5.18k
    ABW_DEBUG_MSG(("libabw::ABWContentCollector::openField: called without type\n"));
1659
5.18k
    return;
1660
5.18k
  }
1661
58.3k
  if (!m_ps->m_inParagraphOrListElement)
1662
1.12k
    return;
1663
57.1k
  librevenge::RVNGPropertyList propList;
1664
57.1k
  std::string typ(type);
1665
57.1k
  size_t len=typ.length();
1666
  // see fp_Fields.h
1667
57.1k
  switch (typ[0])
1668
57.1k
  {
1669
737
  case 'a':
1670
737
    if (len>4 && typ.substr(0,4)=="app_")
1671
217
    {
1672
      // app_ver, app_compiledate, app_compiletime, app_id, app_target, app_options
1673
217
      return;
1674
217
    }
1675
520
    break;
1676
702
  case 'c':
1677
702
    if (len==10 && typ=="char_count")
1678
241
      propList.insert("librevenge:field-type", "text:character-count");
1679
702
    break;
1680
19.3k
  case 'd':
1681
19.3k
    if (len==4 && typ=="date")
1682
1.02k
    {
1683
1.02k
      propList.insert("librevenge:field-type", "text:date");
1684
1.02k
      propList.insert("number:automatic-order", "true");
1685
1.02k
      librevenge::RVNGPropertyListVector pVect;
1686
1.02k
      _convertFieldDTFormat("%A, %B %d,%Y",pVect);
1687
1.02k
      propList.insert("librevenge:value-type", "date");
1688
1.02k
      propList.insert("librevenge:format", pVect);
1689
1.02k
      break;
1690
1.02k
    }
1691
18.2k
    if (len>5 && typ.substr(0,5)=="date_")
1692
14.8k
    {
1693
14.8k
      propList.insert("librevenge:field-type", "text:date");
1694
14.8k
      propList.insert("number:automatic-order", "true");
1695
14.8k
      librevenge::RVNGPropertyListVector pVect;
1696
14.8k
      if (len==10 && typ=="date_ntdlf") // default
1697
322
        ;
1698
14.5k
      else if (len==11 && typ=="date_mmddyy")
1699
2.33k
        _convertFieldDTFormat("%m/%d/%y",pVect);
1700
12.2k
      else if (len==11 && typ=="date_ddmmyy")
1701
300
        _convertFieldDTFormat("%d/%m/%y",pVect);
1702
11.9k
      else if (len==8 && typ=="date_mdy")
1703
89
        _convertFieldDTFormat("%B %d,%Y",pVect);
1704
11.8k
      else if (len==10 && typ=="date_mthdy")
1705
5.19k
        _convertFieldDTFormat("%b %d,%Y",pVect);
1706
6.64k
      else if (len==8 && typ=="date_dfl")
1707
113
        _convertFieldDTFormat("%a %b %d %H:%M:%S %Y",pVect);
1708
6.52k
      else if (len==10 && typ=="date_wkday")
1709
480
        _convertFieldDTFormat("%A",pVect);
1710
6.04k
      else if (len==8 && typ=="date_doy") // normally this is the day of the year
1711
96
        _convertFieldDTFormat("%d",pVect);
1712
14.8k
      if (!pVect.empty())
1713
8.60k
      {
1714
8.60k
        propList.insert("librevenge:value-type", "date");
1715
8.60k
        propList.insert("librevenge:format", pVect);
1716
8.60k
      }
1717
14.8k
      break;
1718
14.8k
    }
1719
3.41k
    if (len==15 && typ=="datetime_custom")   // TODO add format
1720
937
    {
1721
937
      propList.insert("librevenge:field-type", "text:date");
1722
937
      propList.insert("number:automatic-order", "true");
1723
937
      librevenge::RVNGPropertyListVector pVect;
1724
937
      _convertFieldDTFormat("%d/%m/%y %H:%M:%S",pVect);
1725
937
      propList.insert("librevenge:value-type", "date");
1726
937
      propList.insert("librevenge:format", pVect);
1727
937
      break;
1728
937
    }
1729
2.47k
    break;
1730
2.74k
  case 'e':
1731
2.74k
    if (len==12 && typ=="endnote_anch")
1732
67
      return;
1733
2.68k
    if (len==11 && typ=="endnote_ref")
1734
225
      return;
1735
2.45k
    break;
1736
2.53k
  case 'f':
1737
2.53k
    if (len==9 && typ=="file_name")
1738
254
    {
1739
254
      propList.insert("librevenge:field-type", "text:file-name");
1740
254
      propList.insert("text:display", "full");
1741
254
      break;
1742
254
    }
1743
2.27k
    if (len==13 && typ=="footnote_anch")
1744
117
      return;
1745
2.16k
    if (len==12 && typ=="footnote_ref")
1746
278
      return;
1747
1.88k
    break;
1748
3.08k
  case 'l':
1749
3.08k
    if (len==10 && typ=="list_label")
1750
1.87k
      return;
1751
    // line_count
1752
1.20k
    break;
1753
13.3k
  case 'm':
1754
13.3k
    if (len>5 && typ.substr(0,5)=="meta_")
1755
9.29k
    {
1756
9.29k
      if (len==10 && typ=="meta_title")
1757
100
        propList.insert("librevenge:field-type", "text:title");
1758
9.19k
      else if (len==12 && typ=="meta_subject")
1759
248
        propList.insert("librevenge:field-type", "text:subject");
1760
8.94k
      else if (len==12 && typ=="meta_creator")
1761
719
        propList.insert("librevenge:field-type", "text:creator");
1762
8.22k
      else if (len==14 && typ=="meta_publisher")
1763
640
        propList.insert("librevenge:field-type", "text:printed-by");
1764
      //else if (len==16 && typ=="meta_contributor")
1765
      // else if (len==9 && typ=="meta_type")
1766
7.58k
      else if (len==13 && typ=="meta_keywords")
1767
183
        propList.insert("librevenge:field-type", "text:keywords");
1768
      //else if (len==13 && typ=="meta_language")
1769
7.40k
      else if (len==16 && typ=="meta_description")
1770
67
        propList.insert("librevenge:field-type", "text:description");
1771
      //else if (len==13 && typ=="meta_coverage")
1772
      //else if (len==11 && typ=="meta_rights")
1773
7.33k
      else if (len==9 && typ=="meta_date")
1774
196
        propList.insert("librevenge:field-type", "text:creation-date");
1775
7.13k
      else if (len==22 && typ=="meta_date_last_changed")
1776
336
        propList.insert("librevenge:field-type", "text:modification-date");
1777
9.29k
      break;
1778
9.29k
    }
1779
4.09k
    if (len==10 && typ=="mail_merge") // a datafield?
1780
69
      return;
1781
4.02k
    break;
1782
4.02k
  case 'n':
1783
    // nbsp_count
1784
2
    break;
1785
7.77k
  case 'p':
1786
7.77k
    if (len>5 && typ.substr(0,5)=="page_")
1787
3.04k
    {
1788
3.04k
      if (len==11 && typ=="page_number")
1789
283
        propList.insert("librevenge:field-type", "text:page-number");
1790
2.76k
      else if (len==10 && typ=="page_count")
1791
72
        propList.insert("librevenge:field-type", "text:page-count");
1792
      // page_ref ?
1793
3.04k
      break;
1794
3.04k
    }
1795
4.73k
    if (len>5 && typ.substr(0,5)=="para_")
1796
815
    {
1797
815
      if (len==10 && typ=="para_count")
1798
197
        propList.insert("librevenge:field-type", "text:paragraph-count");
1799
815
      break;
1800
815
    }
1801
3.91k
    break;
1802
3.91k
  case 's':
1803
1.07k
    if (len==15 && typ=="short_file_name")
1804
371
    {
1805
371
      propList.insert("librevenge:field-type", "text:file-name");
1806
371
      propList.insert("text:display", "full"); // checkme
1807
371
      break;
1808
371
    }
1809
705
    if (len>4 && typ.substr(0,4)=="sum_")
1810
0
    {
1811
      // sum_cols, sum_rows
1812
0
      break;
1813
0
    }
1814
705
    break;
1815
3.83k
  case 't':
1816
3.83k
    if (len==4 && typ=="time")
1817
69
    {
1818
69
      propList.insert("librevenge:field-type", "text:time");
1819
69
      propList.insert("number:automatic-order", "true");
1820
69
      break;
1821
69
    }
1822
3.76k
    if (len>5 && typ.substr(0,5)=="time_")   // TODO add format
1823
1.30k
    {
1824
1.30k
      propList.insert("librevenge:field-type", "text:time");
1825
1.30k
      propList.insert("number:automatic-order", "true");
1826
1.30k
      librevenge::RVNGPropertyListVector pVect;
1827
1.30k
      if (len==9 && typ=="time_ampm")
1828
147
        _convertFieldDTFormat("%I:%M:%S %p",pVect);
1829
1.30k
      if (len==9 && typ=="time_zone") // CEST, ...
1830
66
        return;
1831
      // if (len==10 && typ=="time_epoch") //second since ...
1832
1.24k
      if (len==12 && typ=="time_miltime") // ""
1833
66
        return;
1834
1.17k
      if (!pVect.empty())
1835
147
      {
1836
147
        propList.insert("librevenge:value-type", "time");
1837
147
        propList.insert("librevenge:format", pVect);
1838
147
      }
1839
1.17k
      break;
1840
1.24k
    }
1841
2.46k
    if (len>4 && typ.substr(0,4)=="toc_")
1842
655
    {
1843
655
      if (len==14 && typ=="toc_list_label")
1844
0
        return;
1845
      // toc_page_number
1846
655
      break;
1847
655
    }
1848
1.80k
    break;
1849
1.80k
  case 'w':
1850
427
    if (len==11 && typ=="word_count")
1851
0
      propList.insert("librevenge:field-type", "text:word-count");
1852
427
    break;
1853
1.54k
  default:
1854
1.54k
    break;
1855
57.1k
  }
1856
54.1k
  if (propList.empty())
1857
32.2k
  {
1858
32.2k
    ABW_DEBUG_MSG(("libabw::ABWContentCollector::openField: sorry, unknown type=%s\n", type));
1859
32.2k
  }
1860
21.9k
  else
1861
21.9k
  {
1862
21.9k
    if (!m_ps->m_isSpanOpened)
1863
779
      _openSpan();
1864
21.9k
    m_outputElements.addInsertField(propList);
1865
21.9k
    m_ps->m_isFirstTextInListElement = false;
1866
21.9k
  }
1867
54.1k
}
1868
1869
bool libabw::ABWContentCollector::_convertFieldDTFormat(std::string const &dtFormat, librevenge::RVNGPropertyListVector &propVect)
1870
10.7k
{
1871
10.7k
  propVect.clear();
1872
10.7k
  size_t len=dtFormat.size();
1873
10.7k
  std::string text("");
1874
10.7k
  librevenge::RVNGPropertyList list;
1875
71.8k
  for (size_t c=0; c < len; ++c)
1876
61.1k
  {
1877
61.1k
    if (dtFormat[c]!='%' || c+1==len)
1878
25.7k
    {
1879
25.7k
      text+=dtFormat[c];
1880
25.7k
      continue;
1881
25.7k
    }
1882
35.4k
    char ch=dtFormat[++c];
1883
35.4k
    if (ch=='%')
1884
0
    {
1885
0
      text += '%';
1886
0
      continue;
1887
0
    }
1888
35.4k
    if (!text.empty())
1889
24.7k
    {
1890
24.7k
      list.clear();
1891
24.7k
      list.insert("librevenge:value-type", "text");
1892
24.7k
      list.insert("librevenge:text", text.c_str());
1893
24.7k
      propVect.append(list);
1894
24.7k
      text.clear();
1895
24.7k
    }
1896
35.4k
    list.clear();
1897
35.4k
    switch (ch)
1898
35.4k
    {
1899
6.42k
    case 'Y':
1900
6.42k
      list.insert("number:style", "long");
1901
    /* FALLTHRU */
1902
9.99k
    case 'y':
1903
9.99k
      list.insert("librevenge:value-type", "year");
1904
9.99k
      propVect.append(list);
1905
9.99k
      break;
1906
1.11k
    case 'B':
1907
1.11k
      list.insert("number:style", "long");
1908
    /* FALLTHRU */
1909
6.42k
    case 'b':
1910
6.42k
    case 'h':
1911
6.42k
      list.insert("librevenge:value-type", "month");
1912
6.42k
      list.insert("number:textual", true);
1913
6.42k
      propVect.append(list);
1914
6.42k
      break;
1915
3.56k
    case 'm':
1916
3.56k
      list.insert("librevenge:value-type", "month");
1917
3.56k
      propVect.append(list);
1918
3.56k
      break;
1919
0
    case 'e':
1920
0
      list.insert("number:style", "long");
1921
    /* FALLTHRU */
1922
10.0k
    case 'd':
1923
10.0k
      list.insert("librevenge:value-type", "day");
1924
10.0k
      propVect.append(list);
1925
10.0k
      break;
1926
1.50k
    case 'A':
1927
1.50k
      list.insert("number:style", "long");
1928
    /* FALLTHRU */
1929
1.61k
    case 'a':
1930
1.61k
      list.insert("librevenge:value-type", "day-of-week");
1931
1.61k
      propVect.append(list);
1932
1.61k
      break;
1933
1934
1.05k
    case 'H':
1935
1.05k
      list.insert("number:style", "long");
1936
    /* FALLTHRU */
1937
1.19k
    case 'I':
1938
1.19k
      list.insert("librevenge:value-type", "hours");
1939
1.19k
      propVect.append(list);
1940
1.19k
      break;
1941
1.19k
    case 'M':
1942
1.19k
      list.insert("librevenge:value-type", "minutes");
1943
1.19k
      list.insert("number:style", "long");
1944
1.19k
      propVect.append(list);
1945
1.19k
      break;
1946
1.19k
    case 'S':
1947
1.19k
      list.insert("librevenge:value-type", "seconds");
1948
1.19k
      list.insert("number:style", "long");
1949
1.19k
      propVect.append(list);
1950
1.19k
      break;
1951
147
    case 'p':
1952
147
      list.clear();
1953
147
      list.insert("librevenge:value-type", "am-pm");
1954
147
      propVect.append(list);
1955
147
      break;
1956
#if !defined(__clang__)
1957
    default:
1958
      ABW_DEBUG_MSG(("libabw::ABWContentCollector::_convertFieldDTFormat: find unimplement command %c(ignored)\n", ch));
1959
#endif
1960
35.4k
    }
1961
35.4k
  }
1962
10.7k
  if (!text.empty())
1963
0
  {
1964
0
    list.clear();
1965
0
    list.insert("librevenge:value-type", "text");
1966
0
    list.insert("librevenge:text", text.c_str());
1967
0
    propVect.append(list);
1968
0
  }
1969
10.7k
  return propVect.count()!=0;
1970
10.7k
}
1971
1972
void libabw::ABWContentCollector::closeField()
1973
63.4k
{
1974
63.4k
}
1975
1976
void libabw::ABWContentCollector::openTable(const char *props)
1977
18.5k
{
1978
18.5k
  _closeBlock();
1979
18.5k
  m_ps->m_currentListLevel = 0;
1980
18.5k
  _changeList();
1981
1982
18.5k
  if (m_ps->m_tableStates.empty())
1983
11.4k
  {
1984
11.4k
    switch (m_ps->m_parsingContext)
1985
11.4k
    {
1986
3.22k
    case ABW_HEADER:
1987
3.22k
      if (!m_ps->m_isHeaderOpened)
1988
893
        _openHeader();
1989
3.22k
      break;
1990
1.63k
    case ABW_FOOTER:
1991
1.63k
      if (!m_ps->m_isFooterOpened)
1992
459
        _openFooter();
1993
1.63k
      break;
1994
6.45k
    case ABW_SECTION:
1995
6.45k
    default:
1996
6.45k
      if (!m_ps->m_isSectionOpened)
1997
1.83k
        _openSection();
1998
6.45k
      break;
1999
18
    case ABW_FRAME_IMAGE:
2000
47
    case ABW_FRAME_TEXTBOX:
2001
132
    case ABW_FRAME_UNKNOWN:
2002
132
      break;
2003
11.4k
    }
2004
11.4k
  }
2005
2006
18.5k
  m_ps->m_tableStates.push(ABWContentTableState());
2007
18.5k
  m_ps->m_tableStates.top().m_currentTableId = m_tableCounter++;
2008
18.5k
  if (props)
2009
2.72k
    parsePropString(props, m_ps->m_tableStates.top().m_currentTableProperties);
2010
2011
18.5k
  _openTable();
2012
18.5k
}
2013
2014
void libabw::ABWContentCollector::closeTable()
2015
18.3k
{
2016
18.3k
  _closeBlock();
2017
18.3k
  m_ps->m_currentListLevel = 0;
2018
18.3k
  _changeList();
2019
18.3k
  _closeTable();
2020
18.3k
}
2021
2022
void libabw::ABWContentCollector::openCell(const char *props)
2023
16.8k
{
2024
16.8k
  if (!m_ps->m_tableStates.empty())
2025
14.9k
  {
2026
14.9k
    if (props)
2027
10.3k
      parsePropString(props, m_ps->m_tableStates.top().m_currentCellProperties);
2028
14.9k
    int currentRow(getCellPos("top-attach", "bottom-attach", m_ps->m_tableStates.top().m_currentTableRow + 1));
2029
14.9k
    if (currentRow > MAX_TABLE_ROW)
2030
272
      currentRow = MAX_TABLE_ROW;
2031
2032
808k
    while (m_ps->m_tableStates.top().m_currentTableRow < currentRow)
2033
793k
    {
2034
793k
      if (m_ps->m_tableStates.top().m_currentTableRow >= 0)
2035
789k
        _closeTableRow();
2036
793k
      _openTableRow();
2037
793k
    }
2038
2039
14.9k
    m_ps->m_tableStates.top().m_currentTableCol =
2040
14.9k
      getCellPos("left-attach", "right-attach", m_ps->m_tableStates.top().m_currentTableCol + 1);
2041
14.9k
  }
2042
16.8k
}
2043
2044
int libabw::ABWContentCollector::getCellPos(const char *startProp, const char *endProp, int defStart)
2045
29.9k
{
2046
29.9k
  int startAttach(0);
2047
29.9k
  const bool haveStart(findInt(_findCellProperty(startProp), startAttach));
2048
29.9k
  int endAttach(0);
2049
29.9k
  const bool haveEnd(findInt(_findCellProperty(endProp), endAttach));
2050
2051
29.9k
  int newStartAttach(startAttach);
2052
2053
29.9k
  if (haveStart && haveEnd)
2054
3.35k
  {
2055
3.35k
    if (endAttach <= startAttach && endAttach > 0)
2056
288
      newStartAttach = endAttach - 1;
2057
3.35k
  }
2058
26.6k
  else if (haveStart && !haveEnd)
2059
8.86k
  {
2060
8.86k
    if (startAttach / 1000 > defStart) // likely a damaged input
2061
939
      newStartAttach = defStart;
2062
8.86k
  }
2063
17.7k
  else if (!haveStart && haveEnd)
2064
773
  {
2065
773
    if (endAttach <= 0 || endAttach / 1000 > defStart) // likely a damaged input
2066
147
      newStartAttach = defStart;
2067
626
    else
2068
626
      newStartAttach = endAttach - 1;
2069
773
  }
2070
16.9k
  else
2071
16.9k
  {
2072
16.9k
    newStartAttach = defStart;
2073
16.9k
  }
2074
2075
29.9k
  return newStartAttach;
2076
29.9k
}
2077
2078
void libabw::ABWContentCollector::closeCell()
2079
16.7k
{
2080
16.7k
  if (!m_ps->m_tableStates.empty())
2081
13.8k
  {
2082
13.8k
    _closeTableCell();
2083
13.8k
    m_ps->m_tableStates.top().m_currentCellProperties.clear();
2084
13.8k
  }
2085
16.7k
}
2086
2087
void libabw::ABWContentCollector::openFrame(const char *props, const char *imageId, const char */*title*/, const char */*alt*/)
2088
23.4k
{
2089
23.4k
  ABWPropertyMap propMap;
2090
23.4k
  if (props)
2091
18.6k
    parsePropString(props, propMap);
2092
23.4k
  ABWPropertyMap::const_iterator iter;
2093
2094
23.4k
  librevenge::RVNGPropertyList propList;
2095
23.4k
  ABWUnit unit(ABW_NONE);
2096
23.4k
  double value(0.0);
2097
  // size
2098
23.4k
  iter = propMap.find("frame-height");
2099
23.4k
  if (iter != propMap.end() && findDouble(iter->second, value, unit) && ABW_IN == unit)
2100
211
    propList.insert("svg:height", value);
2101
23.4k
  iter = propMap.find("frame-width");
2102
23.4k
  if (iter != propMap.end() && findDouble(iter->second, value, unit) && ABW_IN == unit)
2103
271
    propList.insert("svg:width", value);
2104
  // position
2105
23.4k
  bool isParagraph=true;
2106
23.4k
  iter = propMap.find("position-to");
2107
23.4k
  if (iter != propMap.end())
2108
5.57k
  {
2109
5.57k
    if (iter->second=="page-above-text")
2110
266
      isParagraph=false;
2111
5.30k
    else if (iter->second=="column-above-text")
2112
      /* unsure how to retrieve that, so check if the page positions
2113
         are defined, if yes, use a page anchor. */
2114
623
      isParagraph=(propMap.find("frame-page-ypos")==propMap.end());
2115
4.68k
    else if (iter->second!="block-above-text")
2116
4.57k
    {
2117
4.57k
      ABW_DEBUG_MSG(("libabw::ABWContentCollector::openFrame: sorry, unknown pos: %s asume paragraph\n", iter->second.c_str()));
2118
4.57k
    }
2119
5.57k
  }
2120
23.4k
  iter = propMap.find(isParagraph ? "xpos" : "frame-page-xpos");
2121
23.4k
  if (iter != propMap.end() && findDouble(iter->second, value, unit) && ABW_IN == unit)
2122
393
    propList.insert("svg:x", value);
2123
23.4k
  iter = propMap.find(isParagraph ? "ypos" : "frame-page-ypos");
2124
23.4k
  if (iter != propMap.end() && findDouble(iter->second, value, unit) && ABW_IN == unit)
2125
619
    propList.insert("svg:y", value);
2126
23.4k
  if (!isParagraph)
2127
302
  {
2128
302
    propList.insert("style:vertical-rel", "page");
2129
302
    propList.insert("style:horizontal-rel", "page");
2130
302
  }
2131
23.4k
  if (!isParagraph)
2132
302
  {
2133
302
    iter = propMap.find("frame-pref-page");
2134
302
    int page=0;
2135
302
    if (iter != propMap.end() && findInt(iter->second, page))
2136
22
      propList.insert("text:anchor-page-number", page+1);
2137
302
  }
2138
  // style
2139
23.4k
  int intValue;
2140
23.4k
  iter = propMap.find("bg-style"); // 0: none, 1: color=background-color
2141
23.4k
  if (iter != propMap.end() && findInt(iter->second, intValue) && intValue==1)
2142
397
  {
2143
397
    iter = propMap.find("background-color");
2144
397
    if (iter != propMap.end())
2145
38
    {
2146
38
      std::string color("#");
2147
38
      color+=iter->second;
2148
38
      propList.insert("fo:background-color", color.c_str());
2149
38
    }
2150
397
  }
2151
23.4k
  propList.insert("text:anchor-type", isParagraph ? "paragraph" : "page");
2152
23.4k
  iter = propMap.find("wrap-mode");
2153
23.4k
  if (iter != propMap.end())
2154
3.92k
  {
2155
3.92k
    if (iter->second=="wrapped-to-left")
2156
521
      propList.insert("style:wrap", "left");
2157
3.40k
    else if (iter->second=="wrapped-to-right")
2158
251
      propList.insert("style:wrap", "right");
2159
3.15k
    else if (iter->second=="wrapped-to-both")
2160
403
      propList.insert("style:wrap", "parallel");
2161
2.74k
    else if (iter->second=="above-text")
2162
523
    {
2163
523
      propList.insert("style:wrap", "dynamic");
2164
523
      propList.insert("style:run-through", "foreground");
2165
523
    }
2166
2.22k
    else if (iter->second=="below-text")
2167
71
    {
2168
71
      propList.insert("style:wrap", "dynamic");
2169
71
      propList.insert("style:run-through", "background");
2170
71
    }
2171
2.15k
    else
2172
2.15k
    {
2173
2.15k
      ABW_DEBUG_MSG(("libabw::ABWContentCollector::openFrame: sorry, unknown wrap mode: %s\n", iter->second.c_str()));
2174
2.15k
    }
2175
3.92k
  }
2176
23.4k
  m_ps->m_isPageFrame=!isParagraph;
2177
23.4k
  m_outputElements.addOpenFrame(propList);
2178
2179
23.4k
  iter = propMap.find("frame-type");
2180
23.4k
  if (iter==propMap.end())
2181
19.9k
  {
2182
19.9k
    ABW_DEBUG_MSG(("libabw::ABWContentCollector::openFrame: can not find the frame type\n"));
2183
19.9k
  }
2184
3.48k
  else if (iter->second=="image")
2185
613
  {
2186
613
    m_ps->m_parsingContext=ABW_FRAME_IMAGE;
2187
613
    auto imIter = m_data.end();
2188
613
    if (imageId) imIter= m_data.find(imageId);
2189
613
    if (imIter==m_data.end())
2190
534
    {
2191
534
      ABW_DEBUG_MSG(("libabw::ABWContentCollector::openFrame: can not find the image\n"));
2192
534
      return;
2193
534
    }
2194
2195
79
    propList.clear();
2196
79
    propList.insert("librevenge:mime-type", imIter->second.m_mimeType);
2197
79
    propList.insert("office:binary-data", imIter->second.m_binaryData);
2198
79
    m_outputElements.addInsertBinaryObject(propList);
2199
2200
79
    return;
2201
613
  }
2202
2.86k
  else if (iter->second=="textbox")
2203
954
  {
2204
954
    m_ps->m_parsingContext=ABW_FRAME_TEXTBOX;
2205
954
    propList.clear();
2206
954
    m_outputElements.addOpenTextBox(propList);
2207
954
    return;
2208
954
  }
2209
1.91k
  else
2210
1.91k
  {
2211
1.91k
    m_ps->m_parsingContext=ABW_FRAME_UNKNOWN;
2212
1.91k
    ABW_DEBUG_MSG(("libabw::ABWContentCollector::openFrame: sorry, unknown frame type: %s\n", iter->second.c_str()));
2213
1.91k
  }
2214
23.4k
}
2215
2216
void libabw::ABWContentCollector::closeFrame(libabw::ABWOutputElements *(&elements), bool &pageFrame)
2217
23.4k
{
2218
23.4k
  elements=nullptr;
2219
23.4k
  pageFrame=false;
2220
23.4k
  if (m_ps->m_isNote)
2221
0
  {
2222
0
    ABW_DEBUG_MSG(("libabw::ABWContentCollector::closeFrame: sorry, oops, sorry, a note is not closed\n"));
2223
0
    return;
2224
0
  }
2225
23.4k
  if (m_ps->m_parsingContext!=ABW_FRAME_IMAGE && m_ps->m_parsingContext!=ABW_FRAME_TEXTBOX)
2226
21.9k
    return;
2227
2228
1.45k
  while (!m_ps->m_tableStates.empty())
2229
0
    _closeTable();
2230
1.45k
  _closeBlock();
2231
1.45k
  m_ps->m_currentListLevel = 0;
2232
1.45k
  _changeList(); // flush the list
2233
2234
1.45k
  if (m_ps->m_parsingContext==ABW_FRAME_TEXTBOX)
2235
954
    m_outputElements.addCloseTextBox();
2236
1.45k
  m_outputElements.addCloseFrame();
2237
1.45k
  elements=&m_outputElements;
2238
1.45k
  pageFrame=m_ps->m_isPageFrame;
2239
1.45k
}
2240
2241
void libabw::ABWContentCollector::addFrameElements(ABWOutputElements &elements, bool pageFrame)
2242
1.45k
{
2243
1.45k
  if (pageFrame)
2244
26
    m_pageOutputElements.splice(elements);
2245
1.42k
  else
2246
1.42k
  {
2247
1.42k
    _openBlock();
2248
1.42k
    m_outputElements.splice(elements);
2249
1.42k
  }
2250
1.45k
}
2251
2252
void libabw::ABWContentCollector::collectData(const char *, const char *, const librevenge::RVNGBinaryData &)
2253
7.91k
{
2254
7.91k
}
2255
2256
void libabw::ABWContentCollector::insertImage(const char *dataid, const char *props)
2257
10.3k
{
2258
10.3k
  if (!m_ps->m_isSpanOpened)
2259
1.08k
    _openSpan();
2260
2261
10.3k
  ABWPropertyMap properties;
2262
10.3k
  if (props)
2263
6.96k
    parsePropString(props, properties);
2264
10.3k
  if (dataid)
2265
7.79k
  {
2266
7.79k
    auto iter = m_data.find(dataid);
2267
7.79k
    if (iter != m_data.end())
2268
2.83k
    {
2269
2.83k
      librevenge::RVNGPropertyList propList;
2270
2.83k
      ABWUnit unit(ABW_NONE);
2271
2.83k
      double value(0.0);
2272
2.83k
      ABWPropertyMap::const_iterator i = properties.find("height");
2273
2.83k
      if (i != properties.end() && findDouble(i->second, value, unit) && ABW_IN == unit)
2274
439
        propList.insert("svg:height", value);
2275
2.39k
      else
2276
2.39k
        propList.insert("fo:min-height", 1.0);
2277
2.83k
      i = properties.find("width");
2278
2.83k
      if (i != properties.end() && findDouble(i->second, value, unit) && ABW_IN == unit)
2279
512
        propList.insert("svg:width", value);
2280
2.32k
      else
2281
2.32k
        propList.insert("fo:min-width", 1.0);
2282
2.83k
      propList.insert("text:anchor-type", "as-char");
2283
2284
2.83k
      m_outputElements.addOpenFrame(propList);
2285
2286
2.83k
      propList.clear();
2287
2.83k
      propList.insert("librevenge:mime-type", iter->second.m_mimeType);
2288
2.83k
      propList.insert("office:binary-data", iter->second.m_binaryData);
2289
2.83k
      m_outputElements.addInsertBinaryObject(propList);
2290
2291
2.83k
      m_outputElements.addCloseFrame();
2292
2.83k
    }
2293
7.79k
  }
2294
10.3k
}
2295
2296
void libabw::ABWContentCollector::_handleListChange()
2297
314k
{
2298
314k
  int oldListLevel;
2299
314k
  if (m_ps->m_listLevels.empty())
2300
298k
    oldListLevel = 0;
2301
16.5k
  else
2302
16.5k
    oldListLevel = m_ps->m_listLevels.top().first;
2303
2304
314k
  if (m_ps->m_currentListLevel > oldListLevel)
2305
7.89k
  {
2306
7.89k
    if (!m_ps->m_isSectionOpened)
2307
1.07k
      _openSection();
2308
7.89k
    _recurseListLevels(oldListLevel, m_ps->m_currentListLevel, m_ps->m_currentListId);
2309
7.89k
  }
2310
306k
  else if (m_ps->m_currentListLevel < oldListLevel)
2311
6.35k
  {
2312
212k
    while (!m_ps->m_listLevels.empty() && m_ps->m_listLevels.top().first > m_ps->m_currentListLevel)
2313
206k
    {
2314
206k
      if (!m_ps->m_listLevels.top().second || m_ps->m_listLevels.top().second->getType() == ABW_UNORDERED)
2315
205k
        m_outputElements.addCloseUnorderedListLevel();
2316
789
      else
2317
789
        m_outputElements.addCloseOrderedListLevel();
2318
206k
      ABW_DEBUG_MSG(("Popped level %i off the list level stack\n", m_ps->m_listLevels.top().first));
2319
206k
      m_ps->m_listLevels.pop();
2320
206k
    }
2321
6.35k
  }
2322
314k
}
2323
2324
void libabw::ABWContentCollector::_writeOutDummyListLevels(int oldLevel, int newLevel)
2325
278k
{
2326
278k
  if (oldLevel < newLevel)
2327
270k
  {
2328
270k
    _writeOutDummyListLevels(oldLevel, newLevel-1);
2329
270k
    m_dummyListElements.push_back(std::make_shared<ABWUnorderedListElement>());
2330
270k
    m_dummyListElements.back()->m_listLevel = newLevel;
2331
270k
    m_ps->m_listLevels.push(std::make_pair(newLevel, m_dummyListElements.back()));
2332
270k
    librevenge::RVNGPropertyList propList;
2333
270k
    m_dummyListElements.back()->writeOut(propList);
2334
270k
    m_outputElements.addOpenUnorderedListLevel(propList);
2335
270k
  }
2336
278k
}
2337
2338
void libabw::ABWContentCollector::_recurseListLevels(int oldLevel, int newLevel, int newListId)
2339
8.18k
{
2340
8.18k
  if (oldLevel >= newLevel)
2341
185
    return;
2342
8.00k
  const auto iter = m_listElements.find(newListId);
2343
8.00k
  if (iter != m_listElements.end() && iter->second)
2344
7.94k
  {
2345
7.94k
    if (iter->second->m_parentId)
2346
291
      _recurseListLevels(oldLevel, newLevel-1, iter->second->m_parentId);
2347
7.65k
    else
2348
7.65k
      _writeOutDummyListLevels(oldLevel, newLevel-1);
2349
7.94k
    m_ps->m_listLevels.push(std::make_pair(newLevel, iter->second));
2350
7.94k
    librevenge::RVNGPropertyList propList;
2351
7.94k
    iter->second->writeOut(propList);
2352
    // osnola: use the element list id if set, if not use newListId
2353
7.94k
    propList.insert("librevenge:list-id",
2354
7.94k
                    iter->second->m_listId ? iter->second->m_listId : newListId);
2355
7.94k
    if (iter->second->getType() == ABW_UNORDERED)
2356
6.58k
      m_outputElements.addOpenUnorderedListLevel(propList);
2357
1.36k
    else
2358
1.36k
      m_outputElements.addOpenOrderedListLevel(propList);
2359
7.94k
  }
2360
8.00k
}
2361
2362
void libabw::ABWContentCollector::_changeList()
2363
314k
{
2364
314k
  _closeBlock();
2365
314k
  _handleListChange();
2366
314k
}
2367
2368
void libabw::ABWContentCollector::_closeListElement()
2369
18.3k
{
2370
18.3k
  if (m_ps->m_isListElementOpened)
2371
18.3k
  {
2372
18.3k
    if (m_ps->m_isSpanOpened)
2373
0
      _closeSpan();
2374
2375
18.3k
    m_outputElements.addCloseListElement();
2376
18.3k
  }
2377
18.3k
  m_ps->m_isListElementOpened = false;
2378
18.3k
  m_ps->m_isFirstTextInListElement = false;
2379
18.3k
}
2380
2381
void libabw::ABWContentCollector::addMetadataEntry(const char *const key, const char *const value)
2382
2.52k
{
2383
2.52k
  assert(key);
2384
2.52k
  assert(value);
2385
2386
2.52k
  m_metadata[key] = value;
2387
2.52k
}
2388
2389
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */