Coverage Report

Created: 2026-09-06 07:18

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
9.80M
{
74
9.80M
  *this=Struct();
75
9.80M
  long pos=input->tell();
76
9.80M
  if (!input->checkPosition(pos+4))
77
1.83M
    return false;
78
7.96M
  m_size=input->readLong(4);
79
7.96M
  if (m_size==0)
80
2.85M
    return true;
81
5.11M
  if (m_size<12 || !input->checkPosition(pos+4+m_size))
82
1.92M
    return false;
83
84
3.19M
  m_numData =int(input->readULong(2));
85
3.19M
  m_type = int(input->readLong(2));
86
3.19M
  m_values[0] = int(input->readLong(2));
87
3.19M
  m_dataSize = long(input->readULong(2));
88
3.19M
  m_headerSize = long(input->readULong(2));
89
3.19M
  m_values[1] = int(input->readLong(2));
90
3.19M
  if (m_numData && m_dataSize>10000) return false; // too big to be honetx
91
3.12M
  long expectedLength = 12+m_headerSize;
92
3.12M
  if (m_numData>0) expectedLength+=long(m_numData)*m_dataSize;
93
3.12M
  if (expectedLength>m_size || (strict && expectedLength != m_size))
94
262k
    return false;
95
2.85M
  return true;
96
3.12M
}
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.75M
{
101
2.75M
  res.resize(0);
102
2.75M
  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.75M
  MWAWInputStreamPtr input = parserState.m_input;
108
2.75M
  long pos = input->tell();
109
2.75M
  Struct zone;
110
2.75M
  if (!zone.readHeader(input,true)) {
111
2.05M
    MWAW_DEBUG_MSG(("ClarisWksStruct::readIntZone: can not header of %s\n", zoneName ? zoneName : "unamed"));
112
2.05M
  }
113
2.75M
  libmwaw::DebugStream f;
114
2.75M
  libmwaw::DebugFile &ascFile=parserState.m_asciiFile;
115
2.75M
  if (zoneName && strlen(zoneName))
116
2.75M
    f << "Entries(" << zoneName << "):";
117
2.75M
  long endPos = pos+4+zone.m_size;
118
119
2.75M
  if (zone.m_size==0) {
120
2.37M
    if (hasEntete) {
121
0
      ascFile.addPos(pos-4);
122
0
      ascFile.addNote(f.str().c_str());
123
0
    }
124
2.37M
    else {
125
2.37M
      ascFile.addPos(pos);
126
2.37M
      ascFile.addNote("NOP");
127
2.37M
    }
128
2.37M
    return true;
129
2.37M
  }
130
131
382k
  if (zone.m_dataSize != intSz) {
132
154k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
133
154k
    MWAW_DEBUG_MSG(("ClarisWksStruct::readIntZone: unexpected field size\n"));
134
154k
    return false;
135
154k
  }
136
137
228k
  f << zone;
138
228k
  if (zone.m_headerSize) {
139
25.9k
    ascFile.addDelimiter(input->tell(), '|');
140
25.9k
    input->seek(zone.m_headerSize, librevenge::RVNG_SEEK_CUR);
141
25.9k
  }
142
228k
  if (zone.m_numData) ascFile.addDelimiter(input->tell(), '|');
143
228k
  f << "[";
144
2.71G
  for (long i = 0; i < zone.m_numData; i++) {
145
2.71G
    auto val = int(input->readLong(intSz));
146
2.71G
    res.push_back(val);
147
2.71G
    if (val>1000) f << "0x" << std::hex << val << std::dec << ",";
148
2.53G
    else f << val << ",";
149
2.71G
  }
150
228k
  f << "]";
151
152
228k
  ascFile.addPos(hasEntete ? pos-4 : pos);
153
228k
  ascFile.addNote(f.str().c_str());
154
155
228k
  input->seek(endPos,librevenge::RVNG_SEEK_SET);
156
228k
  return true;
157
382k
}
158
159
///////////////////////////////////////////////////////////
160
// try to read a unknown structured zone
161
////////////////////////////////////////////////////////////
162
bool readStructZone(MWAWParserState &parserState, char const *zoneName, bool hasEntete)
163
1.58M
{
164
1.58M
  MWAWInputStreamPtr input = parserState.m_input;
165
1.58M
  long pos = input->tell();
166
1.58M
  Struct zone;
167
1.58M
  if (!zone.readHeader(input,false) || (zone.m_size && zone.m_dataSize<=0)) {
168
350k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
169
350k
    MWAW_DEBUG_MSG(("ClarisWksStruct::readStructZone: can not read header for %s\n", zoneName));
170
350k
    return false;
171
350k
  }
172
1.22M
  libmwaw::DebugFile &ascFile= parserState.m_asciiFile;
173
1.22M
  libmwaw::DebugStream f;
174
1.22M
  f << "Entries(" << zoneName << "):";
175
176
1.22M
  if (zone.m_size == 0) {
177
869k
    if (hasEntete) {
178
0
      ascFile.addPos(pos-4);
179
0
      ascFile.addNote(f.str().c_str());
180
0
    }
181
869k
    else {
182
869k
      ascFile.addPos(pos);
183
869k
      ascFile.addNote("NOP");
184
869k
    }
185
869k
    return true;
186
869k
  }
187
360k
  long endPos=pos+4+zone.m_size;
188
360k
  f << zone;
189
360k
  if (zone.m_headerSize) {
190
26.1k
    ascFile.addDelimiter(input->tell(), '|');
191
26.1k
    input->seek(zone.m_headerSize, librevenge::RVNG_SEEK_CUR);
192
26.1k
  }
193
360k
  ascFile.addPos(hasEntete ? pos-4 : pos);
194
360k
  ascFile.addNote(f.str().c_str());
195
196
360k
  pos=input->tell();
197
476k
  for (long i = 0; i < zone.m_numData; i++) {
198
116k
    f.str("");
199
116k
    f << zoneName << "-" << i << ":";
200
201
116k
    ascFile.addPos(pos);
202
116k
    ascFile.addNote(f.str().c_str());
203
116k
    pos += zone.m_dataSize;
204
116k
  }
205
360k
  if (pos!=endPos) {
206
15.1k
    MWAW_DEBUG_MSG(("ClarisWksStruct::readStructZone: find extra data for %s\n", zoneName));
207
15.1k
    f.str("");
208
15.1k
    f << zoneName << ":###extra";
209
15.1k
    ascFile.addPos(pos);
210
15.1k
    ascFile.addNote(f.str().c_str());
211
15.1k
  }
212
360k
  input->seek(endPos,librevenge::RVNG_SEEK_SET);
213
360k
  return true;
214
1.22M
}
215
216
//------------------------------------------------------------
217
// DSET
218
//------------------------------------------------------------
219
MWAWBox2i DSET::getUnionChildBox() const
220
55.4k
{
221
55.4k
  MWAWBox2f res;
222
55.4k
  long maxX=1000;
223
5.16M
  for (auto const &child : m_childs) {
224
    // highly spurious, better to ignore
225
5.16M
    if (long(child.m_box[1][0])>3*maxX)
226
434k
      continue;
227
4.72M
    if (long(child.m_box[1][0])>maxX)
228
28.1k
      maxX=long(child.m_box[1][0]);
229
4.72M
    res=child.m_box.getUnion(res);
230
4.72M
  }
231
55.4k
  return MWAWBox2i(res);
232
55.4k
}
233
234
void DSET::removeChild(int cId)
235
14.5k
{
236
14.5k
  removeChild(cId, std::find(m_otherChilds.begin(), m_otherChilds.end(), cId)==m_otherChilds.end());
237
14.5k
}
238
239
void DSET::removeChild(int cId, bool normalChild)
240
3.83M
{
241
3.83M
  if (normalChild) {
242
879M
    for (auto it=m_childs.begin(); it!=m_childs.end(); ++it) {
243
879M
      if (it->m_type != C_Zone || it->m_id != cId) continue;
244
863k
      m_childs.erase(it);
245
863k
      return;
246
879M
    }
247
863k
  }
248
2.96M
  else {
249
188M
    for (auto it=m_otherChilds.begin(); it!=m_otherChilds.end(); ++it) {
250
188M
      if (*it != cId) continue;
251
2.96M
      m_otherChilds.erase(it);
252
2.96M
      return;
253
188M
    }
254
2.96M
  }
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.23M
{
261
1.23M
  float const &textWidth=pageDim[0];
262
1.23M
  float textHeight=pageDim[1];
263
1.23M
  if (float(m_pageDimension[1])>0.5f*formLength && float(m_pageDimension[1])<formLength)
264
8.77k
    textHeight=float(m_pageDimension[1]);
265
1.23M
  if (textHeight<=0) {
266
2.21k
    MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::updateChildPositions: the height can not be null\n"));
267
2.21k
    return;
268
2.21k
  }
269
1.23M
  if (numHorizontalPages>1 && textWidth<=0) {
270
1.74k
    MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::updateChildPositions: the width can not be null\n"));
271
1.74k
    numHorizontalPages=1;
272
1.74k
  }
273
1.23M
  MWAWBox2f groupBox;
274
1.23M
  int groupPage=-1;
275
1.23M
  bool firstGroupFound=false;
276
83.8M
  for (auto &child : m_childs) {
277
83.8M
    MWAWBox2f childBdBox=child.getBdBox();
278
83.8M
    auto pageY=int(float(childBdBox[1].y())/textHeight);
279
83.8M
    if (pageY < 0)
280
923k
      continue;
281
82.8M
    if (++pageY > 1) {
282
12.5M
      MWAWVec2f orig = child.m_box[0];
283
12.5M
      MWAWVec2f sz = child.m_box.size();
284
12.5M
      orig[1]-=float(pageY-1)*textHeight;
285
12.5M
      if (orig[1] < 0) {
286
5.75M
        if (orig[1]>=-textHeight*0.1f)
287
15.5k
          orig[1]=0;
288
5.73M
        else if (orig[1]>-1.1f*textHeight) {
289
156k
          orig[1]+=textHeight;
290
156k
          if (orig[1]<0) orig[1]=0;
291
156k
          pageY--;
292
156k
        }
293
5.58M
        else {
294
          // can happen in a drawing document if a form is on several vertical page
295
5.58M
          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.58M
          pageY=int(float(childBdBox[0].y())/textHeight);
300
5.58M
          if (++pageY<0) pageY=0;
301
5.58M
          if (sz[1]>textHeight) {
302
5.57M
            orig[1]=0;
303
5.57M
            sz[1]=textHeight;
304
5.57M
          }
305
1.89k
          else
306
1.89k
            orig[1]=textHeight-sz[1];
307
5.58M
        }
308
5.75M
      }
309
12.5M
      child.m_box = MWAWBox2f(orig, orig+sz);
310
12.5M
    }
311
82.8M
    int pageX=1;
312
82.8M
    if (numHorizontalPages>1) {
313
1.98M
      pageX=int(float(childBdBox[1].x())/textWidth);
314
1.98M
      MWAWVec2f orig = child.m_box[0];
315
1.98M
      MWAWVec2f sz = child.m_box.size();
316
1.98M
      orig[0]-=float(pageX)*textWidth;
317
1.98M
      if (orig[0] < 0) {
318
559k
        if (orig[0]>=-textWidth*0.1f)
319
36.6k
          orig[0]=0;
320
522k
        else if (orig[0]>-1.1f*textWidth) {
321
38.8k
          orig[0]+=textWidth;
322
38.8k
          if (orig[0]<0) orig[0]=0;
323
38.8k
          pageX--;
324
38.8k
        }
325
483k
        else {
326
          // can happen if a form is on several horizontal page
327
483k
          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
483k
          pageX=int(float(childBdBox[0].x())/textWidth);
330
483k
          if (pageX<0) pageX=0;
331
483k
          if (sz[0]>textWidth) {
332
483k
            orig[0]=0;
333
483k
            sz[0]=textWidth;
334
483k
          }
335
218
          else
336
218
            orig[0]=textWidth-sz[0];
337
483k
        }
338
559k
      }
339
1.98M
      child.m_box = MWAWBox2f(orig, orig+sz);
340
1.98M
      pageX++;
341
1.98M
    }
342
82.8M
    int64_t newPage = pageX+int64_t(pageY-1)*numHorizontalPages;
343
82.8M
    if (newPage > MAX_PAGES)
344
140k
      continue;
345
82.7M
    int page = int(newPage);
346
82.7M
    if (!firstGroupFound) {
347
645k
      groupPage=page;
348
645k
      groupBox=child.getBdBox();
349
645k
      firstGroupFound=true;
350
645k
    }
351
82.1M
    else if (groupPage==page)
352
63.9M
      groupBox=groupBox.getUnion(child.getBdBox());
353
18.1M
    else
354
18.1M
      groupPage=-1;
355
82.7M
    child.m_page = page;
356
82.7M
  }
357
1.23M
  if (groupPage>=0) {
358
482k
    m_page=groupPage;
359
482k
    m_box=groupBox;
360
482k
  }
361
1.23M
}
362
363
void DSET::findForbiddenPagesBreaking(float pageDim, float formDim, int dim, MWAWVariable<int> &lastPage) const
364
567k
{
365
567k
  if (isHeaderFooter() || m_position==P_Frame)
366
48.6k
    return;
367
368
519k
  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
519k
  float length=pageDim;
373
519k
  if (float(m_pageDimension[dim])>0.5f*formDim && float(m_pageDimension[dim])<formDim)
374
3.94k
    length=float(m_pageDimension[dim]);
375
519k
  if (length<=0) {
376
2.66k
    MWAW_DEBUG_MSG(("ClarisWksStruct::DSET::findForbiddenPagesBreaking: the length can not be null\n"));
377
2.66k
    return;
378
2.66k
  }
379
516k
  float const eps=0.1f*length;
380
28.9M
  for (auto const &child : m_childs) {
381
28.9M
    MWAWBox2f childBdBox=child.getBdBox();
382
    // as the recomputation of page position is not accurate, just ignore the small size
383
28.9M
    if (childBdBox.size()[dim]<=length)
384
22.3M
      continue;
385
6.60M
    auto pageMax=int(float(childBdBox[1][dim])/length);
386
6.60M
    if (pageMax <= 0)
387
651k
      continue;
388
5.95M
    float diff = child.m_box[1][dim]-float(pageMax)*length;
389
5.95M
    if (diff <= eps)
390
3.10M
      --pageMax;
391
5.95M
    if (!lastPage.isSet() || pageMax > *lastPage)
392
167k
      lastPage = pageMax;
393
5.95M
  }
394
516k
}
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: