source: trunk/libdjvu/GIFFManager.cpp @ 280

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

DJVU plugin: djvulibre updated to version 3.5.22

File size: 18.4 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: GIFFManager.cpp,v 1.10 2008/08/05 20:50:35 bpearlmutter 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#include "GIFFManager.h"
67#include "GException.h"
68#include "debug.h"
69
70
71#ifdef HAVE_NAMESPACES
72namespace DJVU {
73# ifdef NOT_DEFINED // Just to fool emacs c++ mode
74}
75#endif
76#endif
77
78
79GIFFChunk::~GIFFChunk(void) {}
80
81GIFFManager::~GIFFManager(void) {}
82
83GP<GIFFManager> 
84GIFFManager::create(void)
85{
86  GIFFManager *iff=new GIFFManager();
87  GP<GIFFManager> retval=iff;
88  iff->init();
89  return retval;
90}
91
92GP<GIFFManager> 
93GIFFManager::create(const GUTF8String &name)
94{
95  GIFFManager *iff=new GIFFManager();
96  GP<GIFFManager> retval=iff;
97  iff->init(name);
98  return retval;
99}
100
101void
102GIFFChunk::set_name(GUTF8String name)
103{
104  DEBUG_MSG("GIFFChunk::set_name(): name='" << name << "'\n");
105  DEBUG_MAKE_INDENT(3);
106
107  const int colon=name.search(':');
108  if(colon>=0)
109  {
110    type=name.substr(0,colon);
111    name=name.substr(colon+1,(unsigned int)-1);
112    if(name.search(':')>=0)
113      G_THROW( ERR_MSG("GIFFManager.one_colon") );
114  }
115
116  DEBUG_MSG("auto-setting type to '" << type << "'\n");
117
118  if (name.contains(".[]")>=0)
119    G_THROW( ERR_MSG("GIFFManager.bad_char") );
120
121  strncpy(GIFFChunk::name, (const char *)name, 4);
122  GIFFChunk::name[4]=0;
123  for(int i=strlen(GIFFChunk::name);i<4;i++)
124    GIFFChunk::name[i]=' ';
125}
126
127bool
128GIFFChunk::check_name(GUTF8String name)
129{
130  GUTF8String type;
131  const int colon=name.search(':');
132  if(colon>=0)
133    {
134      type=name.substr(0,colon);
135      name=name.substr(colon+1,(unsigned int)-1);
136    }
137
138  const GUTF8String sname=(name.substr(0,4)+"    ").substr(0,4);
139
140  DEBUG_MSG("GIFFChunk::check_name(): type='" << type << "' name='" << sname << "'\n");
141  return (type==GIFFChunk::type || (!type.length() && GIFFChunk::type=="FORM"))
142    && sname==GIFFChunk::name;
143}
144
145void
146GIFFChunk::save(IFFByteStream & istr, bool use_trick)
147{
148  DEBUG_MSG("GIFFChunk::save(): saving chunk '" << get_full_name() << "'\n");
149  DEBUG_MAKE_INDENT(3);
150
151  if (is_container())
152  {
153    istr.put_chunk(get_full_name(), use_trick);
154    if (chunks.size())
155    {
156      GPosition pos;
157      for(pos=chunks;pos;++pos)
158        if (chunks[pos]->get_type()=="PROP")
159          chunks[pos]->save(istr);
160      for(pos=chunks;pos;++pos)
161        if (chunks[pos]->get_type()!="PROP")
162          chunks[pos]->save(istr);
163    } else
164    {
165      DEBUG_MSG("but it's empty => saving empty container.\n");
166    }
167    istr.close_chunk();
168  } else
169  {
170    istr.put_chunk(get_name(), use_trick);
171    istr.get_bytestream()->writall((const char *) data, data.size());
172    istr.close_chunk();
173  }
174}
175
176void
177GIFFChunk::add_chunk(const GP<GIFFChunk> & chunk, int position)
178{
179  DEBUG_MSG("GIFFChunk::add_chunk(): Adding chunk to '" << get_name() <<
180     "' @ position=" << position << "\n");
181  DEBUG_MAKE_INDENT(3);
182
183  if (!type.length())
184  {
185    DEBUG_MSG("Converting the parent to FORM\n");
186    type="FORM";
187  }
188
189  if (chunk->get_type()=="PROP")
190  {
191    DEBUG_MSG("Converting the parent to LIST\n");
192    type="LIST";
193  }
194
195  GPosition pos;
196  if (position>=0 && chunks.nth(position, pos))
197  {
198    chunks.insert_before(pos, chunk);
199  }else
200  {
201    chunks.append(chunk);
202  }
203}
204
205GUTF8String
206GIFFChunk::decode_name(const GUTF8String &name, int &number)
207{
208  DEBUG_MSG("GIFFChunk::decode_name(): Checking brackets in name '" << name << "'\n");
209  DEBUG_MAKE_INDENT(3);
210   
211  if (name.search('.')>=0)
212    G_THROW( ERR_MSG("GIFFManager.no_dots") );
213
214  number=0;
215  const int obracket=name.search('[');
216  GUTF8String short_name;
217  if (obracket >= 0)
218  {
219    const int cbracket=name.search(']',obracket+1);
220    if (cbracket < 0)
221      G_THROW( ERR_MSG("GIFFManager.unmatched") );
222    if (name.length() > (unsigned int)(cbracket+1))
223      G_THROW( ERR_MSG("GIFFManager.garbage") );
224//    number =atoi((const char *)name.substr(obracket+1,cbracket-obracket-1));
225    number= name.substr(obracket+1,cbracket-obracket-1).toInt(); 
226    short_name=name.substr(0,obracket);
227  }else
228  {
229    short_name=name;
230  }
231
232  const int colon=short_name.search(':');
233  if (colon>=0)
234    short_name=short_name.substr(colon+1,(unsigned int)-1);
235
236  for(int i=short_name.length();i<4;i++)
237    short_name.setat(i, ' ');
238   
239  DEBUG_MSG("short_name='" << short_name << "'\n");
240  DEBUG_MSG("number=" << number << "\n");
241   
242  return short_name;
243}
244
245void
246GIFFChunk::del_chunk(const GUTF8String &name)
247   // The name may contain brackets to specify the chunk number
248{
249  DEBUG_MSG("GIFFChunk::del_chunk(): Deleting chunk '" << name <<
250     "' from '" << get_name() << "'\n");
251  DEBUG_MAKE_INDENT(3);
252
253  int number;
254  const GUTF8String short_name=decode_name(name,number);
255
256  GPosition pos=chunks;
257  for(int num=0;pos;++pos)
258  {
259    if ((chunks[pos]->get_name()==short_name)&&(num++ == number))
260    {
261      chunks.del(pos);
262      break;
263    }
264  }
265  if(! pos)
266  {
267    G_THROW( ERR_MSG("GIFFManager.no_chunk") "\t"+short_name+"\t"+GUTF8String(number)+"\t"+get_name());
268  }
269}
270
271GP<GIFFChunk>
272GIFFChunk::get_chunk(const GUTF8String &name, int * pos_ptr)
273   // The name may contain brackets to specify the chunk number
274{
275  DEBUG_MSG("GIFFChunk::get_chunk(): Returning chunk '" << name <<
276     "' from '" << get_name() << "'\n");
277  DEBUG_MAKE_INDENT(3);
278
279  int number;
280  const GUTF8String short_name=decode_name(name,number);
281
282  int num=0;
283  int pos_num;
284  GP<GIFFChunk> retval;
285  GPosition pos;
286  for(pos=chunks, pos_num=0;pos;++pos, pos_num++)
287  {
288    if (chunks[pos]->get_name()==short_name && num++==number)
289    {
290      if (pos_ptr)
291        *pos_ptr=pos_num;
292      retval=chunks[pos];
293      break;
294    }
295  }
296  return retval;
297}
298
299int
300GIFFChunk::get_chunks_number(void)
301{
302  DEBUG_MSG("GIFFChunk::get_chunks_number(): Returning number of chunks '" << name <<
303     "' in '" << get_name() << "'\n");
304  DEBUG_MAKE_INDENT(3);
305  return chunks.size();
306}
307
308int
309GIFFChunk::get_chunks_number(const GUTF8String &name)
310{
311  DEBUG_MSG("GIFFChunk::get_chunks_number(): Returning number of chunks '" << name <<
312     "' in '" << get_name() << "'\n");
313  DEBUG_MAKE_INDENT(3);
314
315  if (name.contains("[]")>=0)
316    G_THROW( ERR_MSG("GIFFManager.no_brackets") );
317 
318  int number; 
319  GUTF8String short_name=decode_name(name,number);
320   
321  int num=0;
322  for(GPosition pos=chunks;pos;++pos)
323     num+=(chunks[pos]->get_name()==short_name);
324  return num;
325}
326
327//************************************************************************
328
329void
330GIFFManager::add_chunk(GUTF8String parent_name, const GP<GIFFChunk> & chunk,
331                       int pos)
332      // parent_name is the fully qualified name of the PARENT
333      //             IT MAY BE EMPTY
334      // All the required chunks will be created
335      // pos=-1 means to append the chunk
336{
337  DEBUG_MSG("GIFFManager::add_chunk(): Adding chunk to name='" << parent_name << "'\n");
338  DEBUG_MAKE_INDENT(3);
339   
340  if (!top_level->get_name().length())
341  {
342    if ((!parent_name.length())||(parent_name[0]!='.'))
343      G_THROW( ERR_MSG("GIFFManager.no_top_name") );
344    if (parent_name.length() < 2)
345    {
346      // 'chunk' is actually the new top-level chunk
347      DEBUG_MSG("since parent_name=='.', making the chunk top-level\n");
348      if (!chunk->is_container())
349        G_THROW( ERR_MSG("GIFFManager.no_top_cont") );
350      top_level=chunk;
351      return;
352    }
353
354    DEBUG_MSG("Setting the name of the top-level chunk\n");
355    const int next_dot=parent_name.search('.',1);
356    if(next_dot>=0)
357    {
358      top_level->set_name(parent_name.substr(1,next_dot-1));
359    }else
360    {
361      top_level->set_name(parent_name.substr(1,(unsigned int)-1));
362    }
363  }
364
365  DEBUG_MSG("top level chunk name='" << top_level->get_name() << "'\n");
366   
367  if (parent_name.length() && parent_name[0] == '.')
368  {
369    int next_dot=parent_name.search('.',1);
370    if(next_dot<0)
371    {
372      next_dot=parent_name.length();
373    }
374    GUTF8String top_name=parent_name.substr(1,next_dot-1);
375    if (!top_level->check_name(top_name))
376      G_THROW( ERR_MSG("GIFFManager.wrong_name") "\t"+top_name);
377    parent_name=parent_name.substr(next_dot,(unsigned int)-1);
378  }
379
380  GP<GIFFChunk> cur_sec=top_level;
381  const char * start, * end=(const char *)parent_name-1;
382  do
383  {
384    for(start=++end;*end&&(*end!='.');end++)
385      EMPTY_LOOP;
386    if (end>start)
387    {
388      GUTF8String name(start,end-start);
389      GUTF8String short_name;
390      int number=0;
391      const int obracket=name.search('[');
392      if (obracket >= 0)
393      {
394        const int cbracket=name.search(']',obracket+1);
395        if (cbracket < 0)
396          G_THROW( ERR_MSG("GIFFManager.unmatched") );
397//        number=atoi((const char *)name.substr(obracket+1,cbracket-obracket-1));
398        number = name.substr(obracket+1,cbracket-obracket-1).toInt();
399        short_name=name.substr(0,obracket);
400      }else
401      {
402        short_name=name;
403      }
404
405      for(int i=cur_sec->get_chunks_number(short_name);i<number+1;i++)
406        cur_sec->add_chunk(GIFFChunk::create(short_name));
407      cur_sec=cur_sec->get_chunk(name);
408      if (!cur_sec)
409        G_THROW( ERR_MSG("GIFFManager.unknown") "\t"+name);
410    }
411  } while(*end);
412  cur_sec->add_chunk(chunk, pos);
413}
414
415void
416GIFFManager::add_chunk(GUTF8String name, const TArray<char> & data)
417      // name is fully qualified name of the chunk TO BE INSERTED.
418      //      it may contain brackets at the end to set the position
419      // All the required chunks will be created
420{
421  DEBUG_MSG("GIFFManager::add_chunk(): adding plain chunk with name='" << name << "'\n");
422  DEBUG_MAKE_INDENT(3);
423
424  GUTF8String chunk_name;
425  const int lastdot=name.rsearch('.');
426  if(lastdot < 0)
427  {
428    chunk_name=name;
429    name=name.substr(0,lastdot);
430  }else
431  {
432    chunk_name=name.substr(lastdot+1,(unsigned int)-1);
433  }
434
435  int pos=-1;
436  const int obracket=chunk_name.search('[');
437  if (obracket >= 0)
438  {
439    const int cbracket=chunk_name.search(']',obracket+1);
440    if (cbracket < 0)
441      G_THROW( ERR_MSG("GIFFManager.unmatched") );
442    if (name.length() > (unsigned int)(cbracket+1))
443      G_THROW( ERR_MSG("GIFFManager.garbage") );
444//    pos=atoi((const char *)chunk_name.substr(obracket+1,cbracket-obracket-1));
445    pos = chunk_name.substr(obracket+1,cbracket-obracket-1).toInt();
446    chunk_name=chunk_name.substr(0,obracket);
447  }
448  DEBUG_MSG("Creating new chunk with name " << chunk_name << "\n");
449  GP<GIFFChunk> chunk;
450  chunk=GIFFChunk::create(chunk_name, data);
451  add_chunk(name, chunk, pos);
452}
453
454void
455GIFFManager::del_chunk(void)
456{
457  DEBUG_MSG("GIFFManager::del_chunk(): Deleting chunk\n");
458  DEBUG_MAKE_INDENT(3);
459   
460  G_THROW( ERR_MSG("GIFFManager.del_empty") );
461}
462
463void
464GIFFManager::del_chunk(GUTF8String name)
465      // "name" should be fully qualified, that is contain dots.
466      // It may also end with [] to set the chunk order number
467{
468  DEBUG_MSG("GIFFManager::del_chunk(): Deleting chunk '" << name << "'\n");
469  DEBUG_MAKE_INDENT(3);
470   
471  if (!name.length())
472    G_THROW( ERR_MSG("GIFFManager.del_empty") );
473
474  if (name[0]=='.')
475  {
476    const int next_dot=name.search('.',1);
477    if (next_dot < 0)
478    {
479      if (top_level->check_name(name.substr(1,(unsigned int)-1)))
480      {
481        DEBUG_MSG("Removing top level chunk..\n");
482        top_level=GIFFChunk::create();
483        return;
484      }
485      G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+name.substr(1,(unsigned int)-1));
486    }
487    const GUTF8String top_name=name.substr(1,next_dot-1);
488    if (!top_level->check_name(top_name))
489      G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+top_name);
490    name=name.substr(next_dot+1,(unsigned int)-1);
491  }
492   
493  GP<GIFFChunk> cur_sec=top_level;
494  const char * start, * end=(const char *)name-1;
495  do
496  {
497    for(start=++end;*end&&(*end!='.');end++)
498      EMPTY_LOOP;
499    if (end>start && *end=='.')
500      cur_sec=cur_sec->get_chunk(GUTF8String(start, end-start));
501    if (!cur_sec)
502      G_THROW( ERR_MSG("GIFFManager.cant_find") "\t"+GUTF8String(name));
503  } while(*end);
504   
505  if (!start[0])
506  {
507    G_THROW(GUTF8String( ERR_MSG("GIFFManager.malformed") "\t")+name);
508  }
509   
510  cur_sec->del_chunk(start);
511}
512
513GP<GIFFChunk>
514GIFFManager::get_chunk(GUTF8String name, int * pos_num)
515      // "name" should be fully qualified, that is contain dots.
516      // It may also end with [] to set the chunk order number
517{
518  DEBUG_MSG("GIFFManager::get_chunk(): Returning chunk '" << name << "'\n");
519  DEBUG_MAKE_INDENT(3);
520   
521  if (!name.length())
522    G_THROW( ERR_MSG("GIFFManager.get_empty") );
523
524  if (name[0]=='.')
525  {
526    const int next_dot=name.search('.',1);
527    if (next_dot < 0)
528    {
529      if (top_level->check_name(name.substr(1,(unsigned int)-1)))
530      {
531        DEBUG_MSG("Removing top level chunk..\n");
532        return top_level;
533      }
534      G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+name.substr(1,(unsigned int)-1));
535    }
536    const GUTF8String top_name=name.substr(1,next_dot-1);
537    if (!top_level->check_name(top_name))
538      G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+top_name);
539    name=name.substr(next_dot+1,(unsigned int)-1);
540  }
541   
542  GP<GIFFChunk> cur_sec=top_level;
543  const char * start, * end=(const char *) name-1;
544  do
545  {
546    for(start=++end;*end&&(*end!='.');end++)
547      EMPTY_LOOP;
548    if (end>start)
549      cur_sec=cur_sec->get_chunk(GUTF8String(start, end-start), pos_num);
550    if (!cur_sec)
551      break;
552  } while(*end);
553   
554  return cur_sec;
555}
556
557int
558GIFFManager::get_chunks_number(void)
559{
560  DEBUG_MSG("GIFFManager::get_chunks_number()\n");
561  DEBUG_MAKE_INDENT(3);
562  return top_level->get_chunks_number();
563}
564
565int
566GIFFManager::get_chunks_number(const GUTF8String &name)
567   // Returns the number of chunks with given fully qualified name
568{
569  DEBUG_MSG("GIFFManager::get_chunks_number(): name='" << name << "'\n");
570  DEBUG_MAKE_INDENT(3);
571
572  int retval;
573  const int last_dot=name.rsearch('.');
574  if (last_dot<0)
575  {
576    retval=top_level->get_chunks_number(name);
577  }else if(!last_dot)
578  {
579    retval=(top_level->get_name()==name.substr(1,(unsigned int)-1))?1:0;
580  }else
581  {
582    GP<GIFFChunk> chunk=get_chunk(name.substr(0,last_dot));
583    retval=( chunk
584      ?(chunk->get_chunks_number(name.substr(last_dot+1,(unsigned int)-1)))
585      :0 );
586  }
587  return retval;
588}
589
590void
591GIFFManager::load_chunk(IFFByteStream & istr, GP<GIFFChunk> chunk)
592{
593  DEBUG_MSG("GIFFManager::load_chunk(): loading contents of chunk '" <<
594    chunk->get_name() << "'\n");
595  DEBUG_MAKE_INDENT(3);
596   
597  int chunk_size;
598  GUTF8String chunk_id;
599  while ((chunk_size=istr.get_chunk(chunk_id)))
600  {
601    if (istr.check_id(chunk_id))
602    {
603      GP<GIFFChunk> ch=GIFFChunk::create(chunk_id);
604      load_chunk(istr, ch);
605      chunk->add_chunk(ch);
606    } else
607    {
608      TArray<char> data(chunk_size-1);
609      istr.get_bytestream()->readall( (char*)data, data.size());
610      GP<GIFFChunk> ch=GIFFChunk::create(chunk_id, data);
611      chunk->add_chunk(ch);
612    }
613    istr.close_chunk();
614  }
615}
616
617void
618GIFFManager::load_file(const TArray<char> & data)
619{
620  GP<ByteStream> str=ByteStream::create((const char *)data, data.size());
621  load_file(str);
622}
623
624void
625GIFFManager::load_file(GP<ByteStream> str)
626{
627  DEBUG_MSG("GIFFManager::load_file(): Loading IFF file.\n");
628  DEBUG_MAKE_INDENT(3);
629   
630  GP<IFFByteStream> gistr=IFFByteStream::create(str);
631  IFFByteStream &istr=*gistr;
632  GUTF8String chunk_id;
633  if (istr.get_chunk(chunk_id))
634  {
635    if (chunk_id.substr(0,5) != "FORM:")
636      G_THROW( ERR_MSG("GIFFManager.cant_find2") );
637    set_name(chunk_id);
638    load_chunk(istr, top_level);
639    istr.close_chunk();
640  }
641}
642
643void
644GIFFManager::save_file(TArray<char> & data)
645{
646  GP<ByteStream> gstr=ByteStream::create();
647  save_file(gstr);
648  data=gstr->get_data();
649}
650
651void
652GIFFManager::save_file(GP<ByteStream> str)
653{
654  GP<IFFByteStream> istr=IFFByteStream::create(str);
655  top_level->save(*istr, 1);
656}
657
658
659#ifdef HAVE_NAMESPACES
660}
661# ifndef NOT_USING_DJVU_NAMESPACE
662using namespace DJVU;
663# endif
664#endif
665
Note: See TracBrowser for help on using the repository browser.