Coverage Report

Created: 2026-04-29 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/Canvas5Structure.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 <iomanip>
35
#include <iostream>
36
#include <set>
37
#include <vector>
38
39
#include "MWAWStringStream.hxx"
40
#include "MWAWPictBitmap.hxx"
41
42
#include "Canvas5Structure.hxx"
43
44
namespace Canvas5Structure
45
{
46
47
std::string getString(unsigned val)
48
0
{
49
0
  if (val<20) return std::to_string(val);
50
0
  std::string res;
51
0
  for (int dec=24; dec>=0; dec-=8) {
52
0
    char c=char((val>>dec)&0xff);
53
0
    if (!std::isprint(c))
54
0
      return std::to_string(val);
55
0
    res+=c;
56
0
  }
57
0
  return res;
58
0
}
59
60
bool readBitmap(Stream &stream, int version, MWAWEmbeddedObject &object, MWAWColor *avgColor)
61
0
{
62
0
  object=MWAWEmbeddedObject();
63
0
  auto input=stream.input();
64
0
  long pos=input->tell();
65
0
  libmwaw::DebugStream f;
66
0
  auto &ascFile=stream.ascii();
67
0
  f << "Entries(Bitmap):";
68
0
  int type0=int(input->readULong(4)); // found type0=5 in texture bw bitmap
69
0
  if (type0!=6) f << "type0=" << type0 << ",";
70
0
  if (!input->checkPosition(pos+64) || (type0!=5 && type0!=6)) {
71
0
    MWAW_DEBUG_MSG(("Canvas5Structure::readBitmap: the zone beginning seems bad\n"));
72
0
    f << "###";
73
0
    ascFile.addPos(pos);
74
0
    ascFile.addNote(f.str().c_str());
75
0
    return false;
76
0
  }
77
0
  int type=int(input->readLong(2)); // 1-3
78
0
  switch (type) {
79
0
  case 0:
80
0
    f << "bw[indexed],"; // 1 bool by bytes
81
0
    break;
82
0
  case 1:
83
0
    f << "bw[color],"; // 1 plane
84
0
    break;
85
0
  case 2:
86
0
    f << "indexed,"; // 1 plane, color map
87
0
    break;
88
0
  case 3:
89
0
    f << "color,"; // with 3 planes
90
0
    break;
91
0
  case 4:
92
0
    f << "color4,"; // with 4 planes
93
0
    break;
94
0
  default:
95
0
    f << "##type=" << type << ",";
96
0
    MWAW_DEBUG_MSG(("Canvas5Structure::readBitmap: unexpected type\n"));
97
0
    ascFile.addPos(pos);
98
0
    ascFile.addNote(f.str().c_str());
99
0
    return false;
100
0
  }
101
0
  int numBytes=int(input->readLong(2)); // number of byte?
102
0
  if (numBytes!=8) {
103
0
    if (numBytes==1 && type==0)
104
0
      f << "num[bytes]=1,";
105
0
    else {
106
0
      MWAW_DEBUG_MSG(("Canvas5Structure::readBitmap: oops, find a number of bytes unexpected, unimplemented\n"));
107
0
      f << "##num[bytes]=" << numBytes << ",";
108
0
    }
109
0
  }
110
0
  int dim[2];
111
0
  for (auto &d : dim) d=int(input->readULong(4));
112
0
  MWAWVec2i dimension(dim[1], dim[0]);
113
0
  f << "dim=" << dimension << ",";
114
0
  int numPlanes=int(input->readLong(2)); // 1-4
115
0
  int val=int(input->readLong(2)); // val
116
0
  if (numPlanes!=val)
117
0
    f << "num[planes]=" << numPlanes << "x" << val << ",";
118
0
  else if (numPlanes!=1) f << "f2=" << val << ",";
119
0
  float fDim[2];
120
0
  for (auto &v : fDim) v=float(input->readULong(4))/65536.f;
121
0
  if (MWAWVec2f(fDim[0],fDim[1])!=MWAWVec2f(72,72))
122
0
    f << "fDim=" << MWAWVec2f(fDim[0],fDim[1]) << ",";
123
0
  for (int i=0; i<4; ++i) { // 0
124
0
    val=int(input->readLong(2));
125
0
    if (val)
126
0
      f << "f" << i+3 << "=" << val << ",";
127
0
  }
128
0
  for (auto &d : dim) d=int(input->readULong(4));
129
0
  MWAWVec2i dim1(dim[1], dim[0]);
130
0
  if (dimension!=dim1)
131
0
    f << "dim1=" << dim1 << ",";
132
0
  ascFile.addPos(pos);
133
0
  ascFile.addNote(f.str().c_str());
134
135
  // FIXME: find correctly the data, color positions
136
  //   but only reconstruct correctly small bitmaps :-~
137
0
  std::shared_ptr<MWAWPictBitmapIndexed> bitmapIndexed;
138
0
  std::shared_ptr<MWAWPictBitmapColor> bitmapColor;
139
0
  switch (type) {
140
0
  case 0:
141
0
  case 2:
142
0
    bitmapIndexed.reset(new MWAWPictBitmapIndexed(dimension));
143
0
    break;
144
0
  case 1:
145
0
  case 3:
146
0
  case 4:
147
0
  default:
148
0
    bitmapColor.reset(new MWAWPictBitmapColor(dimension));
149
0
    break;
150
0
  }
151
152
0
  pos=input->tell();
153
0
  int const width=type==0 ? (dimension[0]+7)/8 : dimension[0];
154
0
  int const planeHeaderLength=(version<9 ? 20 : 40);
155
0
  long dataLength=((type==3||type==4) ? numPlanes : 1)*(planeHeaderLength+width*dimension[1]);
156
0
  if (width<=0 || dimension[1]<=0 || pos+dataLength<pos || !input->checkPosition(pos+dataLength)) {
157
0
    MWAW_DEBUG_MSG(("Canvas5Structure::readBitmap: can not find the bitmap data\n"));
158
0
    f << "###";
159
0
    ascFile.addPos(pos);
160
0
    ascFile.addNote(f.str().c_str());
161
0
    return false;
162
0
  }
163
164
0
  long dataPos=pos;
165
  // first read the color map
166
0
  input->seek(pos+dataLength, librevenge::RVNG_SEEK_SET);
167
0
  pos=input->tell();
168
0
  long len=input->readLong(4);
169
0
  if (pos+4+(len?4:0)+len<pos+4 || !input->checkPosition(pos+4+(len?4:0)+len)) {
170
0
    MWAW_DEBUG_MSG(("Canvas5Structure::readBitmap: can not find the color block\n"));
171
0
    ascFile.addPos(pos);
172
0
    ascFile.addNote("Bitmap[color]:###");
173
0
    return false;
174
0
  }
175
0
  if (len==0) {
176
0
    ascFile.addPos(pos);
177
0
    ascFile.addNote("_");
178
0
  }
179
0
  else {
180
0
    input->seek(4, librevenge::RVNG_SEEK_CUR);
181
0
    unsigned long numBytesRead;
182
0
    auto *data=input->read(size_t(len), numBytesRead);
183
0
    if (!data || long(numBytesRead)!=len) {
184
0
      MWAW_DEBUG_MSG(("Canvas5Structure::readBitmap: can not find the color block\n"));
185
0
      ascFile.addPos(pos);
186
0
      ascFile.addNote("Bitmap[color]:###");
187
0
      return false;
188
0
    }
189
0
    size_t N=size_t(len/3);
190
0
    std::vector<MWAWColor> colors(N);
191
0
    for (size_t c=0; c<N; ++c) colors[c]=MWAWColor(data[c],data[c+N],data[c+2*N]);
192
0
    if (type==2)
193
0
      bitmapIndexed->setColors(colors);
194
0
    ascFile.addPos(pos);
195
0
    ascFile.addNote("Bitmap[color]:");
196
0
    ascFile.skipZone(pos+8, pos+8+len-1);
197
0
  }
198
0
  long endPos=input->tell();
199
0
  if (type==0)
200
0
    bitmapIndexed->setColors({MWAWColor::black(), MWAWColor::white()});
201
  // now read the bitmap data
202
0
  input->seek(dataPos, librevenge::RVNG_SEEK_SET);
203
0
  for (int plane=0; plane<((type==3||type==4) ? numPlanes : 1); ++plane) {
204
0
    pos=input->tell();
205
0
    f.str("");
206
0
    f << "Bitmap-P" << plane << ":";
207
0
    for (int i=0; i<3; ++i) {
208
0
      val=int(input->readLong(4));
209
0
      int const expected[]= {2 /* or 3*/,8,1};
210
0
      if (val==expected[i]) continue;
211
0
      if (i==1)
212
0
        f << "num[bytes]=" << val << ",";
213
0
      else
214
0
        f << "g" << i << "=" << val << ",";
215
0
    }
216
0
    for (auto &d : dim) d=int(input->readULong(4));
217
0
    dim1=MWAWVec2i(dim[1], dim[0]);
218
0
    if (dimension!=dim1)
219
0
      f << "dim2=" << dim1 << ",";
220
0
    input->seek(pos+planeHeaderLength, librevenge::RVNG_SEEK_SET);
221
0
    ascFile.addPos(pos);
222
0
    ascFile.addNote(f.str().c_str());
223
224
0
    if (type==0) {
225
      // checkme: is the picture decomposed by block if dim[0]>128*8 or dim[1]>128 ?
226
0
      for (int y=0; y<dimension[1]; ++y) {
227
0
        int x=0;
228
0
        for (int w=0; w<width; ++w) {
229
0
          val=int(input->readULong(1));
230
0
          for (int v=0, depl=0x80; v<8; ++v, depl>>=1) {
231
0
            if (x>=dimension[0])
232
0
              break;
233
0
            bitmapIndexed->set(x++,y, (val&depl) ? 0 : 1);
234
0
          }
235
0
        }
236
0
      }
237
0
    }
238
0
    else {
239
0
      for (int nY=0; nY<(dimension[1]+127)/128; ++nY) {
240
0
        for (int nW=0; nW<(dimension[0]+127)/128; ++nW) {
241
0
          for (int y=128*nY; y<std::min(dimension[1], 128*(nY+1)); ++y) {
242
0
            for (int w=128*nW; w<std::min(dimension[0], 128*(nW+1)); ++w) {
243
0
              unsigned char c=(unsigned char)input->readULong(1);
244
0
              if (type==1)
245
0
                bitmapColor->set(w,y, MWAWColor(c,c,c));
246
0
              else if (type==2)
247
0
                bitmapIndexed->set(w,y,c);
248
0
              else {
249
0
                if (plane==0)
250
0
                  bitmapColor->set(w,y,MWAWColor(c,0,0));
251
0
                else {
252
0
                  int const decal=plane==3 ? 24 : (16-(8*plane));
253
0
                  uint32_t finalValue=bitmapColor->get(w,y).value()|(uint32_t(c)<<decal);
254
0
                  bitmapColor->set(w,y,MWAWColor(finalValue));
255
0
                }
256
0
              }
257
0
            }
258
0
          }
259
0
        }
260
0
      }
261
0
    }
262
0
    ascFile.skipZone(dataPos+20, dataPos+dataLength-1);
263
0
  }
264
0
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
265
266
0
  bool ok=false;
267
0
  if (type==0 || type==2) {
268
0
    ok=bitmapIndexed->getBinary(object);
269
0
    if (ok && avgColor) *avgColor=bitmapIndexed->getAverageColor();
270
0
  }
271
0
  else if (type==1 || type==3 || type==4) {
272
0
    ok=bitmapColor->getBinary(object);
273
0
    if (ok && avgColor) *avgColor=bitmapColor->getAverageColor();
274
0
  }
275
#ifdef DEBUG_WITH_FILES
276
  if (ok && !object.m_dataList.empty()) {
277
    std::stringstream s;
278
    static int index=0;
279
    s << "file" << ++index << ".png";
280
    libmwaw::Debug::dumpFile(object.m_dataList[0], s.str().c_str());
281
  }
282
#endif
283
0
  return ok && !object.m_dataList.empty();
284
0
}
285
286
bool readBitmapDAD58Bim(Stream &stream, int version, MWAWEmbeddedObject &object)
287
0
{
288
0
  if (!readBitmap(stream, version, object))
289
0
    return false;
290
291
0
  auto input=stream.input();
292
0
  long pos=input->tell();
293
0
  auto &ascFile=stream.ascii();
294
295
  // DAD5 block
296
0
  libmwaw::DebugStream f;
297
0
  f << "Bitmap[DAD5]:";
298
0
  if (!input->checkPosition(pos+12)) {
299
0
    MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim: can not find the DAD5 block\n"));
300
0
    f << "###";
301
0
    ascFile.addPos(pos);
302
0
    ascFile.addNote(f.str().c_str());
303
0
    return false;
304
0
  }
305
0
  int val=int(input->readLong(4));
306
0
  if (val!=1) // checkme: val=0 means probably no data
307
0
    f << "f0=" << val << ",";
308
0
  f << "len?=" << std::hex << input->readULong(4) << std::dec << ",";
309
0
  int N=int(input->readULong(4)); // 1-3
310
0
  f << "N=" << N << ",";
311
0
  if (N<0 || (input->size()-pos-12)/16<N || pos+12+16*N<pos+12 || !input->checkPosition(pos+12+16*N)) {
312
0
    MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim[DAD5]: can not find the number of subblock\n"));
313
0
    f << "###";
314
0
    ascFile.addPos(pos);
315
0
    ascFile.addNote(f.str().c_str());
316
0
    return false;
317
0
  }
318
0
  ascFile.addPos(pos);
319
0
  ascFile.addNote(f.str().c_str());
320
321
0
  for (int j=0; j<N; ++j) {
322
0
    pos=input->tell();
323
0
    f.str("");
324
0
    f << "Bitmap[DAD5-A" << j << "]:";
325
0
    if (!input->checkPosition(pos+16)) {
326
0
      MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim[DAD5]: can not read subblock %d\n", j));
327
0
      f << "###";
328
0
      ascFile.addPos(pos);
329
0
      ascFile.addNote(f.str().c_str());
330
0
      return false;
331
0
    }
332
0
    unsigned types[2];
333
0
    for (auto &t : types) t=unsigned(input->readULong(4));
334
0
    f << getString(types[0]) << ":"  << getString(types[1]) << ",";
335
0
    val=int(input->readLong(4));
336
0
    if (val!=1) f << "f0=" << val << ",";
337
0
    long len=input->readLong(4);
338
0
    if (len<0 || pos+16+len<pos+16 || !input->checkPosition(pos+16+len)) {
339
0
      MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim[DAD5]: can not read subblock %d length\n", j));
340
0
      f << "###len=" << len << ",";
341
0
      ascFile.addPos(pos);
342
0
      ascFile.addNote(f.str().c_str());
343
0
      return false;
344
0
    }
345
    // DAD5::VISM (size 8), DAD5::hack (size 8c) or DAD5::1 (size variable, ie end with a string)
346
0
    if (types[0]==0x44414435) {
347
0
      switch (types[1]) {
348
0
      case 1: {
349
0
        std::string name;
350
0
        for (int k=0; k<len; ++k) {
351
0
          char c=char(input->readULong(1));
352
0
          if (c==0)
353
0
            break;
354
0
          name+=c;
355
0
        }
356
0
        f << "path=" << name << ",";
357
0
        break;
358
0
      }
359
0
      case 0x6861636b: {
360
0
        if (len!=0x8c) {
361
0
          MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim[DAD5,hack]: unexpected length\n"));
362
0
          f << "###";
363
0
          break;
364
0
        }
365
0
        for (int k=0; k<2; ++k) { // 0
366
0
          val=int(input->readLong(4));
367
0
          if (val)
368
0
            f << "f" << k+1 << "=" << val << ",";
369
0
        }
370
0
        int maxN=int(input->readLong(4));
371
0
        f << "maxN=" << maxN << ",";
372
0
        f << "unkn=[";
373
0
        for (int k=0; k<32; ++k) { // unsure where to stop
374
0
          val=int(input->readLong(4));
375
0
          if (val<=0 || val>maxN) break;
376
0
          f << val << ",";
377
0
        }
378
0
        f << "],";
379
0
        ascFile.addDelimiter(input->tell(),'|');
380
0
        break;
381
0
      }
382
0
      case 0x5649534d: // VISM
383
0
        if (len!=8) {
384
0
          MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim[DAD5,VISM]: unexpected length\n"));
385
0
          f << "###";
386
0
          break;
387
0
        }
388
0
        val=int(input->readLong(4)); // 0-1
389
0
        if (val)
390
0
          f << "f1=" << val << ",";
391
0
        val=int(input->readLong(4));
392
0
        if (val!=-1)
393
0
          f << "f2=" << val << ",";
394
0
        break;
395
0
      default:
396
0
        MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim[DAD5]: unexpected type for sub zone\n"));
397
0
        f << "###";
398
0
        break;
399
0
      }
400
0
    }
401
0
    else {
402
0
      MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim[DAD5]: find unknown type0 for subblock %d\n", j));
403
0
      f << "###";
404
0
    }
405
0
    ascFile.addPos(pos);
406
0
    ascFile.addNote(f.str().c_str());
407
0
    input->seek(pos+16+len, librevenge::RVNG_SEEK_SET);
408
0
  }
409
410
  // last block: 8BIM
411
0
  pos=input->tell();
412
0
  long len=input->readLong(4);
413
0
  f.str("");
414
0
  f << "Bitmap[8bim]:";
415
0
  long endBimBlock=pos+4+len;
416
0
  if (len<0 || endBimBlock<pos+4 || !input->checkPosition(endBimBlock)) {
417
0
    MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim: can not read 8bim block\n"));
418
0
    f << "###";
419
0
    ascFile.addPos(pos);
420
0
    ascFile.addNote(f.str().c_str());
421
0
    return false;
422
0
  }
423
0
  ascFile.addPos(pos);
424
0
  ascFile.addNote(f.str().c_str());
425
0
  while (input->tell()<endBimBlock) {
426
0
    pos=input->tell();
427
0
    f.str("");
428
0
    f << "Bitmap[8bim]:";
429
0
    if (pos+12>endBimBlock) {
430
0
      MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim: a 8bim block seems bad\n"));
431
0
      f << "###";
432
0
      ascFile.addPos(pos);
433
0
      ascFile.addNote(f.str().c_str());
434
0
      break;
435
0
    }
436
0
    unsigned type=unsigned(input->readULong(4));
437
0
    f << getString(type) << ","; // 8BIM
438
0
    int id=int(input->readLong(2));
439
0
    f << "id=" << id << ",";
440
0
    val=int(input->readLong(2));
441
0
    if (val)
442
0
      f << "f0=" << val << ",";
443
0
    len=input->readLong(4);
444
0
    if (len<0 || pos+12+len>endBimBlock) {
445
0
      MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim: a 8bim block len seems bad\n"));
446
0
      f << "###";
447
0
      ascFile.addPos(pos);
448
0
      ascFile.addNote(f.str().c_str());
449
0
      break;
450
0
    }
451
0
    switch (type) {
452
0
    case 0x3842494d:
453
0
      switch (id) {
454
0
      case 1006:
455
0
        if (len==0)
456
0
          break;
457
0
        else {
458
0
          int sSz=int(input->readULong(1));
459
0
          if (1+sSz>len) {
460
0
            MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim[8bim,1006]: can not find the string size\n"));
461
0
            f << "###";
462
0
            break;
463
0
          }
464
0
          std::string name;
465
0
          for (int k=0; k<sSz; ++k) {
466
0
            char c=char(input->readULong(1));
467
0
            if (!c)
468
0
              break;
469
0
            name+=c;
470
0
          }
471
0
          f << name << ",";
472
0
        }
473
0
        break;
474
0
      case 1007: {
475
0
        if ((len%14)!=0) {
476
0
          MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim[8bim,1007]: the size seems bad\n"));
477
0
          f << "###";
478
0
          break;
479
0
        }
480
0
        int nUnkn=int(len/14);
481
0
        f << "unkn=[";
482
0
        for (int k=0; k<nUnkn; ++k) {
483
0
          f << "[";
484
0
          for (int l=0; l<7; ++l) {
485
0
            val=int(input->readLong(2));
486
0
            int const expected[]= {0,0/* or 255*/,0,0,0,50 /* or 100 */, 0};
487
0
            if (val!=expected[l])
488
0
              f << "f" << l << "=" << val << ",";
489
0
          }
490
0
          f << "],";
491
0
        }
492
0
        f << "],";
493
0
        break;
494
0
      }
495
0
      default:
496
0
        MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim[8bim]: unknown id=%d\n", id));
497
0
        f << "###";
498
0
        break;
499
0
      }
500
0
      break;
501
0
    default:
502
0
      MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim[8bim]: unknown type=%s\n", getString(type).c_str()));
503
0
      f << "###";
504
0
    }
505
0
    if (input->tell()!=pos+12+len)
506
0
      ascFile.addDelimiter(input->tell(),'|');
507
0
    input->seek(pos+12+len, librevenge::RVNG_SEEK_SET);
508
0
    ascFile.addPos(pos);
509
0
    ascFile.addNote(f.str().c_str());
510
0
  }
511
512
0
  input->seek(endBimBlock, librevenge::RVNG_SEEK_SET);
513
514
0
  if (version<9)
515
0
    return true;
516
517
0
  if (input->isEnd()) // bitmap in cvi file ends here
518
0
    return true;
519
  // last block: unknown
520
0
  pos=input->tell();
521
0
  len=input->readLong(4);
522
0
  f.str("");
523
0
  f << "Bitmap[unknown]:";
524
0
  long endUnknownBlock=pos+4+len;
525
0
  if (len<0 || endUnknownBlock<pos+4 || !input->checkPosition(endUnknownBlock)) {
526
0
    MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim: can not read unknown block\n"));
527
0
    f << "###";
528
0
    ascFile.addPos(pos);
529
0
    ascFile.addNote(f.str().c_str());
530
0
    return false;
531
0
  }
532
0
  if (len) {
533
0
    MWAW_DEBUG_MSG(("Canvas5Structure::readBitmapDAD58Bim: find an unknown block\n"));
534
0
    f << "###";
535
0
    ascFile.addPos(pos);
536
0
    ascFile.addNote(f.str().c_str());
537
0
    input->seek(endUnknownBlock, librevenge::RVNG_SEEK_SET);
538
0
  }
539
0
  else {
540
0
    ascFile.addPos(pos);
541
0
    ascFile.addNote("_");
542
0
  }
543
0
  return true;
544
0
}
545
546
bool readPreview(Stream &stream, bool hasPreviewBitmap)
547
0
{
548
0
  auto input=stream.input();
549
0
  if (!input) return false;
550
0
  long pos=input->tell();
551
0
  if (!input->checkPosition(pos+12+(hasPreviewBitmap ? 12 : 0))) {
552
0
    MWAW_DEBUG_MSG(("Canvas5Structure::readPreview: the zone is too short\n"));
553
0
    return false;
554
0
  }
555
0
  libmwaw::DebugStream f;
556
0
  auto &ascFile=stream.ascii();
557
0
  f << "Entries(Preview):";
558
0
  int dims[3];
559
0
  for (auto &d : dims) d=int(input->readLong(4));
560
0
  f << "dim=" << MWAWVec2i(dims[1], dims[0]) << "[" << dims[2] << "],";
561
0
  int width=hasPreviewBitmap ? int(input->readLong(4)) : 0;
562
0
  if (width) f << "w=" << width << ",";
563
0
  long endPos=pos+(hasPreviewBitmap ? 24 : 12)+long(width*dims[0]);
564
0
  if (!hasPreviewBitmap || dims[0]<=0 || dims[1]<=0 || dims[2]!=3 || width<dims[1]*dims[2] ||
565
0
      endPos<=pos+24 || !input->checkPosition(endPos)) {
566
0
    if (dims[0]==0 && dims[1]==0 && input->checkPosition(endPos)) {
567
0
      ascFile.addPos(pos);
568
0
      ascFile.addNote(f.str().c_str());
569
0
      input->seek(endPos, librevenge::RVNG_SEEK_SET);
570
0
      ascFile.skipZone(input->tell(), endPos-1);
571
0
      return true;
572
0
    }
573
0
    f << "###";
574
0
    MWAW_DEBUG_MSG(("Canvas5Structure::readPreview: the dimensions seems bad\n"));
575
0
    ascFile.addPos(pos);
576
0
    ascFile.addNote(f.str().c_str());
577
0
    return false;
578
0
  }
579
0
  for (int i=0; i<2; ++i) {
580
0
    int val=int(input->readLong(4));
581
0
    int const expected[]= {3,1};
582
0
    if (val!=expected[i])
583
0
      f << "f" << i << "=" << val << ",";
584
0
  }
585
0
  ascFile.addPos(pos);
586
0
  ascFile.addNote(f.str().c_str());
587
588
0
  pos=input->tell();
589
0
  MWAWPictBitmapColor pict(MWAWVec2i(dims[1], dims[0]), dims[2]==4);
590
0
  for (int y=0; y<dims[0]; ++y) {
591
0
    long actPos=input->tell();
592
0
    unsigned char cols[4]= {0,0,0,0};
593
0
    for (int w=0; w<dims[1]; ++w) {
594
0
      for (int c=0; c<dims[2]; ++c) cols[c]=(unsigned char)(input->readULong(1));
595
0
      if (dims[2]==4)
596
0
        pict.set(w, y, MWAWColor(cols[1], cols[2], cols[3], (unsigned char)(255-cols[0])));
597
0
      else
598
0
        pict.set(w, y, MWAWColor(cols[0], cols[1], cols[2]));
599
0
    }
600
0
    input->seek(actPos+width, librevenge::RVNG_SEEK_SET);
601
0
  }
602
603
0
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
604
0
  ascFile.skipZone(pos, endPos-1);
605
#ifdef DEBUG_WITH_FILES
606
  MWAWEmbeddedObject obj;
607
  if (pict.getBinary(obj) && !obj.m_dataList.empty())
608
    libmwaw::Debug::dumpFile(obj.m_dataList[0], "file.png");
609
#endif
610
611
0
  return true;
612
0
}
613
614
////////////////////////////////////////////////////////////
615
// decoder
616
////////////////////////////////////////////////////////////
617
618
//! a basic Unpack decoder
619
struct UnpackDecoder {
620
  //! constructor
621
  UnpackDecoder(unsigned char const *data, unsigned long len)
622
268
    : m_data(data)
623
268
    , m_len(len)
624
625
268
    , m_pos(0)
626
268
  {
627
268
  }
628
629
  bool decode(unsigned long expectedLength, std::vector<unsigned char> &output)
630
268
  {
631
268
    output.clear();
632
268
    output.reserve(expectedLength > 0x8000 ? 0x8000 : expectedLength);
633
41.7k
    while (m_pos+2<=m_len) {
634
41.5k
      unsigned num=unsigned(m_data[m_pos++]);
635
41.5k
      unsigned char val=m_data[m_pos++];
636
41.5k
      if (output.size()+num>expectedLength)
637
68
        return false;
638
3.05M
      for (unsigned i=0; i<num; ++i)
639
3.01M
        output.push_back(val);
640
41.5k
    }
641
200
    return output.size()==expectedLength;
642
268
  }
643
protected:
644
645
  unsigned char const *m_data;
646
  unsigned long m_len;
647
  mutable unsigned long m_pos;
648
};
649
650
//! a basic NIB decoder
651
struct NIBDecoder {
652
  //! constructor
653
  NIBDecoder(unsigned char const *data, unsigned long len)
654
454
    : m_data(data)
655
454
    , m_len(len)
656
657
454
    , m_pos(0)
658
454
  {
659
454
  }
660
661
  bool decode(unsigned long expectedLength, std::vector<unsigned char> &output)
662
454
  {
663
454
    output.clear();
664
454
    output.reserve(expectedLength > 0x8000 ? 0x8000 : expectedLength);
665
454
    unsigned char dict[30];
666
454
    std::set<unsigned char> dictKeys;
667
668
454
    if (m_pos+30>m_len) {
669
17
      MWAW_DEBUG_MSG(("Canvas5Structure::NIBDecoder::can not read a dictionary at pos=%lx\n", m_pos));
670
17
      return false;
671
17
    }
672
13.1k
    for (auto &c : dict) c=m_data[m_pos++];
673
437
    dictKeys.clear();
674
13.1k
    for (auto &c : dict) dictKeys.insert(c);
675
676
437
    int newC=0;
677
437
    bool readC=false;
678
437
    unsigned char c;
679
35.3k
    while (m_pos<=m_len) {
680
35.3k
      bool ok=true;
681
52.6k
      for (int st=0; st<4; ++st) {
682
49.2k
        int val;
683
49.2k
        if (!readC) {
684
24.6k
          if (m_pos>m_len) {
685
0
            ok=false;
686
0
            break;
687
0
          }
688
24.6k
          c=m_data[m_pos++];
689
24.6k
          val=int(c>>4);
690
24.6k
        }
691
24.5k
        else
692
24.5k
          val=int(c&0xf);
693
49.2k
        readC=!readC;
694
695
49.2k
        if (val && st<2) {
696
31.4k
          output.push_back(dict[15*st+val-1]);
697
31.4k
          break;
698
31.4k
        }
699
17.7k
        newC=(newC<<4)|val;
700
17.7k
        if (st==3) {
701
3.90k
          if (dictKeys.find((unsigned char) newC)!=dictKeys.end()) {
702
437
            ok=false;
703
437
            break;
704
437
          }
705
3.47k
          output.push_back((unsigned char) newC);
706
3.47k
          newC=0;
707
3.47k
        }
708
17.7k
      }
709
35.3k
      if (!ok)
710
437
        break;
711
34.9k
      if (m_pos+1>=m_len && output.size()==expectedLength)
712
0
        break;
713
34.9k
    }
714
437
    return output.size()==expectedLength;
715
454
  }
716
protected:
717
718
  unsigned char const *m_data;
719
  unsigned long m_len;
720
  mutable unsigned long m_pos;
721
};
722
723
/** a basic LWZ decoder
724
725
    \note this code is freely inspired from https://github.com/MichaelDipperstein/lzw GLP 3
726
 */
727
struct LWZDecoder {
728
  static int const e_firstCode=(1<<8);
729
  static int const e_maxCodeLen=12;
730
  static int const e_maxCode=(1<<e_maxCodeLen);
731
732
  //! constructor
733
  LWZDecoder(unsigned char const *data, unsigned long len)
734
243
    : m_data(data)
735
243
    , m_len(len)
736
737
243
    , m_pos(0)
738
243
    , m_bit(0)
739
243
    , m_dictionary()
740
243
  {
741
243
    initDictionary();
742
243
  }
743
744
  bool decode(std::vector<unsigned char> &output);
745
746
protected:
747
  void initDictionary()
748
399
  {
749
399
    m_dictionary.resize(2); // 100 and 101
750
399
    m_dictionary.reserve(e_maxCode - e_firstCode); // max table 4000
751
399
  }
752
753
  unsigned getBit() const
754
27.7k
  {
755
27.7k
    if (m_pos>=m_len)
756
29
      throw libmwaw::ParseException();
757
27.7k
    unsigned val=(m_data[m_pos]>>(7-m_bit++))&1;
758
27.7k
    if (m_bit==8) {
759
3.43k
      ++m_pos;
760
3.43k
      m_bit=0;
761
3.43k
    }
762
27.7k
    return val;
763
27.7k
  }
764
  unsigned getCodeWord(unsigned codeLen) const
765
6.95k
  {
766
6.95k
    unsigned code=0;
767
41.6k
    for (unsigned i=0; i<codeLen;) {
768
34.7k
      if (m_bit==0 && (codeLen-i)>=8 && m_pos<m_len) {
769
6.93k
        code = (code<<8) | unsigned(m_data[m_pos++]);
770
6.93k
        i+=8;
771
6.93k
        continue;
772
6.93k
      }
773
27.7k
      code = (code<<1) | getBit();
774
27.7k
      ++i;
775
27.7k
    }
776
6.95k
    return code;
777
6.95k
  }
778
779
  struct LWZEntry {
780
    //! constructor
781
    explicit LWZEntry(unsigned int prefixCode=0, unsigned char suffix=0)
782
6.68k
      : m_suffix(suffix)
783
6.68k
      , m_prefixCode(prefixCode)
784
6.68k
    {
785
6.68k
    }
786
    /** last char in encoded string */
787
    unsigned char m_suffix;
788
    /** code for remaining chars in string */
789
    unsigned int m_prefixCode;
790
  };
791
792
  unsigned char decodeRec(unsigned int code, std::vector<unsigned char> &output)
793
6.82k
  {
794
6.82k
    unsigned char c;
795
6.82k
    unsigned char firstChar;
796
797
6.82k
    if (code >= e_firstCode) {
798
168
      if (code-e_firstCode >= m_dictionary.size()) {
799
101
        MWAW_DEBUG_MSG(("Canvas5Structure::LWZDecoder::decodeRec: bad id=%x/%x\n", code, unsigned(m_dictionary.size())));
800
101
        throw libmwaw::ParseException();
801
101
      }
802
      /* code word is string + c */
803
67
      c = m_dictionary[code - e_firstCode].m_suffix;
804
67
      code = m_dictionary[code - e_firstCode].m_prefixCode;
805
806
      /* evaluate new code word for remaining string */
807
67
      firstChar = decodeRec(code, output);
808
67
    }
809
6.65k
    else /* code word is just c */
810
6.65k
      firstChar = c = (unsigned char)code;
811
812
6.72k
    output.push_back(c);
813
6.72k
    return firstChar;
814
6.82k
  }
815
  LWZDecoder(LWZDecoder const &)=delete;
816
  LWZDecoder &operator=(LWZDecoder const &)=delete;
817
  unsigned char const *m_data;
818
  unsigned long m_len;
819
  mutable unsigned long m_pos, m_bit;
820
821
  std::vector<LWZEntry> m_dictionary;
822
};
823
824
bool LWZDecoder::decode(std::vector<unsigned char> &output)
825
243
try
826
243
{
827
243
  output.reserve(0x8000);
828
829
243
  unsigned int const currentCodeLen = 12;
830
243
  unsigned lastCode=0;
831
243
  unsigned char c=(unsigned char) 0;
832
243
  bool first=true;
833
834
7.08k
  while (true) {
835
6.95k
    unsigned code=getCodeWord(currentCodeLen);
836
6.95k
    if (code==0x100) {
837
156
      initDictionary();
838
156
      first=true;
839
156
      continue;
840
156
    }
841
6.79k
    if (code==0x101) // end of code
842
12
      break;
843
6.78k
    if (code < e_firstCode+m_dictionary.size())
844
      /* we have a known code.  decode it */
845
6.40k
      c = decodeRec(code, output);
846
381
    else {
847
      /***************************************************************
848
       * We got a code that's not in our dictionary.  This must be due
849
       * to the string + char + string + char + string exception.
850
       * Build the decoded string using the last character + the
851
       * string from the last code.
852
       ***************************************************************/
853
381
      unsigned char tmp = c;
854
381
      c = decodeRec(lastCode, output);
855
381
      output.push_back(tmp);
856
381
    }
857
858
    /* if room, add new code to the dictionary */
859
6.78k
    if (!first && m_dictionary.size() < e_maxCode) {
860
6.29k
      if (lastCode>=e_firstCode+m_dictionary.size()) {
861
101
        MWAW_DEBUG_MSG(("Canvas5Structure::LWZDecoder::decode: oops a loop with %x/%x\n", lastCode, unsigned(m_dictionary.size())));
862
101
        break;
863
101
      }
864
6.19k
      m_dictionary.push_back(LWZEntry(lastCode, c));
865
6.19k
    }
866
867
    /* save character and code for use in unknown code word case */
868
6.68k
    lastCode = code;
869
6.68k
    first=false;
870
6.68k
  }
871
243
  return true;
872
243
}
873
243
catch (...)
874
243
{
875
130
  return false;
876
130
}
877
878
879
bool decodeZone5(MWAWInputStreamPtr input, long endPos, int type, unsigned long finalLength,
880
                 std::shared_ptr<MWAWStringStream> &stream)
881
1.28k
{
882
1.28k
  if (type<0 || type>8) {
883
0
    MWAW_DEBUG_MSG(("Canvas5Structure::decodeZone5: unknown type\n"));
884
0
    return false;
885
0
  }
886
1.28k
  std::vector<unsigned long> lengths;
887
1.28k
  lengths.push_back(finalLength);
888
  // checkme this code is only tested when type==0, 7, 8
889
1.28k
  int const nExtraLength[]= {
890
1.28k
    0, 0, 0, 0, 2, // _, _, Z, N, N+Z
891
1.28k
    0, 0, 2, 3 // _, P, P+N, P+N+Z
892
1.28k
  };
893
1.28k
  long pos=input->tell();
894
1.28k
  if (pos+4*nExtraLength[type]>endPos) {
895
11
    MWAW_DEBUG_MSG(("Canvas5Structure::decodeZone5: can not read the extra length\n"));
896
11
    return false;
897
11
  }
898
1.26k
  bool readInverted=input->readInverted();
899
1.26k
  input->setReadInverted(false);
900
1.64k
  for (int n=0; n<nExtraLength[type]; ++n)
901
374
    lengths.push_back(input->readULong(4));
902
1.26k
  if (lengths.size()==1)
903
1.09k
    lengths.push_back((unsigned long)(endPos-pos));
904
1.26k
  input->setReadInverted(readInverted);
905
906
1.26k
  auto l=lengths.back();
907
1.26k
  lengths.pop_back();
908
1.29k
  for (size_t i=lengths.size(); i>0 && l==0xFFFFFFFF; --i) l=lengths[i-1];
909
910
1.26k
  pos=input->tell();
911
1.26k
  unsigned long read;
912
1.26k
  unsigned char const *dt = l<=(unsigned long)(endPos-pos) ? input->read(l, read) : nullptr;
913
1.26k
  if (!dt || read != l) {
914
97
    MWAW_DEBUG_MSG(("Canvas5Structure::decodeZone5: can not read some data\n"));
915
97
    return false;
916
97
  }
917
1.17k
  std::vector<unsigned char> data(dt, dt+l);
918
919
1.17k
  if (type==2 || type==4 || type==8) { // find with type==8
920
253
    l=lengths.back();
921
253
    lengths.pop_back();
922
257
    for (size_t i=lengths.size(); i>0 && l==0xFFFFFFFF; --i) l=lengths[i-1];
923
253
    if (l!=0xffffffff && l!=data.size()) {
924
243
      Canvas5Structure::LWZDecoder decoder(data.data(), data.size());
925
243
      std::vector<unsigned char> data2;
926
243
      if (!decoder.decode(data2) || data2.size()!=l) {
927
243
        MWAW_DEBUG_MSG(("Canvas5Structure::decodeZone5[LWZ]: can not decode some data\n"));
928
243
        return false;
929
243
      }
930
0
      std::swap(data, data2);
931
0
    }
932
253
  }
933
934
929
  if (type==3 || type==4 || type==7 || type==8) { // find with type==7,8
935
466
    l=lengths.back();
936
466
    lengths.pop_back();
937
467
    for (size_t i=lengths.size(); i>0 && l==0xFFFFFFFF; --i) l=lengths[i-1];
938
466
    if (l!=0xffffffff && l!=data.size()) {
939
454
      Canvas5Structure::NIBDecoder decoder(data.data(), data.size());
940
454
      std::vector<unsigned char> data2;
941
454
      if (!decoder.decode(l, data2)) {
942
452
        MWAW_DEBUG_MSG(("Canvas5Structure::decodeZone5[NIB]: can not decode some data\n"));
943
452
        return false;
944
452
      }
945
2
      std::swap(data, data2);
946
2
    }
947
466
  }
948
949
477
  if (type==6 || type==7 || type==8) { // find with type==7,8
950
276
    l=lengths.back();
951
276
    lengths.pop_back();
952
276
    for (size_t i=lengths.size(); i>0 && l==0xFFFFFFFF; --i) l=lengths[i-1];
953
276
    if (l!=0xffffffff && l!=data.size()) {
954
268
      Canvas5Structure::UnpackDecoder decoder(data.data(), data.size());
955
268
      std::vector<unsigned char> data2;
956
268
      if (!decoder.decode(l, data2)) {
957
267
        MWAW_DEBUG_MSG(("Canvas5Structure::decodeZone5[pack]: can not decode some data\n"));
958
267
        return false;
959
267
      }
960
1
      std::swap(data, data2);
961
1
    }
962
276
  }
963
964
210
  if (data.size()!=finalLength) {
965
62
    MWAW_DEBUG_MSG(("Canvas5Structure::decodeZone5[pack]: problem decoding data %lx/%lx\n", (unsigned long)data.size(), finalLength));
966
62
    return false;
967
62
  }
968
969
148
  stream->append(data.data(), unsigned(data.size()));
970
971
148
  if (input->tell()!=endPos) {
972
0
    MWAW_DEBUG_MSG(("Canvas5Structure::decodeZone5: find extra data\n"));
973
0
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
974
0
  }
975
148
  return true;
976
210
}
977
978
}
979
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
980