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