1 /* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2
3 /* libmwaw
4 * Version: MPL 2.0 / LGPLv2+
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 2.0 (the "License"); you may not use this file except in compliance with
8 * the License or as specified alternatively below. You may obtain a copy of
9 * the License at http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * Major Contributor(s):
17 * Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18 * Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20 * Copyright (C) 2006, 2007 Andrew Ziem
21 * Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22 *
23 *
24 * All Rights Reserved.
25 *
26 * For minor contributions see the git repository.
27 *
28 * Alternatively, the contents of this file may be used under the terms of
29 * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30 * in which case the provisions of the LGPLv2+ are applicable
31 * instead of those above.
32 */
33
34 #include <string.h>
35
36 #include <algorithm>
37 #include <iomanip>
38 #include <iostream>
39 #include <sstream>
40
41 #include <librevenge/librevenge.h>
42
43 #include "MWAWDebug.hxx"
44 #include "MWAWInputStream.hxx"
45 #include "MWAWParser.hxx"
46
47 #include "ClarisWksStruct.hxx"
48
49 namespace ClarisWksStruct
50 {
51
52 static const int MAX_PAGES = 1 << 16;
53
operator <<(std::ostream & o,Struct const & z)54 std::ostream &operator<<(std::ostream &o, Struct const &z)
55 {
56 o << "sz=" << z.m_size << ",";
57 if (z.m_numData>0) {
58 o << "N=" << z.m_numData << ",";
59 o << "data[sz]=" << z.m_dataSize << ",";
60 }
61 if (z.m_headerSize>0)
62 o << "header[sz]=" << z.m_headerSize << ",";
63 if (z.m_type>0)
64 o << "type=" << z.m_type << ",";
65 for (int i=0; i<2; ++i) {
66 if (z.m_values[i])
67 o << "f" << i << "=" << z.m_values[i] << ",";
68 }
69 return o;
70 }
71
readHeader(MWAWInputStreamPtr input,bool strict)72 bool Struct::readHeader(MWAWInputStreamPtr input, bool strict)
73 {
74 *this=Struct();
75 long pos=input->tell();
76 if (!input->checkPosition(pos+4))
77 return false;
78 m_size=input->readLong(4);
79 if (m_size==0)
80 return true;
81 if (m_size<12 || !input->checkPosition(pos+4+m_size))
82 return false;
83
84 m_numData =int(input->readULong(2));
85 m_type = int(input->readLong(2));
86 m_values[0] = int(input->readLong(2));
87 m_dataSize = long(input->readULong(2));
88 m_headerSize = long(input->readULong(2));
89 m_values[1] = int(input->readLong(2));
90 if (m_numData && m_dataSize>10000) return false; // too big to be honetx
91 long expectedLength = 12+m_headerSize;
92 if (m_numData>0) expectedLength+=long(m_numData)*m_dataSize;
93 if (expectedLength>m_size || (strict && expectedLength != m_size))
94 return false;
95 return true;
96 }
97
98 // try to read a list of structured zone
readIntZone(MWAWParserState & parserState,char const * zoneName,bool hasEntete,int intSz,std::vector<int> & res)99 bool readIntZone(MWAWParserState &parserState, char const *zoneName, bool hasEntete, int intSz, std::vector<int> &res)
100 {
101 res.resize(0);
102 if (intSz != 1 && intSz != 2 && intSz != 4) {
103 MWAW_DEBUG_MSG(("ClarisWksStruct::readIntZone: unknown int size: %d\n", intSz));
104 return false;
105 }
106
107 MWAWInputStreamPtr input = parserState.m_input;
108 long pos = input->tell();
109 Struct zone;
110 if (!zone.readHeader(input,true)) {
111 MWAW_DEBUG_MSG(("ClarisWksStruct::readIntZone: can not header of %s\n", zoneName ? zoneName : "unamed"));
112 }
113 libmwaw::DebugStream f;
114 libmwaw::DebugFile &ascFile=parserState.m_asciiFile;
115 if (zoneName && strlen(zoneName))
116 f << "Entries(" << zoneName << "):";
117 long endPos = pos+4+zone.m_size;
118
119 if (zone.m_size==0) {
120 if (hasEntete) {
121 ascFile.addPos(pos-4);
122 ascFile.addNote(f.str().c_str());
123 }
124 else {
125 ascFile.addPos(pos);
126 ascFile.addNote("NOP");
127 }
128 return true;
129 }
130
131 if (zone.m_dataSize != intSz) {
132 input->seek(pos, librevenge::RVNG_SEEK_SET);
133 MWAW_DEBUG_MSG(("ClarisWksStruct::readIntZone: unexpected field size\n"));
134 return false;
135 }
136
137 f << zone;
138 if (zone.m_headerSize) {
139 ascFile.addDelimiter(input->tell(), '|');
140 input->seek(zone.m_headerSize, librevenge::RVNG_SEEK_CUR);
141 }
142 if (zone.m_numData) ascFile.addDelimiter(input->tell(), '|');
143 f << "[";
144 for (long i = 0; i < zone.m_numData; i++) {
145 auto val = int(input->readLong(intSz));
146 res.push_back(val);
147 if (val>1000) f << "0x" << std::hex << val << std::dec << ",";
148 else f << val << ",";
149 }
150 f << "]";
151
152 ascFile.addPos(hasEntete ? pos-4 : pos);
153 ascFile.addNote(f.str().c_str());
154
155 input->seek(endPos,librevenge::RVNG_SEEK_SET);
156 return true;
157 }
158
159 ///////////////////////////////////////////////////////////
160 // try to read a unknown structured zone
161 ////////////////////////////////////////////////////////////
readStructZone(MWAWParserState & parserState,char const * zoneName,bool hasEntete)162 bool readStructZone(MWAWParserState &parserState, char const *zoneName, bool hasEntete)
163 {
164 MWAWInputStreamPtr input = parserState.m_input;
165 long pos = input->tell();
166 Struct zone;
167 if (!zone.readHeader(input,false) || (zone.m_size && zone.m_dataSize<=0)) {
168 input->seek(pos, librevenge::RVNG_SEEK_SET);
169 MWAW_DEBUG_MSG(("ClarisWksStruct::readStructZone: can not read header for %s\n", zoneName));
170 return false;
171 }
172 libmwaw::DebugFile &ascFile= parserState.m_asciiFile;
173 libmwaw::DebugStream f;
174 f << "Entries(" << zoneName << "):";
175
176 if (zone.m_size == 0) {
177 if (hasEntete) {
178 ascFile.addPos(pos-4);
179 ascFile.addNote(f.str().c_str());
180 }
181 else {
182 ascFile.addPos(pos);
183 ascFile.addNote("NOP");
184 }
185 return true;
186 }
187 long endPos=pos+4+zone.m_size;
188 f << zone;
189 if (zone.m_headerSize) {
190 ascFile.addDelimiter(input->tell(), '|');
191 input->seek(zone.m_headerSize, librevenge::RVNG_SEEK_CUR);
192 }
193 ascFile.addPos(hasEntete ? pos-4 : pos);
194 ascFile.addNote(f.str().c_str());
195
196 pos=input->tell();
197 for (long i = 0; i < zone.m_numData; i++) {
198 f.str("");
199 f << zoneName << "-" << i << ":";
200
201 ascFile.addPos(pos);
202 ascFile.addNote(f.str().c_str());
203 pos += zone.m_dataSize;
204 }
205 if (pos!=endPos) {
206 MWAW_DEBUG_MSG(("ClarisWksStruct::readStructZone: find extra data for %s\n", zoneName));
207 f.str("");
208 f << zoneName << ":###extra";
209 ascFile.addPos(pos);
210 ascFile.addNote(f.str().c_str());
211 }
212 input->seek(endPos,librevenge::RVNG_SEEK_SET);
213 return true;
214 }
215
216 //------------------------------------------------------------
217 // DSET
218 //------------------------------------------------------------
getUnionChildBox() const219 MWAWBox2i DSET::getUnionChildBox() const
220 {
221 MWAWBox2f res;
222 long maxX=1000;
223 for (auto const &child : m_childs) {
224 // highly spurious, better to ignore
225 if (long(child.m_box[1][0])>3*maxX)
226 continue;
227 if (long(child.m_box[1][0])>maxX)
228 maxX=long(child.m_box[1][0]);
229 res=child.m_box.getUnion(res);
230 }
231 return MWAWBox2i(res);
232 }
233
removeChild(int cId)234 void DSET::removeChild(int cId)
235 {
236 removeChild(cId, std::find(m_otherChilds.begin(), m_otherChilds.end(), cId)==m_otherChilds.end());
237 }
238
removeChild(int cId,bool normalChild)239 void DSET::removeChild(int cId, bool normalChild)
240 {
241 if (normalChild) {
242 for (auto it=m_childs.begin(); it!=m_childs.end(); ++it) {
243 if (it->m_type != C_Zone || it->m_id != cId) continue;
244 m_childs.erase(it);
245 return;
246 }
247 }
248 else {
249 for (auto it=m_otherChilds.begin(); it!=m_otherChilds.end(); ++it) {
250 if (*it != cId) continue;
251 m_otherChilds.erase(it);
252 return;
253 }
254 }
255 MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::removeChild can not detach %d\n", cId));
256 }
257
258
updateChildPositions(MWAWVec2f const & pageDim,float formLength,int numHorizontalPages)259 void DSET::updateChildPositions(MWAWVec2f const &pageDim, float formLength, int numHorizontalPages)
260 {
261 float const &textWidth=pageDim[0];
262 float textHeight=pageDim[1];
263 if (float(m_pageDimension[1])>0.5f*formLength && float(m_pageDimension[1])<formLength)
264 textHeight=float(m_pageDimension[1]);
265 if (textHeight<=0) {
266 MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::updateChildPositions: the height can not be null\n"));
267 return;
268 }
269 if (numHorizontalPages>1 && textWidth<=0) {
270 MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::updateChildPositions: the width can not be null\n"));
271 numHorizontalPages=1;
272 }
273 MWAWBox2f groupBox;
274 int groupPage=-1;
275 bool firstGroupFound=false;
276 for (auto &child : m_childs) {
277 MWAWBox2f childBdBox=child.getBdBox();
278 auto pageY=int(float(childBdBox[1].y())/textHeight);
279 if (pageY < 0)
280 continue;
281 if (++pageY > 1) {
282 MWAWVec2f orig = child.m_box[0];
283 MWAWVec2f sz = child.m_box.size();
284 orig[1]-=float(pageY-1)*textHeight;
285 if (orig[1] < 0) {
286 if (orig[1]>=-textHeight*0.1f)
287 orig[1]=0;
288 else if (orig[1]>-1.1f*textHeight) {
289 orig[1]+=textHeight;
290 if (orig[1]<0) orig[1]=0;
291 pageY--;
292 }
293 else {
294 // can happen in a drawing document if a form is on several vertical page
295 if (m_position!=P_Main) { // can be normal, if this corresponds to the mainZone
296 MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::updateChildPositions: data on several vertical page(move it on the first page)\n"));
297 }
298 // better to move it on the first page, ie. if the position is problematic, we do no create a big number of empty page
299 pageY=int(float(childBdBox[0].y())/textHeight);
300 if (++pageY<0) pageY=0;
301 if (sz[1]>textHeight) {
302 orig[1]=0;
303 sz[1]=textHeight;
304 }
305 else
306 orig[1]=textHeight-sz[1];
307 }
308 }
309 child.m_box = MWAWBox2f(orig, orig+sz);
310 }
311 int pageX=1;
312 if (numHorizontalPages>1) {
313 pageX=int(float(childBdBox[1].x())/textWidth);
314 MWAWVec2f orig = child.m_box[0];
315 MWAWVec2f sz = child.m_box.size();
316 orig[0]-=float(pageX)*textWidth;
317 if (orig[0] < 0) {
318 if (orig[0]>=-textWidth*0.1f)
319 orig[0]=0;
320 else if (orig[0]>-1.1f*textWidth) {
321 orig[0]+=textWidth;
322 if (orig[0]<0) orig[0]=0;
323 pageX--;
324 }
325 else {
326 // can happen if a form is on several horizontal page
327 MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::updateChildPositions: data on several horizontal page(move it on the first page)\n"));
328 // better to move it on the first page, ie. if the position is problematic, we do no create a big number of empty page
329 pageX=int(float(childBdBox[0].x())/textWidth);
330 if (pageX<0) pageX=0;
331 if (sz[0]>textWidth) {
332 orig[0]=0;
333 sz[0]=textWidth;
334 }
335 else
336 orig[0]=textWidth-sz[0];
337 }
338 }
339 child.m_box = MWAWBox2f(orig, orig+sz);
340 pageX++;
341 }
342 int64_t newPage = pageX+int64_t(pageY-1)*numHorizontalPages;
343 if (newPage > MAX_PAGES)
344 continue;
345 int page = int(newPage);
346 if (!firstGroupFound) {
347 groupPage=page;
348 groupBox=child.getBdBox();
349 firstGroupFound=true;
350 }
351 else if (groupPage==page)
352 groupBox=groupBox.getUnion(child.getBdBox());
353 else
354 groupPage=-1;
355 child.m_page = page;
356 }
357 if (groupPage>=0) {
358 m_page=groupPage;
359 m_box=groupBox;
360 }
361 }
362
findForbiddenPagesBreaking(float pageDim,float formDim,int dim,MWAWVariable<int> & lastPage) const363 void DSET::findForbiddenPagesBreaking(float pageDim, float formDim, int dim, MWAWVariable<int> &lastPage) const
364 {
365 if (isHeaderFooter() || m_position==P_Frame)
366 return;
367
368 if (dim<0||dim>1) {
369 MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::findForbiddenPagesBreaking: the height can not be null\n"));
370 return;
371 }
372 float length=pageDim;
373 if (float(m_pageDimension[dim])>0.5f*formDim && float(m_pageDimension[dim])<formDim)
374 length=float(m_pageDimension[dim]);
375 if (length<=0) {
376 MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::findForbiddenPagesBreaking: the length can not be null\n"));
377 return;
378 }
379 float const eps=0.1f*length;
380 for (auto const &child : m_childs) {
381 MWAWBox2f childBdBox=child.getBdBox();
382 // as the recomputation of page position is not accurate, just ignore the small size
383 if (childBdBox.size()[dim]<=length)
384 continue;
385 auto pageMax=int(float(childBdBox[1][dim])/length);
386 if (pageMax <= 0)
387 continue;
388 float diff = child.m_box[1][dim]-float(pageMax)*length;
389 if (diff <= eps)
390 --pageMax;
391 if (!lastPage.isSet() || pageMax > *lastPage)
392 lastPage = pageMax;
393 }
394 }
395
operator <<(std::ostream & o,DSET const & doc)396 std::ostream &operator<<(std::ostream &o, DSET const &doc)
397 {
398 switch (doc.m_position) {
399 case DSET::P_Unknown:
400 break;
401 case DSET::P_Frame:
402 o << "frame,";
403 break;
404 case DSET::P_Header:
405 o << "header,";
406 break;
407 case DSET::P_Footer:
408 o << "footer,";
409 break;
410 case DSET::P_Footnote:
411 o << "footnote,";
412 break;
413 case DSET::P_Main:
414 o << "main,";
415 break;
416 case DSET::P_GraphicMaster:
417 o << "graphic[master],";
418 break;
419 case DSET::P_Slide:
420 o << "slide,";
421 break;
422 case DSET::P_SlideMaster:
423 o << "slide[master],";
424 break;
425 case DSET::P_SlideNote:
426 o << "slide[note],";
427 break;
428 case DSET::P_SlideThumbnail:
429 o << "slide[thumbnail],";
430 break;
431 case DSET::P_Table:
432 o << "table,";
433 break;
434 #if !defined(__clang__)
435 default:
436 o << "#position=" << doc.m_position << ",";
437 break;
438 #endif
439 }
440 switch (doc.m_fileType) {
441 case 0:
442 o << "normal,";
443 break;
444 case 1:
445 o << "text";
446 if (doc.m_textType==0xFF)
447 o << "*,";
448 else if (doc.m_textType==0xa) // appear in graphic file
449 o << "[textbox],";
450 else if (doc.m_textType)
451 o << "[#type=" << std::hex << doc.m_textType<< std::dec << "],";
452 else
453 o << ",";
454 break;
455 case 2:
456 o << "spreadsheet,";
457 break;
458 case 3:
459 o << "database,";
460 break;
461 case 4:
462 o << "bitmap,";
463 break;
464 case 5:
465 o << "presentation,";
466 break;
467 case 6:
468 o << "table,";
469 break;
470 default:
471 o << "#type=" << doc.m_fileType << ",";
472 break;
473 }
474 if (doc.m_page>= 0) o << "pg=" << doc.m_page << ",";
475 if (doc.m_box.size()[0]>0||doc.m_box.size()[1]>0)
476 o << "box=" << doc.m_box << ",";
477 if (doc.m_pageDimension[0]>0 || doc.m_pageDimension[1]>0)
478 o << "zone[dim]=" << doc.m_pageDimension << ",";
479 o << "id=" << doc.m_id << ",";
480 if (!doc.m_fathersList.empty()) {
481 o << "fathers=[";
482 for (auto id : doc.m_fathersList)
483 o << id << ",";
484 o << "],";
485 }
486 o << "N=" << doc.m_numData << ",";
487 if (doc.m_dataSz >=0) o << "dataSz=" << doc.m_dataSz << ",";
488 if (doc.m_headerSz >= 0) o << "headerSz=" << doc.m_headerSz << ",";
489 if (doc.m_beginSelection) o << "begSel=" << doc.m_beginSelection << ",";
490 if (doc.m_endSelection >= 0) o << "endSel=" << doc.m_endSelection << ",";
491 for (int i = 0; i < 4; i++) {
492 if (doc.m_flags[i])
493 o << "fl" << i << "=" << std::hex << doc.m_flags[i] << std::dec << ",";
494 }
495 for (size_t i = 0; i < doc.m_childs.size(); i++)
496 o << "child" << i << "=[" << doc.m_childs[i] << "],";
497 for (size_t i = 0; i < doc.m_otherChilds.size(); i++)
498 o << "otherChild" << i << "=" << doc.m_otherChilds[i] << ",";
499 return o;
500 }
501
502 }
503 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
504