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