Coverage Report

Created: 2026-09-06 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/RagTime5Pipeline.cxx
Line
Count
Source
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
67
  ClusterPipeline()
68
22.8k
    : Cluster(C_Pipeline)
69
22.8k
    , m_dataId(0)
70
22.8k
    , m_masterId(0)
71
22.8k
    , m_layoutId(0)
72
22.8k
    , m_data2Link()
73
22.8k
  {
74
22.8k
  }
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
87
ClusterPipeline::~ClusterPipeline()
88
22.8k
{
89
22.8k
}
90
91
////////////////////////////////////////
92
//! Internal: the state of a RagTime5Pipeline
93
struct State {
94
  //! constructor
95
  State()
96
80.8k
    : m_idPipelineMap()
97
80.8k
  {
98
80.8k
  }
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
////////////////////////////////////////////////////////////
108
RagTime5Pipeline::RagTime5Pipeline(RagTime5Document &doc)
109
80.8k
  : m_document(doc)
110
80.8k
  , m_structManager(m_document.getStructManager())
111
80.8k
  , m_parserState(doc.getParserState())
112
80.8k
  , m_state(new RagTime5PipelineInternal::State)
113
80.8k
{
114
80.8k
}
115
116
RagTime5Pipeline::~RagTime5Pipeline()
117
80.8k
{
118
80.8k
}
119
120
int RagTime5Pipeline::version() const
121
0
{
122
0
  return m_parserState->m_version;
123
0
}
124
125
bool RagTime5Pipeline::send(int pipelineId, MWAWListenerPtr listener, MWAWPosition const &pos, int partId, double totalWidth)
126
4.81k
{
127
4.81k
  if (m_state->m_idPipelineMap.find(pipelineId)==m_state->m_idPipelineMap.end() ||
128
4.81k
      !m_state->m_idPipelineMap.find(pipelineId)->second) {
129
0
    MWAW_DEBUG_MSG(("RagTime5Pipeline::send: can not find container for pipeline %d\n", pipelineId));
130
0
    return false;
131
0
  }
132
4.81k
  int dataId=m_state->m_idPipelineMap.find(pipelineId)->second->m_dataId;
133
4.81k
  if (dataId==0)
134
0
    return true;
135
4.81k
  return m_document.send(dataId, listener, pos, partId, 0, totalWidth);
136
4.81k
}
137
138
RagTime5ClusterManager::Cluster::Type  RagTime5Pipeline::getContainerType(int pipelineId) const
139
5.72k
{
140
5.72k
  if (m_state->m_idPipelineMap.find(pipelineId)==m_state->m_idPipelineMap.end() ||
141
5.72k
      !m_state->m_idPipelineMap.find(pipelineId)->second) {
142
0
    MWAW_DEBUG_MSG(("RagTime5Pipeline::getContainerType: can not find container for pipeline %d\n", pipelineId));
143
0
    return RagTime5ClusterManager::Cluster::C_Unknown;
144
0
  }
145
5.72k
  int dataId=m_state->m_idPipelineMap.find(pipelineId)->second->m_dataId;
146
5.72k
  if (dataId==0) // rare, but can happens
147
163
    return RagTime5ClusterManager::Cluster::C_Unknown;
148
5.56k
  return m_document.getClusterType(dataId);
149
5.72k
}
150
151
////////////////////////////////////////////////////////////
152
//
153
// Intermediate level
154
//
155
////////////////////////////////////////////////////////////
156
157
////////////////////////////////////////////////////////////
158
//
159
// Low level
160
//
161
////////////////////////////////////////////////////////////
162
163
////////////////////////////////////////////////////////////
164
// interface send function
165
////////////////////////////////////////////////////////////
166
void RagTime5Pipeline::flushExtra()
167
0
{
168
0
  MWAW_DEBUG_MSG(("RagTime5Pipeline::flushExtra: not implemented\n"));
169
0
}
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
180
  ClustListParser(RagTime5ClusterManager &clusterManager, int fieldSize, std::string const &zoneName)
181
15.4k
    : RagTime5StructManager::DataParser(zoneName)
182
15.4k
    , m_fieldSize(fieldSize)
183
15.4k
    , m_linkList()
184
15.4k
    , m_clusterManager(clusterManager)
185
15.4k
  {
186
15.4k
    if (m_fieldSize<56) {
187
59
      MWAW_DEBUG_MSG(("RagTime5PipelineInternal::ClustListParser: bad field size\n"));
188
59
      m_fieldSize=0;
189
59
    }
190
15.4k
  }
191
  //! destructor
192
  ~ClustListParser() final;
193
  //! return the cluster name
194
  std::string getClusterDebugName(int id) const
195
10.9k
  {
196
10.9k
    return m_clusterManager.getClusterDebugName(id);
197
10.9k
  }
198
  //! returns the not null list dataId list
199
  std::vector<int> getIdList() const
200
0
  {
201
0
    std::vector<int> res;
202
0
    for (auto const &lnk : m_linkList) {
203
0
      if (lnk.m_dataId>0)
204
0
        res.push_back(lnk.m_dataId);
205
0
    }
206
0
    return res;
207
0
  }
208
  //! try to parse a data
209
  bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int /*n*/, libmwaw::DebugStream &f) final
210
11.5k
  {
211
    // find only cluster with one field
212
11.5k
    long pos=input->tell();
213
11.5k
    if (endPos-pos!=m_fieldSize) {
214
26
      MWAW_DEBUG_MSG(("RagTime5PipelineInternal::ClustListParser::parse: bad data size\n"));
215
26
      return false;
216
26
    }
217
11.5k
    std::vector<int> listIds;
218
11.5k
    if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
219
429
      MWAW_DEBUG_MSG(("RagTime5PipelineInternal::ClustListParser::parse: can not read an cluster id\n"));
220
429
      f << "##clusterIds,";
221
429
      return false;
222
429
    }
223
11.1k
    RagTime5StructManager::ZoneLink link;
224
11.1k
    link.m_dataId=listIds[0];
225
11.1k
    if (listIds[0])
226
10.9k
      f << getClusterDebugName(listIds[0]) << ",";
227
11.1k
    link.m_subZoneId[0]=long(input->readULong(4)); // subId[3]
228
11.1k
    f << link;
229
11.1k
    float dim[2];
230
22.2k
    for (auto &d : dim) d=float(input->readULong(4))/65536.f;
231
11.1k
    f << "dim=" << MWAWVec2f(dim[0],dim[1]) << ",";
232
11.1k
    int val;
233
11.1k
    f << "unkn=[";
234
100k
    for (int i=0; i<8; ++i) {
235
89.0k
      val=static_cast<int>(input->readLong(2));
236
89.0k
      if (val) f << val << ",";
237
54.9k
      else f << "_,";
238
89.0k
    }
239
11.1k
    f << "],";
240
144k
    for (int i=0; i<12; ++i) { // always 0
241
133k
      val=static_cast<int>(input->readLong(2));
242
133k
      if (val) f << "f" << i << "=" << val << ",";
243
133k
    }
244
11.1k
    m_linkList.push_back(link);
245
11.1k
    return true;
246
11.5k
  }
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
261
ClustListParser::~ClustListParser()
262
15.4k
{
263
15.4k
}
264
265
//! Internal: the helper to read a unknown
266
struct UnknownParser final : public RagTime5StructManager::DataParser {
267
  //! constructor
268
  UnknownParser(int fieldSize, std::string const &zoneName)
269
0
    : RagTime5StructManager::DataParser(zoneName)
270
0
    , m_fieldSize(fieldSize)
271
0
  {
272
0
    if (m_fieldSize<12) {
273
0
      MWAW_DEBUG_MSG(("RagTime5PipelineInternal::UnknownParser: bad field size\n"));
274
0
      m_fieldSize=0;
275
0
    }
276
0
  }
277
  //! destructor
278
  ~UnknownParser() final;
279
  //! try to parse a data
280
  bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int /*n*/, libmwaw::DebugStream &f) final
281
0
  {
282
0
    long pos=input->tell();
283
0
    if (endPos-pos!=m_fieldSize) {
284
0
      MWAW_DEBUG_MSG(("RagTime5PipelineInternal::UnknownParser::parse: bad data size\n"));
285
0
      return false;
286
0
    }
287
0
    for (int i=0; i<6; ++i) { // f0=1, f1=5|6, f2=1|2, f5=0|1
288
0
      auto val=static_cast<int>(input->readLong(2));
289
0
      if (val)
290
0
        f << "f" << i << "=" << val << ",";
291
0
    }
292
0
    return true;
293
0
  }
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
304
UnknownParser::~UnknownParser()
305
0
{
306
0
}
307
308
//
309
//! try to read a pipeline cluster: 104,204,4104, 4204
310
//
311
struct PipelineCParser final : public RagTime5ClusterManager::ClusterParser {
312
  //! constructor
313
  PipelineCParser(RagTime5ClusterManager &parser, int type)
314
22.8k
    : ClusterParser(parser, type, "ClustPipeline")
315
22.8k
    , m_cluster(new ClusterPipeline)
316
22.8k
  {
317
22.8k
  }
318
  //! destructor
319
  ~PipelineCParser() final;
320
  //! return the current cluster
321
  std::shared_ptr<RagTime5ClusterManager::Cluster> getCluster() final
322
17.9k
  {
323
17.9k
    return m_cluster;
324
17.9k
  }
325
  //! return the pipeline cluster
326
  std::shared_ptr<ClusterPipeline> getPipelineCluster()
327
35.8k
  {
328
35.8k
    return m_cluster;
329
35.8k
  }
330
  //! parse a field
331
  bool parseField(RagTime5StructManager::Field const &field, int /*m*/, libmwaw::DebugStream &f) final
332
2.95k
  {
333
2.95k
    if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==0x146c015) {
334
185
      f << "unkn0=[";
335
185
      for (auto const &child : field.m_fieldList) {
336
185
        if (child.m_type==RagTime5StructManager::Field::T_Unstructured && child.m_fileType==0xce017) { // find 2
337
185
          f << child << ",";
338
185
          continue;
339
185
        }
340
0
        MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseField: find unexpected child\n"));
341
0
        f << "##[" << child << "],";
342
0
      }
343
185
      f << "],";
344
185
    }
345
2.76k
    else {
346
2.76k
      MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseField: find unknow field\n"));
347
2.76k
      f << "##[" << field << "],";
348
2.76k
    }
349
2.95k
    return true;
350
2.95k
  }
351
  //! parse a zone
352
  bool parseZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f) final
353
20.9k
  {
354
20.9k
    if (flag!=0x31)
355
3.18k
      f << "fl=" << std::hex << flag << std::dec << ",";
356
20.9k
    if (m_dataId || N!=-5) {
357
3.12k
      MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: find unexpected header\n"));
358
3.12k
      f << "###type" << std::hex << N << std::dec;
359
3.12k
      return true;
360
3.12k
    }
361
17.7k
    if (fSz!=76 && fSz!=110) {
362
1.05k
      MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: find unexpected file size\n"));
363
1.05k
      f << "###fSz=" << fSz << ",";
364
1.05k
      return true;
365
1.05k
    }
366
16.7k
    int val;
367
50.2k
    for (int i=0; i<2; ++i) { // always 0?
368
33.4k
      val=static_cast<int>(input->readLong(2));
369
33.4k
      if (val) f << "f" << i+1 << "=" << val << ",";
370
33.4k
    }
371
16.7k
    val=static_cast<int>(input->readLong(2));
372
16.7k
    f << "id=" << val << ",";
373
16.7k
    val=static_cast<int>(input->readULong(2));
374
16.7k
    if (val!=m_type) {
375
3
      MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: the zone type seems odd\n"));
376
3
      f << "##zoneType=" << std::hex << val << std::dec << ",";
377
3
    }
378
16.7k
    val=static_cast<int>(input->readLong(2)); // always 0?
379
16.7k
    if (val) f << "f4=" << val << ",";
380
133k
    for (int i=0; i<7; ++i) { // g1, g2, g3 small int other 0
381
117k
      val=static_cast<int>(input->readLong(4));
382
117k
      if (i==2)
383
16.7k
        m_link.m_N=val;
384
100k
      else if (val) f << "g" << i << "=" << val << ",";
385
117k
    }
386
16.7k
    m_link.m_fileType[1]=input->readULong(2);
387
16.7k
    m_link.m_fieldSize=static_cast<int>(input->readULong(2));
388
389
16.7k
    std::vector<int> listIds;
390
16.7k
    long actPos=input->tell();
391
16.7k
    if (!RagTime5StructManager::readDataIdList(input, 2, listIds)) {
392
1.18k
      MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: can not read the first list id\n"));
393
1.18k
      f << "##listIds,";
394
1.18k
      input->seek(actPos, librevenge::RVNG_SEEK_SET);
395
1.18k
    }
396
15.5k
    else {
397
15.5k
      if (listIds[0]) {
398
15.4k
        m_link.m_ids.push_back(listIds[0]);
399
15.4k
        m_cluster->m_parentLink=m_link;
400
15.4k
        f << "parent[list]=data" << listIds[0] << "A,";
401
15.4k
      }
402
15.5k
      if (listIds[1]) { // the object corresponding to the pipeline
403
15.4k
        m_cluster->m_dataId=listIds[1];
404
15.4k
        f << "data[id]=" << getClusterDebugName(listIds[1]) << ",";
405
15.4k
      }
406
15.5k
    }
407
16.7k
    unsigned long ulVal=input->readULong(4);
408
16.7k
    if (ulVal) {
409
16.7k
      f << "h0=" << (ulVal&0x7FFFFFFF);
410
16.7k
      if (ulVal&0x80000000) f << "[h],";
411
1.17k
      else f << ",";
412
16.7k
    }
413
16.7k
    val=static_cast<int>(input->readLong(2)); // always 1?
414
16.7k
    if (val!=1) f << "h1=" << val << ",";
415
16.7k
    listIds.clear();
416
16.7k
    if (!RagTime5StructManager::readDataIdList(input, 2, listIds)) {
417
670
      MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: can not read the cluster list id\n"));
418
670
      f << "##listClusterIds,";
419
670
      return true;
420
670
    }
421
16.0k
    if (listIds[0]) { // find some master layout and some master pipeline
422
815
      m_cluster->m_masterId=listIds[0];
423
815
      f << "id[master]=" << getClusterDebugName(listIds[0]) << ",";
424
815
    }
425
16.0k
    if (listIds[1]) { // find always layout
426
15.7k
      m_cluster->m_layoutId=listIds[1];
427
15.7k
      f << "id[layout]=" << getClusterDebugName(listIds[1]) << ",";
428
15.7k
    }
429
16.0k
    val=static_cast<int>(input->readULong(2)); // 2[08a][01]
430
16.0k
    f << "fl=" << std::hex << val << std::dec << ",";
431
48.2k
    for (int i=0; i<2; ++i) { // h2=0|4|a, h3=small number
432
32.1k
      val=static_cast<int>(input->readLong(2));
433
32.1k
      if (val) f << "h" << i+2 << "=" << val << ",";
434
32.1k
    }
435
16.0k
    if (fSz==76) return true;
436
437
0
    for (int i=0; i<7; ++i) { // g1, g2, g3 small int other 0
438
0
      val=static_cast<int>(input->readLong(i==0 ? 2 : 4));
439
0
      if (i==2)
440
0
        m_link.m_N=val;
441
0
      else if (val) f << "g" << i << "=" << val << ",";
442
0
    }
443
0
    m_link.m_fileType[1]=input->readULong(2);
444
0
    m_link.m_fieldSize=static_cast<int>(input->readULong(2));
445
446
0
    listIds.clear();
447
0
    if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
448
0
      MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: can not read the second list id\n"));
449
0
      f << "##listIds2,";
450
0
      return true;
451
0
    }
452
0
    if (listIds[0]) {
453
0
      m_link.m_ids.clear();
454
0
      m_link.m_ids.push_back(listIds[0]);
455
0
      m_cluster->m_data2Link=m_link;
456
0
      f << "data2=data" << listIds[0] << "A,";
457
0
    }
458
0
    return true;
459
0
  }
460
protected:
461
  //! the current cluster
462
  std::shared_ptr<ClusterPipeline> m_cluster;
463
};
464
465
PipelineCParser::~PipelineCParser()
466
22.8k
{
467
22.8k
}
468
469
}
470
471
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Pipeline::readPipelineCluster(RagTime5Zone &zone, int zoneType)
472
22.8k
{
473
22.8k
  auto clusterManager=m_document.getClusterManager();
474
22.8k
  if (!clusterManager) {
475
0
    MWAW_DEBUG_MSG(("RagTime5Pipeline::readPipelineCluster: oops can not find the cluster manager\n"));
476
0
    return std::shared_ptr<RagTime5ClusterManager::Cluster>();
477
0
  }
478
22.8k
  RagTime5PipelineInternal::PipelineCParser parser(*clusterManager, zoneType);
479
22.8k
  if (!clusterManager->readCluster(zone, parser) || !parser.getPipelineCluster()) {
480
4.99k
    MWAW_DEBUG_MSG(("RagTime5Pipeline::readPipelineCluster: oops can not find the cluster\n"));
481
4.99k
    return std::shared_ptr<RagTime5ClusterManager::Cluster>();
482
4.99k
  }
483
484
17.9k
  auto cluster=parser.getPipelineCluster();
485
17.9k
  if (cluster->m_parentLink.empty()) {
486
2.44k
    MWAW_DEBUG_MSG(("RagTime5Document::readClusterPipelineData: can not find the parent zone\n"));
487
2.44k
  }
488
15.4k
  else {
489
15.4k
    RagTime5PipelineInternal::ClustListParser linkParser(*clusterManager, cluster->m_parentLink.m_fieldSize, "PipelineParent");
490
15.4k
    m_document.readFixedSizeZone(cluster->m_parentLink, linkParser);
491
15.4k
    m_document.checkClusterList(linkParser.m_linkList);
492
15.4k
  }
493
494
17.9k
  if (!cluster->m_data2Link.empty()) {
495
0
    RagTime5PipelineInternal::UnknownParser data2Parser(cluster->m_data2Link.m_fieldSize, "PipelineUnknown");
496
0
    m_document.readFixedSizeZone(cluster->m_data2Link, data2Parser);
497
0
  }
498
499
17.9k
  if (m_state->m_idPipelineMap.find(zone.m_ids[0])!=m_state->m_idPipelineMap.end()) {
500
3.03k
    MWAW_DEBUG_MSG(("RagTime5Pipeline::readPipelineCluster: cluster %d already exists\n", zone.m_ids[0]));
501
3.03k
  }
502
14.8k
  else
503
14.8k
    m_state->m_idPipelineMap[zone.m_ids[0]]=cluster;
504
17.9k
  return cluster;
505
22.8k
}
506
507
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: