1 /* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2 
3 /* libstaroffice
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 <cstring>
35 #include <iomanip>
36 #include <iostream>
37 #include <limits>
38 #include <set>
39 #include <sstream>
40 
41 #include <librevenge/librevenge.h>
42 
43 #include "StarObjectPageStyle.hxx"
44 
45 #include "STOFFListener.hxx"
46 #include "STOFFPageSpan.hxx"
47 
48 #include "StarAttribute.hxx"
49 #include "StarFormatManager.hxx"
50 #include "StarItemPool.hxx"
51 #include "StarObject.hxx"
52 #include "StarState.hxx"
53 #include "StarWriterStruct.hxx"
54 #include "StarZone.hxx"
55 #include "SWFieldManager.hxx"
56 
57 /** Internal: the structures of a StarObjectPageStyle */
58 namespace StarObjectPageStyleInternal
59 {
60 /** \brief structure to store a endnote/footnote page description
61  */
62 struct NoteDesc {
63 public:
64   //! constructor
NoteDescStarObjectPageStyleInternal::NoteDesc65   explicit NoteDesc(bool isFootnote)
66     : m_isFootnote(isFootnote)
67     , m_adjust(0)
68     , m_penWidth(0)
69     , m_color(STOFFColor::black())
70   {
71     for (float &distance : m_distances) distance=0;
72   }
73   //! operator<<
74   friend std::ostream &operator<<(std::ostream &o, NoteDesc const &desc);
75   //! try to read a noteDesc: '1' or '2'
76   bool read(StarZone &zone);
77   //! a flag to know if this corresponds to a footnote or a endnote
78   bool m_isFootnote;
79   //! the width/height/topDist/bottomDist
80   float m_distances[4];
81   //! the adjust
82   int m_adjust;
83   //! the pen's width
84   int m_penWidth;
85   //! the color
86   STOFFColor m_color;
87 };
88 
read(StarZone & zone)89 bool NoteDesc::read(StarZone &zone)
90 {
91   STOFFInputStreamPtr input=zone.input();
92   libstoff::DebugFile &ascFile=zone.ascii();
93   libstoff::DebugStream f;
94   long pos=input->tell();
95   unsigned char type;
96   if (input->peek()!=(m_isFootnote ? '1' : '2') || !zone.openSWRecord(type)) {
97     input->seek(pos, librevenge::RVNG_SEEK_SET);
98     STOFF_DEBUG_MSG(("StarObjectPageStyleInternal::NoteDesc::read: can not read a noteDesc\n"));
99     return false;
100   }
101   // sw_sw3page.cxx InPageFtnInfo
102   f << "Entries(StarNoteDesc)[" << zone.getRecordLevel() << "]:";
103   for (int i=1; i<4; ++i) m_distances[i]=float(input->readLong(4));
104   m_adjust=int(input->readLong(2));
105   long dim[2];
106   for (long &i : dim) i=long(input->readLong(4));
107   if (!dim[1]) {
108     STOFF_DEBUG_MSG(("StarObjectPageStyleInternal::NoteDesc::read: can not find the width deminator\n"));
109   }
110   else
111     m_distances[0]=float(dim[0])/float(dim[1]);
112   m_penWidth=int(input->readLong(2));
113   if (!input->readColor(m_color)) {
114     STOFF_DEBUG_MSG(("StarObjectPageStyleInternal::NoteDesc::read: can not read a color\n"));
115     f << "###color,";
116   }
117   f << *this;
118   ascFile.addPos(pos);
119   ascFile.addNote(f.str().c_str());
120   zone.closeSWRecord(type, "StarNoteDesc");
121   return true;
122 }
123 
operator <<(std::ostream & o,NoteDesc const & desc)124 std::ostream &operator<<(std::ostream &o, NoteDesc const &desc)
125 {
126   if (desc.m_isFootnote)
127     o << "footnote,";
128   else
129     o << "endnote,";
130   for (int i=0; i<4; ++i) {
131     if (desc.m_distances[i]>=0 && desc.m_distances[i]<=0) continue;
132     char const *wh[]= {"width", "height", "top[dist]", "bottom[dist]"};
133     o << wh[i] << "=" << desc.m_distances[i] << ",";
134   }
135   if (desc.m_adjust) o << "adjust=" << desc.m_adjust << ",";
136   if (desc.m_penWidth) o << "penWidth=" << desc.m_penWidth << ",";
137   if (!desc.m_color.isBlack()) o << "color=" << desc.m_color << ",";
138   return o;
139 }
140 
141 /** \brief structure to store a page description
142  */
143 struct PageDesc {
144 public:
145   //! constructor
PageDescStarObjectPageStyleInternal::PageDesc146   explicit PageDesc()
147     : m_name("")
148     , m_follow("")
149     , m_landscape(false)
150     , m_poolId(0)
151     , m_numType(0)
152     , m_usedOn(3)
153     , m_regCollIdx(0xFFFF)
154   {
155   }
156   //! destructor
~PageDescStarObjectPageStyleInternal::PageDesc157   ~PageDesc()
158   {
159   }
160   //! update pagespan properties
161   void updatePageSpan(StarState &state) const;
162   /** try to update the section*/
163   bool updateState(StarState &state) const;
164   //! operator<<
165   friend std::ostream &operator<<(std::ostream &o, PageDesc const &desc);
166   //! try to read a pageDesc: 'p'
167   bool read(StarZone &zone, StarObject &object);
168   //! the name
169   librevenge::RVNGString m_name;
170   //! the follow
171   librevenge::RVNGString m_follow;
172   //! is the page a lanscape
173   bool m_landscape;
174   //! the poolId
175   int m_poolId;
176   //! the number's type
177   int m_numType;
178   //! the page where is it used ?
179   int m_usedOn;
180   //! the coll idx
181   int m_regCollIdx;
182   //! the foot and page foot desc
183   std::shared_ptr<NoteDesc> m_noteDesc[2];
184   //! the master and left attributes lists
185   std::vector<StarWriterStruct::Attribute> m_attributes[2];
186 };
187 
updatePageSpan(StarState & state) const188 void PageDesc::updatePageSpan(StarState &state) const
189 {
190   updateState(state);
191   auto &page=state.m_global->m_page;
192   if (m_landscape && page.m_propertiesList[0]["fo:page-height"] && page.m_propertiesList[0]["fo:page-width"] &&
193       page.m_propertiesList[0]["fo:page-height"]->getInt() > page.m_propertiesList[0]["fo:page-width"]->getInt()) {
194     // we must inverse fo:page-height and fo:page-width
195     auto height=page.m_propertiesList[0]["fo:page-height"]->getStr();
196     page.m_propertiesList[0].insert("fo:page-height", page.m_propertiesList[0]["fo:page-width"]);
197     page.m_propertiesList[0].insert("fo:page-width", height);
198     page.m_propertiesList[0].insert("style:print-orientation", "landscape");
199   }
200 }
201 
updateState(StarState & state) const202 bool PageDesc::updateState(StarState &state) const
203 {
204   for (const auto &attribute : m_attributes) {
205     for (auto &attr : attribute) {
206       if (attr.m_attribute)
207         attr.m_attribute->addTo(state);
208     }
209   }
210   return true;
211 }
212 
read(StarZone & zone,StarObject & object)213 bool PageDesc::read(StarZone &zone, StarObject &object)
214 {
215   STOFFInputStreamPtr input=zone.input();
216   libstoff::DebugFile &ascFile=zone.ascii();
217   libstoff::DebugStream f;
218   long pos=input->tell();
219   unsigned char type;
220   if (input->peek()!='p' || !zone.openSWRecord(type)) {
221     input->seek(pos, librevenge::RVNG_SEEK_SET);
222     STOFF_DEBUG_MSG(("StarObjectPageStyleInternal::PageDesc::read: can not read a pageDesc\n"));
223     return false;
224   }
225   // sw_sw3page.cxx InPageDesc
226   f << "Entries(StarPageDesc)[" << type << "-" << zone.getRecordLevel() << "]:";
227   int fl=zone.openFlagZone();
228   if (fl&0x10) m_landscape=true;
229   if (fl&0xf0) f << "fl=" << (fl>>4) << ",";
230   auto val=int(input->readULong(2));
231   if (!zone.getPoolName(val, m_name)) {
232     STOFF_DEBUG_MSG(("StarObjectPageStyleInternal::PageDesc::read: can not find a pool name\n"));
233     f << "###nameId=" << val << ",";
234   }
235   val=int(input->readULong(2));
236   if (!zone.getPoolName(val, m_follow)) {
237     STOFF_DEBUG_MSG(("StarObjectPageStyleInternal::PageDesc::read: can not find a pool name\n"));
238     f << "###followId=" << val << ",";
239   }
240   m_poolId=int(input->readULong(2));
241   m_numType=int(input->readULong(1));
242   m_usedOn=int(input->readULong(2));
243   if (zone.isCompatibleWith(0x16,0x22, 0x101))
244     m_regCollIdx=int(input->readULong(2));
245   zone.closeFlagZone();
246   f << *this;
247   ascFile.addPos(pos);
248   ascFile.addNote(f.str().c_str());
249 
250   long lastPos=zone.getRecordLastPosition();
251   int whichAttrib=0;
252   while (input->tell() < lastPos) {
253     pos=input->tell();
254     int rType=input->peek();
255     bool done=false;
256     switch (rType) {
257     case 'S': {
258       std::vector<StarWriterStruct::Attribute> attributeList;
259       done=StarWriterStruct::Attribute::readList(zone, attributeList, object);
260       if (!done) break;
261       if (whichAttrib<2) // first master, second left
262         m_attributes[whichAttrib++]=attributeList;
263       else {
264         STOFF_DEBUG_MSG(("StarObjectPageStyleInternal::PageDesc::read: unexpected attribute list\n"));
265       }
266       break;
267     }
268     case '1': // foot info
269     case '2': { // page foot info
270       std::shared_ptr<NoteDesc> desc(new NoteDesc(rType=='1'));
271       done=desc->read(zone);
272       if (done)
273         m_noteDesc[rType=='1' ? 0 : 1]=desc;
274       break;
275     }
276     default:
277       break;
278     }
279     if (done) continue;
280     input->seek(pos, librevenge::RVNG_SEEK_SET);
281     f.str("");
282     if (!zone.openSWRecord(type)) {
283       input->seek(pos, librevenge::RVNG_SEEK_SET);
284       break;
285     }
286     f << "StarPageDesc[" << type << "-" << zone.getRecordLevel() << "]:";
287     STOFF_DEBUG_MSG(("StarObjectPageStyleInternal::PageDesc::read: find unknown type\n"));
288     f << "###type,";
289     ascFile.addPos(pos);
290     ascFile.addNote(f.str().c_str());
291     zone.closeSWRecord(type, "StarPageDesc");
292   }
293   zone.closeSWRecord('p', "StarPageDesc");
294   return true;
295 }
296 
operator <<(std::ostream & o,PageDesc const & desc)297 std::ostream &operator<<(std::ostream &o, PageDesc const &desc)
298 {
299   o << desc.m_name.cstr() << ",";
300   if (!desc.m_follow.empty()) o << "follow=" << desc.m_follow.cstr() << ",";
301   if (desc.m_landscape) o << "landscape,";
302   if (desc.m_poolId) o << "poolId=" << desc.m_poolId << ",";
303   if (desc.m_numType) o << "numType=" << desc.m_numType << ",";
304   switch (desc.m_usedOn&3) {
305   case 1:
306     o << "left,";
307     break;
308   case 2:
309     o << "right,";
310     break;
311   case 3:
312     o << "all,";
313     break;
314   case 0: // internal
315   default:
316     break;
317   }
318   if (desc.m_usedOn&0x40) o << "header[share],";
319   if (desc.m_usedOn&0x80) o << "footer[share],";
320   if (desc.m_usedOn&0x100) o << "first[share],";
321   if (desc.m_usedOn&0xFE3C) o << "usedOn=" << std::hex << (desc.m_usedOn&0xFE3C) << std::dec << ",";
322   if (desc.m_regCollIdx!=0xFFFF) o << "regCollIdx=" << desc.m_regCollIdx << ",";
323   return o;
324 }
325 
326 ////////////////////////////////////////
327 //! Internal: the state of a StarObjectPageStyle
328 struct State {
329   //! constructor
StateStarObjectPageStyleInternal::State330   State()
331     : m_pageList()
332     , m_nameToPageIdMap()
333     , m_simplifyNameToPageIdMap()
334   {
335   }
336   //! list of pages
337   std::vector<PageDesc> m_pageList;
338   //! map name to id
339   std::map<librevenge::RVNGString, size_t> m_nameToPageIdMap;
340   //! map simplify name to id
341   std::map<librevenge::RVNGString, size_t> m_simplifyNameToPageIdMap;
342 };
343 
344 }
345 
346 ////////////////////////////////////////////////////////////
347 // constructor/destructor, ...
348 ////////////////////////////////////////////////////////////
StarObjectPageStyle(StarObject const & orig,bool duplicateState)349 StarObjectPageStyle::StarObjectPageStyle(StarObject const &orig, bool duplicateState)
350   : StarObject(orig, duplicateState)
351   , m_pageStyleState(new StarObjectPageStyleInternal::State)
352 {
353 }
354 
~StarObjectPageStyle()355 StarObjectPageStyle::~StarObjectPageStyle()
356 {
357 }
358 
359 ////////////////////////////////////////////////////////////
360 // send data
361 ////////////////////////////////////////////////////////////
updatePageSpan(librevenge::RVNGString const & name,StarState & state)362 bool StarObjectPageStyle::updatePageSpan(librevenge::RVNGString const &name, StarState &state)
363 {
364   auto &ps=state.m_global->m_page;
365   ps=STOFFPageSpan();
366   size_t id=0;
367   if (m_pageStyleState->m_nameToPageIdMap.find(name) != m_pageStyleState->m_nameToPageIdMap.end())
368     id=m_pageStyleState->m_nameToPageIdMap.find(name)->second;
369   else {
370     auto simpName=libstoff::simplifyString(name);
371     if (m_pageStyleState->m_simplifyNameToPageIdMap.find(simpName)!=m_pageStyleState->m_simplifyNameToPageIdMap.end())
372       id=m_pageStyleState->m_simplifyNameToPageIdMap.find(simpName)->second;
373     else if (!name.empty()) {
374       STOFF_DEBUG_MSG(("StarObjectPageStyle::updatePageSpan: can not find page %s\n", name.cstr()));
375     }
376   }
377   if (id>=m_pageStyleState->m_pageList.size())
378     return false;
379   size_t listIds[3];
380   std::string listOccurence[3];
381   std::set<librevenge::RVNGString> seen;
382   int numPages=0;
383   for (int i=0; i<3; ++i) {
384     auto const &page=m_pageStyleState->m_pageList[id];
385     listIds[i]=id;
386     numPages=i+1;
387     if ((page.m_usedOn&3)==3) {
388       if (i==1) listOccurence[0]="first";
389       listOccurence[i]="all";
390       break;
391     }
392     listOccurence[i]=(page.m_usedOn&1) ? "left" : "right";
393     seen.insert(page.m_name);
394     auto const &follow=page.m_follow;
395     if (follow.empty() || seen.find(follow)!=seen.end())
396       break;
397     if (m_pageStyleState->m_nameToPageIdMap.find(follow) != m_pageStyleState->m_nameToPageIdMap.end())
398       id=m_pageStyleState->m_nameToPageIdMap.find(follow)->second;
399     else {
400       auto simpName=libstoff::simplifyString(follow);
401       if (m_pageStyleState->m_simplifyNameToPageIdMap.find(simpName)!=m_pageStyleState->m_simplifyNameToPageIdMap.end())
402         id=m_pageStyleState->m_simplifyNameToPageIdMap.find(simpName)->second;
403       else {
404         STOFF_DEBUG_MSG(("StarObjectPageStyle::updatePageSpan: can not find page %s\n", follow.cstr()));
405         break;
406       }
407     }
408     if (id>=m_pageStyleState->m_pageList.size())
409       break;
410   }
411   if (numPages==3) listOccurence[0]="first";
412   for (int p=numPages-1; p>=0; --p) { // invert so that section correspond to the first page
413     ps.m_section=STOFFSection();
414     state.m_global->m_pageOccurence=listOccurence[p];
415     m_pageStyleState->m_pageList[listIds[p]].updatePageSpan(state);
416   }
417   return true;
418 }
419 
updatePageSpans(std::vector<librevenge::RVNGString> const & listNames,std::vector<STOFFPageSpan> & pageSpan,int & number)420 bool StarObjectPageStyle::updatePageSpans
421 (std::vector<librevenge::RVNGString> const &listNames, std::vector<STOFFPageSpan> &pageSpan, int &number)
422 {
423   librevenge::RVNGString lastPageName("");
424   int numPage=0;
425   number=0;
426   auto pool=findItemPool(StarItemPool::T_WriterPool, false);
427   StarState state(pool.get(), *this);
428   auto &ps=state.m_global->m_page;
429   for (size_t i=0; i<=listNames.size(); ++i) {
430     bool newPage=(i==listNames.size()) || (lastPageName!="" && listNames[i]!="" && lastPageName!=listNames[i]);
431     if (!newPage) {
432       if (lastPageName.empty()) lastPageName=listNames[i];
433       ++numPage;
434       continue;
435     }
436     if (i==listNames.size())
437       numPage=10000; // be sure to allow enough page
438     if (numPage) {
439       updatePageSpan(lastPageName, state);
440       ps.m_pageSpan=numPage;
441       pageSpan.push_back(ps);
442       number+=numPage;
443     }
444     if (i==listNames.size()) break;
445     numPage=1;
446     lastPageName=listNames[i];
447   }
448   return number!=0;
449 }
450 
451 ////////////////////////////////////////////////////////////
452 // the parser
453 ////////////////////////////////////////////////////////////
read(StarZone & zone)454 bool StarObjectPageStyle::read(StarZone &zone)
455 try
456 {
457   STOFFInputStreamPtr input=zone.input();
458   if (!zone.readSWHeader()) {
459     STOFF_DEBUG_MSG(("StarObjectPageStyle::read: can not read the header\n"));
460     return false;
461   }
462   zone.readStringsPool();
463   SWFieldManager fieldManager;
464   while (fieldManager.readField(zone,'Y'))
465     ;
466   std::vector<StarWriterStruct::Bookmark> markList;
467   StarWriterStruct::Bookmark::readList(zone, markList);
468   std::vector<std::vector<StarWriterStruct::Redline> > redlineListList;
469   StarWriterStruct::Redline::readListList(zone, redlineListList);
470   getFormatManager()->readSWNumberFormatterList(zone);
471   // sw_sw3page.cxx Sw3IoImp::InPageDesc
472   libstoff::DebugFile &ascFile=zone.ascii();
473   while (!input->isEnd()) {
474     long pos=input->tell();
475     unsigned char type;
476     if (!zone.openSWRecord(type)) {
477       input->seek(pos, librevenge::RVNG_SEEK_SET);
478       break;
479     }
480     libstoff::DebugStream f;
481     f << "StarPageStyleSheets[" << type << "]:";
482     bool done=false;
483     switch (type) {
484     case 'P': {
485       zone.openFlagZone();
486       auto N=int(input->readULong(2));
487       f << "N=" << N << ",";
488       zone.closeFlagZone();
489       for (int i=0; i<N; ++i) {
490         StarObjectPageStyleInternal::PageDesc desc;
491         // read will check that we can read the data, ....
492         if (!desc.read(zone, *this))
493           break;
494         if (m_pageStyleState->m_nameToPageIdMap.find(desc.m_name)!=m_pageStyleState->m_nameToPageIdMap.end()) {
495           STOFF_DEBUG_MSG(("StarObjectPageStyle::read: oops page with name=%s already exists\n", desc.m_name.cstr()));
496         }
497         else {
498           m_pageStyleState->m_nameToPageIdMap[desc.m_name]=m_pageStyleState->m_pageList.size();
499           auto simpName=libstoff::simplifyString(desc.m_name);
500           if (m_pageStyleState->m_simplifyNameToPageIdMap.find(simpName)==m_pageStyleState->m_simplifyNameToPageIdMap.end())
501             m_pageStyleState->m_simplifyNameToPageIdMap[simpName]=m_pageStyleState->m_pageList.size();
502         }
503         m_pageStyleState->m_pageList.push_back(desc);
504       }
505       break;
506     }
507     case 'Z':
508       done=true;
509       break;
510     default:
511       STOFF_DEBUG_MSG(("StarObjectPageStyle::read: find unknown data\n"));
512       f << "###";
513       break;
514     }
515     if (!zone.closeSWRecord(type, "StarPageStyleSheets")) {
516       input->seek(pos, librevenge::RVNG_SEEK_SET);
517       break;
518     }
519     ascFile.addPos(pos);
520     ascFile.addNote(f.str().c_str());
521 
522     if (done)
523       break;
524   }
525 
526   if (!input->isEnd()) {
527     STOFF_DEBUG_MSG(("StarObjectPageStyle::read: find extra data\n"));
528     ascFile.addPos(input->tell());
529     ascFile.addNote("StarPageStyleSheets:##extra");
530   }
531 
532   return true;
533 }
534 catch (...)
535 {
536   return false;
537 }
538 
539 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
540