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