source: trunk/libdjvu/IFFByteStream.cpp @ 280

Last change on this file since 280 was 280, checked in by rbri, 11 years ago

DJVU plugin: djvulibre updated to version 3.5.22

File size: 14.3 KB
Line 
1//C-  -*- C++ -*-
2//C- -------------------------------------------------------------------
3//C- DjVuLibre-3.5
4//C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
5//C- Copyright (c) 2001  AT&T
6//C-
7//C- This software is subject to, and may be distributed under, the
8//C- GNU General Public License, either Version 2 of the license,
9//C- or (at your option) any later version. The license should have
10//C- accompanied the software or you may obtain a copy of the license
11//C- from the Free Software Foundation at http://www.fsf.org .
12//C-
13//C- This program is distributed in the hope that it will be useful,
14//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
15//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16//C- GNU General Public License for more details.
17//C-
18//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from
19//C- Lizardtech Software.  Lizardtech Software has authorized us to
20//C- replace the original DjVu(r) Reference Library notice by the following
21//C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu):
22//C-
23//C-  ------------------------------------------------------------------
24//C- | DjVu (r) Reference Library (v. 3.5)
25//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
26//C- | The DjVu Reference Library is protected by U.S. Pat. No.
27//C- | 6,058,214 and patents pending.
28//C- |
29//C- | This software is subject to, and may be distributed under, the
30//C- | GNU General Public License, either Version 2 of the license,
31//C- | or (at your option) any later version. The license should have
32//C- | accompanied the software or you may obtain a copy of the license
33//C- | from the Free Software Foundation at http://www.fsf.org .
34//C- |
35//C- | The computer code originally released by LizardTech under this
36//C- | license and unmodified by other parties is deemed "the LIZARDTECH
37//C- | ORIGINAL CODE."  Subject to any third party intellectual property
38//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
39//C- | non-exclusive license to make, use, sell, or otherwise dispose of
40//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
41//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
42//C- | General Public License.   This grant only confers the right to
43//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
44//C- | the extent such infringement is reasonably necessary to enable
45//C- | recipient to make, have made, practice, sell, or otherwise dispose
46//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
47//C- | any greater extent that may be necessary to utilize further
48//C- | modifications or combinations.
49//C- |
50//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
51//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
52//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
53//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
54//C- +------------------------------------------------------------------
55//
56// $Id: IFFByteStream.cpp,v 1.13 2008/03/16 14:07:06 leonb Exp $
57// $Name: release_3_5_22 $
58
59#ifdef HAVE_CONFIG_H
60# include "config.h"
61#endif
62#if NEED_GNUG_PRAGMAS
63# pragma implementation
64#endif
65
66// -- Implementation of IFFByteStream
67// - Author: Leon Bottou, 06/1998
68
69// From: Leon Bottou, 1/31/2002
70// This has been changed by Lizardtech to fit better
71// with their re-implementation of ByteStreams.
72
73#include <assert.h>
74#include "IFFByteStream.h"
75
76
77#ifdef HAVE_NAMESPACES
78namespace DJVU {
79# ifdef NOT_DEFINED // Just to fool emacs c++ mode
80}
81#endif
82#endif
83
84
85// Constructor
86IFFByteStream::IFFByteStream(const GP<ByteStream> &xbs,const int xpos)
87  : ByteStream::Wrapper(xbs), ctx(0), dir(0)
88{
89  offset = seekto = xpos;
90  has_magic_att = false;
91  has_magic_sdjv = false;
92}
93
94// Destructor
95IFFByteStream::~IFFByteStream()
96{
97  while (ctx)
98    close_chunk();
99}
100
101GP<IFFByteStream>
102IFFByteStream::create(const GP<ByteStream> &bs)
103{
104  const int pos=bs->tell();
105  return new IFFByteStream(bs,pos);
106}
107
108
109// IFFByteStream::ready
110// -- indicates if bytestream is ready for reading
111//    returns number of bytes available
112
113int 
114IFFByteStream::ready()
115{
116  if (ctx && dir < 0)
117    return ctx->offEnd - offset;
118  else if (ctx)
119    return 1;
120  else
121    return 0;
122}
123
124
125// IFFByteStream::composite
126// -- indicates if bytestream is ready for putting or getting chunks
127
128int 
129IFFByteStream::composite()
130{
131  if (ctx && !ctx->bComposite)
132    return 0;
133  else
134    return 1;
135}
136
137
138
139
140// IFFByteStream::check_id
141// -- checks if an id is legal
142
143int 
144IFFByteStream::check_id(const char *id)
145{
146  int i;
147  // check absence of null bytes
148  for (i=0; i<4; i++)
149    if (id[i]<0x20 || id[i]>0x7e)
150      return -1;
151  // check composite chunks
152  static const char *szComposite[] = { "FORM", "LIST", "PROP", "CAT ", 0 };
153  for (i=0; szComposite[i]; i++) 
154    if (!memcmp(id, szComposite[i], 4))
155      return 1;
156  // check reserved chunks
157  static const char *szReserved[] = { "FOR", "LIS", "CAT", 0 };
158  for (i=0; szReserved[i]; i++) 
159    if (!memcmp(id, szReserved[i], 3) && id[3]>='1' && id[3]<='9')
160      return -1;
161  // regular chunk
162  return 0;
163}
164
165
166
167// IFFByteStream::get_chunk
168// -- get next chunk header
169
170int 
171IFFByteStream::get_chunk(GUTF8String &chkid, int *rawoffsetptr, int *rawsizeptr)
172{
173  int bytes;
174  char buffer[8];
175 
176  // Check that we are allowed to read a chunk
177  if (dir > 0)
178    G_THROW( ERR_MSG("IFFByteStream.read_write") );
179  if (ctx && !ctx->bComposite)
180    G_THROW( ERR_MSG("IFFByteStream.not_ready") );
181  dir = -1;
182
183  // Seek to end of previous chunk if necessary
184  if (seekto > offset)
185    {
186      bs->seek(seekto);
187      offset = seekto;
188    }
189
190  // Skip padding byte
191  if (ctx && offset == ctx->offEnd)
192    return 0;
193  if (offset & 1)
194    {
195      bytes = bs->read( (void*)buffer, 1);
196      if (bytes==0 && !ctx)
197        return 0;
198      offset += bytes;
199    }
200 
201  // Record raw offset
202  int rawoffset = offset;
203 
204  // Read chunk id (skipping magic sequences inserted here to make
205  // DjVu files recognizable.)
206  for(;;)
207  {
208    if (ctx && offset == ctx->offEnd)
209      return 0;
210    if (ctx && offset+4 > ctx->offEnd)
211      G_THROW( ERR_MSG("IFFByteStream.corrupt_end") );
212    bytes = bs->readall( (void*)&buffer[0], 4);
213    offset = seekto = offset + bytes;
214    if (bytes==0 && !ctx)
215      return 0;
216    if (bytes != 4)
217      G_THROW(ByteStream::EndOfFile);
218    if (buffer[0] == 'S' && buffer[1] == 'D' &&
219        buffer[2] == 'J' && buffer[3] == 'V' )
220      {
221        has_magic_sdjv = true;
222        continue;
223      }
224    else if (buffer[0] == 'A' && buffer[1] == 'T' &&
225             buffer[2] == '&' && buffer[3] == 'T' )
226      {
227        has_magic_att=true;
228        continue;
229      }
230    else
231      break;
232  }
233 
234  // Read chunk size
235  if (ctx && offset+4 > ctx->offEnd)
236    G_THROW( ERR_MSG("IFFByteStream.corrupt_end2") );
237  bytes = bs->readall( (void*)&buffer[4], 4);
238  offset = seekto = offset + bytes;
239  if (bytes != 4)
240    G_THROW( ByteStream::EndOfFile );
241  long size = ((unsigned char)buffer[4]<<24) |
242              ((unsigned char)buffer[5]<<16) |
243              ((unsigned char)buffer[6]<<8)  |
244              ((unsigned char)buffer[7]);
245  if (ctx && offset+size > ctx->offEnd)
246    G_THROW( ERR_MSG("IFFByteStream.corrupt_mangled") );
247 
248  // Check if composite
249  int composite = check_id(buffer);
250  if (composite < 0)
251    G_THROW( ERR_MSG("IFFByteStream.corrupt_id") );
252 
253  // Read secondary id of composite chunk
254  if (composite)
255  {
256    if (ctx && ctx->offEnd<offset+4)
257      G_THROW( ERR_MSG("IFFByteStream.corrupt_header") );
258    bytes = bs->readall( (void*)&buffer[4], 4);
259    offset += bytes;
260    if (bytes != 4)
261      G_THROW( ByteStream::EndOfFile );
262    if (check_id(&buffer[4]))
263      G_THROW( ERR_MSG("IFFByteStream.corrupt_2nd_id") );
264  }
265
266  // Create context record
267  IFFContext *nctx = new IFFContext;
268  G_TRY
269  {
270    nctx->next = ctx;
271    nctx->offStart = seekto;
272    nctx->offEnd = seekto + size;
273    if (composite)
274    {
275      memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4);
276      memcpy( (void*)(nctx->idTwo), (void*)&buffer[4], 4);
277      nctx->bComposite = 1;
278    }
279    else
280    {
281      memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4);
282      memset( (void*)(nctx->idTwo), 0, 4);
283      nctx->bComposite = 0;
284    }
285  }
286  G_CATCH_ALL
287  {
288    delete nctx;
289    G_RETHROW;
290  }
291  G_ENDCATCH;
292 
293  // Install context record
294  ctx = nctx;
295  chkid = GUTF8String(ctx->idOne, 4);
296  if (composite)
297    chkid = chkid + ":" + GUTF8String(ctx->idTwo, 4);
298
299  // Return
300  if (rawoffsetptr)
301    *rawoffsetptr = rawoffset;
302  if (rawsizeptr)
303    *rawsizeptr = ( ctx->offEnd - rawoffset + 1) & ~0x1;
304  return size;
305}
306
307
308
309// IFFByteStream::put_chunk
310// -- write new chunk header
311
312void 
313IFFByteStream::put_chunk(const char *chkid, int insert_magic)
314{
315  int bytes;
316  char buffer[8];
317
318  // Check that we are allowed to write a chunk
319  if (dir < 0)
320    G_THROW( ERR_MSG("IFFByteStream.read_write") );
321  if (ctx && !ctx->bComposite)
322    G_THROW( ERR_MSG("IFFByteStream.not_ready2") );
323  dir = +1;
324
325  // Check primary id
326  int composite = check_id(chkid);
327  if ((composite<0) || (composite==0 && chkid[4])
328      || (composite && (chkid[4]!=':' || check_id(&chkid[5]) || chkid[9])) )
329    G_THROW( ERR_MSG("IFFByteStream.bad_chunk") );
330
331  // Write padding byte
332  assert(seekto <= offset);
333  memset((void*)buffer, 0, 8);
334  if (offset & 1)
335    offset += bs->write((void*)&buffer[4], 1);
336
337  // Insert magic to make this file recognizable as DjVu
338  if (insert_magic)
339  {
340    // Don't change the way you do the file magic!
341    // I rely on these bytes letters in some places
342    // (like DjVmFile.cpp and djvm.cpp) -- eaf
343    buffer[0]=0x41;
344    buffer[1]=0x54;
345    buffer[2]=0x26;
346    buffer[3]=0x54;
347    offset += bs->writall((void*)&buffer[0], 4);
348  }
349
350  // Write chunk header
351  memcpy((void*)&buffer[0], (void*)&chkid[0], 4);
352  bytes = bs->writall((void*)&buffer[0], 8);
353  offset = seekto = offset + bytes;
354  if (composite)
355  {
356    memcpy((void*)&buffer[4], (void*)&chkid[5], 4);
357    bytes = bs->writall((void*)&buffer[4], 4);
358    offset = offset + bytes;   
359  }
360
361  // Create new context record
362  IFFContext *nctx = new IFFContext;
363  G_TRY
364  {
365    nctx->next = ctx;
366    nctx->offStart = seekto;
367    nctx->offEnd = 0;
368    if (composite)
369    {
370      memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4);
371      memcpy( (void*)(nctx->idTwo), (void*)&buffer[4], 4);
372      nctx->bComposite = 1;
373    }
374    else
375    {
376      memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4);
377      memset( (void*)(nctx->idTwo), 0, 4);
378      nctx->bComposite = 0;
379    }
380  }
381  G_CATCH_ALL
382  {
383    delete nctx;
384    G_RETHROW;
385  }
386  G_ENDCATCH; 
387  // Install context record and leave
388  ctx = nctx;
389}
390
391
392
393void 
394IFFByteStream::close_chunk()
395{
396  // Check that this is ok
397  if (!ctx)
398    G_THROW( ERR_MSG("IFFByteStream.cant_close") );
399  // Patch size field in new chunk
400  if (dir > 0)
401    {
402      ctx->offEnd = offset;
403      long size = ctx->offEnd - ctx->offStart;
404      char buffer[4];
405      buffer[0] = (unsigned char)(size>>24);
406      buffer[1] = (unsigned char)(size>>16);
407      buffer[2] = (unsigned char)(size>>8);
408      buffer[3] = (unsigned char)(size);
409      bs->seek(ctx->offStart - 4);
410      bs->writall((void*)buffer, 4);
411      bs->seek(offset);
412    }
413  // Arrange for reader to seek at next chunk
414  seekto = ctx->offEnd;
415  // Remove ctx record
416  IFFContext *octx = ctx;
417  ctx = octx->next;
418  assert(ctx==0 || ctx->bComposite);
419  delete octx;
420}
421
422// This is the same as above, but adds a seek to the close
423// Otherwise an EOF in this chunk won't be reported until we
424// try to open the next chunk, which makes error recovery
425// very difficult.
426void 
427IFFByteStream::seek_close_chunk(void)
428{
429  close_chunk();
430  if ((dir <= 0)&&((!ctx)||(ctx->bComposite))&&(seekto > offset))
431  {
432    bs->seek(seekto);
433    offset = seekto;
434  }
435}
436
437// IFFByteStream::short_id
438// Returns the id of the current chunk
439
440void 
441IFFByteStream::short_id(GUTF8String &chkid)
442{
443  if (!ctx)
444    G_THROW( ERR_MSG("IFFByteStream.no_chunk_id") );
445  if (ctx->bComposite)
446    chkid = GUTF8String(ctx->idOne, 4) + ":" + GUTF8String(ctx->idTwo, 4);
447  else
448    chkid = GUTF8String(ctx->idOne, 4);
449}
450
451
452// IFFByteStream::full_id
453// Returns the full chunk id of the current chunk
454
455void 
456IFFByteStream::full_id(GUTF8String &chkid)
457{
458  short_id(chkid);
459  if (ctx->bComposite)
460    return;
461  // Search parent FORM or PROP chunk.
462  for (IFFContext *ct = ctx->next; ct; ct=ct->next)
463    if (memcmp(ct->idOne, "FOR", 3)==0 || 
464        memcmp(ct->idOne, "PRO", 3)==0  )
465      {
466        chkid = GUTF8String(ct->idTwo, 4) + "." + chkid;
467        break;
468      }
469}
470
471
472
473// IFFByteStream::read
474// -- read bytes from IFF file chunk
475
476size_t 
477IFFByteStream::read(void *buffer, size_t size)
478{
479  if (! (ctx && dir < 0))
480    G_THROW( ERR_MSG("IFFByteStream.not_ready3") );
481  // Seek if necessary
482  if (seekto > offset) {
483    bs->seek(seekto);
484    offset = seekto;
485  }
486  // Ensure that read does not extend beyond chunk
487  if (offset > ctx->offEnd)
488    G_THROW( ERR_MSG("IFFByteStream.bad_offset") );
489  if (offset + (long)size >  ctx->offEnd)
490    size = (size_t) (ctx->offEnd - offset);
491  // Read bytes
492  size_t bytes = bs->read(buffer, size);
493  offset += bytes;
494  return bytes;
495}
496
497
498// IFFByteStream::write
499// -- write bytes to IFF file chunk
500
501size_t 
502IFFByteStream::write(const void *buffer, size_t size)
503{
504  if (! (ctx && dir > 0))
505    G_THROW( ERR_MSG("IFFByteStream.not_ready4") );
506  if (seekto > offset)
507    G_THROW( ERR_MSG("IFFByteStream.cant_write") );
508  size_t bytes = bs->write(buffer, size);
509  offset += bytes;
510  return bytes;
511}
512
513// IFFByteStream::tell
514// -- tell position
515
516long 
517IFFByteStream::tell() const
518{
519  return (seekto>offset)?seekto:offset;
520}
521
522bool
523IFFByteStream::compare(IFFByteStream &iff)
524{
525  bool retval=(&iff == this);
526  if(!retval)
527  {
528    GUTF8String chkid1, chkid2;
529    int size;
530    while((size=get_chunk(chkid1)) == iff.get_chunk(chkid2))
531    {
532      if(chkid1 != chkid2)
533      {
534        break;
535      }
536      if(!size)
537      {
538        retval=true;
539        break;
540      }
541      char buf[4096];
542      int len;
543      while((len=read(buf,sizeof(buf))))
544      {
545        int s=0;
546        char buf2[sizeof(buf)];
547        while(s<len)
548        {
549          const int i=iff.read(buf2+s,len-s);
550          if(!i)
551            break;
552          s+=i;
553        }
554        if((s != len)||memcmp(buf,buf2,len))
555          break;
556      }
557      if(len)
558        break;
559      iff.close_chunk();
560      close_chunk();
561    }
562  }
563  return retval;
564}
565
566
567#ifdef HAVE_NAMESPACES
568}
569# ifndef NOT_USING_DJVU_NAMESPACE
570using namespace DJVU;
571# endif
572#endif
Note: See TracBrowser for help on using the repository browser.