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