source: trunk/libdjvu/DjVuImage.cpp @ 81

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

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

File size: 40.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, 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: DjVuImage.cpp,v 1.13 2006/01/19 15:20:41 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 "DjVuImage.h"
65#include "GScaler.h"
66#include "DjVuDocument.h"
67#include "DjVuPalette.h"
68#include "GContainer.h"
69#include "GSmartPointer.h"
70#include "JB2Image.h"
71#include "IW44Image.h"
72#include "DataPool.h"
73#include "ByteStream.h"
74#include "GMapAreas.h"
75#include "DjVuText.h"
76#include "IFFByteStream.h"
77#include "BSByteStream.h"
78#include "debug.h"
79#include <stdarg.h>
80
81
82#ifdef HAVE_NAMESPACES
83namespace DJVU {
84# ifdef NOT_DEFINED // Just to fool emacs c++ mode
85}
86#endif
87#endif
88
89
90
91//// DJVUIMAGE: CONSTRUCTION
92
93DjVuImage::DjVuImage(void) 
94: rotate_count(-1),relayout_sent(false)
95{
96}
97
98void
99DjVuImage::connect(const GP<DjVuFile> & xfile)
100{
101   file=xfile;
102   DjVuPort::get_portcaster()->add_route(file, this);
103}
104
105
106
107
108//// DJVUIMAGE: DATA COLLECTORS
109
110GP<DjVuInfo>
111DjVuImage::get_info(const GP<DjVuFile> & file) const
112{
113   if (file->info)
114   {
115     if(rotate_count<0)
116     {
117       const_cast<DjVuImage *>(this)->init_rotate(*(file->info));
118     }
119     return file->info;
120   }
121   GPList<DjVuFile> list=file->get_included_files();
122   for(GPosition pos=list;pos;++pos)
123   {
124      GP<DjVuInfo> info=get_info(list[pos]);
125      if (info) 
126      {
127        if(rotate_count<0)
128        {
129          const_cast<DjVuImage *>(this)->init_rotate(*(file->info));
130        }
131        return info;
132      }
133   }
134   return 0;
135}
136
137GP<IW44Image>
138DjVuImage::get_bg44(const GP<DjVuFile> & file) const
139{
140   if (file->bg44)
141     return file->bg44;
142   GPList<DjVuFile> list=file->get_included_files();
143   for(GPosition pos=list;pos;++pos)
144   {
145      GP<IW44Image> bg44=get_bg44(list[pos]);
146      if (bg44)
147        return bg44;
148   }
149   return 0;
150}
151
152GP<GPixmap>
153DjVuImage::get_bgpm(const GP<DjVuFile> & file) const
154{
155   if (file->bgpm)
156     return file->bgpm;
157   GPList<DjVuFile> list=file->get_included_files();
158   for(GPosition pos=list;pos;++pos)
159   {
160      GP<GPixmap> bgpm=get_bgpm(list[pos]);
161      if (bgpm) return bgpm;
162   }
163   return 0;
164}
165
166GP<JB2Image>
167DjVuImage::get_fgjb(const GP<DjVuFile> & file) const
168{
169   if (file->fgjb)
170     return file->fgjb;
171   GPList<DjVuFile> list=file->get_included_files();
172   for(GPosition pos=list;pos;++pos)
173   {
174      GP<JB2Image> fgjb=get_fgjb(list[pos]);
175      if (fgjb)
176        return fgjb;
177   }
178   return 0;
179}
180
181GP<GPixmap>
182DjVuImage::get_fgpm(const GP<DjVuFile> & file) const
183{
184   if (file->fgpm)
185     return file->fgpm;
186   GPList<DjVuFile> list=file->get_included_files();
187   for(GPosition pos=list;pos;++pos)
188   {
189      GP<GPixmap> fgpm=get_fgpm(list[pos]);
190      if (fgpm)
191        return fgpm;
192   }
193   return 0;
194}
195
196GP<DjVuPalette>
197DjVuImage::get_fgbc(const GP<DjVuFile> & file) const
198{
199   if (file->fgbc)
200     return file->fgbc;
201   GPList<DjVuFile> list=file->get_included_files();
202   for(GPosition pos=list;pos;++pos)
203   {
204      GP<DjVuPalette> fgbc=get_fgbc(list[pos]);
205      if (fgbc) return fgbc;
206   }
207   return 0;
208}
209
210GP<DjVuInfo>   
211DjVuImage::get_info() const
212{
213   if (file)
214   {
215     return get_info(file);
216   }else
217   {
218     return 0;
219   }
220}
221
222GP<ByteStream>
223DjVuImage::get_anno() const
224{
225   GP<ByteStream> out = ByteStream::create();
226   ByteStream &mbs = *out;
227   if (file) 
228   {
229     file->merge_anno(mbs);
230   }
231   mbs.seek(0);
232   if(!mbs.size())
233   {
234     out=0;
235   }
236   return out;
237}
238
239GP<ByteStream>
240DjVuImage::get_text() const
241{
242   GP<ByteStream> out = ByteStream::create();
243   ByteStream &mbs = *out;
244   if (file) 
245   {
246     file->get_text(mbs);
247   }
248   mbs.seek(0);
249   if(!mbs.size())
250   {
251     out=0;
252   }
253   return out;
254}
255
256GP<ByteStream>
257DjVuImage::get_meta() const
258{
259   GP<ByteStream> out = ByteStream::create();
260   ByteStream &mbs = *out;
261   if (file) 
262   {
263     file->get_meta(mbs);
264   }
265   mbs.seek(0);
266   if(!mbs.size())
267   {
268     out=0;
269   }
270   return out;
271}
272
273GP<IW44Image>   
274DjVuImage::get_bg44() const
275{
276   if (file)
277     return get_bg44(file);
278   else
279     return 0;
280}
281
282GP<GPixmap>   
283DjVuImage::get_bgpm() const
284{
285   if (file)
286     return get_bgpm(file);
287   else
288     return 0;
289}
290
291GP<JB2Image>   
292DjVuImage::get_fgjb() const
293{
294   if (file)
295     return get_fgjb(file);
296   else
297     return 0;
298}
299
300GP<GPixmap>   
301DjVuImage::get_fgpm() const
302{
303   if (file)
304     return get_fgpm(file);
305   else
306     return 0;
307}
308
309GP<DjVuPalette>   
310DjVuImage::get_fgbc() const
311{
312   if (file)
313     return get_fgbc(file);
314   else
315     return 0;
316}
317
318int
319DjVuImage::get_width() const
320{
321   GP<DjVuInfo> info=get_info();
322   return info?((rotate_count&1)?(info->height):(info->width)):0;
323}
324
325int
326DjVuImage::get_height() const
327{
328   GP<DjVuInfo> info=get_info();
329   return info?((rotate_count&1)?(info->width):(info->height)):0;
330}
331
332int
333DjVuImage::get_real_width() const
334{
335   GP<DjVuInfo> info=get_info();
336   return info ? info->width : 0;
337}
338
339int
340DjVuImage::get_real_height() const
341{
342   GP<DjVuInfo> info=get_info();
343   return info ? info->height : 0;
344}
345
346int
347DjVuImage::get_version() const
348{
349   GP<DjVuInfo> info=get_info();
350   return info ? info->version : DJVUVERSION;
351}
352
353int
354DjVuImage::get_dpi() const
355{
356   GP<DjVuInfo> info=get_info();
357   return info ? info->dpi : 300;
358}
359
360int
361DjVuImage::get_rounded_dpi() const
362{
363   return (get_dpi()+5)/10*10;
364#if 0   
365      /* This code used to round the reported dpi to 25, 50, 75, 100, 150,
366         300, and 600. Now we just round the dpi to 10ths and return it */
367   int dpi=get_dpi();
368   if (dpi>700) return dpi;
369 
370   const int std_dpi[]={ 25, 50, 75, 100, 150, 300, 600 };
371   const int std_dpis=sizeof(std_dpi)/sizeof(std_dpi[0]);
372   int min_dist=abs(dpi-std_dpi[0]);
373   int min_idx=0;
374   for(int i=1;i<std_dpis;i++)
375      if (abs(std_dpi[i]-dpi)<min_dist)
376      {
377         min_dist=abs(std_dpi[i]-dpi);
378         min_idx=i;
379      };
380   return std_dpi[min_idx];
381#endif
382}
383
384double
385DjVuImage::get_gamma() const
386{
387   GP<DjVuInfo> info=get_info();
388   return info ? info->gamma : 2.2;
389}
390
391GUTF8String
392DjVuImage::get_mimetype() const
393{
394   return file ? file->mimetype : GUTF8String();
395}
396
397
398//// DJVUIMAGE: UTILITIES
399
400GUTF8String
401DjVuImage::get_short_description() const
402{
403  GUTF8String msg = "Empty";
404  int width = get_width();
405  int height = get_height();
406  if (width && height)
407    if (file && file->file_size>100)
408      //msg.format("%dx%d in %0.1f Kb", width, height, file->file_size/1024.0);
409      msg.format( ERR_MSG("DjVuImage.short1") "\t%d\t%d\t%0.1f", width, height, file->file_size/1024.0 );
410    else
411      //msg.format("%dx%d", width, height);
412      msg.format( ERR_MSG("DjVuImage.short2") "\t%d\t%d", width, height );
413  return msg;
414}
415
416GUTF8String
417DjVuImage::get_long_description() const
418{
419  return file?(file->description):GUTF8String();
420}
421
422
423void
424DjVuImage::notify_chunk_done(const DjVuPort *, const GUTF8String & name)
425{
426   if (!relayout_sent &&
427     ( !name.cmp("INFO", 4) ||
428       !name.cmp("PMxx", 2) ||
429       !name.cmp("BMxx", 2)  ) )
430   {
431      DjVuPort::get_portcaster()->notify_relayout(this);
432      relayout_sent=true;
433   } 
434   else if (!name.cmp("Sxxx", 1) ||
435            !name.cmp("BGxx", 2) ||
436            !name.cmp("FGxx", 2) ||
437            !name.cmp("BMxx", 2) ||
438            !name.cmp("PMxx", 2)  )
439     DjVuPort::get_portcaster()->notify_redisplay(this);
440}
441
442
443
444
445
446
447//// DJVUIMAGE: OLD-STYLE DECODING
448
449DjVuInterface::~DjVuInterface() 
450{
451}
452
453class DjVuImageNotifier : public DjVuPort
454{
455  friend class DjVuImage;
456  DjVuInterface  *notifier;
457  GP<DataPool>    stream_pool;
458  GURL            stream_url;
459public:
460  DjVuImageNotifier(DjVuInterface *notifier);
461  GP<DataPool> request_data(const DjVuPort *src, const GURL & url);
462  void notify_chunk_done(const DjVuPort *, const GUTF8String &name);
463  void notify_redisplay(const class DjVuImage * source);
464  void notify_relayout(const class DjVuImage * source);
465};
466
467DjVuImageNotifier::DjVuImageNotifier(DjVuInterface *notifier)
468  : notifier(notifier)
469{
470}
471
472GP<DataPool> 
473DjVuImageNotifier::request_data(const DjVuPort *src, const GURL & url)
474{
475  if (url!=stream_url)
476    G_THROW( ERR_MSG("DjVuImage.not_decode") );
477  return stream_pool;
478}
479
480void 
481DjVuImageNotifier::notify_redisplay(const class DjVuImage * source)
482{
483  if (notifier)
484    notifier->notify_redisplay();
485}
486
487void 
488DjVuImageNotifier::notify_relayout(const class DjVuImage * source)
489{
490  if (notifier)
491    notifier->notify_relayout();
492}
493
494void 
495DjVuImageNotifier::notify_chunk_done(const DjVuPort *, const GUTF8String &name)
496{
497  if (notifier)
498    notifier->notify_chunk(name, "" );
499}
500
501void
502DjVuImage::decode(ByteStream & str, DjVuInterface *notifier)
503{
504  DEBUG_MSG("DjVuImage::decode(): decoding old way...\n");
505  DEBUG_MAKE_INDENT(3);
506  if (file) 
507    G_THROW( ERR_MSG("DjVuImage.bad_call") );
508  GP<DjVuImageNotifier> pport = new DjVuImageNotifier(notifier);
509  pport->stream_url=GURL::UTF8("internal://fake/fake.djvu");
510  pport->stream_pool=DataPool::create();
511  // Get all the data first
512  int length;
513  char buffer[1024];
514  while((length=str.read(buffer, 1024)))
515    pport->stream_pool->add_data(buffer, length);
516  pport->stream_pool->set_eof();
517  GP<DjVuDocument> doc = DjVuDocument::create_wait(pport->stream_url, (DjVuImageNotifier*)pport);
518  GP<DjVuImage> dimg=doc->get_page(-1, true, (DjVuImageNotifier*)pport);
519  file=dimg->get_djvu_file();
520  if (file->is_decode_stopped())
521    G_THROW( DataPool::Stop );
522  if (file->is_decode_failed())
523    G_THROW( ByteStream::EndOfFile ); // guess
524  if (!file->is_decode_ok())
525    G_THROW( ERR_MSG("DjVuImage.mult_error") );
526  DEBUG_MSG("decode DONE\n");
527}
528
529
530//// DJVUIMAGE: CHECKING
531
532static int
533compute_red(int w, int h, int rw, int rh)
534{
535  for (int red=1; red<16; red++)
536    if (((w+red-1)/red==rw) && ((h+red-1)/red==rh))
537      return red;
538  return 16;
539}
540
541
542int 
543DjVuImage::is_legal_bilevel() const
544{
545  // Components
546  GP<DjVuInfo> info = get_info();
547  GP<JB2Image> fgjb = get_fgjb();
548  GP<IW44Image> bg44 = get_bg44();
549  GP<GPixmap>  bgpm = get_bgpm();
550  GP<GPixmap>  fgpm = get_fgpm();
551  // Check info
552  if (! info)
553    return 0;
554  int width = info->width;
555  int height = info->height;
556  if (! (width>0 && height>0))
557    return 0;
558  // Check fgjb
559  if (!fgjb)
560    return 0;
561  if (fgjb->get_width()!=width || fgjb->get_height()!=height)
562    return 0;
563  // Check that color information is not present.
564  if (bg44 || bgpm || fgpm)
565    return 0;
566  // Ok.
567  return 1;
568}
569
570int 
571DjVuImage::is_legal_photo() const
572{
573  // Components
574  GP<DjVuInfo> info = get_info();
575  GP<JB2Image> fgjb = get_fgjb(); 
576  GP<IW44Image> bg44 = get_bg44();
577  GP<GPixmap>  bgpm = get_bgpm();
578  GP<GPixmap>  fgpm = get_fgpm();
579  // Check info
580  if (! info)
581    return 0;
582  int width = info->width;
583  int height = info->height;
584  if (! (width>0 && height>0))
585    return 0;
586  // Check that extra information is not present.
587  if (fgjb || fgpm)
588    return 0;
589  // Check bg44
590  if (bg44 && bg44->get_width()==width && bg44->get_height()==height)
591    return 1;
592  // Check bgpm
593  if (bgpm && (int)bgpm->columns()==width && (int)bgpm->rows()==height)
594    return 1;
595  // Ok.
596  return 0;
597}
598
599int 
600DjVuImage::is_legal_compound() const
601{
602  // Components
603  GP<DjVuInfo>     info = get_info();
604  GP<JB2Image>     fgjb = get_fgjb();
605  GP<IW44Image>     bg44 = get_bg44();
606  GP<GPixmap>      bgpm = get_bgpm();
607  GP<GPixmap>      fgpm = get_fgpm();
608  GP<DjVuPalette>  fgbc = get_fgbc();
609  // Check size
610  if (! info)
611    return 0;
612  int width = info->width;
613  int height = info->height;
614  if (! (width>0 && height>0))
615    return 0;
616  // Check fgjb
617  if (!fgjb)
618    return 0;
619  if (fgjb->get_width()!=width || fgjb->get_height()!=height)
620    return 0;
621  // Check background
622  int bgred = 0;
623  if (bg44)
624    bgred = compute_red(width, height, bg44->get_width(), bg44->get_height());
625  else if (bgpm)
626    bgred = compute_red(width, height, bgpm->columns(), bgpm->rows());
627  if (bgred<1 || bgred>12)
628    return 0;
629  // Check foreground colors
630  int fgred = 0;
631  if (fgbc)
632    fgred = 1;
633  else if (fgpm)
634    fgred = compute_red(width, height, fgpm->columns(), fgpm->rows());
635  if (fgred<1 || fgred>12)
636    return 0;
637  // Check that all components are present
638  if (fgjb && bgred && fgred)
639    return 1;
640  // Unrecognized
641  return 0;
642}
643
644
645//// DJVUIMAGE: LOW LEVEL RENDERING
646
647GP<GBitmap>
648DjVuImage::get_bitmap(const GRect &rect, 
649                      int subsample, int align) const
650{
651  // Access image size
652  int width = get_real_width();
653  int height = get_real_height();
654  GP<JB2Image> fgjb = get_fgjb();
655  if ( width && height && fgjb && 
656       (fgjb->get_width() == width) && 
657       (fgjb->get_height() == height) ) 
658    {
659      return fgjb->get_bitmap(rect, subsample, align);
660    }
661  return 0;
662}
663
664GP<GPixmap>
665DjVuImage::get_bg_pixmap(const GRect &rect, 
666                         int subsample, double gamma) const
667{
668  GP<GPixmap> pm = 0;
669  // Access image size
670 
671  GP<DjVuInfo> info = get_info();
672  int width = get_real_width();
673  int height = get_real_height();
674
675
676  if (width<=0 || height<=0 || !info) return 0;
677  // Compute gamma_correction
678  double gamma_correction = 1.0;
679  if (gamma > 0)
680    gamma_correction = gamma / info->gamma;
681  if (gamma_correction < 0.1)
682    gamma_correction = 0.1;
683  else if (gamma_correction > 10)
684    gamma_correction = 10;
685 
686  // CASE1: Incremental BG IW44Image
687  GP<IW44Image> bg44 = get_bg44();
688  if (bg44)
689    {
690      int w = bg44->get_width();
691      int h = bg44->get_height();
692      // Avoid silly cases
693      if (w==0 || h==0 || width==0 || height==0)
694        return 0;
695      // Determine how much bg44 is reduced
696      int red = compute_red(width,height,w,h);
697      if (red<1 || red>12)
698        return 0;
699      // Handle pure downsampling cases
700      if (subsample == red)
701        pm = bg44->get_pixmap(1,rect);
702      else if (subsample == 2*red)
703        pm = bg44->get_pixmap(2,rect);   
704      else if (subsample == 4*red)
705        pm = bg44->get_pixmap(4,rect); 
706      else if (subsample == 8*red)
707        pm = bg44->get_pixmap(8,rect); 
708      // Handle fractional downsampling case
709      else if (red*4 == subsample*3)
710        {
711          GRect nrect = rect;
712          GRect xrect = rect;
713          xrect.xmin = (xrect.xmin/3)*4;
714          xrect.ymin = (xrect.ymin/3)*4;
715          xrect.xmax = ((xrect.xmax+2)/3)*4;
716          xrect.ymax = ((xrect.ymax+2)/3)*4;
717          nrect.translate(-xrect.xmin*3/4, -xrect.ymin*3/4);
718          if (xrect.xmax > w) 
719            xrect.xmax = w;
720          if (xrect.ymax > h) 
721            xrect.ymax = h;
722          GP<GPixmap> ipm = bg44->get_pixmap(1,xrect);
723          pm = GPixmap::create();
724          pm->downsample43(ipm, &nrect);
725        }
726      // Handle all other cases with pixmapscaler
727      else
728        {
729          // find suitable power of two
730          int po2 = 16;
731          while (po2>1 && subsample<po2*red)
732            po2 >>= 1;
733          // setup pixmap scaler
734          int inw = (w+po2-1)/po2;
735          int inh = (h+po2-1)/po2;
736          int outw = (width+subsample-1)/subsample;
737          int outh = (height+subsample-1)/subsample;
738          GP<GPixmapScaler> gps=GPixmapScaler::create(inw, inh, outw, outh);
739          GPixmapScaler &ps=*gps;
740          ps.set_horz_ratio(red*po2, subsample);
741          ps.set_vert_ratio(red*po2, subsample);
742          // run pixmap scaler
743          GRect xrect;
744          ps.get_input_rect(rect,xrect);
745          GP<GPixmap> ipm = bg44->get_pixmap(po2,xrect);
746          pm = GPixmap::create();
747          ps.scale(xrect, *ipm, rect, *pm);
748        }
749      // Apply gamma correction
750      if (pm && gamma_correction!=1.0)
751        pm->color_correct(gamma_correction);
752      return pm;
753    }
754
755  // CASE 2: Raw background pixmap
756  GP<GPixmap>  bgpm = get_bgpm();
757  if (bgpm)
758    {
759      int w = bgpm->columns();
760      int h = bgpm->rows();
761      // Avoid silly cases
762      if (w==0 || h==0 || width==0 || height==0)
763        return 0;
764      // Determine how much bgpm is reduced
765      int red = compute_red(width,height,w,h);
766      if (red<1 || red>12)
767        return 0;
768      // Handle pure downsampling cases
769      int ratio = subsample/red;
770      if (subsample==ratio*red && ratio>=1)
771        {
772          pm = GPixmap::create();
773          if (ratio == 1)
774            pm->init(*bgpm, rect);
775          else if (ratio > 1)
776            pm->downsample(bgpm, ratio, &rect);
777        }
778      // Handle all other cases with pixmapscaler
779      else
780        {
781          // setup pixmap scaler
782          int outw = (width+subsample-1)/subsample;
783          int outh = (height+subsample-1)/subsample;
784          GP<GPixmapScaler> gps=GPixmapScaler::create(w, h, outw, outh);
785          GPixmapScaler &ps=*gps;
786          ps.set_horz_ratio(red, subsample);
787          ps.set_vert_ratio(red, subsample);
788          // run pixmap scaler
789          pm = GPixmap::create();
790          GRect xrect(0,0,w,h);
791          ps.scale(xrect, *bgpm, rect, *pm);
792        }
793      // Apply gamma correction
794      if (pm && gamma_correction!=1.0)
795        pm->color_correct(gamma_correction);
796      return pm;
797    }
798
799  // FAILURE
800  return 0;
801}
802
803
804
805int 
806DjVuImage::stencil(GPixmap *pm, const GRect &rect,
807                   int subsample, double gamma) const
808{
809  // Warping and blending.
810  if (!pm)
811    return 0;
812  // Access components
813 
814  GP<DjVuInfo> info = get_info();
815  int width = get_real_width();
816  int height = get_real_height();
817
818
819  if (width<=0 || height<=0 || !info) return 0;
820  GP<JB2Image> fgjb = get_fgjb();
821  GP<GPixmap> fgpm = get_fgpm();
822  GP<DjVuPalette> fgbc = get_fgbc();
823 
824  // Compute gamma_correction
825  double gamma_correction = 1.0;
826  if (gamma > 0)
827    gamma_correction = gamma / info->gamma;
828  if (gamma_correction < 0.1)
829    gamma_correction = 0.1;
830  else if (gamma_correction > 10)
831    gamma_correction = 10;
832
833  // Compute alpha map and relevant JB2Image components
834  GList<int> components;
835  GP<GBitmap> bm;
836  if (fgjb)
837    {
838      JB2Image *jimg = fgjb;
839      if (! (width && height && 
840             jimg->get_width() == width && 
841             jimg->get_height() == height ) )
842        return 0;
843      // Decode bitmap
844      bm = GBitmap::create(rect.height(), rect.width());
845      bm->set_grays(1+subsample*subsample);
846      int rxmin = rect.xmin * subsample;
847      int rymin = rect.ymin * subsample;
848      for (int blitno = 0; blitno < jimg->get_blit_count(); blitno++)
849        {
850          const JB2Blit *pblit = jimg->get_blit(blitno);
851          const JB2Shape  &pshape = jimg->get_shape(pblit->shapeno);
852          if (pshape.bits &&
853              pblit->left <= rect.xmax * subsample &&
854              pblit->bottom <= rect.ymax * subsample &&
855              pblit->left + (int)pshape.bits->columns() >= rect.xmin * subsample &&
856              pblit->bottom + (int)pshape.bits->rows() >= rect.ymin * subsample )
857            {
858              // Record component list
859              if (fgbc) components.append(blitno);
860              // Blit
861              bm->blit(pshape.bits, 
862                       pblit->left - rxmin, pblit->bottom - rymin, 
863                       subsample);
864            }
865        }
866    }
867
868
869  // TWO LAYER MODEL
870  if (bm && fgbc)
871    {
872      // Perform attenuation from scratch
873      pm->attenuate(bm, 0, 0);
874      // Check that fgbc has the correct size
875      JB2Image *jimg = fgjb;
876      DjVuPalette *fg = fgbc;
877      if (jimg->get_blit_count() != fg->colordata.size())
878        return 0;
879      // Copy and color correct palette
880      int palettesize = fg->size();
881      GTArray<GPixel> colors(0,palettesize-1);
882      for (int i=0; i<palettesize; i++)
883        fg->index_to_color(i, colors[i]);
884      GPixmap::color_correct(gamma_correction, colors, palettesize);
885      // Blit all components (one color at a time)
886      while (components.size() > 0)
887        {
888          GPosition nullpos;
889          GPosition pos = components;
890          int lastx = 0;
891          int colorindex = fg->colordata[components[pos]];
892          if (colorindex >= palettesize)
893            G_THROW( ERR_MSG("DjVuImage.corrupted") );
894          // Gather relevant components and relevant rectangle
895          GList<int> compset;
896          GRect comprect;
897          while (pos)
898            {
899              int blitno = components[pos];
900              const JB2Blit *pblit = jimg->get_blit(blitno);
901              if (pblit->left < lastx) break; 
902              lastx = pblit->left;
903              if (fg->colordata[blitno] == colorindex)
904                {
905                  const JB2Shape  &pshape = jimg->get_shape(pblit->shapeno);
906                  GRect rect(pblit->left, pblit->bottom, 
907                             pshape.bits->columns(), pshape.bits->rows());
908                  comprect.recthull(comprect, rect);
909                  compset.insert_before(nullpos, components, pos);
910                  continue;
911                }
912              ++pos;
913            }
914          // Round alpha map rectangle
915          comprect.xmin = comprect.xmin / subsample;
916          comprect.ymin = comprect.ymin / subsample;
917          comprect.xmax = (comprect.xmax+subsample-1) / subsample;
918          comprect.ymax = (comprect.ymax+subsample-1) / subsample;
919          comprect.intersect(comprect, rect);
920          // Compute alpha map for that color
921          bm = 0;
922          bm = GBitmap::create(comprect.height(), comprect.width());
923          bm->set_grays(1+subsample*subsample);
924          int rxmin = comprect.xmin * subsample;
925          int rymin = comprect.ymin * subsample;
926          for (pos=compset; pos; ++pos)
927            {
928              int blitno = compset[pos];
929              const JB2Blit *pblit = jimg->get_blit(blitno);
930              const JB2Shape  &pshape = jimg->get_shape(pblit->shapeno);
931              bm->blit(pshape.bits, 
932                       pblit->left - rxmin, pblit->bottom - rymin, 
933                       subsample);
934            }
935          // Blend color into background pixmap
936          pm->blit(bm, comprect.xmin-rect.xmin, comprect.ymin-rect.ymin, &colors[colorindex]);
937        }
938      return 1;
939    }
940
941
942  // THREE LAYER MODEL
943  if (bm && fgpm)
944    {
945      // This follows fig. 4 in Adelson "Layered representations for image
946      // coding" (1991) http://www-bcs.mit.edu/people/adelson/papers.html.
947      // The properly warped background is already in PM.  The properly warped
948      // alpha map is already in BM.  We just have to warp the foreground and
949      // perform alpha blending.
950#ifdef SIMPLE_THREE_LAYER_RENDERING
951      int w = fgpm->columns();
952      int h = fgpm->rows();
953      // Determine foreground reduction
954      int red = compute_red(width,height, w, h);
955      if (red<1 || red>12)
956        return 0;
957      // Warp foreground pixmap
958      GPixmapScaler ps(w,h,width/subsample+1,height/subsample+1);
959      ps.set_horz_ratio(red,subsample);
960      ps.set_vert_ratio(red,subsample);
961      GP<GPixmap> nfg = new GPixmap;
962      GRect provided(0,0,w,h);
963      ps.scale(provided, *fgpm, rect, *nfg);
964      // Attenuate background and blit
965      nfg->color_correct(gamma_correction);
966      pm->blend(bm, 0, 0, nfg); // blend == attenuate + blit
967      return 1;
968#else
969      // Things are now a little bit more complex because the convenient
970      // function GPixmap::stencil() simultaneously upsamples the foreground
971      // by an integer factor and performs the alpha blending.  We have
972      // to determine how and when this facility can be used.
973      int w = fgpm->columns();
974      int h = fgpm->rows();
975      // Determine foreground reduction
976      int red = compute_red(width,height,w,h);
977      if (red<1 || red>12)
978        return 0;
979      // Try supersampling foreground pixmap by an integer factor
980      int supersample = ( red>subsample ? red/subsample : 1);
981      int wantedred = supersample*subsample;
982      // Try simple foreground upsampling
983      if (red == wantedred)
984        {
985          // Simple foreground upsampling is enough.
986          pm->stencil(bm, fgpm, supersample, &rect, gamma_correction);
987          return 1;
988        }
989      else 
990        {
991          // Must pre-warp foreground pixmap
992          GP<GPixmap> nfg;
993          int desw = (w*red+wantedred-1)/wantedred;
994          int desh = (h*red+wantedred-1)/wantedred;
995          // Cache rescaled fgpm for speed
996          static const DjVuImage *tagimage  = 0;
997          static const GPixmap *tagfgpm   = 0;
998          static GP<GPixmap> cachednfg = 0;
999          // Check whether cached fgpm applies.
1000          if ( cachednfg && this==tagimage && fgpm==tagfgpm
1001               && desw==(int)cachednfg->columns()
1002               && desh==(int)cachednfg->rows() )
1003            {
1004              nfg = cachednfg;
1005            }
1006          else
1007            {
1008              GP<GPixmapScaler> gps=GPixmapScaler::create(w,h,desw,desh);
1009              GPixmapScaler &ps=*gps;
1010              ps.set_horz_ratio(red, wantedred);
1011              ps.set_vert_ratio(red, wantedred);
1012              nfg = GPixmap::create();
1013              GRect provided(0,0,w,h);
1014              GRect desired(0,0,desw,desh);
1015              ps.scale(provided, *fgpm, desired, *nfg);
1016            }
1017          // Use combined warp+blend function
1018          pm->stencil(bm, nfg, supersample, &rect, gamma_correction);
1019          // Cache
1020          tagimage = this;
1021          tagfgpm = fgpm;
1022          cachednfg = nfg;
1023          return 1;
1024        }
1025#endif
1026    }
1027 
1028  // FAILURE
1029  return 0;
1030}
1031
1032
1033GP<GPixmap>
1034DjVuImage::get_fg_pixmap(const GRect &rect, 
1035                         int subsample, double gamma) const
1036{
1037  // Obtain white background pixmap
1038  GP<GPixmap> pm;
1039  // Access components
1040  const int width = get_real_width();
1041  const int height = get_real_height();
1042  if (width && height)
1043  {
1044    pm = GPixmap::create(rect.height(),rect.width(), &GPixel::WHITE);
1045    if (!stencil(pm, rect, subsample, gamma))
1046      pm=0;
1047  }
1048  return pm;
1049}
1050
1051
1052GP<GPixmap>
1053DjVuImage::get_pixmap(const GRect &rect, int subsample, double gamma) const
1054{
1055  // Get background
1056  GP<GPixmap> pm = get_bg_pixmap(rect, subsample, gamma);
1057  // Superpose foreground
1058  if (! stencil(pm, rect, subsample, gamma))
1059    // Avoid ugly progressive display (hack)
1060    if (get_fgjb()) return 0;
1061  // Return
1062  return pm;
1063}
1064
1065
1066//// DJVUIMAGE: RENDERING (ARBITRARY SCALE)
1067
1068typedef GP<GBitmap>(DjVuImage::*BImager)(const GRect &, int, int) const;
1069typedef GP<GPixmap>(DjVuImage::*PImager)(const GRect &, int, double) const;
1070
1071static GP<GBitmap>
1072do_bitmap(const DjVuImage &dimg, BImager get,
1073          const GRect &inrect, const GRect &inall, int align )
1074{
1075    GRect rect=inrect;
1076    GRect all=inall;
1077    if( dimg.get_rotate() )
1078    {
1079        GRectMapper mapper;
1080        mapper.rotate(-dimg.get_rotate());
1081        mapper.map(rect);
1082        mapper.map(all);
1083    }
1084
1085  // Sanity
1086  if (! ( all.contains(rect.xmin, rect.ymin) &&
1087          all.contains(rect.xmax-1, rect.ymax-1) ))
1088    G_THROW( ERR_MSG("DjVuImage.bad_rect") );
1089  // Check for integral reduction
1090  int red;
1091  int w = dimg.get_real_width();
1092  int h = dimg.get_real_height();
1093
1094  int rw = all.width();
1095  int rh = all.height();
1096  GRect zrect = rect; 
1097  zrect.translate(-all.xmin, -all.ymin);
1098  for (red=1; red<=15; red++)
1099    if (rw*red>w-red && rw*red<w+red && rh*red>h-red && rh*red<h+red)
1100    {
1101        GP<GBitmap> bm=(dimg.*get)(zrect, red, align);
1102        if(bm)
1103            return bm->rotate(dimg.get_rotate());
1104        else
1105                return NULL;
1106    }
1107  // Find best reduction
1108  for (red=15; red>1; red--)
1109    if ( (rw*red < w && rh*red < h) ||
1110         (rw*red*3 < w || rh*red*3 < h) )
1111      break;
1112  // Setup bitmap scaler
1113  if (w<=0 || h<=0) return 0;
1114  GP<GBitmapScaler> gbs=GBitmapScaler::create();
1115  GBitmapScaler &bs=*gbs;
1116  bs.set_input_size( (w+red-1)/red, (h+red-1)/red );
1117  bs.set_output_size( rw, rh );
1118  bs.set_horz_ratio( rw*red, w );
1119  bs.set_vert_ratio( rh*red, h );
1120  // Scale
1121  GRect srect;
1122  bs.get_input_rect(zrect, srect);
1123  GP<GBitmap> sbm = (dimg.*get)(srect, red, 1);
1124  if (!sbm) return 0;
1125  int border = ((zrect.width() + align - 1) & ~(align - 1)) - zrect.width();
1126  GP<GBitmap> bm = GBitmap::create(zrect.height(), zrect.width(), border);
1127  bs.scale(srect, *sbm, zrect, *bm);
1128  if( bm )
1129      return bm->rotate(dimg.get_rotate());
1130  else
1131      return NULL;
1132}
1133
1134static GP<GPixmap>
1135do_pixmap(const DjVuImage &dimg, PImager get,
1136          const GRect &inrect, const GRect &inall, double gamma )
1137{
1138    GRect rect=inrect;
1139    GRect all=inall;
1140    if( dimg.get_rotate()%4 )
1141    {
1142        GRectMapper mapper;
1143      mapper.rotate(-dimg.get_rotate());
1144        mapper.map(rect);
1145        mapper.map(all);
1146    }
1147
1148  // Sanity
1149  if (! ( all.contains(rect.xmin, rect.ymin) &&
1150          all.contains(rect.xmax-1, rect.ymax-1) ))
1151    G_THROW( ERR_MSG("DjVuImage.bad_rect2") );
1152  // Check for integral reduction
1153  int red, w=0, h=0, rw=0, rh=0;
1154  w = dimg.get_real_width();
1155  h = dimg.get_real_height();
1156 
1157
1158  rw = all.width();
1159  rh = all.height();
1160  GRect zrect = rect; 
1161  zrect.translate(-all.xmin, -all.ymin);
1162  for (red=1; red<=15; red++)
1163    if (rw*red>w-red && rw*red<w+red && rh*red>h-red && rh*red<h+red)
1164    {
1165        GP<GPixmap> pm = (dimg.*get)(zrect, red, gamma);
1166        if( pm ) 
1167            return pm->rotate(dimg.get_rotate());
1168        else
1169            return NULL;
1170    }
1171  // These reductions usually go faster (improve!)
1172  static int fastred[] = { 12,6,4,3,2,1 };
1173  // Find best reduction
1174  for (int i=0; (red=fastred[i])>1; i++)
1175    if ( (rw*red < w && rh*red < h) ||
1176         (rw*red*3 < w || rh*red*3 < h) )
1177      break;
1178  // Setup pixmap scaler
1179  if (w<=0 || h<=0) return 0;
1180  GP<GPixmapScaler> gps=GPixmapScaler::create();
1181  GPixmapScaler &ps=*gps;
1182  ps.set_input_size( (w+red-1)/red, (h+red-1)/red );
1183  ps.set_output_size( rw, rh );
1184  ps.set_horz_ratio( rw*red, w );
1185  ps.set_vert_ratio( rh*red, h );
1186  // Scale
1187  GRect srect;
1188  ps.get_input_rect(zrect, srect);
1189  GP<GPixmap> spm = (dimg.*get)(srect, red, gamma);
1190  if (!spm) return 0;
1191  GP<GPixmap> pm = GPixmap::create();
1192  ps.scale(srect, *spm, zrect, *pm);
1193  if(pm)
1194      return pm->rotate(dimg.get_rotate());
1195  else
1196      return NULL;
1197}
1198
1199GP<GPixmap> 
1200DjVuImage::get_pixmap(const GRect &rect, const GRect &all, double gamma) const
1201{
1202  return do_pixmap(*this, & DjVuImage::get_pixmap, rect, all, gamma);
1203}
1204
1205GP<GBitmap> 
1206DjVuImage::get_bitmap(const GRect &rect, const GRect &all, int align) const
1207{
1208  return do_bitmap(*this, & DjVuImage::get_bitmap, rect, all, align);
1209}
1210
1211GP<GPixmap> 
1212DjVuImage::get_bg_pixmap(const GRect &rect, const GRect &all, double gamma) const
1213{
1214  return do_pixmap(*this, & DjVuImage::get_bg_pixmap, rect, all, gamma);
1215}
1216
1217GP<GPixmap> 
1218DjVuImage::get_fg_pixmap(const GRect &rect, const GRect &all, double gamma) const
1219{
1220  return do_pixmap(*this, & DjVuImage::get_fg_pixmap, rect, all, gamma);
1221}
1222
1223int 
1224DjVuImage::get_rotate() const
1225{
1226  return (rotate_count<0) ? 0 : rotate_count;
1227}
1228
1229void
1230DjVuImage::init_rotate(const DjVuInfo &info)
1231{ 
1232  rotate_count = info.orientation;
1233}
1234
1235void DjVuImage::set_rotate(int count) 
1236{ 
1237  rotate_count = count % 4;
1238}
1239
1240GP<DjVuAnno> 
1241DjVuImage::get_decoded_anno()
1242{
1243    GP<DjVuInfo> djvuinfo = get_info();
1244    GP<DjVuAnno> djvuanno = DjVuAnno::create();
1245    GP<ByteStream> bs=get_anno();
1246    if( bs )
1247    {
1248      int rotate_count=get_rotate(); 
1249      /// Brain damaged adjustment of annotation
1250      /// coordinates that reflect the orientation
1251      /// flag in the info chunk....
1252      if (djvuinfo)
1253        rotate_count = rotate_count - djvuinfo->orientation;
1254      ///decode
1255        djvuanno->decode(bs);
1256            ///map hyperlinks correctly for rotation           
1257      if( rotate_count & 3 )
1258        {   
1259            GRect input, output;
1260          input = GRect(0,0,get_width(),get_height());
1261          if (rotate_count & 1)
1262            output = GRect(0,0,get_height(),get_width());
1263          else
1264            output = GRect(0,0,get_width(),get_height());
1265            GRectMapper mapper;
1266            mapper.clear();
1267            mapper.set_input(input);
1268            mapper.set_output(output);               
1269          mapper.rotate(-rotate_count);
1270            GPList<GMapArea> &list=djvuanno->ant->map_areas;
1271            for(GPosition pos=list;pos;++pos)
1272                list[pos]->unmap(mapper);
1273            }
1274        return djvuanno;
1275    }
1276    else
1277        return NULL;
1278}
1279
1280
1281void
1282DjVuImage::map(GRect &rect) const
1283{
1284    GRect input, output;
1285    const int rotate_count=get_rotate(); 
1286    if(rotate_count>0)
1287    { 
1288        input = GRect(0,0,get_width(), get_height());
1289        output = GRect(0,0, get_real_width(), get_real_height());
1290
1291        GRectMapper mapper;
1292        mapper.clear();
1293        mapper.set_input(input);
1294        mapper.set_output(output);               
1295        mapper.rotate(-rotate_count);
1296        mapper.map(rect);
1297    }
1298}
1299
1300void
1301DjVuImage::unmap(GRect &rect) const
1302{
1303    GRect input, output;
1304    const int rotate_count=get_rotate(); 
1305    if(rotate_count>0)
1306    { 
1307        input = GRect(0,0,get_width(), get_height());
1308        output = GRect(0,0, get_real_width(), get_real_height());
1309
1310        GRectMapper mapper;
1311        mapper.clear();
1312        mapper.set_input(input);
1313        mapper.set_output(output);               
1314        mapper.rotate(-rotate_count);
1315        mapper.unmap(rect);
1316    }
1317}
1318
1319void
1320DjVuImage::map(int &x, int &y) const
1321{
1322    GRect input, output;
1323    const int rotate_count=get_rotate(); 
1324    if(rotate_count>0)
1325    { 
1326        input = GRect(0,0,get_width(), get_height());
1327        output = GRect(0,0, get_real_width(), get_real_height());
1328
1329        GRectMapper mapper;
1330        mapper.clear();
1331        mapper.set_input(input);
1332        mapper.set_output(output);               
1333        mapper.rotate(-rotate_count);
1334        mapper.map(x, y);
1335    }
1336}
1337
1338void
1339DjVuImage::unmap(int &x, int &y) const
1340{
1341    GRect input, output;
1342    const int rotate_count=get_rotate(); 
1343    if(rotate_count>0)
1344    { 
1345        input = GRect(0,0,get_width(), get_height());
1346        output = GRect(0,0, get_real_width(), get_real_height());
1347
1348        GRectMapper mapper;
1349        mapper.clear();
1350        mapper.set_input(input);
1351        mapper.set_output(output);               
1352        mapper.rotate(-rotate_count);
1353        mapper.unmap(x, y);
1354    }
1355}
1356
1357bool
1358DjVuImage::wait_for_complete_decode(void)
1359{
1360  if (file) 
1361  {
1362    file->resume_decode(true);
1363    return file->is_decode_ok();
1364  }
1365  return 0;
1366}
1367
1368// Write out a DjVuXML object tag and map tag.
1369void
1370DjVuImage::writeXML(ByteStream &str_out,const GURL &doc_url,const int flags) const
1371{
1372  const int height=get_height();
1373 
1374  static const char *Object="<OBJECT data=\"";
1375  const GURL url(get_djvu_file()->get_url());
1376  const GUTF8String pagename(url.fname());
1377  GUTF8String page_param;
1378  if(doc_url.is_valid() && !doc_url.is_empty() && (doc_url != url))
1379  {
1380    str_out.writestring(Object+doc_url.get_string());
1381    page_param="<PARAM name=\"PAGE\" value=\""+pagename+"\" />\n";
1382  }else
1383  {
1384    str_out.writestring(Object+doc_url.get_string());
1385  }
1386  str_out.writestring("\" type=\""+get_mimetype()+"\" height=\""
1387    +GUTF8String(height)+"\" width=\""+GUTF8String(get_width())
1388    +"\" usemap=\""+pagename.toEscaped()+"\" >\n");
1389  if(!(flags & NOINFO))
1390  {
1391    const GP<DjVuInfo> info(get_info());
1392    if(info)
1393    {
1394      info->writeParam(str_out);
1395    }
1396  }
1397  str_out.writestring(page_param);
1398  const GP<DjVuAnno> anno(DjVuAnno::create());
1399  if(!(flags & NOINFO)||!(flags&NOMAP))
1400  {
1401    const GP<ByteStream> anno_str(get_anno());
1402    if(anno_str)
1403    {
1404      anno->decode(anno_str);
1405    }
1406    if(!(flags & NOINFO))
1407    {
1408      anno->writeParam(str_out);
1409    }
1410  }
1411  if(!(flags & NOTEXT))
1412  {
1413    const GP<DjVuText> text(DjVuText::create());
1414    {
1415      const GP<ByteStream> text_str(get_text());
1416      if(text_str)
1417      {
1418        text->decode(text_str);
1419      }
1420      text->writeText(str_out,height);
1421    }
1422  }
1423  if(!(flags & NOMETA))
1424  {
1425    const GP<ByteStream> meta_str(get_meta());
1426    if(meta_str)
1427    {
1428      GP<IFFByteStream> giff=IFFByteStream::create(meta_str);
1429      IFFByteStream &iff=*giff;
1430      GUTF8String chkid;
1431      while( iff.get_chunk(chkid))
1432      {
1433        GP<ByteStream> gbs(iff.get_bytestream());
1434        if(chkid == "METa")
1435        {
1436           str_out.copy(*gbs);
1437          //str_out.writestring(gbs->getAsUTF8());
1438        }else if(chkid == "METz")
1439        {
1440          gbs=BSByteStream::create(gbs);
1441          str_out.copy(*gbs);
1442          //str_out.writestring(gbs->getAsUTF8());
1443        }
1444        iff.close_chunk();
1445      }
1446    }
1447  }
1448  str_out.writestring(GUTF8String("</OBJECT>\n"));
1449  if(!(flags & NOMAP))
1450  {
1451    anno->writeMap(str_out,pagename,height);
1452  }
1453}
1454
1455// Write out a DjVuXML object tag and map tag.
1456void
1457DjVuImage::writeXML(ByteStream &str_out) const
1458{
1459  writeXML(str_out,GURL());
1460}
1461
1462// Write out a DjVuXML object tag and map tag.
1463GUTF8String
1464DjVuImage::get_XML(const GURL &doc_url,const int flags) const
1465{
1466  GP<ByteStream> gbs(ByteStream::create());
1467  ByteStream &bs=*gbs;
1468  writeXML(bs,doc_url);
1469  bs.seek(0L);
1470  return bs.getAsUTF8();
1471}
1472
1473// Write out a DjVuXML object tag and map tag.
1474GUTF8String
1475DjVuImage::get_XML(void) const
1476{
1477  return get_XML(GURL());
1478}
1479
1480
1481#ifdef HAVE_NAMESPACES
1482}
1483# ifndef NOT_USING_DJVU_NAMESPACE
1484using namespace DJVU;
1485# endif
1486#endif
Note: See TracBrowser for help on using the repository browser.