source: trunk/libdjvu/GIFFManager.cpp @ 136

Last change on this file since 136 was 17, checked in by Eugene Romanenko, 16 years ago

update makefiles, remove absolute paths, update djvulibre to version 3.5.17

File size: 18.9 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, Version 2. The license should have
9//C- accompanied the software or you may obtain a copy of the license
10//C- from the Free Software Foundation at http://www.fsf.org .
11//C-
12//C- This program is distributed in the hope that it will be useful,
13//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
14//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15//C- GNU General Public License for more details.
16//C-
17//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
18//C- distributed by Lizardtech Software.  On July 19th 2002, Lizardtech
19//C- Software authorized us to replace the original DjVu(r) Reference
20//C- Library notice by the following text (see doc/lizard2002.djvu):
21//C-
22//C-  ------------------------------------------------------------------
23//C- | DjVu (r) Reference Library (v. 3.5)
24//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
25//C- | The DjVu Reference Library is protected by U.S. Pat. No.
26//C- | 6,058,214 and patents pending.
27//C- |
28//C- | This software is subject to, and may be distributed under, the
29//C- | GNU General Public License, Version 2. The license should have
30//C- | accompanied the software or you may obtain a copy of the license
31//C- | from the Free Software Foundation at http://www.fsf.org .
32//C- |
33//C- | The computer code originally released by LizardTech under this
34//C- | license and unmodified by other parties is deemed "the LIZARDTECH
35//C- | ORIGINAL CODE."  Subject to any third party intellectual property
36//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
37//C- | non-exclusive license to make, use, sell, or otherwise dispose of
38//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
39//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
40//C- | General Public License.   This grant only confers the right to
41//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
42//C- | the extent such infringement is reasonably necessary to enable
43//C- | recipient to make, have made, practice, sell, or otherwise dispose
44//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
45//C- | any greater extent that may be necessary to utilize further
46//C- | modifications or combinations.
47//C- |
48//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
49//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
50//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
51//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
52//C- +------------------------------------------------------------------
53//
54// $Id: GIFFManager.cpp,v 1.8 2003/11/07 22:08:21 leonb Exp $
55// $Name:  $
56
57#ifdef HAVE_CONFIG_H
58# include "config.h"
59#endif
60#if NEED_GNUG_PRAGMAS
61# pragma implementation
62#endif
63
64#include "GIFFManager.h"
65#include "GException.h"
66#include "debug.h"
67
68
69#ifdef HAVE_NAMESPACES
70namespace DJVU {
71# ifdef NOT_DEFINED // Just to fool emacs c++ mode
72}
73#endif
74#endif
75
76
77GIFFChunk::~GIFFChunk(void) {}
78
79GIFFManager::~GIFFManager(void) {}
80
81GP<GIFFManager> 
82GIFFManager::create(void)
83{
84  GIFFManager *iff=new GIFFManager();
85  GP<GIFFManager> retval=iff;
86  iff->init();
87  return retval;
88}
89
90GP<GIFFManager> 
91GIFFManager::create(const GUTF8String &name)
92{
93  GIFFManager *iff=new GIFFManager();
94  GP<GIFFManager> retval=iff;
95  iff->init(name);
96  return retval;
97}
98
99void
100GIFFChunk::set_name(GUTF8String name)
101{
102  DEBUG_MSG("GIFFChunk::set_name(): name='" << name << "'\n");
103  DEBUG_MAKE_INDENT(3);
104
105  const int colon=name.search(':');
106  if(colon>=0)
107  {
108    type=name.substr(0,colon);
109    name=name.substr(colon+1,(unsigned int)-1);
110    if(name.search(':')>=0)
111      G_THROW( ERR_MSG("GIFFManager.one_colon") );
112  }
113
114  DEBUG_MSG("auto-setting type to '" << type << "'\n");
115
116  if (name.contains(".[]")>=0)
117    G_THROW( ERR_MSG("GIFFManager.bad_char") );
118   
119  strncpy(GIFFChunk::name, (const char *)name, 4);
120  GIFFChunk::name[4]=0;
121  for(int i=strlen(GIFFChunk::name);i<4;i++)
122    GIFFChunk::name[i]=' ';
123}
124
125bool
126GIFFChunk::check_name(GUTF8String name)
127{
128  GUTF8String type;
129  const int colon=name.search(':');
130  if(colon>=0)
131  {
132    type=name.substr(0,colon);
133    name=name.substr(colon+1,(unsigned int)-1);
134  }
135 
136  const GUTF8String sname=(name.substr(0,4)+"    ").substr(0,4);
137
138  DEBUG_MSG("GIFFChunk::check_name(): type='" << type << "' name='" << sname << "'\n");
139  return (type==GIFFChunk::type || !type.length() && GIFFChunk::type=="FORM")
140       && sname==GIFFChunk::name;
141}
142
143void
144GIFFChunk::save(IFFByteStream & istr, bool use_trick)
145{
146  DEBUG_MSG("GIFFChunk::save(): saving chunk '" << get_full_name() << "'\n");
147  DEBUG_MAKE_INDENT(3);
148
149  if (is_container())
150  {
151    istr.put_chunk(get_full_name(), use_trick);
152    if (chunks.size())
153    {
154      GPosition pos;
155      for(pos=chunks;pos;++pos)
156        if (chunks[pos]->get_type()=="PROP")
157          chunks[pos]->save(istr);
158      for(pos=chunks;pos;++pos)
159        if (chunks[pos]->get_type()!="PROP")
160          chunks[pos]->save(istr);
161    } else
162    {
163      DEBUG_MSG("but it's empty => saving empty container.\n");
164    }
165    istr.close_chunk();
166  } else
167  {
168    istr.put_chunk(get_name(), use_trick);
169    istr.get_bytestream()->writall((const char *) data, data.size());
170    istr.close_chunk();
171  }
172}
173
174void
175GIFFChunk::add_chunk(const GP<GIFFChunk> & chunk, int position)
176{
177  DEBUG_MSG("GIFFChunk::add_chunk(): Adding chunk to '" << get_name() <<
178     "' @ position=" << position << "\n");
179  DEBUG_MAKE_INDENT(3);
180
181  if (!type.length())
182  {
183    DEBUG_MSG("Converting the parent to FORM\n");
184    type="FORM";
185  }
186
187  if (chunk->get_type()=="PROP")
188  {
189    DEBUG_MSG("Converting the parent to LIST\n");
190    type="LIST";
191  }
192
193  GPosition pos;
194  if (position>=0 && chunks.nth(position, pos))
195  {
196    chunks.insert_before(pos, chunk);
197  }else
198  {
199    chunks.append(chunk);
200  }
201}
202
203GUTF8String
204GIFFChunk::decode_name(const GUTF8String &name, int &number)
205{
206  DEBUG_MSG("GIFFChunk::decode_name(): Checking brackets in name '" << name << "'\n");
207  DEBUG_MAKE_INDENT(3);
208   
209  if (name.search('.')>=0)
210    G_THROW( ERR_MSG("GIFFManager.no_dots") );
211
212  number=0;
213  const int obracket=name.search('[');
214  GUTF8String short_name;
215  if (obracket >= 0)
216  {
217    const int cbracket=name.search(']',obracket+1);
218    if (cbracket < 0)
219      G_THROW( ERR_MSG("GIFFManager.unmatched") );
220    if (name.length() > (unsigned int)(cbracket+1))
221      G_THROW( ERR_MSG("GIFFManager.garbage") );
222//    number =atoi((const char *)name.substr(obracket+1,cbracket-obracket-1));
223    number= name.substr(obracket+1,cbracket-obracket-1).toInt(); 
224    short_name=name.substr(0,obracket);
225  }else
226  {
227    short_name=name;
228  }
229
230  const int colon=short_name.search(':');
231  if (colon>=0)
232    short_name=short_name.substr(colon+1,(unsigned int)-1);
233
234  for(int i=short_name.length();i<4;i++)
235    short_name.setat(i, ' ');
236   
237  DEBUG_MSG("short_name='" << short_name << "'\n");
238  DEBUG_MSG("number=" << number << "\n");
239   
240  return short_name;
241}
242
243void
244GIFFChunk::del_chunk(const GUTF8String &name)
245   // The name may contain brackets to specify the chunk number
246{
247  DEBUG_MSG("GIFFChunk::del_chunk(): Deleting chunk '" << name <<
248     "' from '" << get_name() << "'\n");
249  DEBUG_MAKE_INDENT(3);
250
251  int number;
252  const GUTF8String short_name=decode_name(name,number);
253
254  GPosition pos=chunks;
255  for(int num=0;pos;++pos)
256  {
257    if ((chunks[pos]->get_name()==short_name)&&(num++ == number))
258    {
259      chunks.del(pos);
260      break;
261    }
262  }
263  if(! pos)
264  {
265    G_THROW( ERR_MSG("GIFFManager.no_chunk") "\t"+short_name+"\t"+GUTF8String(number)+"\t"+get_name());
266  }
267}
268
269GP<GIFFChunk>
270GIFFChunk::get_chunk(const GUTF8String &name, int * pos_ptr)
271   // The name may contain brackets to specify the chunk number
272{
273  DEBUG_MSG("GIFFChunk::get_chunk(): Returning chunk '" << name <<
274     "' from '" << get_name() << "'\n");
275  DEBUG_MAKE_INDENT(3);
276
277  int number;
278  const GUTF8String short_name=decode_name(name,number);
279
280  int num=0;
281  int pos_num;
282  GP<GIFFChunk> retval;
283  GPosition pos;
284  for(pos=chunks, pos_num=0;pos;++pos, pos_num++)
285  {
286    if (chunks[pos]->get_name()==short_name && num++==number)
287    {
288      if (pos_ptr)
289        *pos_ptr=pos_num;
290      retval=chunks[pos];
291      break;
292    }
293  }
294  return retval;
295}
296
297int
298GIFFChunk::get_chunks_number(void)
299{
300  DEBUG_MSG("GIFFChunk::get_chunks_number(): Returning number of chunks '" << name <<
301     "' in '" << get_name() << "'\n");
302  DEBUG_MAKE_INDENT(3);
303  return chunks.size();
304}
305
306int
307GIFFChunk::get_chunks_number(const GUTF8String &name)
308{
309  DEBUG_MSG("GIFFChunk::get_chunks_number(): Returning number of chunks '" << name <<
310     "' in '" << get_name() << "'\n");
311  DEBUG_MAKE_INDENT(3);
312
313  if (name.contains("[]")>=0)
314    G_THROW( ERR_MSG("GIFFManager.no_brackets") );
315 
316  int number; 
317  GUTF8String short_name=decode_name(name,number);
318   
319  int num=0;
320  for(GPosition pos=chunks;pos;++pos)
321     num+=(chunks[pos]->get_name()==short_name);
322  return num;
323}
324
325//************************************************************************
326
327void
328GIFFManager::add_chunk(GUTF8String parent_name, const GP<GIFFChunk> & chunk,
329                       int pos)
330      // parent_name is the fully qualified name of the PARENT
331      //             IT MAY BE EMPTY
332      // All the required chunks will be created
333      // pos=-1 means to append the chunk
334{
335  DEBUG_MSG("GIFFManager::add_chunk(): Adding chunk to name='" << parent_name << "'\n");
336  DEBUG_MAKE_INDENT(3);
337   
338  if (!top_level->get_name().length())
339  {
340    if ((!parent_name.length())||(parent_name[0]!='.'))
341      G_THROW( ERR_MSG("GIFFManager.no_top_name") );
342    if (parent_name.length() < 2)
343    {
344      // 'chunk' is actually the new top-level chunk
345      DEBUG_MSG("since parent_name=='.', making the chunk top-level\n");
346      if (!chunk->is_container())
347        G_THROW( ERR_MSG("GIFFManager.no_top_cont") );
348      top_level=chunk;
349      return;
350    }
351
352    DEBUG_MSG("Setting the name of the top-level chunk\n");
353    const int next_dot=parent_name.search('.',1);
354    if(next_dot>=0)
355    {
356      top_level->set_name(parent_name.substr(1,next_dot-1));
357    }else
358    {
359      top_level->set_name(parent_name.substr(1,(unsigned int)-1));
360    }
361  }
362
363  DEBUG_MSG("top level chunk name='" << top_level->get_name() << "'\n");
364   
365  if (parent_name.length() && parent_name[0] == '.')
366  {
367    int next_dot=parent_name.search('.',1);
368    if(next_dot<0)
369    {
370      next_dot=parent_name.length();
371    }
372    GUTF8String top_name=parent_name.substr(1,next_dot-1);
373    if (!top_level->check_name(top_name))
374      G_THROW( ERR_MSG("GIFFManager.wrong_name") "\t"+top_name);
375    parent_name=parent_name.substr(next_dot,(unsigned int)-1);
376  }
377
378  GP<GIFFChunk> cur_sec=top_level;
379  const char * start, * end=(const char *)parent_name-1;
380  do
381  {
382    for(start=++end;*end&&(*end!='.');end++)
383      EMPTY_LOOP;
384    if (end>start)
385    {
386      GUTF8String name(start,end-start);
387      GUTF8String short_name;
388      int number=0;
389      const int obracket=name.search('[');
390      if (obracket >= 0)
391      {
392        const int cbracket=name.search(']',obracket+1);
393        if (cbracket < 0)
394          G_THROW( ERR_MSG("GIFFManager.unmatched") );
395//        number=atoi((const char *)name.substr(obracket+1,cbracket-obracket-1));
396        number = name.substr(obracket+1,cbracket-obracket-1).toInt();
397        short_name=name.substr(0,obracket);
398      }else
399      {
400        short_name=name;
401      }
402
403      for(int i=cur_sec->get_chunks_number(short_name);i<number+1;i++)
404        cur_sec->add_chunk(GIFFChunk::create(short_name));
405      cur_sec=cur_sec->get_chunk(name);
406      if (!cur_sec)
407        G_THROW( ERR_MSG("GIFFManager.unknown") "\t"+name);
408    }
409  } while(*end);
410  cur_sec->add_chunk(chunk, pos);
411}
412
413void
414GIFFManager::add_chunk(GUTF8String name, const TArray<char> & data)
415      // name is fully qualified name of the chunk TO BE INSERTED.
416      //      it may contain brackets at the end to set the position
417      // All the required chunks will be created
418{
419  DEBUG_MSG("GIFFManager::add_chunk(): adding plain chunk with name='" << name << "'\n");
420  DEBUG_MAKE_INDENT(3);
421
422  GUTF8String chunk_name;
423  const int lastdot=name.rsearch('.');
424  if(lastdot < 0)
425  {
426    chunk_name=name;
427    name=name.substr(0,lastdot);
428  }else
429  {
430    chunk_name=name.substr(lastdot+1,(unsigned int)-1);
431  }
432
433  int pos=-1;
434  const int obracket=chunk_name.search('[');
435  if (obracket >= 0)
436  {
437    const int cbracket=chunk_name.search(']',obracket+1);
438    if (cbracket < 0)
439      G_THROW( ERR_MSG("GIFFManager.unmatched") );
440    if (name.length() > (unsigned int)(cbracket+1))
441      G_THROW( ERR_MSG("GIFFManager.garbage") );
442//    pos=atoi((const char *)chunk_name.substr(obracket+1,cbracket-obracket-1));
443    pos = chunk_name.substr(obracket+1,cbracket-obracket-1).toInt();
444    chunk_name=chunk_name.substr(0,obracket);
445  }
446  DEBUG_MSG("Creating new chunk with name " << chunk_name << "\n");
447  GP<GIFFChunk> chunk;
448  chunk=GIFFChunk::create(chunk_name, data);
449  add_chunk(name, chunk, pos);
450}
451
452void
453GIFFManager::del_chunk(void)
454{
455  DEBUG_MSG("GIFFManager::del_chunk(): Deleting chunk\n");
456  DEBUG_MAKE_INDENT(3);
457   
458  G_THROW( ERR_MSG("GIFFManager.del_empty") );
459}
460
461void
462GIFFManager::del_chunk(GUTF8String name)
463      // "name" should be fully qualified, that is contain dots.
464      // It may also end with [] to set the chunk order number
465{
466  DEBUG_MSG("GIFFManager::del_chunk(): Deleting chunk '" << name << "'\n");
467  DEBUG_MAKE_INDENT(3);
468   
469  if (!name.length())
470    G_THROW( ERR_MSG("GIFFManager.del_empty") );
471
472  if (name[0]=='.')
473  {
474    const int next_dot=name.search('.',1);
475    if (next_dot < 0)
476    {
477      if (top_level->check_name(name.substr(1,(unsigned int)-1)))
478      {
479        DEBUG_MSG("Removing top level chunk..\n");
480        top_level=GIFFChunk::create();
481        return;
482      }
483      G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+name.substr(1,(unsigned int)-1));
484    }
485    const GUTF8String top_name=name.substr(1,next_dot-1);
486    if (!top_level->check_name(top_name))
487      G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+top_name);
488    name=name.substr(next_dot+1,(unsigned int)-1);
489  }
490   
491  GP<GIFFChunk> cur_sec=top_level;
492  const char * start, * end=(const char *)name-1;
493  do
494  {
495    for(start=++end;*end&&(*end!='.');end++)
496      EMPTY_LOOP;
497    if (end>start && *end=='.')
498      cur_sec=cur_sec->get_chunk(GUTF8String(start, end-start));
499    if (!cur_sec)
500      G_THROW( ERR_MSG("GIFFManager.cant_find") "\t"+GUTF8String(name));
501  } while(*end);
502   
503  if (!start[0])
504  {
505    G_THROW(GUTF8String( ERR_MSG("GIFFManager.malformed") "\t")+name);
506  }
507   
508  cur_sec->del_chunk(start);
509}
510
511GP<GIFFChunk>
512GIFFManager::get_chunk(GUTF8String name, int * pos_num)
513      // "name" should be fully qualified, that is contain dots.
514      // It may also end with [] to set the chunk order number
515{
516  DEBUG_MSG("GIFFManager::get_chunk(): Returning chunk '" << name << "'\n");
517  DEBUG_MAKE_INDENT(3);
518   
519  if (!name.length())
520    G_THROW( ERR_MSG("GIFFManager.get_empty") );
521
522  if (name[0]=='.')
523  {
524    const int next_dot=name.search('.',1);
525    if (next_dot < 0)
526    {
527      if (top_level->check_name(name.substr(1,(unsigned int)-1)))
528      {
529        DEBUG_MSG("Removing top level chunk..\n");
530        return top_level;
531      }
532      G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+name.substr(1,(unsigned int)-1));
533    }
534    const GUTF8String top_name=name.substr(1,next_dot-1);
535    if (!top_level->check_name(top_name))
536      G_THROW( ERR_MSG("GIFFManager.wrong_name2") "\t"+top_name);
537    name=name.substr(next_dot+1,(unsigned int)-1);
538  }
539   
540  GP<GIFFChunk> cur_sec=top_level;
541  const char * start, * end=(const char *) name-1;
542  do
543  {
544    for(start=++end;*end&&(*end!='.');end++)
545      EMPTY_LOOP;
546    if (end>start)
547      cur_sec=cur_sec->get_chunk(GUTF8String(start, end-start), pos_num);
548    if (!cur_sec)
549      break;
550  } while(*end);
551   
552  return cur_sec;
553}
554
555int
556GIFFManager::get_chunks_number(void)
557{
558  DEBUG_MSG("GIFFManager::get_chunks_number()\n");
559  DEBUG_MAKE_INDENT(3);
560  return top_level->get_chunks_number();
561}
562
563int
564GIFFManager::get_chunks_number(const GUTF8String &name)
565   // Returns the number of chunks with given fully qualified name
566{
567  DEBUG_MSG("GIFFManager::get_chunks_number(): name='" << name << "'\n");
568  DEBUG_MAKE_INDENT(3);
569
570  int retval;
571  const int last_dot=name.rsearch('.');
572  if (last_dot<0)
573  {
574    retval=top_level->get_chunks_number(name);
575  }else if(!last_dot)
576  {
577    retval=(top_level->get_name()==name.substr(1,(unsigned int)-1))?1:0;
578  }else
579  {
580    GP<GIFFChunk> chunk=get_chunk(name.substr(0,last_dot));
581    retval=( chunk
582      ?(chunk->get_chunks_number(name.substr(last_dot+1,(unsigned int)-1)))
583      :0 );
584  }
585  return retval;
586}
587
588void
589GIFFManager::load_chunk(IFFByteStream & istr, GP<GIFFChunk> chunk)
590{
591  DEBUG_MSG("GIFFManager::load_chunk(): loading contents of chunk '" <<
592    chunk->get_name() << "'\n");
593  DEBUG_MAKE_INDENT(3);
594   
595  int chunk_size;
596  GUTF8String chunk_id;
597  while ((chunk_size=istr.get_chunk(chunk_id)))
598  {
599    if (istr.check_id(chunk_id))
600    {
601      GP<GIFFChunk> ch=GIFFChunk::create(chunk_id);
602      load_chunk(istr, ch);
603      chunk->add_chunk(ch);
604    } else
605    {
606      TArray<char> data(chunk_size-1);
607      istr.get_bytestream()->readall( (char*)data, data.size());
608      GP<GIFFChunk> ch=GIFFChunk::create(chunk_id, data);
609      chunk->add_chunk(ch);
610    }
611    istr.close_chunk();
612  }
613}
614
615void
616GIFFManager::load_file(const TArray<char> & data)
617{
618  GP<ByteStream> str=ByteStream::create((const char *)data, data.size());
619  load_file(str);
620}
621
622void
623GIFFManager::load_file(GP<ByteStream> str)
624{
625  DEBUG_MSG("GIFFManager::load_file(): Loading IFF file.\n");
626  DEBUG_MAKE_INDENT(3);
627   
628  GP<IFFByteStream> gistr=IFFByteStream::create(str);
629  IFFByteStream &istr=*gistr;
630  GUTF8String chunk_id;
631  if (istr.get_chunk(chunk_id))
632  {
633    if (chunk_id.substr(0,5) != "FORM:")
634      G_THROW( ERR_MSG("GIFFManager.cant_find2") );
635    set_name(chunk_id);
636    load_chunk(istr, top_level);
637    istr.close_chunk();
638  }
639}
640
641void
642GIFFManager::save_file(TArray<char> & data)
643{
644  GP<ByteStream> gstr=ByteStream::create();
645  save_file(gstr);
646  data=gstr->get_data();
647}
648
649void
650GIFFManager::save_file(GP<ByteStream> str)
651{
652  GP<IFFByteStream> istr=IFFByteStream::create(str);
653  top_level->save(*istr, 1);
654}
655
656
657#ifdef HAVE_NAMESPACES
658}
659# ifndef NOT_USING_DJVU_NAMESPACE
660using namespace DJVU;
661# endif
662#endif
663
Note: See TracBrowser for help on using the repository browser.