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 <cmath>
35 #include <iomanip>
36 #include <iostream>
37 #include <limits>
38 #include <map>
39 #include <set>
40 #include <sstream>
41 #include <stack>
42 
43 #include <librevenge/librevenge.h>
44 
45 #include "MWAWFont.hxx"
46 #include "MWAWFontConverter.hxx"
47 #include "MWAWListener.hxx"
48 #include "MWAWParagraph.hxx"
49 #include "MWAWPosition.hxx"
50 #include "MWAWSubDocument.hxx"
51 
52 #include "RagTime5ClusterManager.hxx"
53 #include "RagTime5Document.hxx"
54 #include "RagTime5StructManager.hxx"
55 #include "RagTime5StyleManager.hxx"
56 
57 #include "RagTime5Pipeline.hxx"
58 
59 #include "libmwaw_internal.hxx"
60 
61 /** Internal: the structures of a RagTime5Pipeline */
62 namespace RagTime5PipelineInternal
63 {
64 //! the pipeline cluster ( 4001 zone)
65 struct ClusterPipeline final : public RagTime5ClusterManager::Cluster {
66   //! constructor
ClusterPipelineRagTime5PipelineInternal::ClusterPipeline67   ClusterPipeline()
68     : Cluster(C_Pipeline)
69     , m_dataId(0)
70     , m_masterId(0)
71     , m_layoutId(0)
72     , m_data2Link()
73   {
74   }
75   //! destructor
76   ~ClusterPipeline() final;
77   //! the data id
78   int m_dataId;
79   //! the master id
80   int m_masterId;
81   //! the layout id
82   int m_layoutId;
83   //! the second data link(rare)
84   RagTime5ClusterManager::Link m_data2Link;
85 };
86 
~ClusterPipeline()87 ClusterPipeline::~ClusterPipeline()
88 {
89 }
90 
91 ////////////////////////////////////////
92 //! Internal: the state of a RagTime5Pipeline
93 struct State {
94   //! constructor
StateRagTime5PipelineInternal::State95   State()
96     : m_idPipelineMap()
97   {
98   }
99   //! map data id to text zone
100   std::map<int, std::shared_ptr<ClusterPipeline> > m_idPipelineMap;
101 };
102 
103 }
104 
105 ////////////////////////////////////////////////////////////
106 // constructor/destructor, ...
107 ////////////////////////////////////////////////////////////
RagTime5Pipeline(RagTime5Document & doc)108 RagTime5Pipeline::RagTime5Pipeline(RagTime5Document &doc)
109   : m_document(doc)
110   , m_structManager(m_document.getStructManager())
111   , m_parserState(doc.getParserState())
112   , m_state(new RagTime5PipelineInternal::State)
113 {
114 }
115 
~RagTime5Pipeline()116 RagTime5Pipeline::~RagTime5Pipeline()
117 {
118 }
119 
version() const120 int RagTime5Pipeline::version() const
121 {
122   return m_parserState->m_version;
123 }
124 
send(int pipelineId,MWAWListenerPtr listener,MWAWPosition const & pos,int partId,double totalWidth)125 bool RagTime5Pipeline::send(int pipelineId, MWAWListenerPtr listener, MWAWPosition const &pos, int partId, double totalWidth)
126 {
127   if (m_state->m_idPipelineMap.find(pipelineId)==m_state->m_idPipelineMap.end() ||
128       !m_state->m_idPipelineMap.find(pipelineId)->second) {
129     MWAW_DEBUG_MSG(("RagTime5Pipeline::send: can not find container for pipeline %d\n", pipelineId));
130     return false;
131   }
132   int dataId=m_state->m_idPipelineMap.find(pipelineId)->second->m_dataId;
133   if (dataId==0)
134     return true;
135   return m_document.send(dataId, listener, pos, partId, 0, totalWidth);
136 }
137 
getContainerType(int pipelineId) const138 RagTime5ClusterManager::Cluster::Type  RagTime5Pipeline::getContainerType(int pipelineId) const
139 {
140   if (m_state->m_idPipelineMap.find(pipelineId)==m_state->m_idPipelineMap.end() ||
141       !m_state->m_idPipelineMap.find(pipelineId)->second) {
142     MWAW_DEBUG_MSG(("RagTime5Pipeline::getContainerType: can not find container for pipeline %d\n", pipelineId));
143     return RagTime5ClusterManager::Cluster::C_Unknown;
144   }
145   int dataId=m_state->m_idPipelineMap.find(pipelineId)->second->m_dataId;
146   if (dataId==0) // rare, but can happens
147     return RagTime5ClusterManager::Cluster::C_Unknown;
148   return m_document.getClusterType(dataId);
149 }
150 
151 ////////////////////////////////////////////////////////////
152 //
153 // Intermediate level
154 //
155 ////////////////////////////////////////////////////////////
156 
157 ////////////////////////////////////////////////////////////
158 //
159 // Low level
160 //
161 ////////////////////////////////////////////////////////////
162 
163 ////////////////////////////////////////////////////////////
164 // interface send function
165 ////////////////////////////////////////////////////////////
flushExtra()166 void RagTime5Pipeline::flushExtra()
167 {
168   MWAW_DEBUG_MSG(("RagTime5Pipeline::flushExtra: not implemented\n"));
169 }
170 
171 ////////////////////////////////////////////////////////////
172 // cluster parser
173 ////////////////////////////////////////////////////////////
174 
175 namespace RagTime5PipelineInternal
176 {
177 //! Internal: the helper to read a clustList
178 struct ClustListParser final : public RagTime5StructManager::DataParser {
179   //! constructor
ClustListParserRagTime5PipelineInternal::ClustListParser180   ClustListParser(RagTime5ClusterManager &clusterManager, int fieldSize, std::string const &zoneName)
181     : RagTime5StructManager::DataParser(zoneName)
182     , m_fieldSize(fieldSize)
183     , m_linkList()
184     , m_clusterManager(clusterManager)
185   {
186     if (m_fieldSize<56) {
187       MWAW_DEBUG_MSG(("RagTime5PipelineInternal::ClustListParser: bad field size\n"));
188       m_fieldSize=0;
189     }
190   }
191   //! destructor
192   ~ClustListParser() final;
193   //! return the cluster name
getClusterDebugNameRagTime5PipelineInternal::ClustListParser194   std::string getClusterDebugName(int id) const
195   {
196     return m_clusterManager.getClusterDebugName(id);
197   }
198   //! returns the not null list dataId list
getIdListRagTime5PipelineInternal::ClustListParser199   std::vector<int> getIdList() const
200   {
201     std::vector<int> res;
202     for (auto const &lnk : m_linkList) {
203       if (lnk.m_dataId>0)
204         res.push_back(lnk.m_dataId);
205     }
206     return res;
207   }
208   //! try to parse a data
parseDataRagTime5PipelineInternal::ClustListParser209   bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int /*n*/, libmwaw::DebugStream &f) final
210   {
211     // find only cluster with one field
212     long pos=input->tell();
213     if (endPos-pos!=m_fieldSize) {
214       MWAW_DEBUG_MSG(("RagTime5PipelineInternal::ClustListParser::parse: bad data size\n"));
215       return false;
216     }
217     std::vector<int> listIds;
218     if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
219       MWAW_DEBUG_MSG(("RagTime5PipelineInternal::ClustListParser::parse: can not read an cluster id\n"));
220       f << "##clusterIds,";
221       return false;
222     }
223     RagTime5StructManager::ZoneLink link;
224     link.m_dataId=listIds[0];
225     if (listIds[0])
226       f << getClusterDebugName(listIds[0]) << ",";
227     link.m_subZoneId[0]=long(input->readULong(4)); // subId[3]
228     f << link;
229     float dim[2];
230     for (auto &d : dim) d=float(input->readULong(4))/65536.f;
231     f << "dim=" << MWAWVec2f(dim[0],dim[1]) << ",";
232     int val;
233     f << "unkn=[";
234     for (int i=0; i<8; ++i) {
235       val=static_cast<int>(input->readLong(2));
236       if (val) f << val << ",";
237       else f << "_,";
238     }
239     f << "],";
240     for (int i=0; i<12; ++i) { // always 0
241       val=static_cast<int>(input->readLong(2));
242       if (val) f << "f" << i << "=" << val << ",";
243     }
244     m_linkList.push_back(link);
245     return true;
246   }
247 
248   //! the field size
249   int m_fieldSize;
250   //! the list of read cluster
251   std::vector<RagTime5StructManager::ZoneLink> m_linkList;
252 private:
253   //! the main zone manager
254   RagTime5ClusterManager &m_clusterManager;
255   //! copy constructor, not implemented
256   ClustListParser(ClustListParser &orig) = delete;
257   //! copy operator, not implemented
258   ClustListParser &operator=(ClustListParser &orig) = delete;
259 };
260 
~ClustListParser()261 ClustListParser::~ClustListParser()
262 {
263 }
264 
265 //! Internal: the helper to read a unknown
266 struct UnknownParser final : public RagTime5StructManager::DataParser {
267   //! constructor
UnknownParserRagTime5PipelineInternal::UnknownParser268   UnknownParser(int fieldSize, std::string const &zoneName)
269     : RagTime5StructManager::DataParser(zoneName)
270     , m_fieldSize(fieldSize)
271   {
272     if (m_fieldSize<12) {
273       MWAW_DEBUG_MSG(("RagTime5PipelineInternal::UnknownParser: bad field size\n"));
274       m_fieldSize=0;
275     }
276   }
277   //! destructor
278   ~UnknownParser() final;
279   //! try to parse a data
parseDataRagTime5PipelineInternal::UnknownParser280   bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int /*n*/, libmwaw::DebugStream &f) final
281   {
282     long pos=input->tell();
283     if (endPos-pos!=m_fieldSize) {
284       MWAW_DEBUG_MSG(("RagTime5PipelineInternal::UnknownParser::parse: bad data size\n"));
285       return false;
286     }
287     for (int i=0; i<6; ++i) { // f0=1, f1=5|6, f2=1|2, f5=0|1
288       auto val=static_cast<int>(input->readLong(2));
289       if (val)
290         f << "f" << i << "=" << val << ",";
291     }
292     return true;
293   }
294 
295   //! the field size
296   int m_fieldSize;
297 private:
298   //! copy constructor, not implemented
299   UnknownParser(UnknownParser &orig) = delete;
300   //! copy operator, not implemented
301   UnknownParser &operator=(UnknownParser &orig) = delete;
302 };
303 
~UnknownParser()304 UnknownParser::~UnknownParser()
305 {
306 }
307 
308 //
309 //! try to read a pipeline cluster: 104,204,4104, 4204
310 //
311 struct PipelineCParser final : public RagTime5ClusterManager::ClusterParser {
312   //! constructor
PipelineCParserRagTime5PipelineInternal::PipelineCParser313   PipelineCParser(RagTime5ClusterManager &parser, int type)
314     : ClusterParser(parser, type, "ClustPipeline")
315     , m_cluster(new ClusterPipeline)
316   {
317   }
318   //! destructor
319   ~PipelineCParser() final;
320   //! return the current cluster
getClusterRagTime5PipelineInternal::PipelineCParser321   std::shared_ptr<RagTime5ClusterManager::Cluster> getCluster() final
322   {
323     return m_cluster;
324   }
325   //! return the pipeline cluster
getPipelineClusterRagTime5PipelineInternal::PipelineCParser326   std::shared_ptr<ClusterPipeline> getPipelineCluster()
327   {
328     return m_cluster;
329   }
330   //! parse a field
parseFieldRagTime5PipelineInternal::PipelineCParser331   bool parseField(RagTime5StructManager::Field const &field, int /*m*/, libmwaw::DebugStream &f) final
332   {
333     if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==0x146c015) {
334       f << "unkn0=[";
335       for (auto const &child : field.m_fieldList) {
336         if (child.m_type==RagTime5StructManager::Field::T_Unstructured && child.m_fileType==0xce017) { // find 2
337           f << child << ",";
338           continue;
339         }
340         MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseField: find unexpected child\n"));
341         f << "##[" << child << "],";
342       }
343       f << "],";
344     }
345     else {
346       MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseField: find unknow field\n"));
347       f << "##[" << field << "],";
348     }
349     return true;
350   }
351   //! parse a zone
parseZoneRagTime5PipelineInternal::PipelineCParser352   bool parseZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f) final
353   {
354     if (flag!=0x31)
355       f << "fl=" << std::hex << flag << std::dec << ",";
356     if (m_dataId || N!=-5) {
357       MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: find unexpected header\n"));
358       f << "###type" << std::hex << N << std::dec;
359       return true;
360     }
361     if (fSz!=76 && fSz!=110) {
362       MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: find unexpected file size\n"));
363       f << "###fSz=" << fSz << ",";
364       return true;
365     }
366     int val;
367     for (int i=0; i<2; ++i) { // always 0?
368       val=static_cast<int>(input->readLong(2));
369       if (val) f << "f" << i+1 << "=" << val << ",";
370     }
371     val=static_cast<int>(input->readLong(2));
372     f << "id=" << val << ",";
373     val=static_cast<int>(input->readULong(2));
374     if (val!=m_type) {
375       MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: the zone type seems odd\n"));
376       f << "##zoneType=" << std::hex << val << std::dec << ",";
377     }
378     val=static_cast<int>(input->readLong(2)); // always 0?
379     if (val) f << "f4=" << val << ",";
380     for (int i=0; i<7; ++i) { // g1, g2, g3 small int other 0
381       val=static_cast<int>(input->readLong(4));
382       if (i==2)
383         m_link.m_N=val;
384       else if (val) f << "g" << i << "=" << val << ",";
385     }
386     m_link.m_fileType[1]=input->readULong(2);
387     m_link.m_fieldSize=static_cast<int>(input->readULong(2));
388 
389     std::vector<int> listIds;
390     long actPos=input->tell();
391     if (!RagTime5StructManager::readDataIdList(input, 2, listIds)) {
392       MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: can not read the first list id\n"));
393       f << "##listIds,";
394       input->seek(actPos, librevenge::RVNG_SEEK_SET);
395     }
396     else {
397       if (listIds[0]) {
398         m_link.m_ids.push_back(listIds[0]);
399         m_cluster->m_parentLink=m_link;
400         f << "parent[list]=data" << listIds[0] << "A,";
401       }
402       if (listIds[1]) { // the object corresponding to the pipeline
403         m_cluster->m_dataId=listIds[1];
404         f << "data[id]=" << getClusterDebugName(listIds[1]) << ",";
405       }
406     }
407     unsigned long ulVal=input->readULong(4);
408     if (ulVal) {
409       f << "h0=" << (ulVal&0x7FFFFFFF);
410       if (ulVal&0x80000000) f << "[h],";
411       else f << ",";
412     }
413     val=static_cast<int>(input->readLong(2)); // always 1?
414     if (val!=1) f << "h1=" << val << ",";
415     listIds.clear();
416     if (!RagTime5StructManager::readDataIdList(input, 2, listIds)) {
417       MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: can not read the cluster list id\n"));
418       f << "##listClusterIds,";
419       return true;
420     }
421     if (listIds[0]) { // find some master layout and some master pipeline
422       m_cluster->m_masterId=listIds[0];
423       f << "id[master]=" << getClusterDebugName(listIds[0]) << ",";
424     }
425     if (listIds[1]) { // find always layout
426       m_cluster->m_layoutId=listIds[1];
427       f << "id[layout]=" << getClusterDebugName(listIds[1]) << ",";
428     }
429     val=static_cast<int>(input->readULong(2)); // 2[08a][01]
430     f << "fl=" << std::hex << val << std::dec << ",";
431     for (int i=0; i<2; ++i) { // h2=0|4|a, h3=small number
432       val=static_cast<int>(input->readLong(2));
433       if (val) f << "h" << i+2 << "=" << val << ",";
434     }
435     if (fSz==76) return true;
436 
437     for (int i=0; i<7; ++i) { // g1, g2, g3 small int other 0
438       val=static_cast<int>(input->readLong(i==0 ? 2 : 4));
439       if (i==2)
440         m_link.m_N=val;
441       else if (val) f << "g" << i << "=" << val << ",";
442     }
443     m_link.m_fileType[1]=input->readULong(2);
444     m_link.m_fieldSize=static_cast<int>(input->readULong(2));
445 
446     listIds.clear();
447     if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
448       MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: can not read the second list id\n"));
449       f << "##listIds2,";
450       return true;
451     }
452     if (listIds[0]) {
453       m_link.m_ids.clear();
454       m_link.m_ids.push_back(listIds[0]);
455       m_cluster->m_data2Link=m_link;
456       f << "data2=data" << listIds[0] << "A,";
457     }
458     return true;
459   }
460 protected:
461   //! the current cluster
462   std::shared_ptr<ClusterPipeline> m_cluster;
463 };
464 
~PipelineCParser()465 PipelineCParser::~PipelineCParser()
466 {
467 }
468 
469 }
470 
readPipelineCluster(RagTime5Zone & zone,int zoneType)471 std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Pipeline::readPipelineCluster(RagTime5Zone &zone, int zoneType)
472 {
473   auto clusterManager=m_document.getClusterManager();
474   if (!clusterManager) {
475     MWAW_DEBUG_MSG(("RagTime5Pipeline::readPipelineCluster: oops can not find the cluster manager\n"));
476     return std::shared_ptr<RagTime5ClusterManager::Cluster>();
477   }
478   RagTime5PipelineInternal::PipelineCParser parser(*clusterManager, zoneType);
479   if (!clusterManager->readCluster(zone, parser) || !parser.getPipelineCluster()) {
480     MWAW_DEBUG_MSG(("RagTime5Pipeline::readPipelineCluster: oops can not find the cluster\n"));
481     return std::shared_ptr<RagTime5ClusterManager::Cluster>();
482   }
483 
484   auto cluster=parser.getPipelineCluster();
485   if (cluster->m_parentLink.empty()) {
486     MWAW_DEBUG_MSG(("RagTime5Document::readClusterPipelineData: can not find the parent zone\n"));
487   }
488   else {
489     RagTime5PipelineInternal::ClustListParser linkParser(*clusterManager, cluster->m_parentLink.m_fieldSize, "PipelineParent");
490     m_document.readFixedSizeZone(cluster->m_parentLink, linkParser);
491     m_document.checkClusterList(linkParser.m_linkList);
492   }
493 
494   if (!cluster->m_data2Link.empty()) {
495     RagTime5PipelineInternal::UnknownParser data2Parser(cluster->m_data2Link.m_fieldSize, "PipelineUnknown");
496     m_document.readFixedSizeZone(cluster->m_data2Link, data2Parser);
497   }
498 
499   if (m_state->m_idPipelineMap.find(zone.m_ids[0])!=m_state->m_idPipelineMap.end()) {
500     MWAW_DEBUG_MSG(("RagTime5Pipeline::readPipelineCluster: cluster %d already exists\n", zone.m_ids[0]));
501   }
502   else
503     m_state->m_idPipelineMap[zone.m_ids[0]]=cluster;
504   return cluster;
505 }
506 
507 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
508