/src/libmspub/src/lib/MSPUBParser97.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 libmspub project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | */ |
9 | | |
10 | | #include "MSPUBParser97.h" |
11 | | |
12 | | #include <algorithm> |
13 | | #include <limits> |
14 | | #include <map> |
15 | | #include <memory> |
16 | | |
17 | | #include "MSPUBCollector.h" |
18 | | #include "MSPUBTypes.h" |
19 | | #include "libmspub_utils.h" |
20 | | |
21 | | namespace libmspub |
22 | | { |
23 | | |
24 | | MSPUBParser97::MSPUBParser97(librevenge::RVNGInputStream *input, MSPUBCollector *collector) |
25 | 4.04k | : MSPUBParser2k(input, collector), m_isBanner(false) |
26 | 4.04k | { |
27 | 4.04k | m_collector->useEncodingHeuristic(); |
28 | 4.04k | } |
29 | | |
30 | | unsigned short MSPUBParser97::getTextMarker() const |
31 | 22.5k | { |
32 | 22.5k | return 0x0000; |
33 | 22.5k | } |
34 | | |
35 | | unsigned MSPUBParser97::getTextIdOffset() const |
36 | 17.5k | { |
37 | 17.5k | return 0x46; |
38 | 17.5k | } |
39 | | |
40 | | bool MSPUBParser97::parse() |
41 | 4.04k | { |
42 | 4.04k | std::unique_ptr<librevenge::RVNGInputStream> contents(m_input->getSubStreamByName("Contents")); |
43 | 4.04k | if (!contents) |
44 | 0 | { |
45 | 0 | MSPUB_DEBUG_MSG(("Couldn't get contents stream.\n")); |
46 | 0 | return false; |
47 | 0 | } |
48 | 4.04k | if (!parseContents(contents.get())) |
49 | 49 | { |
50 | 49 | MSPUB_DEBUG_MSG(("Couldn't parse contents stream.\n")); |
51 | 49 | return false; |
52 | 49 | } |
53 | 3.99k | return m_collector->go(); |
54 | 4.04k | } |
55 | | |
56 | | bool MSPUBParser97::parseDocument(librevenge::RVNGInputStream *input) |
57 | 3.09k | { |
58 | 3.09k | if (bool(m_documentChunkIndex)) |
59 | 3.04k | { |
60 | 3.04k | input->seek(m_contentChunks[m_documentChunkIndex.get()].offset + 0x12, librevenge::RVNG_SEEK_SET); |
61 | 3.04k | unsigned short coordinateSystemMark = readU16(input); |
62 | 3.04k | m_isBanner = coordinateSystemMark == 0x0007; |
63 | 3.04k | unsigned width = readU32(input); |
64 | 3.04k | unsigned height = readU32(input); |
65 | 3.04k | m_collector->setWidthInEmu(width); |
66 | 3.04k | m_collector->setHeightInEmu(height); |
67 | 3.04k | return true; |
68 | 3.04k | } |
69 | 49 | return false; |
70 | 3.09k | } |
71 | | |
72 | | void MSPUBParser97::parseContentsTextIfNecessary(librevenge::RVNGInputStream *input) |
73 | 4.04k | { |
74 | 4.04k | input->seek(0x12, librevenge::RVNG_SEEK_SET); |
75 | 4.04k | input->seek(readU32(input), librevenge::RVNG_SEEK_SET); |
76 | 4.04k | input->seek(14, librevenge::RVNG_SEEK_CUR); |
77 | 4.04k | unsigned textStart = readU32(input); |
78 | 4.04k | unsigned textEnd = readU32(input); |
79 | 4.04k | unsigned prop1Index = readU16(input); |
80 | 4.04k | unsigned prop2Index = readU16(input); |
81 | 4.04k | unsigned prop3Index = readU16(input); |
82 | 4.04k | unsigned prop3End = readU16(input); |
83 | 4.04k | std::vector<SpanInfo97> spanInfos = getSpansInfo(input, prop1Index, |
84 | 4.04k | prop2Index, prop3Index, prop3End); |
85 | 4.04k | input->seek(textStart, librevenge::RVNG_SEEK_SET); |
86 | 4.04k | TextInfo97 textInfo = getTextInfo(input, textEnd - textStart); |
87 | 4.04k | unsigned iParaEnd = 0, iSpanEnd = 0; |
88 | 4.04k | unsigned currentParaIndex = 0; |
89 | 4.04k | unsigned currentSpanIndex = 0; |
90 | 540k | for (size_t iShapeEnd = 0; iShapeEnd < textInfo.m_shapeEnds.size(); ++iShapeEnd) |
91 | 536k | { |
92 | 536k | unsigned shapeEnd = std::min<unsigned>(textInfo.m_shapeEnds[iShapeEnd], textInfo.m_chars.size()); |
93 | 536k | std::vector<TextParagraph> shapeParas; |
94 | 1.11M | while (currentParaIndex < shapeEnd) |
95 | 574k | { |
96 | 574k | unsigned paraEnd = iParaEnd < textInfo.m_paragraphEnds.size() ? |
97 | 522k | textInfo.m_paragraphEnds[iParaEnd++] : shapeEnd; |
98 | 574k | if (paraEnd > shapeEnd) |
99 | 484k | { |
100 | 484k | --iParaEnd; |
101 | 484k | paraEnd = shapeEnd; |
102 | 484k | } |
103 | 574k | std::vector<TextSpan> paraSpans; |
104 | 5.19M | while (currentSpanIndex < paraEnd) |
105 | 4.62M | { |
106 | 4.62M | const SpanInfo97 &spanInfo = iSpanEnd < spanInfos.size() ? |
107 | 4.13M | spanInfos[iSpanEnd++] : |
108 | 4.62M | SpanInfo97(paraEnd, CharacterStyle()); |
109 | 4.62M | unsigned spanEnd = spanInfo.m_spanEnd; |
110 | 4.62M | if (spanEnd > paraEnd) |
111 | 84.8k | { |
112 | 84.8k | --iSpanEnd; |
113 | 84.8k | spanEnd = paraEnd; |
114 | 84.8k | } |
115 | 4.62M | const CharacterStyle &spanStyle = spanInfo.m_style; |
116 | 4.62M | std::vector<unsigned char> spanChars; |
117 | 4.62M | spanChars.reserve(std::min(spanEnd - currentSpanIndex, m_length)); |
118 | 268M | for (unsigned i = currentSpanIndex; i < spanEnd; ++i) |
119 | 263M | { |
120 | 263M | unsigned char ch = textInfo.m_chars[i]; |
121 | 263M | if (ch == 0xB) // Pub97 interprets vertical tab as nonbreaking space. |
122 | 295k | { |
123 | 295k | spanChars.push_back('\n'); |
124 | 295k | } |
125 | 263M | else if (ch == 0x0D) |
126 | 1.07M | { |
127 | 1.07M | if (i + 1 < spanEnd && textInfo.m_chars[i + 1] == 0x0A) |
128 | 204k | { |
129 | 204k | ++i; // ignore the 0x0D and advance past the 0x0A |
130 | 204k | } |
131 | 1.07M | } |
132 | 262M | else if (ch == 0x0C) |
133 | 1.20M | { |
134 | | // ignore the 0x0C |
135 | 1.20M | } |
136 | 261M | else |
137 | 261M | { |
138 | 261M | spanChars.push_back(ch); |
139 | 261M | } |
140 | 263M | } |
141 | 4.62M | paraSpans.push_back(TextSpan(spanChars, spanStyle)); |
142 | 4.62M | currentSpanIndex = spanEnd; |
143 | 4.62M | } |
144 | 574k | shapeParas.push_back(TextParagraph(paraSpans, ParagraphStyle())); |
145 | 574k | currentParaIndex = paraEnd; |
146 | 574k | } |
147 | 536k | m_collector->addTextString(shapeParas, iShapeEnd); |
148 | 536k | } |
149 | 4.04k | } |
150 | | |
151 | | std::vector<MSPUBParser97::SpanInfo97> MSPUBParser97::getSpansInfo( |
152 | | librevenge::RVNGInputStream *input, |
153 | | unsigned prop1Index, unsigned prop2Index, unsigned /* prop3Index */, |
154 | | unsigned /* prop3End */) |
155 | 3.95k | { |
156 | 3.95k | std::vector<unsigned> spanEnds; |
157 | 3.95k | std::vector<SpanInfo97> ret; |
158 | 9.68k | for (unsigned i = prop1Index; i < prop2Index; ++i) |
159 | 5.72k | { |
160 | 5.72k | unsigned offset = i * 0x200; |
161 | 5.72k | input->seek(offset + 0x1FF, librevenge::RVNG_SEEK_SET); |
162 | 5.72k | unsigned numEntries = readU8(input); |
163 | 5.72k | input->seek(offset, librevenge::RVNG_SEEK_SET); |
164 | | // Skip the first thing; it is not an end |
165 | 5.72k | unsigned start = readU32(input); |
166 | 347k | for (unsigned j = 0; j < numEntries; ++j) |
167 | 342k | { |
168 | 342k | spanEnds.push_back(readU32(input) - start); |
169 | 342k | } |
170 | 5.72k | std::vector<unsigned char> spanStyleIndices; |
171 | 18.8M | for (size_t j = 0; j < spanEnds.size(); ++j) |
172 | 18.8M | { |
173 | 18.8M | spanStyleIndices.push_back(readU8(input)); |
174 | 18.8M | } |
175 | 28.2k | while (stillReading(input, offset + 0x200) && readU8(input) == 0) |
176 | 22.5k | { |
177 | 22.5k | ; |
178 | 22.5k | } |
179 | 5.72k | input->seek(-1, librevenge::RVNG_SEEK_CUR); |
180 | 5.72k | std::map<unsigned char, CharacterStyle> stylesByIndex; |
181 | 121k | while (stillReading(input, offset + 0x1FF)) |
182 | 115k | { |
183 | 115k | unsigned length = readU8(input); |
184 | 115k | unsigned nextOffset = input->tell() + length; |
185 | 115k | auto index = static_cast<unsigned char>((input->tell() - 1 - offset) / 2); |
186 | 115k | stylesByIndex[index] = readCharacterStyle(input, length); |
187 | 115k | input->seek(nextOffset, librevenge::RVNG_SEEK_SET); |
188 | 115k | } |
189 | 18.7M | for (size_t j = 0; j < spanEnds.size(); ++j) |
190 | 18.7M | { |
191 | 18.7M | ret.push_back(SpanInfo97(spanEnds[j], j < spanStyleIndices.size() ? |
192 | 18.7M | stylesByIndex[spanStyleIndices[j]] : CharacterStyle())); |
193 | 18.7M | } |
194 | 5.72k | } |
195 | 3.95k | return ret; |
196 | 3.95k | } |
197 | | |
198 | | CharacterStyle MSPUBParser97::readCharacterStyle( |
199 | | librevenge::RVNGInputStream *input, unsigned length) |
200 | 115k | { |
201 | 115k | CharacterStyle style; |
202 | | |
203 | 115k | unsigned begin = input->tell(); |
204 | 115k | int textSizeVariationFromDefault = 0; |
205 | | |
206 | 115k | if (length >= 1) |
207 | 14.5k | { |
208 | 14.5k | unsigned char biFlags = readU8(input); |
209 | 14.5k | style.bold = biFlags & 0x1; |
210 | 14.5k | style.italic = biFlags & 0x2; |
211 | 14.5k | } |
212 | 115k | if (length >= 3) |
213 | 13.4k | { |
214 | 13.4k | input->seek(begin + 0x2, librevenge::RVNG_SEEK_SET); |
215 | 13.4k | style.fontIndex = readU8(input); |
216 | 13.4k | } |
217 | 115k | if (length >= 9) |
218 | 10.0k | { |
219 | 10.0k | input->seek(begin + 0x8, librevenge::RVNG_SEEK_SET); |
220 | 10.0k | if (readU8(input) & 0x1) |
221 | 2.45k | style.underline = Underline::Single; |
222 | 10.0k | } |
223 | 115k | if (length >= 5) |
224 | 13.2k | { |
225 | 13.2k | input->seek(begin + 0x4, librevenge::RVNG_SEEK_SET); |
226 | 13.2k | textSizeVariationFromDefault = |
227 | 13.2k | length >= 6 ? readS16(input) : readS8(input); |
228 | 13.2k | } |
229 | 115k | if (length >= 16) |
230 | 8.54k | { |
231 | 8.54k | input->seek(begin + 0xC, librevenge::RVNG_SEEK_SET); |
232 | 8.54k | style.colorIndex = getColorIndexByQuillEntry(readU32(input)); |
233 | 8.54k | } |
234 | 115k | style.textSizeInPt = 10 + |
235 | 115k | static_cast<double>(textSizeVariationFromDefault) / 2; |
236 | | |
237 | 115k | return style; |
238 | 115k | } |
239 | | |
240 | | MSPUBParser97::TextInfo97 MSPUBParser97::getTextInfo(librevenge::RVNGInputStream *input, unsigned length) |
241 | 3.85k | { |
242 | 3.85k | length = std::min(length, m_length); // sanity check |
243 | 3.85k | std::vector<unsigned char> chars; |
244 | 3.85k | chars.reserve(length); |
245 | 3.85k | std::vector<unsigned> paragraphEnds; |
246 | 3.85k | std::vector<unsigned> shapeEnds; |
247 | 3.85k | unsigned start = input->tell(); |
248 | 3.85k | unsigned char last = '\0'; |
249 | 13.6M | while (stillReading(input, start + length)) |
250 | 13.6M | { |
251 | 13.6M | chars.push_back(readU8(input)); |
252 | 13.6M | if (last == 0xD && chars.back() == 0xA) |
253 | 41.5k | { |
254 | 41.5k | paragraphEnds.push_back(chars.size()); |
255 | 41.5k | } |
256 | 13.5M | else if (chars.back() == 0xC) |
257 | 536k | { |
258 | 536k | shapeEnds.push_back(chars.size()); |
259 | 536k | } |
260 | 13.6M | last = chars.back(); |
261 | 13.6M | } |
262 | 3.85k | return TextInfo97(chars, paragraphEnds, shapeEnds); |
263 | 3.85k | } |
264 | | |
265 | | int MSPUBParser97::translateCoordinateIfNecessary(int coordinate) const |
266 | 225k | { |
267 | 225k | const int offset = (m_isBanner ? 120 : 25) * EMUS_IN_INCH; |
268 | 225k | if (std::numeric_limits<int>::min() + offset > coordinate) |
269 | 3.13k | return std::numeric_limits<int>::min(); |
270 | 222k | else |
271 | 222k | return coordinate - offset; |
272 | 225k | } |
273 | | |
274 | | unsigned MSPUBParser97::getFirstLineOffset() const |
275 | 36.6k | { |
276 | 36.6k | return 0x22; |
277 | 36.6k | } |
278 | | |
279 | | unsigned MSPUBParser97::getSecondLineOffset() const |
280 | 19.4k | { |
281 | 19.4k | return 0x2D; |
282 | 19.4k | } |
283 | | |
284 | | unsigned MSPUBParser97::getShapeFillTypeOffset() const |
285 | 35.8k | { |
286 | 35.8k | return 0x20; |
287 | 35.8k | } |
288 | | |
289 | | unsigned MSPUBParser97::getShapeFillColorOffset() const |
290 | 5.47k | { |
291 | 5.47k | return 0x18; |
292 | 5.47k | } |
293 | | |
294 | | } |
295 | | |
296 | | /* vim:set shiftwidth=2 softtabstop=2 expandtab: */ |