Coverage Report

Created: 2026-07-20 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwpd/src/lib/WP42Parser.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* libwpd
3
 * Version: MPL 2.0 / LGPLv2.1+
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * Major Contributor(s):
10
 * Copyright (C) 2003 William Lachance (wrlach@gmail.com)
11
 * Copyright (C) 2003 Marc Maurer (uwog@uwog.net)
12
 * Copyright (C) 2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13
 *
14
 * For minor contributions see the git repository.
15
 *
16
 * Alternatively, the contents of this file may be used under the terms
17
 * of the GNU Lesser General Public License Version 2.1 or later
18
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
19
 * applicable instead of those above.
20
 *
21
 * For further information visit http://libwpd.sourceforge.net
22
 */
23
24
/* "This product is not manufactured, approved, or supported by
25
 * Corel Corporation or Corel Corporation Limited."
26
 */
27
28
#include "WP42Parser.h"
29
30
#include <memory>
31
32
#include "WP42Part.h"
33
#include "WPXHeader.h"
34
#include "libwpd_internal.h"
35
#include "WPXTable.h"
36
#include "WP42FileStructure.h"
37
#include "WP42StylesListener.h"
38
#include "WP42ContentListener.h"
39
40
WP42Parser::WP42Parser(librevenge::RVNGInputStream *input, WPXEncryption *encryption) :
41
0
  WPXParser(input, nullptr, encryption)
42
0
{
43
0
}
44
45
WP42Parser::~WP42Parser()
46
0
{
47
0
}
48
49
void WP42Parser::parse(librevenge::RVNGInputStream *input, WPXEncryption *encryption, WP42Listener *listener)
50
0
{
51
0
  listener->startDocument();
52
53
0
  input->seek(0, librevenge::RVNG_SEEK_SET);
54
55
0
  WPD_DEBUG_MSG(("WordPerfect: Starting document body parse (position = %ld)\n",(long)input->tell()));
56
57
0
  parseDocument(input, encryption, listener);
58
59
0
  listener->endDocument();
60
0
}
61
62
// parseDocument: parses a document body (may call itself recursively, on other streams, or itself)
63
void WP42Parser::parseDocument(librevenge::RVNGInputStream *input, WPXEncryption *encryption, WP42Listener *listener)
64
0
{
65
0
  while (!input->isEnd())
66
0
  {
67
0
    unsigned char readVal;
68
0
    readVal = readU8(input, encryption);
69
70
0
    if (readVal < (unsigned char)0x20)
71
0
    {
72
0
      WPD_DEBUG_MSG(("Offset: %i, Handling Control Character 0x%2x\n", (unsigned int)input->tell(), readVal));
73
74
0
      switch (readVal)
75
0
      {
76
0
      case 0x09: // tab
77
0
        listener->insertTab(0, 0.0);
78
0
        break;
79
0
      case 0x0A: // hard new line
80
0
        listener->insertEOL();
81
0
        break;
82
0
      case 0x0B: // soft new page
83
0
        listener->insertBreak(WPX_SOFT_PAGE_BREAK);
84
0
        break;
85
0
      case 0x0C: // hard new page
86
0
        listener->insertBreak(WPX_PAGE_BREAK);
87
0
        break;
88
0
      case 0x0D: // soft new line
89
0
        listener->insertCharacter(' ');
90
0
        break;
91
0
      default:
92
        // unsupported or undocumented token, ignore
93
0
        break;
94
0
      }
95
0
    }
96
0
    else if (readVal >= (unsigned char)0x20 && readVal <= (unsigned char)0x7F)
97
0
    {
98
0
      WPD_DEBUG_MSG(("Offset: %i, Handling Ascii Character 0x%2x\n", (unsigned int)input->tell(), readVal));
99
100
      // normal ASCII characters
101
0
      listener->insertCharacter(readVal);
102
0
    }
103
0
    else if (readVal >= (unsigned char)0x80 && readVal <= (unsigned char)0xBF)
104
0
    {
105
0
      WPD_DEBUG_MSG(("Offset: %i, Handling Single Character Function 0x%2x\n", (unsigned int)input->tell(), readVal));
106
107
      // single character function codes
108
0
      switch (readVal)
109
0
      {
110
0
      case 0x92:
111
0
        listener->attributeChange(true, WP42_ATTRIBUTE_STRIKE_OUT);
112
0
        break;
113
0
      case 0x93:
114
0
        listener->attributeChange(false, WP42_ATTRIBUTE_STRIKE_OUT);
115
0
        break;
116
0
      case 0x94:
117
0
        listener->attributeChange(true, WP42_ATTRIBUTE_UNDERLINE);
118
0
        break;
119
0
      case 0x95:
120
0
        listener->attributeChange(false, WP42_ATTRIBUTE_UNDERLINE);
121
0
        break;
122
123
0
      case 0x90:
124
0
        listener->attributeChange(true, WP42_ATTRIBUTE_REDLINE);
125
0
        break;
126
0
      case 0x91:
127
0
        listener->attributeChange(false, WP42_ATTRIBUTE_REDLINE);
128
0
        break;
129
130
0
      case 0x9C:
131
0
        listener->attributeChange(false, WP42_ATTRIBUTE_BOLD);
132
0
        break;
133
0
      case 0x9D:
134
0
        listener->attributeChange(true, WP42_ATTRIBUTE_BOLD);
135
0
        break;
136
137
0
      case 0xB2:
138
0
        listener->attributeChange(true, WP42_ATTRIBUTE_ITALICS);
139
0
        break;
140
0
      case 0xB3:
141
0
        listener->attributeChange(false, WP42_ATTRIBUTE_ITALICS);
142
0
        break;
143
0
      case 0xB4:
144
0
        listener->attributeChange(true, WP42_ATTRIBUTE_SHADOW);
145
0
        break;
146
0
      case 0xB5:
147
0
        listener->attributeChange(false, WP42_ATTRIBUTE_SHADOW);
148
0
        break;
149
150
0
      default:
151
        // unsupported or undocumented token, ignore
152
0
        break;
153
0
      }
154
0
    }
155
0
    else if (readVal >= (unsigned char)0xC0 && readVal <= (unsigned char)0xFE)
156
0
    {
157
0
      std::unique_ptr<WP42Part> part(WP42Part::constructPart(input, encryption, readVal));
158
0
      if (part)
159
0
        part->parse(listener);
160
0
    }
161
    // ignore the rest since they are not documented and at least 0xFF is a special character that
162
    // marks end of variable length part in variable length multi-byte functions
163
0
  }
164
0
}
165
166
void WP42Parser::parse(librevenge::RVNGTextInterface *documentInterface)
167
0
{
168
0
  librevenge::RVNGInputStream *input = getInput();
169
0
  WPXEncryption *encryption = getEncryption();
170
0
  std::list<WPXPageSpan> pageList;
171
172
0
  try
173
0
  {
174
    // do a "first-pass" parse of the document
175
    // gather table border information, page properties (per-page)
176
0
    WP42StylesListener stylesListener(pageList);
177
0
    parse(input, encryption, &stylesListener);
178
179
    // postprocess the pageList == remove duplicate page spans due to the page breaks
180
0
    auto previousPage = pageList.begin();
181
0
    for (auto Iter=pageList.begin(); Iter != pageList.end();)
182
0
    {
183
0
      if ((Iter != previousPage) && ((*previousPage)==(*Iter)))
184
0
      {
185
0
        (*previousPage).setPageSpan((*previousPage).getPageSpan() + (*Iter).getPageSpan());
186
0
        Iter = pageList.erase(Iter);
187
0
      }
188
0
      else
189
0
      {
190
0
        previousPage = Iter;
191
0
        ++Iter;
192
0
      }
193
0
    }
194
195
    // second pass: here is where we actually send the messages to the target app
196
    // that are necessary to emit the body of the target document
197
0
    WP42ContentListener listener(pageList, documentInterface);
198
0
    parse(input, encryption, &listener);
199
0
  }
200
0
  catch (FileException)
201
0
  {
202
0
    WPD_DEBUG_MSG(("WordPerfect: File Exception. Parse terminated prematurely."));
203
0
    throw FileException();
204
0
  }
205
206
0
}
207
208
void WP42Parser::parseSubDocument(librevenge::RVNGTextInterface *documentInterface)
209
0
{
210
0
  std::list<WPXPageSpan> pageList;
211
212
0
  librevenge::RVNGInputStream *input = getInput();
213
214
0
  try
215
0
  {
216
0
    WP42StylesListener stylesListener(pageList);
217
0
    stylesListener.startSubDocument();
218
0
    parseDocument(input, nullptr, &stylesListener);
219
0
    stylesListener.endSubDocument();
220
221
0
    WP42ContentListener listener(pageList, documentInterface);
222
0
    listener.startSubDocument();
223
0
    parseDocument(input, nullptr, &listener);
224
0
    listener.endSubDocument();
225
0
  }
226
0
  catch (FileException)
227
0
  {
228
0
    WPD_DEBUG_MSG(("WordPerfect: File Exception. Parse terminated prematurely."));
229
0
    throw FileException();
230
0
  }
231
0
}
232
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */