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) 2002-2003 William Lachance (wrlach@gmail.com)
11  * Copyright (C) 2002 Marc Maurer (uwog@uwog.net)
12  * Copyright (C) 2004 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 "WP6EOLGroup.h"
29 #include "WP6Listener.h"
30 #include "libwpd_internal.h"
31 
32 #include "WP6FillStylePacket.h" // for the fill packet
33 
WP6EOLGroup(librevenge::RVNGInputStream * input,WPXEncryption * encryption)34 WP6EOLGroup::WP6EOLGroup(librevenge::RVNGInputStream *input, WPXEncryption *encryption) :
35 	WP6VariableLengthGroup(),
36 	m_colSpan(1),
37 	m_rowSpan(1),
38 	m_boundFromAbove(false),
39 
40 	m_useCellAttributes(false),
41 	m_useCellJustification(false),
42 	m_ignoreInCalculations(false),
43 	m_cellIsLocked(false),
44 	m_cellAttributes(0),
45 	m_cellJustification(0),
46 	m_cellVerticalAlign(TOP),
47 
48 	m_cellFgColor(),
49 	m_cellBgColor(),
50 	m_cellBorderColor(new RGBSColor(0x00,0x00,0x00,0x64)),
51 
52 	m_cellBorders(0x00),
53 
54 	m_isHeaderRow(false),
55 	m_isMinimumHeight(true),
56 	m_rowHeight(0),
57 	m_isDontEndAParagraphStyleForThisHardReturn(false)
58 {
59 	_read(input, encryption);
60 }
61 
~WP6EOLGroup()62 WP6EOLGroup::~WP6EOLGroup()
63 {
64 }
65 
_readContents(librevenge::RVNGInputStream * input,WPXEncryption * encryption)66 void WP6EOLGroup::_readContents(librevenge::RVNGInputStream *input, WPXEncryption *encryption)
67 {
68 	WPD_DEBUG_MSG(("WordPerfect: EOL Group: Reading Embedded Sub-Function Data\n"));
69 	long startPosition = input->tell();
70 	unsigned short sizeDeletableSubFunctionData = readU16(input, encryption);
71 	WPD_DEBUG_MSG(("WordPerfect: EOL Group: Size of Deletable Sub-Function Data: %ld,  Size of Deletable and Non-deletable sub-function data: %ld\n", (long) sizeDeletableSubFunctionData, (long) getSizeNonDeletable()));
72 	if ((long)sizeDeletableSubFunctionData > (long)getSizeNonDeletable())
73 	{
74 		WPD_DEBUG_MSG(("WordPerfect: EOL Group: Possible corruption detected, bailing out\n"));
75 		throw FileException();
76 	}
77 
78 	input->seek(sizeDeletableSubFunctionData, librevenge::RVNG_SEEK_CUR);
79 	while ((long)input->tell() < (long)(startPosition + getSizeNonDeletable()))
80 	{
81 		unsigned char byte;
82 		unsigned short numBytesToSkip = 0;
83 		byte = readU8(input, encryption);
84 		long startPosition2 = input->tell();
85 		switch (byte)
86 		{
87 		case WP6_EOL_GROUP_ROW_INFORMATION:
88 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: ROW_INFORMATION\n"));
89 			numBytesToSkip = WP6_EOL_GROUP_ROW_INFORMATION_SIZE;
90 			unsigned char rowFlags;
91 			rowFlags = readU8(input, encryption);
92 			if ((rowFlags & 0x04) == 0x04)
93 				m_isHeaderRow = true;
94 			if ((rowFlags & 0x02) == 0x02)
95 			{
96 				if ((rowFlags & 0x10) == 0x10)
97 					m_isMinimumHeight = true;
98 				else
99 					m_isMinimumHeight = false;
100 				m_rowHeight = readU16(input, encryption);
101 			}
102 			else
103 			{
104 				m_isMinimumHeight = true;
105 				m_rowHeight = 0x0000;
106 			}
107 			break;
108 		case WP6_EOL_GROUP_CELL_FORMULA:
109 			unsigned short embeddedSubGroupSize;
110 			embeddedSubGroupSize = readU16(input, encryption);
111 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: CELL_FORMULA (length: %ld)\n",
112 			               (long) embeddedSubGroupSize));
113 			numBytesToSkip = embeddedSubGroupSize;
114 			break;
115 		case WP6_EOL_GROUP_TOP_GUTTER_SPACING:
116 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: TOP_GUTTER_SPACING\n"));
117 			numBytesToSkip = WP6_EOL_GROUP_TOP_GUTTER_SPACING_SIZE;
118 			break;
119 		case WP6_EOL_GROUP_BOTTOM_GUTTER_SPACING:
120 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: BOTTOM_GUTTER_SPACING\n"));
121 			numBytesToSkip = WP6_EOL_GROUP_BOTTOM_GUTTER_SPACING_SIZE;
122 			break;
123 		case WP6_EOL_GROUP_CELL_INFORMATION:
124 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: CELL_INFORMATION\n"));
125 			numBytesToSkip = WP6_EOL_GROUP_CELL_INFORMATION_SIZE;
126 			unsigned char cellFlag, tmpCellVerticalAlign;
127 			unsigned short attributeWord1, attributeWord2;
128 			cellFlag = readU8(input, encryption);
129 			if ((cellFlag & 0x01) == 0x01)
130 				m_useCellAttributes = true;
131 			if ((cellFlag & 0x02) == 0x02)
132 				m_useCellJustification = true;
133 			if ((cellFlag & 0x40) == 0x40)
134 				m_ignoreInCalculations = true;
135 			if ((cellFlag & 0x80) == 0x80)
136 				m_cellIsLocked = true;
137 			m_cellJustification = (readU8(input, encryption) & 0x07);
138 			tmpCellVerticalAlign = readU8(input, encryption);
139 			switch (tmpCellVerticalAlign & 0x03)
140 			{
141 			case 0x00: // top
142 				m_cellVerticalAlign = TOP;
143 				break;
144 			case 0x01: // center
145 				m_cellVerticalAlign = MIDDLE;
146 				break;
147 			case 0x02: // bottom
148 				m_cellVerticalAlign = BOTTOM;
149 				break;
150 			case 0x03: // full
151 				m_cellVerticalAlign = FULL;
152 				break;
153 			default:
154 				break;
155 			}
156 			attributeWord1 = readU16(input, encryption);
157 			attributeWord2 = readU16(input, encryption);
158 			m_cellAttributes = (unsigned)(((attributeWord2 & 0x03) << 16) + attributeWord1);
159 			break;
160 		case WP6_EOL_GROUP_CELL_SPANNING_INFORMATION:
161 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: CELL_SPANNING_INFORMATION\n"));
162 			numBytesToSkip = WP6_EOL_GROUP_CELL_SPANNING_INFORMATION_SIZE;
163 			m_colSpan = readU8(input, encryption);
164 			m_rowSpan = readU8(input, encryption);
165 			WPD_DEBUG_MSG(("WordPerfect: num cells spanned (h:%ld, v:%ld)\n",
166 			               (long) m_colSpan, (long) m_rowSpan));
167 			if (m_colSpan >= 128)
168 				m_boundFromAbove = true;
169 			break;
170 		case WP6_EOL_GROUP_CELL_FILL_COLORS:
171 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: CELL_FILL_COLORS\n"));
172 			numBytesToSkip = WP6_EOL_GROUP_CELL_FILL_COLORS_SIZE;
173 			unsigned char fR, fG, fB, fS;
174 			unsigned char bR, bG, bB, bS;
175 
176 			fR = readU8(input, encryption);
177 			fG = readU8(input, encryption);
178 			fB = readU8(input, encryption);
179 			fS = readU8(input, encryption);
180 			bR = readU8(input, encryption);
181 			bG = readU8(input, encryption);
182 			bB = readU8(input, encryption);
183 			bS = readU8(input, encryption);
184 
185 			m_cellFgColor.reset(new RGBSColor(fR,fG,fB,fS));
186 			m_cellBgColor.reset(new RGBSColor(bR,bG,bB,bS));
187 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded FG Color (%i, %i, %i, %i) BG Color (%i, %i, %i, %i)\n",
188 			               m_cellFgColor->m_r, m_cellFgColor->m_g, m_cellFgColor->m_b, m_cellFgColor->m_s,
189 			               m_cellBgColor->m_r, m_cellBgColor->m_g, m_cellBgColor->m_b, m_cellBgColor->m_s));
190 			break;
191 		case WP6_EOL_GROUP_CELL_LINE_COLOR:
192 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: CELL_LINE_COLOR\n"));
193 			numBytesToSkip = WP6_EOL_GROUP_CELL_LINE_COLOR_SIZE;
194 
195 			m_cellBorderColor->m_r = readU8(input, encryption);
196 			m_cellBorderColor->m_g = readU8(input, encryption);
197 			m_cellBorderColor->m_b = readU8(input, encryption);
198 			m_cellBorderColor->m_s = readU8(input, encryption);
199 
200 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Border Color (%i, %i, %i, %i)\n",
201 			               m_cellBorderColor->m_r, m_cellBorderColor->m_g, m_cellBorderColor->m_b, m_cellBorderColor->m_s));
202 			break;
203 		case WP6_EOL_GROUP_CELL_NUMBER_TYPE:
204 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: CELL_NUMBER_TYPE\n"));
205 			numBytesToSkip = WP6_EOL_GROUP_CELL_NUMBER_TYPE_SIZE;
206 			break;
207 		case WP6_EOL_GROUP_CELL_FLOATING_POINT_NUMBER:
208 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: CELL_FLOATING_POINT_NUMBER\n"));
209 			numBytesToSkip = WP6_EOL_GROUP_CELL_FLOATING_POINT_NUMBER_SIZE;
210 			break;
211 		case WP6_EOL_GROUP_CELL_PREFIX_FLAG:
212 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: CELL_PREFIX_FLAG\n"));
213 			numBytesToSkip = WP6_EOL_GROUP_CELL_PREFIX_FLAG_SIZE;
214 			m_cellBorders = readU8(input, encryption);
215 			break;
216 		case WP6_EOL_GROUP_CELL_RECALCULATION_ERROR_NUMBER:
217 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: CELL_RECALCULATION_ERROR_NUMBER\n"));
218 			numBytesToSkip = WP6_EOL_GROUP_CELL_RECALCULATION_ERROR_NUMBER_SIZE;
219 			break;
220 		case WP6_EOL_GROUP_DONT_END_A_PARAGRAPH_STYLE_FOR_THIS_HARD_RETURN:
221 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: DONT_END_A_PARAGRAPH_STYLE_FOR_THIS_HARD_RETURN\n"));
222 			numBytesToSkip = WP6_EOL_GROUP_DONT_END_A_PARAGRAPH_STYLE_FOR_THIS_HARD_RETURN_SIZE;
223 			m_isDontEndAParagraphStyleForThisHardReturn = true;
224 			break;
225 		case 0x8e:
226 		case 0x8f:
227 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: UNKNOWN SUBFUNCTION (%x) (BAD BAD BAD)\n", byte));
228 			numBytesToSkip = readU16(input, encryption); // It seems that these two unknow sub-functions have their
229 			// length information embedded: <subfunction>[length]...[length]<subfunction>
230 			break;
231 		default:
232 			WPD_DEBUG_MSG(("WordPerfect: EOL Group Embedded Sub-Function: UNKNOWN SUBFUNCTION (%x) (BAD BAD BAD)\n", byte));
233 			WPD_DEBUG_MSG(("WordPerfect: Possible corruption detected, bailing out\n"));
234 			throw FileException();
235 		}
236 
237 		if (startPosition2 + numBytesToSkip - 1 - input->tell() < 0)
238 			throw FileException();
239 		input->seek((startPosition2 + numBytesToSkip - 1), librevenge::RVNG_SEEK_SET);
240 	}
241 }
242 
parse(WP6Listener * listener)243 void WP6EOLGroup::parse(WP6Listener *listener)
244 {
245 	WPD_DEBUG_MSG(("WordPerfect: handling an EOL group\n"));
246 
247 	// first off, grab any prefix information which may be useful
248 	const RGBSColor *cellFgColor = m_cellFgColor.get();
249 	const RGBSColor *cellBgColor = m_cellBgColor.get();
250 	const RGBSColor *cellBorderColor = m_cellBorderColor.get();
251 
252 	if (!cellFgColor && !cellBgColor)
253 	{
254 		for (int i=0; i<getNumPrefixIDs(); i++)
255 		{
256 			if (const auto *fsPacket = dynamic_cast<const WP6FillStylePacket *>(listener->getPrefixDataPacket(getPrefixIDs()[i])))
257 			{
258 
259 				cellFgColor = fsPacket->getFgColor();
260 				cellBgColor = fsPacket->getBgColor();
261 			}
262 		}
263 	}
264 
265 	// main search + dispatch for messages
266 	switch (getSubGroup())
267 	{
268 	case 0: // 0x00 (beginning of file)
269 		break; // ignore
270 	case WP6_EOL_GROUP_SOFT_EOL:
271 	case WP6_EOL_GROUP_SOFT_EOC:
272 	case WP6_EOL_GROUP_SOFT_EOC_AT_EOP: // 0x03 (soft EOC at EOP)
273 		listener->insertCharacter((unsigned) ' ');
274 		break;
275 	case WP6_EOL_GROUP_DELETABLE_HARD_EOL: // 0x17 (deletable hard EOL)
276 	case WP6_EOL_GROUP_DELETABLE_HARD_EOL_AT_EOC: // 0x18 (deletable hard EOL at EOC)
277 	case WP6_EOL_GROUP_DELETABLE_HARD_EOL_AT_EOP: // 0x19 (deletable hard EOL at EOP)
278 	case WP6_EOL_GROUP_DELETABLE_HARD_EOP: // deletable hard EOP
279 	case WP6_EOL_GROUP_HARD_EOL:
280 	case WP6_EOL_GROUP_HARD_EOL_AT_EOC:
281 	case WP6_EOL_GROUP_HARD_EOL_AT_EOP:
282 		listener->insertEOL();
283 		break;
284 	case WP6_EOL_GROUP_DELETABLE_SOFT_EOL: // 0x014 (deletable soft EOL)
285 		if (m_isDontEndAParagraphStyleForThisHardReturn)
286 			listener->handleLineBreak();
287 		break;
288 	case WP6_EOL_GROUP_HARD_EOC: // 0x07 (hard end of column)
289 	case WP6_EOL_GROUP_HARD_EOC_AT_EOP:
290 	case WP6_EOL_GROUP_DELETABLE_HARD_EOC:
291 	case WP6_EOL_GROUP_DELETABLE_HARD_EOC_AT_EOP:
292 		listener->insertBreak(WPX_COLUMN_BREAK);
293 		break;
294 	case WP6_EOL_GROUP_HARD_EOP: // hard EOP
295 		listener->insertBreak(WPX_PAGE_BREAK);
296 		break;
297 	case WP6_EOL_GROUP_TABLE_CELL: // Table Cell
298 		WPD_DEBUG_MSG(("WordPerfect: EOL group: table cell\n"));
299 		if (!m_boundFromAbove)
300 		{
301 			listener->insertCell(m_colSpan, m_rowSpan, m_cellBorders, cellFgColor, cellBgColor,
302 			                     cellBorderColor, m_cellVerticalAlign, m_useCellAttributes, m_cellAttributes);
303 			if (m_useCellJustification)
304 				listener->justificationChange(m_cellJustification);
305 		}
306 		break;
307 	case WP6_EOL_GROUP_TABLE_ROW_AND_CELL:
308 	case WP6_EOL_GROUP_TABLE_ROW_AT_EOC:
309 	case WP6_EOL_GROUP_TABLE_ROW_AT_EOP:
310 	case WP6_EOL_GROUP_TABLE_ROW_AT_HARD_EOC:
311 	case WP6_EOL_GROUP_TABLE_ROW_AT_HARD_EOC_AT_HARD_EOP:
312 	case WP6_EOL_GROUP_TABLE_ROW_AT_HARD_EOP:
313 		WPD_DEBUG_MSG(("WordPerfect: EOL group: table row and cell\n"));
314 
315 		listener->insertRow(m_rowHeight, m_isMinimumHeight, m_isHeaderRow);
316 		// the cellBorders variable already represent the cell border bits as well
317 		if (!m_boundFromAbove)
318 		{
319 			listener->insertCell(m_colSpan, m_rowSpan, m_cellBorders, cellFgColor, cellBgColor,
320 			                     cellBorderColor, m_cellVerticalAlign, m_useCellAttributes, m_cellAttributes);
321 			if (m_useCellJustification)
322 				listener->justificationChange(m_cellJustification);
323 		}
324 		break;
325 	case WP6_EOL_GROUP_TABLE_OFF:
326 	case WP6_EOL_GROUP_TABLE_OFF_AT_EOC:
327 	case WP6_EOL_GROUP_TABLE_OFF_AT_EOC_AT_EOP:
328 		listener->endTable();
329 		break;
330 //		case WP6_EOL_GROUP_DELETABLE_SOFT_EOC: // 0x15 (deletable soft EOC)
331 //		case WP6_EOL_GROUP_DELETABLE_SOFT_EOC_AT_EOP: // 0x16 (deleteable soft EOC at EOP)
332 	default: // something else we don't support yet
333 		break;
334 	}
335 
336 	// search for soft page breaks and dispatch messages to that effect
337 	switch (getSubGroup())
338 	{
339 	case WP6_EOL_GROUP_HARD_EOL_AT_EOP:
340 	case WP6_EOL_GROUP_TABLE_ROW_AT_EOP:
341 	case WP6_EOL_GROUP_TABLE_ROW_AT_HARD_EOC_AT_HARD_EOP:
342 	case WP6_EOL_GROUP_TABLE_ROW_AT_HARD_EOP:
343 	case WP6_EOL_GROUP_TABLE_OFF_AT_EOC_AT_EOP:
344 		listener->insertBreak(WPX_SOFT_PAGE_BREAK);
345 		break;
346 	default:
347 		break;
348 	}
349 }
350 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
351