1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* libwps
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) 2009, 2011 Alonso Laurent (alonso@loria.fr)
11  * Copyright (C) 2006, 2007 Andrew Ziem
12  * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13  * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
14  * Copyright (C) 2003-2005 William Lachance (william.lachance@sympatico.ca)
15  *
16  * For minor contributions see the git repository.
17  *
18  * Alternatively, the contents of this file may be used under the terms
19  * of the GNU Lesser General Public License Version 2.1 or later
20  * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
21  * applicable instead of those above.
22  *
23  * For further information visit http://libwps.sourceforge.net
24  */
25 
26 #include <iomanip>
27 #include <iostream>
28 
29 #include <librevenge/librevenge.h>
30 
31 #include "libwps_internal.h"
32 #include "WPSContentListener.h"
33 #include "WPSEntry.h"
34 #include "WPSFont.h"
35 #include "WPSOLEParser.h"
36 #include "WPSParagraph.h"
37 #include "WPSPosition.h"
38 
39 #include "WPS4.h"
40 
41 #include "WPS4Graph.h"
42 
43 /** Internal: the structures of a WPS4Graph */
44 namespace WPS4GraphInternal
45 {
46 //! Internal: the state of a WPS4Graph
47 struct State
48 {
StateWPS4GraphInternal::State49 	State() : m_version(-1), m_numPages(0), m_objects(), m_objectsId(), m_parsed() {}
50 	//! the version
51 	int m_version;
52 	//! the number page
53 	int m_numPages;
54 
55 	//! the list of objects
56 	std::vector<WPSOLEParserObject> m_objects;
57 	//! the list of object's ids
58 	std::vector<int> m_objectsId;
59 	//! list of flags to know if the data are been sent to the listener
60 	std::vector<bool> m_parsed;
61 };
62 }
63 
64 // constructor/destructor
WPS4Graph(WPS4Parser & parser)65 WPS4Graph::WPS4Graph(WPS4Parser &parser):
66 	m_listener(), m_mainParser(parser), m_state(new WPS4GraphInternal::State),
67 	m_asciiFile(parser.ascii())
68 {
69 }
70 
~WPS4Graph()71 WPS4Graph::~WPS4Graph()
72 {
73 }
74 
75 // small functions: version/numpages/update position
version() const76 int WPS4Graph::version() const
77 {
78 	if (m_state->m_version <= 0)
79 		m_state->m_version = m_mainParser.version();
80 	return m_state->m_version;
81 }
numPages() const82 int WPS4Graph::numPages() const
83 {
84 	return m_state->m_numPages;
85 }
86 
computePositions() const87 void WPS4Graph::computePositions() const
88 {
89 	size_t numObject = m_state->m_objects.size();
90 	m_state->m_numPages = numObject ? 1 : 0;
91 	m_state->m_parsed.resize(numObject, false);
92 }
93 
94 // update the positions and send data to the listener
storeObjects(std::vector<WPSOLEParserObject> const & objects,std::vector<int> const & ids)95 void WPS4Graph::storeObjects(std::vector<WPSOLEParserObject> const &objects,
96                              std::vector<int> const &ids)
97 {
98 	size_t numObject = objects.size();
99 	if (numObject != ids.size())
100 	{
101 		WPS_DEBUG_MSG(("WPS4Graph::storeObjects: unconsistent arguments\n"));
102 		return;
103 	}
104 	for (size_t i = 0; i < numObject; i++)
105 	{
106 		m_state->m_objects.push_back(objects[i]);
107 		m_state->m_objectsId.push_back(ids[i]);
108 	}
109 }
110 
111 // send object
sendObject(Vec2f const & sz,int id)112 void WPS4Graph::sendObject(Vec2f const &sz, int id)
113 {
114 	if (m_listener.get() == 0L)
115 	{
116 		WPS_DEBUG_MSG(("WPS4Graph::sendObject: listener is not set\n"));
117 		return;
118 	}
119 
120 	size_t numObject = m_state->m_objects.size();
121 	int pos = -1;
122 	for (size_t g = 0; g < numObject; g++)
123 	{
124 		if (m_state->m_objectsId[g] != id) continue;
125 		pos = int(g);
126 	}
127 
128 	if (pos < 0)
129 	{
130 		WPS_DEBUG_MSG(("WPS4Graph::sendObject: can not find %d object\n", id));
131 		return;
132 	}
133 
134 	m_state->m_parsed[size_t(pos)] = true;
135 	WPSPosition posi(Vec2f(),sz);
136 	posi.setRelativePosition(WPSPosition::CharBaseLine);
137 	posi.m_wrapping = WPSPosition::WDynamic;
138 	float scale = float(1.0/m_state->m_objects[size_t(pos)].m_position.getInvUnitScale(librevenge::RVNG_INCH));
139 	posi.setNaturalSize(scale*m_state->m_objects[size_t(pos)].m_position.naturalSize());
140 	m_listener->insertPicture(posi, m_state->m_objects[size_t(pos)].m_data, m_state->m_objects[size_t(pos)].m_mime);
141 }
142 
sendObjects(int page)143 void WPS4Graph::sendObjects(int page)
144 {
145 	if (page != -1) return;
146 	if (m_listener.get() == 0L)
147 	{
148 		WPS_DEBUG_MSG(("WPS4Graph::sendObjects: listener is not set\n"));
149 		return;
150 	}
151 
152 	size_t numObject = m_state->m_objects.size();
153 #ifdef DEBUG
154 	bool firstSend = false;
155 #endif
156 	for (size_t g = 0; g < numObject; g++)
157 	{
158 		if (m_state->m_parsed[g]) continue;
159 #ifdef DEBUG
160 		if (!firstSend)
161 		{
162 			firstSend = true;
163 			WPS_DEBUG_MSG(("WPS4Graph::sendObjects: find some extra pictures\n"));
164 			m_listener->setFont(WPSFont::getDefault());
165 			m_listener->setParagraph(WPSParagraph());
166 			m_listener->insertEOL();
167 			librevenge::RVNGString message = "--------- The original document has some extra pictures: -------- ";
168 			m_listener->insertUnicodeString(message);
169 			m_listener->insertEOL();
170 		}
171 #endif
172 		m_state->m_parsed[g] = true;
173 		// as we do not have the size of the data, we insert small picture
174 		WPSPosition pos(Vec2f(),Vec2f(1.,1.));
175 		pos.setRelativePosition(WPSPosition::CharBaseLine);
176 		pos.m_wrapping = WPSPosition::WDynamic;
177 		m_listener->insertPicture(pos, m_state->m_objects[g].m_data, m_state->m_objects[g].m_mime);
178 	}
179 }
180 
181 ////////////////////////////////////////////////////////////
182 //  low level
183 ////////////////////////////////////////////////////////////
readObject(RVNGInputStreamPtr input,WPSEntry const & entry)184 int WPS4Graph::readObject(RVNGInputStreamPtr input, WPSEntry const &entry)
185 {
186 	int resId = -1;
187 	if (!entry.valid() || entry.length() <= 4)
188 	{
189 		WPS_DEBUG_MSG(("WPS4Graph::readObject: invalid object\n"));
190 		return false;
191 	}
192 	long endPos = entry.end();
193 	input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
194 
195 	libwps::DebugStream f;
196 	int numFind = 0;
197 
198 	librevenge::RVNGBinaryData pict;
199 	WPSPosition pictPos;
200 	int actConfidence = -100;
201 	int oleId = -1;
202 	bool replace = false;
203 
204 	long lastPos;
205 	while (1)
206 	{
207 		WPSPosition actPictPos;
208 		lastPos = input->tell();
209 		if (lastPos >= endPos) break;
210 
211 		f.str("");
212 		f << "ZZEOBJ" << entry.id() << "-" << numFind++ << "(Contents):";
213 		int type = libwps::readU16(input);
214 		if (type == 0x4f4d)   // OM
215 		{
216 			if (lastPos+8 > endPos) break;
217 
218 			oleId = libwps::read16(input);
219 			f << "Ole" << oleId << ",";
220 			int unkn = libwps::read16(input);
221 			if (unkn) f << "#unkn=" << unkn << ",";
222 			int val = libwps::read16(input);
223 			f << val << ",";
224 			if (lastPos+10 <= endPos) f << std::hex << libwps::read16(input);
225 
226 			for (size_t i = 0; i < m_state->m_objectsId.size(); i++)
227 			{
228 				if (m_state->m_objectsId[i] != oleId) continue;
229 				if (0 > actConfidence)
230 				{
231 					actConfidence = 0;
232 					pict = m_state->m_objects[i].m_data;
233 					replace = false;
234 				}
235 			}
236 			ascii().addPos(lastPos);
237 			ascii().addNote(f.str().c_str());
238 			continue;
239 		}
240 		bool readData = false;
241 		long endDataPos = -1;
242 		int confidence = -1;
243 		if (type == 0x8)
244 		{
245 			if (lastPos+6 > endPos) break;
246 			// a simple metafile object ?
247 			float dim[2];
248 			for (int i = 0; i < 2; i++) dim[i] = float(libwps::read16(input)/1440.);
249 			// look like the final size : so we can use it as the picture size :-~
250 			f << "sz=(" << dim[0] << "," << dim[1] << ")," << libwps::read16(input);
251 			readData = true;
252 			endDataPos = endPos-1;
253 			confidence = 0;
254 		}
255 		else if (type == 0x501)
256 		{
257 			// find some metafile picture and MSDraw picture in v2 file
258 			if (lastPos+24 > endPos) break;
259 			// list of ole object, metafile, ...
260 			long val = libwps::readU16(input);
261 			if (val) f << "#unkn=" << val << ",";
262 			int type_ = libwps::read32(input);
263 			f << "type=" << type_ << ",";
264 			long nSize = libwps::read32(input);
265 			if (nSize <= 0 || lastPos+22+nSize > endPos) break;
266 
267 			std::string name;
268 			for (int i = 0; i < nSize; i++)
269 			{
270 				char c = char(libwps::readU8(input));
271 				if (c==0) break;
272 				name+= c;
273 			}
274 			f << "name='" << name << "',";
275 			for (int i = 0; i < 2; i++)
276 			{
277 				val = (long) libwps::readU32(input);
278 				if (val) f << "f" << i << "=" << std::hex << val << ",";
279 			}
280 			long dSize = (long) libwps::readU32(input);
281 			long actPos = input->tell();
282 
283 			bool  ok = dSize > 0 && dSize+actPos <= endPos;
284 			if (ok)
285 			{
286 				if (name == "METAFILEPICT" && dSize > 8)
287 				{
288 					f << "type=" << libwps::read16(input) << ",sz=(";
289 					for (int i = 0; i < 2; i++)
290 						f << libwps::read16(input)/1440. << ",";
291 					f << ")," << libwps::read16(input);
292 					actPos+=8;
293 					dSize-=8;
294 					confidence = 3;
295 				}
296 				readData = true;
297 				endDataPos = dSize+actPos-1;
298 			}
299 
300 			if (!ok) f << "###";
301 			f << "dSize=" << std::hex << dSize << std::dec;
302 		}
303 		else
304 			break;
305 
306 		librevenge::RVNGBinaryData data;
307 		long actPos = input->tell();
308 		bool ok = readData && libwps::readData(input,(unsigned long)(endDataPos+1-actPos), data);
309 		if (confidence > actConfidence && data.size())
310 		{
311 			actConfidence = confidence;
312 			pict = data;
313 			replace = true;
314 		}
315 		if (actPictPos.naturalSize().x() > 0 && actPictPos.naturalSize().y() > 0)
316 		{
317 			if (replace || pictPos.naturalSize().x() <= 0 || pictPos.naturalSize().y() <=0)
318 				pictPos = actPictPos;
319 		}
320 		ascii().addPos(lastPos);
321 		ascii().addNote(f.str().c_str());
322 
323 		if (!ok)
324 		{
325 			if (endDataPos > 0 && endDataPos+1 <= endPos)
326 				input->seek(endDataPos+1, librevenge::RVNG_SEEK_SET);
327 			else
328 				break;
329 		}
330 
331 		ascii().skipZone(actPos, endDataPos);
332 #ifdef DEBUG_WITH_FILES
333 		std::stringstream f2;
334 		f2 << "Eobj" << entry.id() << "-" << numFind-1;
335 		libwps::Debug::dumpFile(data, f2.str().c_str());
336 #endif
337 
338 		input->seek(endDataPos+1, librevenge::RVNG_SEEK_SET);
339 	}
340 
341 	if (lastPos != endPos)
342 	{
343 		ascii().addPos(lastPos);
344 		ascii().addNote("ZZEOBJ(Contents)###");
345 		ascii().addPos(endPos);
346 		ascii().addNote("_");
347 	}
348 
349 	if (!pict.size())
350 		WPS_DEBUG_MSG(("WPS4Graph::readObject: Can not find picture for object: %d\n", oleId));
351 	else if (replace)
352 	{
353 		bool found = false;
354 		int maxId = -1;
355 		for (size_t i = 0; i < m_state->m_objectsId.size(); i++)
356 		{
357 			if (m_state->m_objectsId[i] != oleId)
358 			{
359 				if (m_state->m_objectsId[i] > maxId) maxId = m_state->m_objectsId[i];
360 				continue;
361 			}
362 			m_state->m_objects[i].m_data = pict;
363 			m_state->m_objects[i].m_mime = "image/pict";
364 			if (pictPos.naturalSize().x() > 0 && pictPos.naturalSize().y() > 0)
365 			{
366 				float scale = float(1.0/pictPos.getInvUnitScale(m_state->m_objects[i].m_position.unit()));
367 				m_state->m_objects[i].m_position.setNaturalSize(scale*pictPos.naturalSize());
368 			}
369 			found = true;
370 		}
371 		if (!found)
372 		{
373 			if (oleId < 0) oleId = maxId+1;
374 			WPSOLEParserObject object;
375 			object.m_data=pict;
376 			object.m_position=pictPos;
377 			m_state->m_objects.push_back(object);
378 			m_state->m_objectsId.push_back(oleId);
379 		}
380 		resId = oleId;
381 	}
382 	else
383 		resId = oleId;
384 
385 	return resId;
386 }
387 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
388