Coverage Report

Created: 2026-06-13 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/ClarisWksStruct.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 <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
54
std::ostream &operator<<(std::ostream &o, Struct const &z)
55
0
{
56
0
  o << "sz=" << z.m_size << ",";
57
0
  if (z.m_numData>0) {
58
0
    o << "N=" << z.m_numData << ",";
59
0
    o << "data[sz]=" << z.m_dataSize << ",";
60
0
  }
61
0
  if (z.m_headerSize>0)
62
0
    o << "header[sz]=" << z.m_headerSize << ",";
63
0
  if (z.m_type>0)
64
0
    o << "type=" << z.m_type << ",";
65
0
  for (int i=0; i<2; ++i) {
66
0
    if (z.m_values[i])
67
0
      o << "f" << i << "=" << z.m_values[i] << ",";
68
0
  }
69
0
  return o;
70
0
}
71
72
bool Struct::readHeader(MWAWInputStreamPtr input, bool strict)
73
8.92M
{
74
8.92M
  *this=Struct();
75
8.92M
  long pos=input->tell();
76
8.92M
  if (!input->checkPosition(pos+4))
77
1.52M
    return false;
78
7.39M
  m_size=input->readLong(4);
79
7.39M
  if (m_size==0)
80
2.62M
    return true;
81
4.76M
  if (m_size<12 || !input->checkPosition(pos+4+m_size))
82
1.72M
    return false;
83
84
3.03M
  m_numData =int(input->readULong(2));
85
3.03M
  m_type = int(input->readLong(2));
86
3.03M
  m_values[0] = int(input->readLong(2));
87
3.03M
  m_dataSize = long(input->readULong(2));
88
3.03M
  m_headerSize = long(input->readULong(2));
89
3.03M
  m_values[1] = int(input->readLong(2));
90
3.03M
  if (m_numData && m_dataSize>10000) return false; // too big to be honetx
91
2.97M
  long expectedLength = 12+m_headerSize;
92
2.97M
  if (m_numData>0) expectedLength+=long(m_numData)*m_dataSize;
93
2.97M
  if (expectedLength>m_size || (strict && expectedLength != m_size))
94
238k
    return false;
95
2.73M
  return true;
96
2.97M
}
97
98
// try to read a list of structured zone
99
bool readIntZone(MWAWParserState &parserState, char const *zoneName, bool hasEntete, int intSz, std::vector<int> &res)
100
2.38M
{
101
2.38M
  res.resize(0);
102
2.38M
  if (intSz != 1 && intSz != 2 && intSz != 4) {
103
0
    MWAW_DEBUG_MSG(("ClarisWksStruct::readIntZone: unknown int size: %d\n", intSz));
104
0
    return false;
105
0
  }
106
107
2.38M
  MWAWInputStreamPtr input = parserState.m_input;
108
2.38M
  long pos = input->tell();
109
2.38M
  Struct zone;
110
2.38M
  if (!zone.readHeader(input,true)) {
111
1.72M
    MWAW_DEBUG_MSG(("ClarisWksStruct::readIntZone: can not header of %s\n", zoneName ? zoneName : "unamed"));
112
1.72M
  }
113
2.38M
  libmwaw::DebugStream f;
114
2.38M
  libmwaw::DebugFile &ascFile=parserState.m_asciiFile;
115
2.38M
  if (zoneName && strlen(zoneName))
116
2.38M
    f << "Entries(" << zoneName << "):";
117
2.38M
  long endPos = pos+4+zone.m_size;
118
119
2.38M
  if (zone.m_size==0) {
120
2.04M
    if (hasEntete) {
121
0
      ascFile.addPos(pos-4);
122
0
      ascFile.addNote(f.str().c_str());
123
0
    }
124
2.04M
    else {
125
2.04M
      ascFile.addPos(pos);
126
2.04M
      ascFile.addNote("NOP");
127
2.04M
    }
128
2.04M
    return true;
129
2.04M
  }
130
131
347k
  if (zone.m_dataSize != intSz) {
132
140k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
133
140k
    MWAW_DEBUG_MSG(("ClarisWksStruct::readIntZone: unexpected field size\n"));
134
140k
    return false;
135
140k
  }
136
137
206k
  f << zone;
138
206k
  if (zone.m_headerSize) {
139
24.4k
    ascFile.addDelimiter(input->tell(), '|');
140
24.4k
    input->seek(zone.m_headerSize, librevenge::RVNG_SEEK_CUR);
141
24.4k
  }
142
206k
  if (zone.m_numData) ascFile.addDelimiter(input->tell(), '|');
143
206k
  f << "[";
144
2.14G
  for (long i = 0; i < zone.m_numData; i++) {
145
2.14G
    auto val = int(input->readLong(intSz));
146
2.14G
    res.push_back(val);
147
2.14G
    if (val>1000) f << "0x" << std::hex << val << std::dec << ",";
148
2.02G
    else f << val << ",";
149
2.14G
  }
150
206k
  f << "]";
151
152
206k
  ascFile.addPos(hasEntete ? pos-4 : pos);
153
206k
  ascFile.addNote(f.str().c_str());
154
155
206k
  input->seek(endPos,librevenge::RVNG_SEEK_SET);
156
206k
  return true;
157
347k
}
158
159
///////////////////////////////////////////////////////////
160
// try to read a unknown structured zone
161
////////////////////////////////////////////////////////////
162
bool readStructZone(MWAWParserState &parserState, char const *zoneName, bool hasEntete)
163
1.44M
{
164
1.44M
  MWAWInputStreamPtr input = parserState.m_input;
165
1.44M
  long pos = input->tell();
166
1.44M
  Struct zone;
167
1.44M
  if (!zone.readHeader(input,false) || (zone.m_size && zone.m_dataSize<=0)) {
168
319k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
169
319k
    MWAW_DEBUG_MSG(("ClarisWksStruct::readStructZone: can not read header for %s\n", zoneName));
170
319k
    return false;
171
319k
  }
172
1.12M
  libmwaw::DebugFile &ascFile= parserState.m_asciiFile;
173
1.12M
  libmwaw::DebugStream f;
174
1.12M
  f << "Entries(" << zoneName << "):";
175
176
1.12M
  if (zone.m_size == 0) {
177
785k
    if (hasEntete) {
178
0
      ascFile.addPos(pos-4);
179
0
      ascFile.addNote(f.str().c_str());
180
0
    }
181
785k
    else {
182
785k
      ascFile.addPos(pos);
183
785k
      ascFile.addNote("NOP");
184
785k
    }
185
785k
    return true;
186
785k
  }
187
344k
  long endPos=pos+4+zone.m_size;
188
344k
  f << zone;
189
344k
  if (zone.m_headerSize) {
190
23.9k
    ascFile.addDelimiter(input->tell(), '|');
191
23.9k
    input->seek(zone.m_headerSize, librevenge::RVNG_SEEK_CUR);
192
23.9k
  }
193
344k
  ascFile.addPos(hasEntete ? pos-4 : pos);
194
344k
  ascFile.addNote(f.str().c_str());
195
196
344k
  pos=input->tell();
197
458k
  for (long i = 0; i < zone.m_numData; i++) {
198
114k
    f.str("");
199
114k
    f << zoneName << "-" << i << ":";
200
201
114k
    ascFile.addPos(pos);
202
114k
    ascFile.addNote(f.str().c_str());
203
114k
    pos += zone.m_dataSize;
204
114k
  }
205
344k
  if (pos!=endPos) {
206
14.7k
    MWAW_DEBUG_MSG(("ClarisWksStruct::readStructZone: find extra data for %s\n", zoneName));
207
14.7k
    f.str("");
208
14.7k
    f << zoneName << ":###extra";
209
14.7k
    ascFile.addPos(pos);
210
14.7k
    ascFile.addNote(f.str().c_str());
211
14.7k
  }
212
344k
  input->seek(endPos,librevenge::RVNG_SEEK_SET);
213
344k
  return true;
214
1.12M
}
215
216
//------------------------------------------------------------
217
// DSET
218
//------------------------------------------------------------
219
MWAWBox2i DSET::getUnionChildBox() const
220
50.7k
{
221
50.7k
  MWAWBox2f res;
222
50.7k
  long maxX=1000;
223
5.07M
  for (auto const &child : m_childs) {
224
    // highly spurious, better to ignore
225
5.07M
    if (long(child.m_box[1][0])>3*maxX)
226
443k
      continue;
227
4.62M
    if (long(child.m_box[1][0])>maxX)
228
28.6k
      maxX=long(child.m_box[1][0]);
229
4.62M
    res=child.m_box.getUnion(res);
230
4.62M
  }
231
50.7k
  return MWAWBox2i(res);
232
50.7k
}
233
234
void DSET::removeChild(int cId)
235
14.1k
{
236
14.1k
  removeChild(cId, std::find(m_otherChilds.begin(), m_otherChilds.end(), cId)==m_otherChilds.end());
237
14.1k
}
238
239
void DSET::removeChild(int cId, bool normalChild)
240
3.77M
{
241
3.77M
  if (normalChild) {
242
943M
    for (auto it=m_childs.begin(); it!=m_childs.end(); ++it) {
243
943M
      if (it->m_type != C_Zone || it->m_id != cId) continue;
244
864k
      m_childs.erase(it);
245
864k
      return;
246
943M
    }
247
864k
  }
248
2.90M
  else {
249
197M
    for (auto it=m_otherChilds.begin(); it!=m_otherChilds.end(); ++it) {
250
197M
      if (*it != cId) continue;
251
2.90M
      m_otherChilds.erase(it);
252
2.90M
      return;
253
197M
    }
254
2.90M
  }
255
139
  MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::removeChild can not detach %d\n", cId));
256
139
}
257
258
259
void DSET::updateChildPositions(MWAWVec2f const &pageDim, float formLength, int numHorizontalPages)
260
1.15M
{
261
1.15M
  float const &textWidth=pageDim[0];
262
1.15M
  float textHeight=pageDim[1];
263
1.15M
  if (float(m_pageDimension[1])>0.5f*formLength && float(m_pageDimension[1])<formLength)
264
8.27k
    textHeight=float(m_pageDimension[1]);
265
1.15M
  if (textHeight<=0) {
266
2.07k
    MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::updateChildPositions: the height can not be null\n"));
267
2.07k
    return;
268
2.07k
  }
269
1.15M
  if (numHorizontalPages>1 && textWidth<=0) {
270
1.56k
    MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::updateChildPositions: the width can not be null\n"));
271
1.56k
    numHorizontalPages=1;
272
1.56k
  }
273
1.15M
  MWAWBox2f groupBox;
274
1.15M
  int groupPage=-1;
275
1.15M
  bool firstGroupFound=false;
276
76.1M
  for (auto &child : m_childs) {
277
76.1M
    MWAWBox2f childBdBox=child.getBdBox();
278
76.1M
    auto pageY=int(float(childBdBox[1].y())/textHeight);
279
76.1M
    if (pageY < 0)
280
914k
      continue;
281
75.2M
    if (++pageY > 1) {
282
12.6M
      MWAWVec2f orig = child.m_box[0];
283
12.6M
      MWAWVec2f sz = child.m_box.size();
284
12.6M
      orig[1]-=float(pageY-1)*textHeight;
285
12.6M
      if (orig[1] < 0) {
286
5.80M
        if (orig[1]>=-textHeight*0.1f)
287
15.8k
          orig[1]=0;
288
5.78M
        else if (orig[1]>-1.1f*textHeight) {
289
155k
          orig[1]+=textHeight;
290
155k
          if (orig[1]<0) orig[1]=0;
291
155k
          pageY--;
292
155k
        }
293
5.62M
        else {
294
          // can happen in a drawing document if a form is on several vertical page
295
5.62M
          if (m_position!=P_Main) { // can be normal, if this corresponds to the mainZone
296
4.40M
            MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::updateChildPositions: data on several vertical page(move it on the first page)\n"));
297
4.40M
          }
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
5.62M
          pageY=int(float(childBdBox[0].y())/textHeight);
300
5.62M
          if (++pageY<0) pageY=0;
301
5.62M
          if (sz[1]>textHeight) {
302
5.62M
            orig[1]=0;
303
5.62M
            sz[1]=textHeight;
304
5.62M
          }
305
1.46k
          else
306
1.46k
            orig[1]=textHeight-sz[1];
307
5.62M
        }
308
5.80M
      }
309
12.6M
      child.m_box = MWAWBox2f(orig, orig+sz);
310
12.6M
    }
311
75.2M
    int pageX=1;
312
75.2M
    if (numHorizontalPages>1) {
313
2.02M
      pageX=int(float(childBdBox[1].x())/textWidth);
314
2.02M
      MWAWVec2f orig = child.m_box[0];
315
2.02M
      MWAWVec2f sz = child.m_box.size();
316
2.02M
      orig[0]-=float(pageX)*textWidth;
317
2.02M
      if (orig[0] < 0) {
318
610k
        if (orig[0]>=-textWidth*0.1f)
319
40.6k
          orig[0]=0;
320
569k
        else if (orig[0]>-1.1f*textWidth) {
321
40.7k
          orig[0]+=textWidth;
322
40.7k
          if (orig[0]<0) orig[0]=0;
323
40.7k
          pageX--;
324
40.7k
        }
325
528k
        else {
326
          // can happen if a form is on several horizontal page
327
528k
          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
528k
          pageX=int(float(childBdBox[0].x())/textWidth);
330
528k
          if (pageX<0) pageX=0;
331
528k
          if (sz[0]>textWidth) {
332
528k
            orig[0]=0;
333
528k
            sz[0]=textWidth;
334
528k
          }
335
206
          else
336
206
            orig[0]=textWidth-sz[0];
337
528k
        }
338
610k
      }
339
2.02M
      child.m_box = MWAWBox2f(orig, orig+sz);
340
2.02M
      pageX++;
341
2.02M
    }
342
75.2M
    int64_t newPage = pageX+int64_t(pageY-1)*numHorizontalPages;
343
75.2M
    if (newPage > MAX_PAGES)
344
117k
      continue;
345
75.1M
    int page = int(newPage);
346
75.1M
    if (!firstGroupFound) {
347
617k
      groupPage=page;
348
617k
      groupBox=child.getBdBox();
349
617k
      firstGroupFound=true;
350
617k
    }
351
74.5M
    else if (groupPage==page)
352
56.1M
      groupBox=groupBox.getUnion(child.getBdBox());
353
18.3M
    else
354
18.3M
      groupPage=-1;
355
75.1M
    child.m_page = page;
356
75.1M
  }
357
1.15M
  if (groupPage>=0) {
358
469k
    m_page=groupPage;
359
469k
    m_box=groupBox;
360
469k
  }
361
1.15M
}
362
363
void DSET::findForbiddenPagesBreaking(float pageDim, float formDim, int dim, MWAWVariable<int> &lastPage) const
364
534k
{
365
534k
  if (isHeaderFooter() || m_position==P_Frame)
366
46.7k
    return;
367
368
487k
  if (dim<0||dim>1) {
369
0
    MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::findForbiddenPagesBreaking: the height can not be null\n"));
370
0
    return;
371
0
  }
372
487k
  float length=pageDim;
373
487k
  if (float(m_pageDimension[dim])>0.5f*formDim && float(m_pageDimension[dim])<formDim)
374
3.76k
    length=float(m_pageDimension[dim]);
375
487k
  if (length<=0) {
376
2.37k
    MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::findForbiddenPagesBreaking: the length can not be null\n"));
377
2.37k
    return;
378
2.37k
  }
379
485k
  float const eps=0.1f*length;
380
25.5M
  for (auto const &child : m_childs) {
381
25.5M
    MWAWBox2f childBdBox=child.getBdBox();
382
    // as the recomputation of page position is not accurate, just ignore the small size
383
25.5M
    if (childBdBox.size()[dim]<=length)
384
18.5M
      continue;
385
6.99M
    auto pageMax=int(float(childBdBox[1][dim])/length);
386
6.99M
    if (pageMax <= 0)
387
680k
      continue;
388
6.31M
    float diff = child.m_box[1][dim]-float(pageMax)*length;
389
6.31M
    if (diff <= eps)
390
3.29M
      --pageMax;
391
6.31M
    if (!lastPage.isSet() || pageMax > *lastPage)
392
153k
      lastPage = pageMax;
393
6.31M
  }
394
485k
}
395
396
std::ostream &operator<<(std::ostream &o, DSET const &doc)
397
0
{
398
0
  switch (doc.m_position) {
399
0
  case DSET::P_Unknown:
400
0
    break;
401
0
  case DSET::P_Frame:
402
0
    o << "frame,";
403
0
    break;
404
0
  case DSET::P_Header:
405
0
    o << "header,";
406
0
    break;
407
0
  case DSET::P_Footer:
408
0
    o << "footer,";
409
0
    break;
410
0
  case DSET::P_Footnote:
411
0
    o << "footnote,";
412
0
    break;
413
0
  case DSET::P_Main:
414
0
    o << "main,";
415
0
    break;
416
0
  case DSET::P_GraphicMaster:
417
0
    o << "graphic[master],";
418
0
    break;
419
0
  case DSET::P_Slide:
420
0
    o << "slide,";
421
0
    break;
422
0
  case DSET::P_SlideMaster:
423
0
    o << "slide[master],";
424
0
    break;
425
0
  case DSET::P_SlideNote:
426
0
    o << "slide[note],";
427
0
    break;
428
0
  case DSET::P_SlideThumbnail:
429
0
    o << "slide[thumbnail],";
430
0
    break;
431
0
  case DSET::P_Table:
432
0
    o << "table,";
433
0
    break;
434
#if !defined(__clang__)
435
  default:
436
    o << "#position=" << doc.m_position << ",";
437
    break;
438
#endif
439
0
  }
440
0
  switch (doc.m_fileType) {
441
0
  case 0:
442
0
    o << "normal,";
443
0
    break;
444
0
  case 1:
445
0
    o << "text";
446
0
    if (doc.m_textType==0xFF)
447
0
      o << "*,";
448
0
    else if (doc.m_textType==0xa) // appear in graphic file
449
0
      o << "[textbox],";
450
0
    else if (doc.m_textType)
451
0
      o << "[#type=" << std::hex << doc.m_textType<< std::dec << "],";
452
0
    else
453
0
      o << ",";
454
0
    break;
455
0
  case 2:
456
0
    o << "spreadsheet,";
457
0
    break;
458
0
  case 3:
459
0
    o << "database,";
460
0
    break;
461
0
  case 4:
462
0
    o << "bitmap,";
463
0
    break;
464
0
  case 5:
465
0
    o << "presentation,";
466
0
    break;
467
0
  case 6:
468
0
    o << "table,";
469
0
    break;
470
0
  default:
471
0
    o << "#type=" << doc.m_fileType << ",";
472
0
    break;
473
0
  }
474
0
  if (doc.m_page>= 0) o << "pg=" << doc.m_page << ",";
475
0
  if (doc.m_box.size()[0]>0||doc.m_box.size()[1]>0)
476
0
    o << "box=" << doc.m_box << ",";
477
0
  if (doc.m_pageDimension[0]>0 || doc.m_pageDimension[1]>0)
478
0
    o << "zone[dim]=" << doc.m_pageDimension << ",";
479
0
  o << "id=" << doc.m_id << ",";
480
0
  if (!doc.m_fathersList.empty()) {
481
0
    o << "fathers=[";
482
0
    for (auto id : doc.m_fathersList)
483
0
      o << id << ",";
484
0
    o << "],";
485
0
  }
486
0
  o << "N=" << doc.m_numData << ",";
487
0
  if (doc.m_dataSz >=0) o << "dataSz=" << doc.m_dataSz << ",";
488
0
  if (doc.m_headerSz >= 0) o << "headerSz=" << doc.m_headerSz << ",";
489
0
  if (doc.m_beginSelection) o << "begSel=" << doc.m_beginSelection << ",";
490
0
  if (doc.m_endSelection >= 0) o << "endSel=" << doc.m_endSelection << ",";
491
0
  for (int i = 0; i < 4; i++) {
492
0
    if (doc.m_flags[i])
493
0
      o << "fl" << i << "=" << std::hex << doc.m_flags[i] << std::dec << ",";
494
0
  }
495
0
  for (size_t i = 0; i < doc.m_childs.size(); i++)
496
0
    o << "child" << i << "=[" << doc.m_childs[i] << "],";
497
0
  for (size_t i = 0; i < doc.m_otherChilds.size(); i++)
498
0
    o << "otherChild" << i << "=" << doc.m_otherChilds[i] << ",";
499
0
  return o;
500
0
}
501
502
}
503
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: