Coverage Report

Created: 2026-09-06 07:18

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