source: trunk/libdjvu/DjVuToPS.cpp @ 17

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

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

File size: 81.6 KB
Line 
1//C-  -*- C++ -*-
2//C- -------------------------------------------------------------------
3//C- DjVuLibre-3.5
4//C- Copyright (c) 2002-2003  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: DjVuToPS.cpp,v 1.23 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 "DjVuToPS.h"
65#include "IFFByteStream.h"
66#include "BSByteStream.h"
67#include "DjVuImage.h"
68#include "DjVuText.h"
69#include "DataPool.h"
70#include "IW44Image.h"
71#include "JB2Image.h"
72#include "GBitmap.h"
73#include "GPixmap.h"
74#include "debug.h"
75#include <stdarg.h>
76#include <stdlib.h>
77#include <stdio.h>
78#include <time.h>
79#include <math.h>
80#ifdef UNIX
81#include <pwd.h>
82#include <grp.h>
83#include <unistd.h>
84#endif
85
86
87#ifdef HAVE_NAMESPACES
88namespace DJVU {
89# ifdef NOT_DEFINED // Just to fool emacs c++ mode
90}
91#endif
92#endif
93
94
95static const size_t ps_string_size=15000;
96
97// ***************************************************************************
98// ****************************** Options ************************************
99// ***************************************************************************
100
101DjVuToPS::Options::
102Options(void)
103: format(PS), 
104  level(2), 
105  orientation(AUTO), 
106  mode(COLOR), 
107  zoom(0),
108  color(true), 
109  calibrate(true), 
110  text(false),
111  gamma((double)2.2), 
112  copies(1), 
113  frame(false),
114  cropmarks(false),
115  bookletmode(OFF),
116  bookletmax(0),
117  bookletalign(0),
118  bookletfold(18),
119  bookletxfold(200)
120{}
121
122void
123DjVuToPS::Options::
124set_format(Format xformat)
125{
126  if (xformat != EPS && xformat != PS)
127    G_THROW(ERR_MSG("DjVuToPS.bad_format"));
128  format=xformat;
129}
130
131void
132DjVuToPS::Options::
133set_level(int xlevel)
134{
135  if (xlevel<1 || xlevel>3)
136    G_THROW(ERR_MSG("DjVuToPS.bad_level")
137           + GUTF8String("\t") + GUTF8String(xlevel));
138  level=xlevel;
139}
140
141void
142DjVuToPS::Options::
143set_orientation(Orientation xorientation)
144{
145  if (xorientation!=PORTRAIT && 
146      xorientation!=LANDSCAPE &&
147      xorientation!=AUTO )
148    G_THROW(ERR_MSG("DjVuToPS.bad_orient"));
149  orientation=xorientation;
150}
151
152void
153DjVuToPS::Options::
154set_mode(Mode xmode)
155{
156  if (xmode!=COLOR && xmode!=FORE && xmode!=BACK && xmode!=BW)
157    G_THROW(ERR_MSG("DjVuToPS.bad_mode"));
158  mode=xmode;
159}
160
161void
162DjVuToPS::Options::
163set_zoom(int xzoom)
164{
165  if (xzoom!=0 && !(xzoom>=5 && xzoom<=999))
166    G_THROW(ERR_MSG("DjVuToPS.bad_zoom"));
167  zoom=xzoom;
168}
169
170void
171DjVuToPS::Options::
172set_color(bool xcolor)
173{
174  color=xcolor;
175}
176
177void 
178DjVuToPS::Options::
179set_sRGB(bool xcalibrate)
180{
181  calibrate=xcalibrate;
182}
183
184void
185DjVuToPS::Options::
186set_gamma(double xgamma)
187{
188  if  (xgamma<(double)(0.3-0.0001) || xgamma>(double)(5.0+0.0001))
189    G_THROW(ERR_MSG("DjVuToPS.bad_gamma"));
190  gamma=xgamma;
191}
192
193void
194DjVuToPS::Options::
195set_copies(int xcopies)
196{
197  if (xcopies<=0)
198    G_THROW(ERR_MSG("DjVuToPS.bad_number"));
199  copies=xcopies;
200}
201
202void
203DjVuToPS::Options::
204set_frame(bool xframe)
205{
206  frame=xframe;
207}
208
209void
210DjVuToPS::Options::
211set_cropmarks(bool xmarks)
212{
213  cropmarks=xmarks;
214}
215
216void
217DjVuToPS::Options::
218set_text(bool xtext)
219{
220  text=xtext;
221}
222
223void 
224DjVuToPS::Options::
225set_bookletmode(BookletMode m)
226{
227  bookletmode = m;
228}
229
230void 
231DjVuToPS::Options::
232set_bookletmax(int m)
233{
234  bookletmax = 0;
235  if (m > 0)
236    bookletmax = (m+3)/4;
237  bookletmax *= 4;
238}
239
240void 
241DjVuToPS::Options::
242set_bookletalign(int m)
243{
244  bookletalign = m;
245}
246
247void 
248DjVuToPS::Options::
249set_bookletfold(int fold, int xfold)
250{
251  if (fold >= 0)
252    bookletfold = fold;
253  if (xfold >= 0)
254    bookletxfold = xfold;
255}
256
257
258// ***************************************************************************
259// ******************************* DjVuToPS **********************************
260// ***************************************************************************
261
262static char bin2hex[256][2];
263
264DjVuToPS::DjVuToPS(void)
265{
266  DEBUG_MSG("DjVuToPS::DjVuToPS(): initializing...\n");
267  DEBUG_MAKE_INDENT(3);
268  DEBUG_MSG("Initializing dig2hex[]\n");
269  // Creating tables for bin=>text translation
270  static char * dig2hex="0123456789ABCDEF";
271  int i;
272  for(i=0;i<256;i++)
273    {
274      bin2hex[i][0]=dig2hex[i/16];
275      bin2hex[i][1]=dig2hex[i%16];
276    }
277  refresh_cb=0;
278  refresh_cl_data=0;
279  prn_progress_cb=0;
280  prn_progress_cl_data=0;
281  dec_progress_cb=0;
282  dec_progress_cl_data=0;
283  info_cb=0;
284  info_cl_data=0;
285}
286
287#ifdef __GNUC__
288static void
289write(ByteStream &str, const char *format, ...)
290__attribute__((format (printf, 2, 3)));
291#endif
292
293static void
294write(ByteStream &str, const char *format, ...)
295{
296  /* Will output the formated string to the specified \Ref{ByteStream}
297     like #fprintf# would do it for a #FILE#. */
298  va_list args;
299  va_start(args, format);
300  GUTF8String tmp;
301  tmp.vformat(format, args);
302  str.writall((const char *) tmp, tmp.length());
303}
304
305// ************************* DOCUMENT LEVEL *********************************
306
307void
308DjVuToPS::
309store_doc_prolog(ByteStream &str, int pages, int dpi, GRect *grect)
310{
311  /* Will store the {\em document prolog}, which is basically a
312     block of document-level comments in PS DSC 3.0 format.
313     @param str Stream where PostScript data should be written
314     @param pages Total number of pages
315     @param dpi (EPS mode only)
316     @param grect (EPS mode only) */
317  DEBUG_MSG("storing the document prolog\n");
318  DEBUG_MAKE_INDENT(3);
319  if (options.get_format()==Options::EPS)
320    write(str,
321          "%%!PS-Adobe-3.0 EPSF 3.0\n"
322          "%%%%BoundingBox: 0 0 %d %d\n",
323          (grect->width()*100+dpi-1)/dpi, 
324          (grect->height()*100+dpi-1)/dpi );
325  else
326    write(str, "%%!PS-Adobe-3.0\n");
327  write(str,
328        "%%%%Title: DjVu PostScript document\n"
329        "%%%%Copyright: Copyright (c) 1998-1999 AT&T\n"
330        "%%%%Creator: DjVu (code by Andrei Erofeev)\n"
331        "%%%%DocumentData: Clean7Bit\n");
332  // Date
333  time_t tm=time(0);
334  write(str, "%%%%CreationDate: %s", ctime(&tm));
335  // For
336#ifdef UNIX
337  passwd *pswd = getpwuid(getuid());
338  if (pswd)
339    {
340      char *s = strchr(pswd->pw_gecos, ',');
341      if (s) 
342        *s = 0;
343      s = 0;
344      if (pswd->pw_gecos && strlen(pswd->pw_gecos))
345        s = pswd->pw_gecos;
346      else if (pswd->pw_name && strlen(pswd->pw_name))
347        s = pswd->pw_name;
348      if (s)
349        write(str, "%%%%For: %s\n", s);
350    }
351#endif
352  // Language
353  write(str, "%%%%LanguageLevel: %d\n", options.get_level());
354  if (options.get_level()<2 && options.get_color())
355    write(str, "%%%%Extensions: CMYK\n");
356  // Pages
357  write(str, "%%%%Pages: %d\n",pages );
358  write(str, "%%%%PageOrder: Ascend\n");
359  // Orientation
360  if (options.get_orientation() != Options::AUTO)
361    write(str, "%%%%Orientation: %s\n", 
362          options.get_orientation()==Options::PORTRAIT ?
363          "Portrait" : "Landscape" );
364  // Requirements
365  if (options.get_format() == Options::PS)
366    {
367      write(str, "%%%%Requirements:");
368      if (options.get_color())
369        write(str, " color");
370      if (options.get_copies()>1)
371        write(str, " numcopies(%d)", options.get_copies());
372      if (options.get_level()>=2)
373        {
374          if (options.get_copies()>1)
375            write(str, " collate");
376          if (options.get_bookletmode() == Options::RECTOVERSO)
377            write(str, " duplex(tumble)");
378        }
379      write(str, "\n");
380    }
381  // End
382  write(str,
383        "%%%%EndComments\n"
384        "%%%%EndProlog\n"
385        "\n");
386}
387
388void
389DjVuToPS::
390store_doc_setup(ByteStream &str)
391{
392  /* Will store the {\em document setup}, which is a set of
393     PostScript commands and functions used to inspect and prepare
394     the PostScript interpreter environment before displaying images. */
395  write(str, 
396        "%%%%BeginSetup\n"
397        "/doc-origstate save def\n");
398  if (options.get_level()>=2)
399    {
400      if (options.get_format() == Options::PS)
401        {
402          if (options.get_copies()>1)
403            write(str, 
404                  "[{\n"
405                  "%%%%BeginFeature: NumCopies %d\n"
406                  "<< /NumCopies %d >> setpagedevice\n"
407                  "%%%%EndFeature\n"
408                  "} stopped cleartomark\n"
409                  "[{\n"
410                  "%%%%BeginFeature: Collate\n"
411                  "<< /Collate true >> setpagedevice\n"
412                  "%%%%EndFeature\n"
413                  "} stopped cleartomark\n",
414                  options.get_copies(),
415                  options.get_copies() );
416          if (options.get_bookletmode()==Options::RECTOVERSO)
417            write(str, 
418                  "[{\n"
419                  "%%%%BeginFeature: Duplex DuplexTumble\n"
420                  "<< /Duplex true /Tumble true >> setpagedevice\n"
421                  "%%%%EndFeature\n"
422                  "} stopped cleartomark\n");
423        }
424      if (options.get_color())
425        write(str, 
426              "%% -- procs for reading color image\n"
427              "/readR () def\n"
428              "/readG () def\n"
429              "/readB () def\n"
430              "/ReadData {\n"
431              "   currentfile /ASCII85Decode filter dup\n"
432              "   /RunLengthDecode filter\n"
433              "   bufferR readstring pop /readR exch def\n"
434              "   dup status { flushfile } { pop } ifelse\n"
435              "   currentfile /ASCII85Decode filter dup\n"
436              "   /RunLengthDecode filter\n"
437              "   bufferG readstring pop /readG exch def\n"
438              "   dup status { flushfile } { pop } ifelse\n"
439              "   currentfile /ASCII85Decode filter dup\n"
440              "   /RunLengthDecode filter\n"
441              "   bufferB readstring pop /readB exch def\n"
442              "   dup status { flushfile } { pop } ifelse\n"
443              "} bind def\n"
444              "/ReadR {\n"
445              "   readR length 0 eq { ReadData } if\n"
446              "   readR /readR () def\n"
447              "} bind def\n"
448              "/ReadG {\n"
449              "   readG length 0 eq { ReadData } if\n"
450              "   readG /readG () def\n"
451              "} bind def\n"
452              "/ReadB {\n"
453              "   readB length 0 eq { ReadData } if\n"
454              "   readB /readB () def\n"
455              "} bind def\n");
456      write(str,
457            "%% -- procs for foreground layer\n"
458            "/g {gsave 0 0 0 0 5 index 5 index setcachedevice\n"
459            "    true [1 0 0 1 0 0] 5 4 roll imagemask grestore\n"
460            "} bind def\n"
461            "/gn {gsave 0 0 0 0 6 index 6 index setcachedevice\n"
462            "  true [1 0 0 1 0 0] 3 2 roll 5 1 roll \n"
463            "  { 1 sub 0 index 2 add 1 index  1 add roll\n"
464            "  } imagemask grestore pop \n"
465            "} bind def\n"
466            "/c {setcolor rmoveto glyphshow} bind def\n"
467            "/s {rmoveto glyphshow} bind def\n"
468            "/S {rmoveto gsave show grestore} bind def\n" 
469            "/F {(Helvetica) findfont exch scalefont setfont} bind def\n"
470            "%% -- emulations\n"
471            "systemdict /rectstroke known not {\n"
472            "  /rectstroke  %% stack : x y width height \n"
473            "  { newpath 4 2 roll moveto 1 index 0 rlineto\n"
474            "    0 exch rlineto neg 0 rlineto closepath stroke\n"
475            "  } bind def } if\n"
476            "systemdict /rectclip known not {\n"
477            "  /rectclip  %% stack : x y width height \n"
478            "  { newpath 4 2 roll moveto 1 index 0 rlineto\n"
479            "    0 exch rlineto neg 0 rlineto closepath clip\n"
480            "  } bind def } if\n"
481            "%% -- color space\n" );
482      if (options.get_sRGB())
483        write(str,
484              "/DjVuColorSpace [ %s\n"
485              "<< /DecodeLMN [ { dup 0.03928 le {\n"
486              "       12.92321 div\n"
487              "     } {\n"
488              "       0.055 add 1.055 div 2.4 exp\n"
489              "     } ifelse } bind dup dup ]\n"
490              "   /MatrixLMN [\n"
491              "      0.412457 0.212673 0.019334\n"
492              "      0.357576 0.715152 0.119192\n"
493              "      0.180437 0.072175 0.950301 ]\n"
494              "   /WhitePoint [ 0.9505 1 1.0890 ] %% D65 \n"
495              "   /BlackPoint[0 0 0] >> ] def\n",
496              (options.get_color()) ? "/CIEBasedABC" : "/CIEBasedA" );
497      else if (options.get_color())
498        write(str,"/DjVuColorSpace /DeviceRGB def\n");
499      else
500        write(str,"/DjVuColorSpace /DeviceGray def\n");
501    } 
502  else 
503    {
504      // level<2
505      if (options.get_format() == Options::PS)
506        if (options.get_copies() > 1)
507          write(str,"/#copies %d def\n", options.get_copies());
508      if (options.get_color())
509        write(str, 
510              "%% -- buffers for reading image\n"
511              "/buffer8 () def\n"
512              "/buffer24 () def\n"
513              "%% -- colorimage emulation\n"
514              "systemdict /colorimage known {\n"
515              "   /ColorProc {\n"
516              "      currentfile buffer24 readhexstring pop\n"
517              "   } bind def\n"
518              "   /ColorImage {\n"
519              "      colorimage\n"
520              "   } bind def\n"
521              "} {\n"
522              "   /ColorProc {\n"
523              "      currentfile buffer24 readhexstring pop\n"
524              "      /data exch def /datalen data length def\n"
525              "      /cnt 0 def\n"
526              "      0 1 datalen 3 idiv 1 sub {\n"
527              "         buffer8 exch\n"
528              "                data cnt get 20 mul /cnt cnt 1 add def\n"
529              "                data cnt get 32 mul /cnt cnt 1 add def\n"
530              "                data cnt get 12 mul /cnt cnt 1 add def\n"
531              "                add add 64 idiv put\n"
532              "      } for\n"
533              "      buffer8 0 datalen 3 idiv getinterval\n"
534              "   } bind def\n"
535              "   /ColorImage {\n"
536              "      pop pop image\n"
537              "   } bind def\n"
538              "} ifelse\n");
539    } // level<2
540  write(str, "%%%%EndSetup\n\n");
541}
542
543void
544DjVuToPS::
545store_doc_trailer(ByteStream &str)
546{
547  /* Will store the {\em document trailer}, which is a clean-up code
548     used to return the PostScript interpeter back to the state, in which
549     it was before displaying this document. */
550  write(str, 
551        "%%%%Trailer\n"
552        "doc-origstate restore\n"
553        "%%%%EOF\n");
554}
555
556// ***********************************************************************
557// ***************************** PAGE LEVEL ******************************
558// ***********************************************************************
559
560static unsigned char *
561ASCII85_encode(unsigned char * dst, 
562               const unsigned char * src_start,
563               const unsigned char * src_end)
564{
565  /* Will read data between #src_start# and #src_end# pointers (excluding byte
566     pointed by #src_end#), encode it using {\bf ASCII85} algorithm, and
567     output the result into the destination buffer pointed by #dst#.  The
568     function returns pointer to the first unused byte in the destination
569     buffer. */
570  int symbols=0;
571  const unsigned char * ptr;
572  for(ptr=src_start;ptr<src_end;ptr+=4)
573    {
574      unsigned int num=0;
575      if (ptr+3<src_end)
576        {
577          num |= ptr[0] << 24; 
578          num |= ptr[1] << 16; 
579          num |= ptr[2] << 8; 
580          num |= ptr[3];
581        }
582      else
583        {
584          num |= ptr[0] << 24; 
585          if (ptr+1<src_end) 
586            num |= ptr[1] << 16; 
587          if (ptr+2<src_end) 
588            num |= ptr[2] << 8; 
589        }
590      int a1, a2, a3, a4, a5;
591      a5=num % 85; num/=85;
592      a4=num % 85; num/=85;
593      a3=num % 85; num/=85;
594      a2=num % 85;
595      a1=num / 85;
596      *dst++ = a1+33;
597      *dst++ = a2+33;
598      if (ptr+1<src_end)
599        *dst++ = a3+33;
600      if (ptr+2<src_end)
601        *dst++ = a4+33;
602      if (ptr+3<src_end)
603        *dst++ = a5+33;
604      symbols += 5;
605      if (symbols > 70 && ptr+4<src_end)
606        { 
607          *dst++='\n'; 
608          symbols=0; 
609        }
610    }
611  return dst;
612}
613
614static unsigned char *
615RLE_encode(unsigned char * dst,
616           const unsigned char * src_start,
617           const unsigned char * src_end)
618{
619  /* Will read data between #src_start# and #src_end# pointers (excluding byte
620     pointed by #src_end#), RLE encode it, and output the result into the
621     destination buffer pointed by #dst#.  #counter# is used to count the
622     number of output bytes.  The function returns pointer to the first unused
623     byte in the destination buffer. */
624  const unsigned char * ptr;
625  for(ptr=src_start;ptr<src_end;ptr++)
626    {
627      if (ptr==src_end-1)
628        {
629          *dst++=0; *dst++=*ptr;
630        } 
631      else if (ptr[0]!=ptr[1])
632        {
633          // Guess how many non repeating bytes we have
634          const unsigned char * ptr1;
635          for(ptr1=ptr+1;ptr1<src_end-1;ptr1++)
636            if (ptr1[0]==ptr1[1] || ptr1-ptr>=128) break;
637          int pixels=ptr1-ptr;
638          *dst++=pixels-1;
639          for(int cnt=0;cnt<pixels;cnt++)
640            *dst++=*ptr++;
641          ptr--;
642        } 
643      else
644        {
645          // Get the number of repeating bytes
646          const unsigned char * ptr1;
647          for(ptr1=ptr+1;ptr1<src_end-1;ptr1++)
648            if (ptr1[0]!=ptr1[1] || ptr1-ptr+1>=128) break;
649          int pixels=ptr1-ptr+1;
650          *dst++=257-pixels;
651          *dst++=*ptr;
652          ptr=ptr1;
653        }
654    }
655  return dst;
656}
657
658#define GRAY(r,g,b) (((r)*20+(g)*32+(b)*12)/64)
659
660void
661DjVuToPS::
662store_page_setup(ByteStream &str, 
663                 int dpi, 
664                 const GRect &grect, 
665                 int align )
666{
667  /* Will store PostScript code necessary to prepare page for
668     the coming \Ref{DjVuImage}. This is basically a scaling
669     code plus initialization of some buffers. */
670  if (options.get_format() == Options::EPS)
671    write(str, 
672          "/page-origstate save def\n"
673          "%% -- coordinate system\n"
674          "/image-dpi %d def\n"
675          "/image-x 0 def\n"
676          "/image-y 0 def\n"
677          "/image-width  %d def\n"
678          "/image-height %d def\n"
679          "/coeff 100 image-dpi div def\n"
680          "/a11 coeff def\n"
681          "/a12 0 def\n"
682          "/a13 0 def\n"
683          "/a21 0 def\n"
684          "/a22 coeff def\n"
685          "/a23 0 def\n"
686          "[a11 a21 a12 a22 a13 a23] concat\n"
687          "gsave 0 0 image-width image-height rectclip\n"
688          "%% -- begin printing\n",
689          dpi, grect.width(), grect.height() );
690  else
691    {
692      int margin = 0;
693      const char *xauto = "false";
694      const char *xportrait = "false";
695      const char *xfit = "false";
696      if (options.get_orientation()==Options::AUTO)
697        xauto = "true";
698      if (options.get_orientation()==Options::PORTRAIT)
699        xportrait = "true";
700      if (options.get_zoom()<=0)
701        xfit = "true";
702      if (options.get_cropmarks())
703        margin = 36;
704      else if (options.get_frame())
705        margin = 6;
706      write(str, 
707            "/page-origstate save def\n"
708            "%% -- coordinate system\n"
709            "/auto-orient %s def\n"
710            "/portrait %s def\n"
711            "/fit-page %s def\n"
712            "/zoom %d def\n"
713            "/image-dpi %d def\n"
714            "clippath pathbbox newpath\n"
715            "2 index sub exch 3 index sub\n"
716            "/page-width exch def\n"
717            "/page-height exch def\n"
718            "/page-y exch def\n"
719            "/page-x exch def\n"
720            "/image-x 0 def\n"
721            "/image-y 0 def\n"
722            "/image-width  %d def\n"
723            "/image-height %d def\n"
724            "/margin %d def\n"
725            "/halign %d def\n"
726            "/valign 0 def\n",
727            xauto, xportrait, xfit, options.get_zoom(), 
728            dpi, grect.width(), grect.height(),
729            margin, align );
730      write(str, 
731            "%% -- position page\n"
732            "auto-orient {\n"
733            "  image-height image-width sub\n"
734            "  page-height page-width sub\n"
735            "  mul 0 ge /portrait exch def\n" 
736            "} if\n"
737            "fit-page {\n"
738            "  /page-width page-width margin sub\n"
739            "     halign 0 eq { margin sub } if def\n"
740            "  /page-height page-height margin sub\n"
741            "     valign 0 eq { margin sub } if def\n"
742            "  /page-x page-x halign 0 ge { margin add } if def\n"
743            "  /page-y page-y valign 0 ge { margin add } if def\n"
744            "} if\n"
745            "portrait {\n"
746            "  fit-page {\n"
747            "    image-height page-height div\n"
748            "    image-width page-width div\n"
749            "    gt {\n"
750            "      page-height image-height div /coeff exch def\n"
751            "    } {\n"
752            "      page-width image-width div /coeff exch def\n"
753            "    } ifelse\n"
754            "  } {\n"
755            "    /coeff 72 image-dpi div zoom mul 100 div def\n"
756            "  } ifelse\n"
757            "  /start-x page-x page-width image-width\n"
758            "    coeff mul sub 2 div halign 1 add mul add def\n"
759            "  /start-y page-y page-height image-height\n"
760            "    coeff mul sub 2 div valign 1 add mul add def\n"
761            "  /a11 coeff def\n"
762            "  /a12 0 def\n"
763            "  /a13 start-x def\n"
764            "  /a21 0 def\n"
765            "  /a22 coeff def\n"
766            "  /a23 start-y def\n"
767            "} { %% landscape\n"
768            "  fit-page {\n"
769            "    image-height page-width div\n"
770            "    image-width page-height div\n"
771            "    gt {\n"
772            "      page-width image-height div /coeff exch def\n"
773            "    } {\n"
774            "      page-height image-width div /coeff exch def\n"
775            "    } ifelse\n"
776            "  } {\n"
777            "    /coeff 72 image-dpi div zoom mul 100 div def\n"
778            "  } ifelse\n"
779            "  /start-x page-x page-width add page-width image-height\n"
780            "    coeff mul sub 2 div valign 1 add mul sub def\n"
781            "  /start-y page-y page-height image-width\n"
782            "    coeff mul sub 2 div halign 1 add mul add def\n"
783            "  /a11 0 def\n"
784            "  /a12 coeff neg def\n"
785            "  /a13 start-x image-y coeff neg mul sub def\n"
786            "  /a21 coeff def\n"
787            "  /a22 0 def\n"
788            "  /a23 start-y image-x coeff mul add def \n"
789            "} ifelse\n"
790            "[a11 a21 a12 a22 a13 a23] concat\n"
791            "gsave 0 0 image-width image-height rectclip\n"
792            "%% -- begin print\n");
793    }
794}
795
796void
797DjVuToPS::
798store_page_trailer(ByteStream &str)
799{
800  write(str, 
801        "%% -- end print\n" 
802        "grestore\n");
803  if (options.get_frame())
804    write(str, 
805          "%% Drawing frame\n"
806          "gsave 0.7 setgray 0.5 coeff div setlinewidth 0 0\n"
807          "image-width image-height rectstroke\n"
808          "grestore\n");
809  if (options.get_cropmarks() &&
810      options.get_format() != Options::EPS )
811    write(str,
812          "%% Drawing crop marks\n"
813          "/cm { gsave translate rotate 1 coeff div dup scale\n"
814          "      0 setgray 0.5 setlinewidth -36 0 moveto 0 0 lineto\n"
815          "      0 -36 lineto stroke grestore } bind def\n"
816          "0 0 0 cm 180 image-width image-height cm\n"
817          "90 image-width 0 cm 270 0 image-height cm\n");
818  write(str,
819        "page-origstate restore\n");
820}
821
822static int
823compute_red(int w, int h, int rw, int rh)
824{
825  for (int red=1; red<16; red++)
826    if (((w+red-1)/red==rw) && ((h+red-1)/red==rh))
827      return red;
828  return 16;
829}
830
831static int
832get_bg_red(GP<DjVuImage> dimg) 
833{
834  GP<GPixmap> pm = 0;
835  // Access image size
836  int width = dimg->get_width();
837  int height = dimg->get_height();
838  if (width<=0 || height<=0) return 0;
839  // CASE1: Incremental BG IW44Image
840  GP<IW44Image> bg44 = dimg->get_bg44();
841  if (bg44)
842    {
843      int w = bg44->get_width();
844      int h = bg44->get_height();
845      // Avoid silly cases
846      if (w==0 || h==0 || width==0 || height==0)
847        return 0;
848      return compute_red(width,height,w,h);
849    }
850  // CASE 2: Raw background pixmap
851  GP<GPixmap>  bgpm = dimg->get_bgpm();
852  if (bgpm)
853    {
854      int w = bgpm->columns();
855      int h = bgpm->rows();
856      // Avoid silly cases
857      if (w==0 || h==0 || width==0 || height==0)
858        return 0;
859      return compute_red(width,height,w,h);
860    }
861  return 0;
862}
863
864static GP<GPixmap>
865get_bg_pixmap(GP<DjVuImage> dimg, const GRect &rect)
866{
867  GP<GPixmap> pm = 0;
868  // Access image size
869  int width = dimg->get_width();
870  int height = dimg->get_height();
871  GP<DjVuInfo> info = dimg->get_info();
872  if (width<=0 || height<=0 || !info) return 0;
873  // CASE1: Incremental BG IW44Image
874  GP<IW44Image> bg44 = dimg->get_bg44();
875  if (bg44)
876    {
877      int w = bg44->get_width();
878      int h = bg44->get_height();
879      // Avoid silly cases
880      if (w==0 || h==0 || width==0 || height==0)
881        return 0;
882      pm = bg44->get_pixmap(1,rect);
883      return pm;
884    }
885  // CASE 2: Raw background pixmap
886  GP<GPixmap>  bgpm = dimg->get_bgpm();
887  if (bgpm)
888    {
889      int w = bgpm->columns();
890      int h = bgpm->rows();
891      // Avoid silly cases
892      if (w==0 || h==0 || width==0 || height==0)
893        return 0;
894      pm->init(*bgpm, rect);
895      return pm;
896    }
897  // FAILURE
898  return 0;
899}
900
901void 
902DjVuToPS::
903make_gamma_ramp(GP<DjVuImage> dimg)
904{
905  double targetgamma = options.get_gamma();
906  double whitepoint = (options.get_sRGB() ? 255 : 280);
907  for (int i=0; i<256; i++)
908    ramp[i] = i;
909  if (! dimg->get_info()) 
910    return;
911  if (targetgamma < 0.1)
912    return;
913  double filegamma = dimg->get_info()->gamma;
914  double correction = filegamma / targetgamma;
915  if (correction<0.1 || correction>10)
916    return;
917  {
918    for (int i=0; i<256; i++)
919    {
920      double x = (double)(i)/255.0;
921      if (correction != 1.0) 
922        x = pow(x, correction);       
923      int j = (int) floor(whitepoint * x + 0.5);
924      ramp[i] = (j>255) ? 255 : (j<0) ? 0 : j;
925    }
926  }
927}
928
929void
930DjVuToPS::
931print_fg_2layer(ByteStream &str, 
932                GP<DjVuImage> dimg,
933                const GRect &prn_rect, 
934                unsigned char *blit_list)
935{
936  // Pure-jb2 or color-jb2 case.
937  GPixel p;
938  int currentx=0;
939  int currenty=0;
940  GP<DjVuPalette> pal = dimg->get_fgbc();
941  GP<JB2Image> jb2 = dimg->get_fgjb();
942  if (! jb2) return;
943  int num_blits = jb2->get_blit_count();
944  int current_blit;
945  for(current_blit=0; current_blit<num_blits; current_blit++)
946    {
947      if (blit_list[current_blit])
948        {
949          JB2Blit *blit = jb2->get_blit(current_blit);
950          if ((pal) && !(options.get_mode()==Options::BW))
951            {
952              pal->index_to_color(pal->colordata[current_blit], p);
953              if (options.get_color())
954                {
955                  write(str,"/%d %d %d %f %f %f c\n",
956                        blit->shapeno, 
957                        blit->left-currentx, blit->bottom-currenty,
958                        ramp[p.r]/255.0, ramp[p.g]/255.0, ramp[p.b]/255.0);
959                } 
960              else
961                {
962                  write(str,"/%d %d %d %f c\n",
963                        blit->shapeno, 
964                        blit->left-currentx, blit->bottom-currenty,
965                        ramp[GRAY(p.r, p.g, p.b)]/255.0);
966                }
967            }
968          else
969            {
970              write(str,"/%d %d %d s\n", 
971                    blit->shapeno, 
972                    blit->left-currentx, blit->bottom-currenty);
973            }
974          currentx = blit->left;
975          currenty = blit->bottom;
976        }
977    }
978}
979
980void
981DjVuToPS::
982print_fg_3layer(ByteStream &str, 
983                GP<DjVuImage> dimg,
984                const GRect &cprn_rect, 
985                unsigned char *blit_list )
986{
987  GRect prn_rect;
988  GP<GPixmap> brush = dimg->get_fgpm();
989  if (! brush) return;
990  int br = brush->rows();
991  int bc = brush->columns();
992  int red = compute_red(dimg->get_width(),dimg->get_height(),bc,br);
993  prn_rect.ymin = (cprn_rect.ymin)/red;
994  prn_rect.xmin = (cprn_rect.xmin)/red;
995  prn_rect.ymax = (cprn_rect.ymax+red-1)/red;
996  prn_rect.xmax = (cprn_rect.xmax+red-1)/red;
997  int color_nb = ((options.get_color()) ? 3 : 1);
998  GP<JB2Image> jb2 = dimg->get_fgjb();
999  if (! jb2) return;
1000  int pw = bc;
1001  int ph = 2;
1002
1003  write(str,
1004        "/P {\n" 
1005        "  11 dict dup begin 4 1 roll\n"
1006        "    /PatternType 1 def\n"
1007        "    /PaintType 1 def\n"
1008        "    /TilingType 1 def\n"
1009        "    /H exch def\n"
1010        "    /W exch def\n"
1011        "    /Red %d def\n"
1012        "    /PatternString exch def\n"
1013        "    /XStep W Red mul def\n"
1014        "    /YStep H Red mul def\n"
1015        "    /BBox [0 0 XStep YStep] def\n"
1016        "    /PaintProc { begin\n"
1017        "       Red dup scale\n"
1018        "       << /ImageType 1 /Width W /Height H\n"
1019        "          /BitsPerComponent 8 /Interpolate false\n"
1020        "          /Decode [%s] /ImageMatrix [1 0 0 1 0 0]\n"
1021        "          /DataSource PatternString >> image\n"
1022        "       end } bind def\n"
1023        "     0 0 XStep YStep rectclip\n"
1024        "     end matrix makepattern\n"
1025        "  /Pattern setcolorspace setpattern\n"
1026        "  0 0 moveto\n"
1027        "} def\n", red, (color_nb == 1) ? "0 1" : "0 1 0 1 0 1" );
1028
1029  unsigned char *s;
1030  GPBuffer<unsigned char> gs(s,pw*ph*color_nb);
1031  unsigned char *s_ascii_encoded;
1032  GPBuffer<unsigned char> gs_ascii_encoded(s_ascii_encoded,pw*ph*2*color_nb);
1033    {
1034      for (int y=prn_rect.ymin; y<prn_rect.ymax; y+=ph)
1035        for (int x=prn_rect.xmin; x<prn_rect.xmax; x+=pw)
1036          {
1037            int w = ((x+pw > prn_rect.xmax) ? prn_rect.xmax-x : pw);
1038            int h = ((y+ph > prn_rect.ymax) ? prn_rect.ymax-y : ph);
1039            int currentx = x * red;
1040            int currenty = y * red;
1041            // Find first intersecting blit
1042            int current_blit;
1043            int num_blits = jb2->get_blit_count();
1044            GRect rect1(currentx,currenty, w*red, h*red);
1045            for(current_blit=0; current_blit<num_blits; current_blit++)
1046              if (blit_list[current_blit])
1047                {
1048                  JB2Blit *blit = jb2->get_blit(current_blit);
1049                  GRect rect2(blit->left, blit->bottom,
1050                              jb2->get_shape(blit->shapeno).bits->columns(),
1051                              jb2->get_shape(blit->shapeno).bits->rows());
1052                  if (rect2.intersect(rect1,rect2)) 
1053                    break;
1054                }
1055            if (current_blit >= num_blits)
1056              continue;
1057            // Setup pattern
1058            write(str,"gsave %d %d translate\n", currentx, currenty);
1059            write(str,"<~");
1060            unsigned char *q = s;
1061            for(int current_row = y; current_row<y+h; current_row++)
1062              { 
1063                GPixel *row_pix = (*brush)[current_row];
1064                for(int current_col = x; current_col<x+w; current_col++)
1065                  { 
1066                    GPixel &p = row_pix[current_col];
1067                    if (color_nb>1)
1068                      {
1069                        *q++ = ramp[p.r];
1070                        *q++ = ramp[p.g];
1071                        *q++ = ramp[p.b];
1072                      }
1073                    else
1074                      {
1075                        *q++ = ramp[GRAY(p.r,p.g,p.b)];
1076                      }
1077                  }
1078              }
1079            unsigned char *stop_ascii = 
1080              ASCII85_encode(s_ascii_encoded,s,s+w*h*color_nb);
1081            *stop_ascii++='\0';
1082            write(str,"%s",s_ascii_encoded);
1083            write(str,"~> %d %d P\n", w, h);
1084            // Keep performing blits
1085            for(; current_blit<num_blits; current_blit++)
1086              if (blit_list[current_blit])
1087                {
1088                  JB2Blit *blit = jb2->get_blit(current_blit);
1089                  GRect rect2(blit->left, blit->bottom,
1090                              jb2->get_shape(blit->shapeno).bits->columns(),
1091                              jb2->get_shape(blit->shapeno).bits->rows()); 
1092                  if (rect2.intersect(rect1,rect2)) 
1093                    {   
1094                      write(str,"/%d %d %d s\n",
1095                            blit->shapeno, 
1096                            blit->left-currentx, blit->bottom-currenty);
1097                      currentx = blit->left;
1098                      currenty = blit->bottom;
1099                    }
1100                }
1101            write(str,"grestore\n");
1102          }
1103      // Cleanup
1104    }
1105}
1106
1107void
1108DjVuToPS::
1109print_fg(ByteStream &str, 
1110         GP<DjVuImage> dimg,
1111         const GRect &prn_rect )
1112{
1113  GP<JB2Image> jb2=dimg->get_fgjb();
1114  if (! jb2) return;
1115  int num_blits = jb2->get_blit_count();
1116  int num_shapes = jb2->get_shape_count();
1117  unsigned char *dict_shapes = 0;
1118  unsigned char *blit_list = 0;
1119  GPBuffer<unsigned char> gdict_shapes(dict_shapes,num_shapes);
1120  GPBuffer<unsigned char> gblit_list(blit_list,num_blits);
1121  for(int i=0; i<num_shapes; i++)
1122  {
1123    dict_shapes[i]=0;
1124  }
1125  for(int current_blit=0; current_blit<num_blits; current_blit++)
1126  {
1127    JB2Blit *blit = jb2->get_blit(current_blit);
1128    JB2Shape *shape = & jb2->get_shape(blit->shapeno);
1129    blit_list[current_blit] = 0;
1130    if (! shape->bits) 
1131      continue;
1132    GRect rect2(blit->left, blit->bottom, 
1133      shape->bits->columns(), shape->bits->rows());
1134    if (rect2.intersect(rect2, prn_rect))
1135    {
1136      dict_shapes[blit->shapeno] = 1;
1137      blit_list[current_blit] = 1;
1138    }
1139  }
1140  write(str,
1141    "%% --- now doing the foreground\n"
1142    "gsave DjVuColorSpace setcolorspace\n" );
1143      // Define font
1144  write(str,
1145    "/$DjVuLocalFont 7 dict def\n"
1146    "$DjVuLocalFont begin\n"
1147    "/FontType 3 def \n"
1148    "/FontMatrix [1 0 0 1 0 0] def\n"
1149    "/FontBBox [0 0 1 .5] def\n"
1150    "/CharStrings %d dict def\n"
1151    "/Encoding 2 array def\n"
1152    "0 1 1 {Encoding exch /.notdef put} for \n"
1153    "CharStrings begin\n"
1154    "/.notdef {} def\n",
1155    num_shapes+1);
1156  for(int current_shape=0; current_shape<num_shapes; current_shape++)
1157  {
1158    if (dict_shapes[current_shape])
1159    {
1160      JB2Shape *shape = & jb2->get_shape(current_shape);
1161      GP<GBitmap> bitmap = shape->bits;
1162      int rows = bitmap->rows();
1163      int columns = bitmap->columns();
1164      int nbytes = (columns+7)/8*rows+1;
1165      int nrows = rows;
1166      int nstrings=0;
1167      if (nbytes>(int)ps_string_size)   //max string length
1168      {
1169        nrows=ps_string_size/((columns+7)/8);
1170        nbytes=(columns+7)/8*nrows+1;
1171      }
1172      unsigned char *s_start;
1173      GPBuffer<unsigned char> gs_start(s_start,nbytes);
1174      unsigned char *s_ascii;
1175      GPBuffer<unsigned char> gs_ascii(s_ascii,nbytes*2);
1176      write(str,"/%d {",current_shape);
1177
1178      unsigned char *s = s_start;
1179      for(int current_row=0; current_row<rows; current_row++)
1180      { 
1181        unsigned char * row_bits = (*bitmap)[current_row];
1182        unsigned char acc = 0;
1183        unsigned char mask = 0;
1184        for(int current_col=0; current_col<columns; current_col++)
1185        {
1186          if (mask == 0)
1187            mask = 0x80;
1188          if (row_bits[current_col])
1189            acc |= mask;
1190          mask >>= 1;
1191          if (mask == 0)
1192          {
1193            *s=acc;
1194            s++;
1195            acc = mask = 0;
1196          }
1197        }
1198        if (mask != 0)
1199        {
1200          *s=acc;
1201          s++;
1202        }
1203        if (!((current_row+1)%nrows))
1204        {
1205          unsigned char *stop_ascii = ASCII85_encode(s_ascii,s_start,s); 
1206          *stop_ascii++='\0';
1207          write(str,"<~%s~> ",s_ascii);
1208          s=s_start;
1209          nstrings++;
1210        }
1211      }
1212      if (s!=s_start)
1213      {
1214        unsigned char *stop_ascii = ASCII85_encode(s_ascii,s_start,s);
1215        *stop_ascii++='\0';
1216        write(str,"<~%s~> ",s_ascii);
1217          nstrings++;
1218      }
1219      if (nstrings==1)
1220        write(str," %d %d g} def\n", columns, rows);                 
1221      else
1222        write(str," %d %d %d gn} def\n", columns, rows,nstrings);
1223    }
1224  }
1225  write(str, 
1226    "end\n"
1227    "/BuildGlyph {\n"
1228    "  exch /CharStrings get exch\n"
1229    "  2 copy known not\n"
1230    "  {pop /.notdef} if\n"
1231    "  get exec \n"
1232    "} bind def\n"
1233    "end\n"
1234    "/LocalDjVuFont $DjVuLocalFont definefont pop\n"
1235    "/LocalDjVuFont findfont setfont\n" );
1236  write(str,
1237    "-%d -%d translate\n"
1238    "0 0 moveto\n",
1239    prn_rect.xmin, prn_rect.ymin);
1240  // Print the foreground layer
1241  if (dimg->get_fgpm() && !(options.get_mode()==Options::BW)) 
1242    print_fg_3layer(str, dimg, prn_rect, blit_list);
1243  else
1244    print_fg_2layer(str, dimg, prn_rect, blit_list);       
1245  write(str, "/LocalDjVuFont undefinefont grestore\n");
1246}
1247
1248
1249void 
1250DjVuToPS::
1251print_bg(ByteStream &str, 
1252         GP<DjVuImage> dimg,
1253         const GRect &cprn_rect)
1254{
1255  GP<GPixmap> pm;
1256  GRect prn_rect;
1257  double print_done = 0;
1258  int red = 0;
1259  write(str, "%% --- now doing the background\n");
1260  if (! (red = get_bg_red(dimg)))
1261    return;
1262  write(str, 
1263        "gsave -%d -%d translate\n"
1264        "/bgred %d def bgred bgred scale\n",
1265        cprn_rect.xmin % red, 
1266        cprn_rect.ymin % red, 
1267        red);
1268  prn_rect.ymin = (cprn_rect.ymin)/red;
1269  prn_rect.ymax = (cprn_rect.ymax+red-1)/red;
1270  prn_rect.xmin = (cprn_rect.xmin)/red;
1271  prn_rect.xmax = (cprn_rect.xmax+red-1)/red;
1272  // Display image
1273  int band_bytes = 125000;
1274  int band_height = band_bytes/prn_rect.width();
1275  int buffer_size = band_height*prn_rect.width();
1276  int ps_chunk_height = 30960/prn_rect.width()+1;
1277  buffer_size = buffer_size*23/10;
1278  bool do_color = options.get_color();
1279  if (!dimg->is_legal_photo() && 
1280      !dimg->is_legal_compound() ||
1281      options.get_mode()==Options::BW) 
1282    do_color = false;
1283  if (do_color) 
1284    buffer_size *= 3;
1285  if (do_color)
1286    write(str, 
1287          "/bufferR %d string def\n"
1288          "/bufferG %d string def\n"
1289          "/bufferB %d string def\n"
1290          "DjVuColorSpace setcolorspace\n"
1291          "<< /ImageType 1\n"
1292          "   /Width %d\n"
1293          "   /Height %d\n"
1294          "   /BitsPerComponent 8\n"
1295          "   /Decode [0 1 0 1 0 1]\n"
1296          "   /ImageMatrix [1 0 0 1 0 0]\n"
1297          "   /MultipleDataSources true\n"
1298          "   /DataSource [ { ReadR } { ReadG } { ReadB } ]\n"
1299          "   /Interpolate false >> image\n",
1300          ps_chunk_height*prn_rect.width(),
1301          ps_chunk_height*prn_rect.width(),
1302          ps_chunk_height*prn_rect.width(),
1303          prn_rect.width(), prn_rect.height());
1304  else
1305    write(str, 
1306          "DjVuColorSpace setcolorspace\n"
1307          "<< /ImageType 1\n"
1308          "   /Width %d\n"
1309          "   /Height %d\n"
1310          "   /BitsPerComponent 8\n"
1311          "   /Decode [0 1]\n"
1312          "   /ImageMatrix [1 0 0 1 0 0]\n"
1313          "   /DataSource currentfile /ASCII85Decode\n"
1314          "      filter /RunLengthDecode filter\n"
1315          "   /Interpolate false >> image\n",
1316          prn_rect.width(), prn_rect.height());
1317 
1318  unsigned char *buffer;
1319  GPBuffer<unsigned char> gbuffer(buffer,buffer_size);
1320  unsigned char *rle_in;
1321  GPBuffer<unsigned char> grle_in(rle_in,ps_chunk_height*prn_rect.width());
1322  unsigned char *rle_out;
1323  GPBuffer<unsigned char> grle_out(rle_out,2*ps_chunk_height*prn_rect.width());
1324  {
1325    // Start storing image in bands
1326    unsigned char * rle_out_end = rle_out;
1327    GRect grectBand = prn_rect;
1328    grectBand.ymax = grectBand.ymin;
1329    while(grectBand.ymax < prn_rect.ymax)
1330      {
1331        GP<GPixmap> pm = 0;
1332        // Compute next band
1333        grectBand.ymin=grectBand.ymax;
1334        grectBand.ymax=grectBand.ymin+band_bytes/grectBand.width();
1335        if (grectBand.ymax>prn_rect.ymax)
1336          grectBand.ymax=prn_rect.ymax;
1337        pm = get_bg_pixmap(dimg, grectBand);
1338        unsigned char *buf_ptr = buffer;
1339        if (pm)
1340          {
1341            if (do_color)
1342              {
1343                int y=0;
1344                while(y<grectBand.height())
1345                  {
1346                    int row, y1;
1347                    unsigned char *ptr, *ptr1;
1348                    // Doing R component of current chunk
1349                    for (row=0,ptr=rle_in,y1=y; 
1350                         row<ps_chunk_height && y1<grectBand.height(); 
1351                         row++,y1++)
1352                      {
1353                        GPixel *pix = (*pm)[y1];
1354                        for (int x=grectBand.width(); x>0; x--,pix++)
1355                          *ptr++ = ramp[pix->r];
1356                      }
1357                    ptr1 = RLE_encode(rle_out, rle_in, ptr); 
1358                    *ptr1++ = 0x80;
1359                    buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1);
1360                    *buf_ptr++ = '~'; *buf_ptr++ = '>'; *buf_ptr++ = '\n';
1361                    // Doing G component of current chunk
1362                    for (row=0,ptr=rle_in,y1=y; 
1363                         row<ps_chunk_height && y1<grectBand.height(); 
1364                         row++,y1++)
1365                      {
1366                        GPixel *pix = (*pm)[y1];
1367                        for (int x=grectBand.width(); x>0; x--,pix++)
1368                          *ptr++ = ramp[pix->g];
1369                      }
1370                    ptr1 = RLE_encode(rle_out, rle_in, ptr); 
1371                    *ptr1++ = 0x80;
1372                    buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1);
1373                    *buf_ptr++ = '~'; 
1374                    *buf_ptr++ = '>'; 
1375                    *buf_ptr++ = '\n';
1376                    // Doing B component of current chunk
1377                    for (row=0, ptr=rle_in, y1=y;
1378                         row<ps_chunk_height && y1<grectBand.height(); 
1379                         row++,y1++)
1380                      {
1381                        GPixel *pix = (*pm)[y1];
1382                        for (int x=grectBand.width(); x>0; x--,pix++)
1383                          *ptr++ = ramp[pix->b];
1384                      }
1385                    ptr1 = RLE_encode(rle_out, rle_in, ptr);
1386                    *ptr1++ = 0x80;
1387                    buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1);
1388                    *buf_ptr++ = '~'; 
1389                    *buf_ptr++ = '>'; 
1390                    *buf_ptr++ = '\n';
1391                    y=y1;
1392                    if (refresh_cb) 
1393                      refresh_cb(refresh_cl_data);
1394                  } //while (y>=0)
1395              } 
1396            else
1397              {
1398                // Don't use color
1399                int y=0;
1400                while(y<grectBand.height())
1401                  {
1402                    unsigned char *ptr = rle_in;
1403                    for(int row=0; 
1404                        row<ps_chunk_height && y<grectBand.height(); 
1405                        row++,y++)
1406                      {
1407                        GPixel *pix = (*pm)[y];
1408                        for (int x=grectBand.width(); x>0; x--,pix++)
1409                          *ptr++ = ramp[GRAY(pix->r,pix->g,pix->b)];
1410                      }
1411                    rle_out_end = RLE_encode(rle_out_end, rle_in, ptr);
1412                    unsigned char *encode_to
1413                      = rle_out+(rle_out_end-rle_out)/4*4;
1414                    int bytes_left = rle_out_end-encode_to;
1415                    buf_ptr = ASCII85_encode(buf_ptr, rle_out, encode_to);
1416                    *buf_ptr++ = '\n';
1417                    memcpy(rle_out, encode_to, bytes_left);
1418                    rle_out_end = rle_out+bytes_left;
1419                    if (refresh_cb) 
1420                      refresh_cb(refresh_cl_data);
1421                  }
1422              }
1423          } // if (pm)
1424        str.writall(buffer, buf_ptr-buffer);
1425        if (prn_progress_cb)
1426          {
1427            double done=(double)(grectBand.ymax
1428                                 - prn_rect.ymin)/prn_rect.height();
1429            if ((int) (20*print_done)!=(int) (20*done))
1430              {
1431                print_done=done;
1432                prn_progress_cb(done, prn_progress_cl_data);
1433              }
1434          }
1435      } // while(grectBand.yax<grect.ymax)
1436    if (! do_color)
1437      {
1438        unsigned char * buf_ptr = buffer;
1439        *rle_out_end++ = 0x80;
1440        buf_ptr = ASCII85_encode(buf_ptr, rle_out, rle_out_end);
1441        *buf_ptr++='~'; 
1442        *buf_ptr++='>'; 
1443        *buf_ptr++='\n';
1444        str.writall(buffer, buf_ptr-buffer);
1445      }
1446  } 
1447  //restore the scaling
1448  write(str, "grestore\n");
1449}
1450
1451void
1452DjVuToPS::
1453print_image_lev1(ByteStream &str, 
1454                 GP<DjVuImage> dimg,
1455                 const GRect &prn_rect)
1456{         
1457  double print_done=0;
1458  GRect all(0,0, dimg->get_width(),dimg->get_height());
1459  GP<GPixmap> pm;
1460  GP<GBitmap> bm;
1461  GRect test(0,0,1,1);
1462  if (options.get_mode() == Options::FORE)
1463    pm = dimg->get_fg_pixmap(test, all);
1464  else if (options.get_mode() == Options::BACK)
1465    pm = dimg->get_bg_pixmap(test, all);
1466  else if (options.get_mode() != Options::BW)
1467    pm = dimg->get_pixmap(test, all);
1468  if (! pm)
1469    bm = dimg->get_bitmap(test,all);
1470  if (! pm && ! bm)
1471    return;
1472  write(str,
1473        "%% --- now doing a level 1 image\n"
1474        "gsave\n");
1475  // Display image
1476  int band_bytes=125000;
1477  int band_height = band_bytes/prn_rect.width();
1478  int buffer_size = band_height*prn_rect.width();
1479  buffer_size = buffer_size*21/10;
1480  bool do_color = false;
1481  bool do_color_or_gray = false;
1482  if (pm && (options.get_mode() != Options::BW))
1483    do_color_or_gray = true;
1484  if (do_color_or_gray && options.get_color())
1485    do_color = true;
1486  if (do_color) 
1487    buffer_size *= 3;
1488  if (do_color)
1489    write(str, "/buffer24 %d string def\n", 3*prn_rect.width());
1490  if (do_color_or_gray)
1491    write(str, "/buffer8 %d string def\n", prn_rect.width());
1492  else
1493    write(str, "/buffer8 %d string def\n", (prn_rect.width()+7)/8);
1494  if (do_color)
1495    {
1496      write(str,
1497            "%d %d 8 [ 1 0 0 1 0 0 ]\n"
1498            "{ ColorProc } false 3 ColorImage\n",
1499            prn_rect.width(), prn_rect.height());
1500    } 
1501  else if (do_color_or_gray)
1502    {
1503      write(str,
1504            "%d %d 8 [ 1 0 0 1 0 0 ]\n"
1505            "{ currentfile buffer8 readhexstring pop } image\n",
1506            prn_rect.width(), prn_rect.height());
1507    } 
1508  else
1509    {
1510      write(str,
1511            "%d %d 1 [ 1 0 0 1 0 0 ]\n"
1512            "{ currentfile buffer8 readhexstring pop } image\n",
1513            prn_rect.width(), prn_rect.height());
1514    }
1515  unsigned char * buffer;
1516  GPBuffer<unsigned char> gbuffer(buffer,buffer_size);
1517    {
1518      // Start storing image in bands
1519      GRect grectBand = prn_rect;
1520      grectBand.ymax = grectBand.ymin;
1521      while(grectBand.ymax < prn_rect.ymax)
1522        {
1523          // Compute next band
1524          grectBand.ymin = grectBand.ymax;
1525          grectBand.ymax = grectBand.ymin+band_bytes/grectBand.width();
1526          if (grectBand.ymax > prn_rect.ymax)
1527            grectBand.ymax = prn_rect.ymax;
1528          GRect all(0,0, dimg->get_width(),dimg->get_height());
1529          pm = 0;
1530          bm = 0;
1531          if (do_color_or_gray)
1532            {
1533              if (options.get_mode() == Options::FORE)
1534                pm = dimg->get_fg_pixmap(grectBand, all);
1535              else if (options.get_mode() == Options::BACK)
1536                pm = dimg->get_bg_pixmap(grectBand, all);
1537              else
1538                pm = dimg->get_pixmap(grectBand, all);
1539            }
1540          else 
1541            {
1542              bm = dimg->get_bitmap(grectBand, all);
1543            }
1544          // Store next band
1545          unsigned char *buf_ptr = buffer;
1546          int symbols=0;
1547          for (int y=0; y<grectBand.height(); y++)
1548            {
1549              if (pm && do_color_or_gray)
1550                {
1551                  GPixel *pix = (*pm)[y];
1552                  for (int x=grectBand.width(); x>0; x--, pix++)
1553                    {
1554                      if (do_color)
1555                        {
1556                          char *data;
1557                          data = bin2hex[ramp[pix->r]];
1558                          *buf_ptr++ = data[0];
1559                          *buf_ptr++ = data[1];
1560                          data = bin2hex[ramp[pix->g]];
1561                          *buf_ptr++ = data[0];
1562                          *buf_ptr++ = data[1];
1563                          data = bin2hex[ramp[pix->b]];
1564                          *buf_ptr++ = data[0];
1565                          *buf_ptr++ = data[1];
1566                          symbols += 6;
1567                        }
1568                      else
1569                        {
1570                          char *data;
1571                          data = bin2hex[ramp[GRAY(pix->r,pix->g,pix->b)]];
1572                          *buf_ptr++ = data[0];
1573                          *buf_ptr++ = data[1];
1574                          symbols += 2;
1575                        }
1576                      if (symbols>70) 
1577                        { 
1578                          *buf_ptr++ = '\n'; 
1579                          symbols=0; 
1580                        }
1581                    }
1582                }
1583              else if (bm)
1584                {
1585                  unsigned char *pix = (*bm)[y];
1586                  unsigned char acc = 0;
1587                  unsigned char mask = 0;
1588                  char *data;
1589                  for (int x=grectBand.width(); x>0; x--, pix++)
1590                    {
1591                      if (mask == 0)
1592                        mask = 0x80;
1593                      if (! *pix)
1594                        acc |= mask;
1595                      mask >>= 1;
1596                      if (mask == 0)
1597                        {
1598                          data = bin2hex[acc];
1599                          acc = 0;
1600                          *buf_ptr++ = data[0];
1601                          *buf_ptr++ = data[1];
1602                          symbols += 2;
1603                          if (symbols>70) 
1604                            { 
1605                              *buf_ptr++ = '\n'; 
1606                              symbols = 0; 
1607                            }
1608                        }
1609                    }
1610                  if (mask != 0) 
1611                    {
1612                      data = bin2hex[acc];
1613                      *buf_ptr++ = data[0];
1614                      *buf_ptr++ = data[1];
1615                      symbols += 2;
1616                    }
1617                }
1618              if (refresh_cb) 
1619                refresh_cb(refresh_cl_data);
1620            }
1621          str.writall(buffer, buf_ptr-buffer);
1622          if (prn_progress_cb)
1623            {
1624              double done=(double) (grectBand.ymax
1625                                    - prn_rect.ymin)/prn_rect.height();
1626              if ((int) (20*print_done)!=(int) (20*done))
1627                {
1628                  print_done=done;
1629                  prn_progress_cb(done, prn_progress_cl_data);
1630                }
1631            }
1632        }
1633      write(str, "\n");
1634    } 
1635  write(str, "grestore\n");
1636}
1637
1638void
1639DjVuToPS::
1640print_image_lev2(ByteStream &str, 
1641                 GP<DjVuImage> dimg,
1642                 const GRect &prn_rect)
1643{         
1644  double print_done=0;
1645  GRect all(0,0, dimg->get_width(),dimg->get_height());
1646  GP<GPixmap> pm;
1647  GRect test(0,0,1,1);
1648  if (options.get_mode() == Options::FORE)
1649    pm = dimg->get_fg_pixmap(test, all);
1650  else if (options.get_mode() == Options::BACK)
1651    pm = dimg->get_bg_pixmap(test, all);
1652  else if (options.get_mode() != Options::BW)
1653    pm = dimg->get_pixmap(test, all);
1654  if (! pm)
1655    return;
1656  write(str,
1657        "%% --- now doing a level 2 image\n"
1658        "gsave\n");
1659  // Display image
1660  int band_bytes=125000;
1661  int band_height = band_bytes/prn_rect.width();
1662  int buffer_size = band_height*prn_rect.width();
1663  int ps_chunk_height = 30960/prn_rect.width()+1;
1664  buffer_size = buffer_size*21/10 + 32;
1665  bool do_color = options.get_color();
1666  if (do_color)
1667    {
1668      buffer_size *= 3;
1669      write(str, 
1670            "/bufferR %d string def\n"
1671            "/bufferG %d string def\n"
1672            "/bufferB %d string def\n"
1673            "DjVuColorSpace setcolorspace\n"
1674            "<< /ImageType 1\n"
1675            "   /Width %d\n"
1676            "   /Height %d\n"
1677            "   /BitsPerComponent 8\n"
1678            "   /Decode [0 1 0 1 0 1]\n"
1679            "   /ImageMatrix [1 0 0 1 0 0]\n"
1680            "   /MultipleDataSources true\n"
1681            "   /DataSource [ { ReadR } { ReadG } { ReadB } ]\n"
1682            "   /Interpolate false >> image\n",
1683            ps_chunk_height*prn_rect.width(),
1684            ps_chunk_height*prn_rect.width(),
1685            ps_chunk_height*prn_rect.width(),
1686            prn_rect.width(), prn_rect.height());
1687    } 
1688  else
1689    {
1690      write(str, 
1691            "DjVuColorSpace setcolorspace\n"
1692            "<< /ImageType 1\n"
1693            "   /Width %d\n"
1694            "   /Height %d\n"
1695            "   /BitsPerComponent 8\n"
1696            "   /Decode [0 1]\n"
1697            "   /ImageMatrix [1 0 0 1 0 0]\n"
1698            "   /DataSource currentfile /ASCII85Decode\n"
1699            "       filter /RunLengthDecode filter\n"
1700            "   /Interpolate false >> image\n",
1701            prn_rect.width(), prn_rect.height());
1702    } 
1703  unsigned char *buffer;
1704  GPBuffer<unsigned char> gbuffer(buffer,buffer_size);
1705  unsigned char *rle_in;
1706  GPBuffer<unsigned char> grle_in(rle_in,ps_chunk_height*prn_rect.width());
1707  unsigned char *rle_out;
1708  GPBuffer<unsigned char> grle_out(rle_out,2*ps_chunk_height*prn_rect.width());
1709    {
1710      // Start storing image in bands
1711      unsigned char * rle_out_end = rle_out;
1712      GRect grectBand = prn_rect;
1713      grectBand.ymax = grectBand.ymin;
1714      while(grectBand.ymax < prn_rect.ymax)
1715        {
1716          // Compute next band
1717          grectBand.ymin = grectBand.ymax;
1718          grectBand.ymax = grectBand.ymin+band_bytes/grectBand.width();
1719          if (grectBand.ymax > prn_rect.ymax)
1720            grectBand.ymax = prn_rect.ymax;
1721          GRect all(0,0, dimg->get_width(),dimg->get_height());
1722          pm = 0;
1723          if (options.get_mode() == Options::FORE)
1724            pm = dimg->get_fg_pixmap(grectBand, all);
1725          else if (options.get_mode() == Options::BACK)
1726            pm = dimg->get_bg_pixmap(grectBand, all);
1727          else
1728            pm = dimg->get_pixmap(grectBand, all);
1729          // Store next band
1730          unsigned char *buf_ptr = buffer;
1731          if (do_color && pm)
1732            {
1733              int y=0;
1734              while(y<grectBand.height())
1735                {
1736                  int row, y1;
1737                  unsigned char *ptr, *ptr1;
1738                  // Doing R component of current chunk
1739                  for (row=0,ptr=rle_in,y1=y; 
1740                       row<ps_chunk_height && y1<grectBand.height(); 
1741                       row++,y1++)
1742                    {
1743                      GPixel *pix = (*pm)[y1];
1744                      for (int x=grectBand.width(); x>0; x--,pix++)
1745                        *ptr++ = ramp[pix->r];
1746                    }
1747                  ptr1 = RLE_encode(rle_out, rle_in, ptr); 
1748                  *ptr1++ = 0x80;
1749                  buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1);
1750                  *buf_ptr++ = '~'; *buf_ptr++ = '>'; *buf_ptr++ = '\n';
1751                  // Doing G component of current chunk
1752                  for (row=0,ptr=rle_in,y1=y; 
1753                       row<ps_chunk_height && y1<grectBand.height(); 
1754                       row++,y1++)
1755                    {
1756                      GPixel *pix = (*pm)[y1];
1757                      for (int x=grectBand.width(); x>0; x--,pix++)
1758                        *ptr++ = ramp[pix->g];
1759                    }
1760                  ptr1 = RLE_encode(rle_out, rle_in, ptr); 
1761                  *ptr1++ = 0x80;
1762                  buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1);
1763                  *buf_ptr++ = '~'; 
1764                  *buf_ptr++ = '>'; 
1765                  *buf_ptr++ = '\n';
1766                  // Doing B component of current chunk
1767                  for (row=0, ptr=rle_in, y1=y;
1768                       row<ps_chunk_height && y1<grectBand.height(); 
1769                       row++,y1++)
1770                    {
1771                      GPixel *pix = (*pm)[y1];
1772                      for (int x=grectBand.width(); x>0; x--,pix++)
1773                        *ptr++ = ramp[pix->b];
1774                    }
1775                  ptr1 = RLE_encode(rle_out, rle_in, ptr);
1776                  *ptr1++ = 0x80;
1777                  buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1);
1778                  *buf_ptr++ = '~'; 
1779                  *buf_ptr++ = '>'; 
1780                  *buf_ptr++ = '\n';
1781                  y=y1;
1782                  if (refresh_cb) 
1783                    refresh_cb(refresh_cl_data);
1784                } //while (y>=0)
1785            } 
1786          else if (pm)
1787            {
1788              // Don't use color
1789              int y=0;
1790              while(y<grectBand.height())
1791                {
1792                  unsigned char *ptr = rle_in;
1793                  for(int row=0;
1794                      row<ps_chunk_height && y<grectBand.height(); 
1795                      row++,y++)
1796                    {
1797                      GPixel *pix = (*pm)[y];
1798                      for (int x=grectBand.width(); x>0; x--,pix++)
1799                        *ptr++ = ramp[GRAY(pix->r,pix->g,pix->b)];
1800                    }
1801                  rle_out_end = RLE_encode(rle_out_end, rle_in, ptr);
1802                  unsigned char *encode_to = rle_out
1803                    + (rle_out_end-rle_out)/4*4;
1804                  int bytes_left = rle_out_end-encode_to;
1805                  buf_ptr = ASCII85_encode(buf_ptr, rle_out, encode_to);
1806                  *buf_ptr++ = '\n';
1807                  memcpy(rle_out, encode_to, bytes_left);
1808                  rle_out_end = rle_out+bytes_left;
1809                  if (refresh_cb) 
1810                    refresh_cb(refresh_cl_data);
1811                }
1812              if (grectBand.ymax >= prn_rect.ymax)
1813                {
1814                  *rle_out_end++ = 0x80; // Add EOF marker
1815                  buf_ptr = ASCII85_encode(buf_ptr, rle_out, rle_out_end);
1816                  *buf_ptr++ = '~'; 
1817                  *buf_ptr++ = '>'; 
1818                  *buf_ptr++ = '\n';
1819                }
1820            }
1821          str.writall(buffer, buf_ptr-buffer);
1822          if (prn_progress_cb)
1823            {
1824              double done=(double) (grectBand.ymax
1825                                    - prn_rect.ymin)/prn_rect.height();
1826              if ((int) (20*print_done)!=(int) (20*done))
1827                {
1828                  print_done=done;
1829                  prn_progress_cb(done, prn_progress_cl_data);
1830                }
1831            }
1832        }
1833      write(str, "\n");
1834    } 
1835  write(str, "grestore\n");
1836}
1837
1838static void 
1839get_anno_sub(IFFByteStream &iff, IFFByteStream &out)
1840{
1841  GUTF8String chkid;
1842  while (iff.get_chunk(chkid))
1843    {
1844      if (iff.composite())
1845        get_anno_sub(iff, out);
1846      else if (chkid == "ANTa" || chkid == "ANTz" ||
1847               chkid == "TXTa" || chkid == "TXTz"   )
1848        {
1849          out.put_chunk(chkid);
1850          out.copy(*iff.get_bytestream());
1851          out.close_chunk();
1852        }
1853      iff.close_chunk();
1854    }
1855}
1856
1857static GP<ByteStream>
1858get_anno(GP<DjVuFile> f)
1859{
1860  if (! f->anno) 
1861    {
1862      GP<ByteStream> bs = f->get_init_data_pool()->get_stream();
1863      GP<ByteStream> anno = ByteStream::create();
1864      GP<IFFByteStream> in = IFFByteStream::create(bs);
1865      GP<IFFByteStream> out = IFFByteStream::create(anno);
1866      get_anno_sub(*in, *out);
1867      f->anno = anno;
1868    }
1869  f->anno->seek(0);
1870  return f->anno;
1871}
1872
1873static GP<DjVuTXT>
1874get_text(GP<DjVuFile> file)
1875{ 
1876  GUTF8String chkid;
1877  GP<IFFByteStream> iff = IFFByteStream::create(get_anno(file));
1878  while (iff->get_chunk(chkid))
1879    {
1880      if (chkid == "TXTa") 
1881        {
1882          GP<DjVuTXT> txt = DjVuTXT::create();
1883          txt->decode(iff->get_bytestream());
1884          return txt;
1885        }
1886      else if (chkid == "TXTz") 
1887        {
1888          GP<DjVuTXT> txt = DjVuTXT::create();
1889          GP<ByteStream> bsiff = BSByteStream::create(iff->get_bytestream());
1890          txt->decode(bsiff);
1891          return txt;
1892        }
1893      iff->close_chunk();
1894    }
1895  return 0;
1896}
1897
1898static void
1899print_ps_string(const char *data, int length, ByteStream &out)
1900{
1901  while (*data && length>0) 
1902    {
1903      int span = 0;
1904      while (span<length && data[span]>=0x20 && data[span]<0x7f 
1905             && data[span]!='(' && data[span]!=')' && data[span]!='\\' )
1906        span++;
1907      if (span > 0) 
1908        {
1909          out.write(data, span);
1910          data += span;
1911          length -= span;
1912        }
1913      else
1914        {
1915          char buffer[5];
1916          sprintf(buffer,"\\%03o", *data);
1917          out.write(buffer,4);
1918          data += 1;
1919          length -= 1;
1920        }
1921    }
1922}
1923
1924static void
1925print_txt_sub(DjVuTXT &txt, DjVuTXT::Zone &zone, 
1926              ByteStream &out,int &lastx,int &lasty)
1927{
1928  // Get separator
1929  char separator = 0;
1930  switch(zone.ztype)
1931    {
1932    case DjVuTXT::COLUMN: 
1933      separator = DjVuTXT::end_of_column; break;
1934    case DjVuTXT::REGION: 
1935      separator = DjVuTXT::end_of_region; break;
1936    case DjVuTXT::PARAGRAPH: 
1937      separator = DjVuTXT::end_of_paragraph; break;
1938    case DjVuTXT::LINE: 
1939      separator = DjVuTXT::end_of_line; break;
1940    case DjVuTXT::WORD: 
1941      separator = ' '; break;
1942    default:
1943      separator = 0; break;
1944    }
1945  // Zone children
1946  if (zone.children.isempty()) 
1947    {
1948      const char *data = (const char*)txt.textUTF8 + zone.text_start;
1949      int length = zone.text_length;
1950      if (data[length-1] == separator)
1951        length -= 1;
1952      out.write("( ",2);
1953      print_ps_string(data,length,out);
1954      out.write(")",1);
1955      GUTF8String message;
1956      int tmpx= zone.rect.xmin-lastx;
1957      int tmpy= zone.rect.ymin-lasty;
1958      message.format(" %d %d S \n", tmpx, tmpy);
1959      lastx=zone.rect.xmin;
1960      lasty=zone.rect.ymin;
1961      out.write((const char*)message, message.length());
1962    }
1963  else
1964    {
1965      if (zone.ztype==DjVuTXT::LINE)
1966        {
1967          GUTF8String message;
1968          message.format("%d F\n",zone.rect.ymax-zone.rect.ymin);
1969          out.write((const char*)message,message.length());
1970        }
1971      for (GPosition pos=zone.children; pos; ++pos)
1972        print_txt_sub(txt, zone.children[pos], out,lastx,lasty);
1973    }
1974}
1975
1976static void
1977print_txt(GP<DjVuTXT> txt, 
1978          ByteStream &out )
1979{
1980  if (txt)
1981    {
1982      int lastx=0;
1983      int lasty=0;
1984      GUTF8String message = 
1985        "%% -- now doing hidden text\n"
1986        "gsave -1 -1 0 0 clip 0 0 moveto\n";
1987      out.write((const char*)message,message.length());
1988      print_txt_sub(*txt, txt->page_zone, out,lastx,lasty);
1989      message = 
1990        "grestore \n";
1991      out.write((const char*)message,message.length());
1992    }
1993}
1994
1995void
1996DjVuToPS::
1997print_image(ByteStream &str, 
1998            GP<DjVuImage> dimg,
1999            const GRect &prn_rect, 
2000            GP<DjVuTXT> txt)
2001{
2002  /* Just outputs the specified image. The function assumes, that
2003     all add-ons (like {\em document setup}, {\em page setup}) are
2004     already there. It will just output the image. Since
2005     output of this function will generate PostScript errors when
2006     used without output of auxiliary functions, it should be
2007     used carefully. */
2008  DEBUG_MSG("DjVuToPS::print_image(): Printing DjVuImage to a stream\n");
2009  DEBUG_MAKE_INDENT(3);
2010  if (!dimg)
2011    G_THROW(ERR_MSG("DjVuToPS.empty_image"));
2012  if (prn_rect.isempty())
2013    G_THROW(ERR_MSG("DjVuToPS.empty_rect"));
2014  if (prn_progress_cb)
2015    prn_progress_cb(0, prn_progress_cl_data);
2016  // Compute information for chosen display mode
2017  print_txt(txt, str);
2018  make_gamma_ramp(dimg);
2019  if (options.get_level() < 2)
2020    {
2021      print_image_lev1(str, dimg, prn_rect);
2022    }
2023  else if (options.get_level() < 3 && dimg->get_fgpm())
2024    {
2025      switch(options.get_mode())
2026        {
2027        case Options::COLOR:
2028        case Options::FORE:
2029          print_image_lev2(str, dimg, prn_rect);
2030          break;
2031        case Options::BW:
2032          print_fg(str, dimg, prn_rect);
2033          break;
2034        case Options::BACK:
2035          print_bg(str, dimg, prn_rect);
2036          break;
2037        }
2038    }
2039  else 
2040    {
2041      switch(options.get_mode())
2042        {
2043        case Options::COLOR:
2044          print_bg(str, dimg, prn_rect);
2045          print_fg(str, dimg, prn_rect);
2046          break;
2047        case Options::FORE:
2048        case Options::BW:
2049          print_fg(str, dimg, prn_rect);
2050          break;
2051        case Options::BACK:
2052          print_bg(str, dimg, prn_rect);
2053          break;
2054        }
2055    }
2056  if (prn_progress_cb)
2057    prn_progress_cb(1, prn_progress_cl_data);
2058}
2059
2060
2061
2062
2063// ***********************************************************************
2064// ******* PUBLIC FUNCTION FOR PRINTING A SINGLE PAGE ********************
2065// ***********************************************************************
2066
2067
2068
2069
2070void
2071DjVuToPS::
2072print(ByteStream &str, 
2073      GP<DjVuImage> dimg,
2074      const GRect &prn_rect_in, 
2075      const GRect &img_rect,
2076      int override_dpi)
2077{
2078  DEBUG_MSG("DjVuToPS::print(): Printing DjVu page to a stream\n");
2079  DEBUG_MAKE_INDENT(3);
2080  GRect prn_rect;
2081  prn_rect.intersect(prn_rect_in, img_rect);
2082  DEBUG_MSG("prn_rect=(" << prn_rect.xmin << ", " << prn_rect.ymin << ", " <<
2083            prn_rect.width() << ", " << prn_rect.height() << ")\n");
2084  DEBUG_MSG("img_rect=(" << img_rect.xmin << ", " << img_rect.ymin << ", " <<
2085            img_rect.width() << ", " << img_rect.height() << ")\n");
2086  if (!dimg)
2087    G_THROW(ERR_MSG("DjVuToPS.empty_image"));
2088  if (prn_rect.isempty())
2089    G_THROW(ERR_MSG("DjVuToPS.empty_rect"));
2090  if (img_rect.isempty())
2091    G_THROW(ERR_MSG("DjVuToPS.bad_scale"));
2092  GRectMapper mapper;
2093  mapper.set_input(img_rect);
2094  GRect full_rect(0, 0, dimg->get_width(), dimg->get_height());
2095  mapper.set_output(full_rect);
2096  mapper.map(prn_rect);
2097  int image_dpi =  dimg->get_dpi();
2098  if (override_dpi>0) 
2099    image_dpi = override_dpi;
2100  if (image_dpi <= 0) 
2101    image_dpi = 300;
2102  store_doc_prolog(str, 1, (int)(image_dpi), &prn_rect);
2103  store_doc_setup(str);
2104  write(str,"%%%%Page: 1 1\n");
2105  store_page_setup(str, (int)(image_dpi), prn_rect);
2106  print_image(str, dimg, prn_rect, 0);
2107  store_page_trailer(str);
2108  write(str,"showpage\n");
2109  store_doc_trailer(str);
2110}
2111
2112
2113
2114
2115// ***********************************************************************
2116// *************************** DOCUMENT LEVEL ****************************
2117// ***********************************************************************
2118
2119
2120void
2121DjVuToPS::
2122parse_range(GP<DjVuDocument> doc, 
2123             GUTF8String page_range, 
2124             GList<int> &pages_todo)
2125{
2126  int doc_pages = doc->get_pages_num();
2127  if (!page_range.length())
2128    page_range.format("1-%d", doc_pages);
2129  DEBUG_MSG("page_range='" << (const char *)page_range << "'\n");
2130  int spec = 0;
2131  int both = 1;
2132  int start_page = 1;
2133  int end_page = doc_pages;
2134  const char *q = (const char*)page_range;
2135  char *p = (char*)q;
2136  while (*p)
2137    {
2138      while (*p==' ')
2139        p += 1;
2140      if (! *p)
2141        break;
2142      if (*p>='0' && *p<='9') 
2143        {
2144          end_page = strtol(p, &p, 10);
2145          spec = 1;
2146        } 
2147      else if (*p=='$') 
2148        {
2149          spec = 1;
2150          end_page = doc_pages;
2151          p += 1;
2152        } 
2153      else if (both) 
2154        {
2155          end_page = 1;
2156        } 
2157      else 
2158        {
2159          end_page = doc_pages;
2160        }
2161      while (*p==' ')
2162        p += 1;
2163      if (both)
2164        {
2165          start_page = end_page;
2166          if (*p == '-') 
2167            {
2168              p += 1;
2169              both = 0;
2170              continue;
2171            }
2172        }
2173      both = 1;
2174      while (*p==' ')
2175        p += 1;
2176      if (*p && *p != ',')
2177        G_THROW(ERR_MSG("DjVuToPS.bad_range") 
2178                + GUTF8String("\t") + GUTF8String(p) );
2179      if (*p == ',')
2180        p += 1;
2181      if (! spec)
2182        G_THROW(ERR_MSG("DjVuToPS.bad_range") 
2183                + GUTF8String("\t") + page_range );
2184      spec = 0;
2185      if (end_page < 0)
2186        end_page = 0;
2187      if (start_page < 0)
2188        start_page = 0;
2189      if (end_page > doc_pages)
2190        end_page = doc_pages;
2191      if (start_page > doc_pages)
2192        start_page = doc_pages;
2193      if (start_page <= end_page)
2194        for(int page_num=start_page; page_num<=end_page; page_num++)
2195          pages_todo.append(page_num-1);
2196      else
2197        for(int page_num=start_page; page_num>=end_page; page_num--)
2198          pages_todo.append(page_num-1);
2199    }
2200}
2201
2202class DjVuToPS::DecodePort : public DjVuPort
2203{
2204protected:
2205  DecodePort(void);
2206public:
2207  static GP<DecodePort> create(void);
2208  GEvent decode_event;
2209  bool decode_event_received;
2210  double decode_done;
2211  GURL decode_page_url;
2212  virtual void notify_file_flags_changed(const DjVuFile*,long,long);
2213  virtual void notify_decode_progress(const DjVuPort*,double);
2214};
2215
2216DjVuToPS::DecodePort::
2217DecodePort(void)
2218  : decode_event_received(false),
2219    decode_done((double)0) 
2220{
2221}
2222
2223GP<DjVuToPS::DecodePort> 
2224DjVuToPS::DecodePort::
2225create(void)
2226{
2227  return new DecodePort;
2228}
2229
2230void 
2231DjVuToPS::DecodePort::
2232notify_file_flags_changed(const DjVuFile *source, 
2233                          long set_mask, long clr_mask)
2234{
2235  // WARNING! This function is called from another thread
2236  if (set_mask & (DjVuFile::DECODE_OK | 
2237                  DjVuFile::DECODE_FAILED | 
2238                  DjVuFile::DECODE_STOPPED ))
2239    {
2240      if (source->get_url() == decode_page_url)
2241        {
2242          decode_event_received=true;
2243          decode_event.set();
2244        }
2245    }
2246}
2247
2248void 
2249DjVuToPS::DecodePort::
2250notify_decode_progress(const DjVuPort *source, double done)
2251{
2252  // WARNING! This function is called from another thread
2253  if (source->inherits("DjVuFile"))
2254    {
2255      DjVuFile * file=(DjVuFile *) source;
2256      if (file->get_url()==decode_page_url)
2257        if ((int) (decode_done*20)!=(int) (done*20))
2258          {
2259            decode_done=done;
2260            decode_event_received=true;
2261            decode_event.set();
2262          }
2263    }
2264}
2265
2266void 
2267DjVuToPS::
2268set_refresh_cb(void (*_refresh_cb)(void*), void *_refresh_cl_data)
2269{
2270  refresh_cb = _refresh_cb;
2271  refresh_cl_data = _refresh_cl_data;
2272}
2273
2274void 
2275DjVuToPS::
2276set_prn_progress_cb(void (*_prn_progress_cb)(double, void *),
2277                    void *_prn_progress_cl_data)
2278{
2279  prn_progress_cb=_prn_progress_cb;
2280  prn_progress_cl_data=_prn_progress_cl_data;
2281}
2282
2283void 
2284DjVuToPS::
2285set_dec_progress_cb(void (*_dec_progress_cb)(double, void *),
2286                    void *_dec_progress_cl_data)
2287{
2288  dec_progress_cb=_dec_progress_cb;
2289  dec_progress_cl_data=_dec_progress_cl_data;
2290}
2291
2292void 
2293DjVuToPS::
2294set_info_cb(void (*_info_cb)(int, int, int, Stage, void*),
2295            void *_info_cl_data)
2296{
2297  info_cb=_info_cb;
2298  info_cl_data=_info_cl_data;
2299}
2300
2301GP<DjVuImage>
2302DjVuToPS::
2303decode_page(GP<DjVuDocument> doc, 
2304            int page_num, int cnt, int todo)
2305{
2306  DEBUG_MSG("processing page #" << page_num << "\n");
2307  if (! port)
2308    {
2309      port = DecodePort::create();
2310      DjVuPort::get_portcaster()->add_route((DjVuDocument*)doc, port);
2311    }
2312  port->decode_event_received = false;
2313  port->decode_done = 0;
2314  GP<DjVuFile> djvu_file;
2315  GP<DjVuImage> dimg;
2316  if (page_num >= 0 && page_num < doc->get_pages_num())
2317    djvu_file = doc->get_djvu_file(page_num);
2318  if (! djvu_file )
2319    return 0;
2320  if (djvu_file->is_decode_ok())
2321    return doc->get_page(page_num, false);
2322  // This is the best place to call info_cb(). Note, that
2323  // get_page() will start decoding if necessary, and will not
2324  // return until the decoding is over in a single threaded
2325  // environment. That's why we call get_djvu_file() first.
2326  if (info_cb)
2327    info_cb(page_num, cnt, todo, DECODING, info_cl_data);
2328  // Do NOT decode the page synchronously here!!!
2329  // The plugin will deadlock otherwise.
2330  dimg = doc->get_page(page_num, false);
2331  djvu_file = dimg->get_djvu_file();
2332  port->decode_page_url = djvu_file->get_url();
2333  if (djvu_file->is_decode_ok())
2334    return dimg;
2335  DEBUG_MSG("decoding\n");
2336  if (dec_progress_cb)
2337    dec_progress_cb(0, dec_progress_cl_data);
2338  while(! djvu_file->is_decode_ok())
2339    {
2340      while(!port->decode_event_received && 
2341            !djvu_file->is_decode_ok())
2342        {
2343          port->decode_event.wait(250);
2344          if (refresh_cb) 
2345            refresh_cb(refresh_cl_data);
2346        }
2347      port->decode_event_received = false;
2348      if (djvu_file->is_decode_failed() || 
2349          djvu_file->is_decode_stopped())
2350        G_THROW(ERR_MSG("DjVuToPS.no_image") 
2351                + GUTF8String("\t") 
2352                + GUTF8String(page_num));
2353      if (dec_progress_cb)
2354        dec_progress_cb(port->decode_done, dec_progress_cl_data);
2355    }
2356  if (dec_progress_cb)
2357    dec_progress_cb(1, dec_progress_cl_data);
2358  return dimg;
2359}
2360
2361void
2362DjVuToPS::
2363process_single_page(ByteStream &str, 
2364                    GP<DjVuDocument> doc,
2365                    int page_num, int cnt, int todo,
2366                    int magic)
2367{
2368  GP<DjVuTXT> txt;
2369  GP<DjVuImage> dimg;
2370  dimg = decode_page(doc, page_num, cnt, todo);
2371  if (options.get_text())
2372    txt = get_text(dimg->get_djvu_file());
2373  if (info_cb)
2374    info_cb(page_num, cnt, todo, PRINTING, info_cl_data);
2375  if (!magic)
2376    write(str, "%%%%Page: %d %d\n", page_num+1, cnt+1);
2377  if (dimg)
2378    {
2379      int dpi = dimg->get_dpi();
2380      dpi = ((dpi <= 0) ? 300 : dpi);
2381      GRect img_rect(0, 0, dimg->get_width(), dimg->get_height());
2382      store_page_setup(str, dpi, img_rect, magic);
2383      print_image(str, dimg, img_rect,txt);
2384      store_page_trailer(str);
2385    }
2386  if (!magic)
2387    write(str,"showpage\n");
2388}
2389
2390
2391struct pdata {
2392  int page1, page2;
2393  int smax, spos;
2394  int offset;
2395};
2396
2397void 
2398DjVuToPS::
2399process_double_page(ByteStream &str, 
2400                    GP<DjVuDocument> doc,
2401                    void *v, int cnt, int todo)
2402{
2403  const pdata *inf = (const pdata*)v;
2404  int off = abs(inf->offset);
2405  write(str,
2406        "%%%%Page: (%d,%d) %d\n"
2407        "gsave\n"
2408        "/fold-dict 8 dict dup 3 1 roll def begin\n"
2409        " clippath pathbbox newpath pop pop translate\n"
2410        " clippath pathbbox newpath 4 2 roll pop pop\n"
2411        " /ph exch def\n"
2412        " /pw exch def\n"
2413        " /w ph %d sub 2 div def\n"
2414        " /m1 %d def\n"
2415        " /m2 %d def\n"
2416        "end\n",
2417        inf->page1 + 1, inf->page2 + 1, cnt,
2418        2 * (off + options.get_bookletfold(inf->smax-1)),
2419        inf->offset + options.get_bookletfold(inf->spos),
2420        inf->offset - options.get_bookletfold(inf->spos));
2421  if (options.get_cropmarks())
2422    write(str,
2423          "%% -- folding marks\n"
2424          "fold-dict begin\n"
2425          " 0 setgray 0.5 setlinewidth\n"
2426          " ph m1 m2 add add 2 div dup\n"
2427          " 0 exch moveto 36 0 rlineto stroke\n"
2428          " pw exch moveto -36 0 rlineto stroke\n"
2429          "end\n");
2430  write(str,
2431        "%% -- first page\n"
2432        "gsave fold-dict begin\n"
2433        " 0 ph 2 div w add m1 add translate 270 rotate\n"
2434        " 0 0 w pw rectclip end\n");
2435  if (inf->page1 >= 0)
2436    process_single_page(str, doc, inf->page1, cnt*2, todo*2, +1);
2437  write(str,
2438        "grestore\n"
2439        "%% -- second page\n"
2440        "gsave fold-dict begin\n"
2441        " 0 ph 2 div m2 add translate 270 rotate\n"
2442        " 0 0 w pw rectclip end\n");
2443  if (inf->page2 >= 0)
2444    process_single_page(str, doc, inf->page2, cnt*2+1, todo*2, -1);
2445  write(str,
2446        "grestore\n"
2447        "grestore\n"
2448        "showpage\n");
2449}
2450
2451static void
2452booklet_order(GList<int>& pages, int smax)
2453{
2454  // -- make a multiple of four
2455  while (pages.size() & 0x3)
2456    pages.append(-1);
2457  // -- copy to array
2458  int i = 0;
2459  int n = pages.size();
2460  GTArray<int> p(0,n-1);
2461  for (GPosition pos=pages; pos; ++pos)
2462    p[i++] = pages[pos];
2463  // -- rebuild
2464  pages.empty();
2465  for (i=0; i<n; i+=smax)
2466    {
2467      int lo = i;
2468      int hi = i+smax-1;
2469      if (hi >= n)
2470        hi = n-1;
2471      while (lo < hi)
2472        {
2473          pages.append(p[hi--]);
2474          pages.append(p[lo++]);
2475          pages.append(p[lo++]);
2476          pages.append(p[hi--]);
2477        }
2478    }
2479}
2480
2481
2482// ***********************************************************************
2483// ******* PUBLIC FUNCTIONS FOR PRINTING MULTIPLE PAGES ******************
2484// ***********************************************************************
2485
2486
2487
2488void
2489DjVuToPS::
2490print(ByteStream &str, 
2491      GP<DjVuDocument> doc, 
2492      GUTF8String page_range)
2493{
2494  DEBUG_MSG("DjVuToPS::print(): Printing DjVu document\n");
2495  DEBUG_MAKE_INDENT(3);
2496  // Get page range
2497  GList<int> pages_todo;
2498  parse_range(doc, page_range, pages_todo);
2499  int todo = pages_todo.size();
2500  if (options.get_format()==Options::EPS)
2501    {
2502      /* Encapsulated Postscript mode */
2503      if (todo != 1)
2504        G_THROW(ERR_MSG("DjVuToPS.only_one_page"));
2505      GPosition pos = pages_todo;
2506      int page_num = pages_todo[pos];
2507      GP<DjVuImage> dimg = decode_page(doc,page_num,0,todo);
2508      if (! dimg)
2509        G_THROW(ERR_MSG("DjVuToPS.no_image") + GUTF8String("\t1"));
2510      GRect bbox(0, 0, dimg->get_width(), dimg->get_height());
2511      store_doc_prolog(str, 1, dimg->get_dpi(), &bbox);
2512      store_doc_setup(str);
2513      process_single_page(str, doc, page_num, 0, todo, 0);
2514    }
2515  else if (options.get_bookletmode()==Options::OFF)
2516    {
2517      /* Normal mode */
2518      int cnt = 0;
2519      store_doc_prolog(str, todo, 0, 0);
2520      store_doc_setup(str);
2521      for(GPosition pos = pages_todo; pos; ++pos)
2522        process_single_page(str,doc,pages_todo[pos],cnt++,todo,0);
2523      store_doc_trailer(str);
2524    }
2525  else
2526    {
2527      /* Booklet mode */
2528      int sheets_left = (todo+3)/4;
2529      int sides_todo = sheets_left;
2530      if (options.get_bookletmode() == Options::RECTOVERSO)
2531        sides_todo *= 2;
2532      int sheets_max = (options.get_bookletmax()+3)/4;
2533      if (! sheets_max)
2534        sheets_max = sheets_left;
2535      // -- reorder pages
2536      booklet_order(pages_todo, sheets_max*4);
2537      // -- print
2538      int sides = 0;
2539      int sheetpos = sheets_max;
2540      store_doc_prolog(str, sides_todo, 0, 0);
2541      store_doc_setup(str);
2542      for (GPosition p=pages_todo; p; ++p)
2543        {
2544          struct pdata inf;
2545          inf.page1 = pages_todo[p]; 
2546          inf.page2 = pages_todo[++p]; 
2547          inf.smax = sheets_max;
2548          inf.spos = --sheetpos;
2549          inf.offset = options.get_bookletalign();
2550          if (options.get_bookletmode() != Options::VERSO)
2551            process_double_page(str,doc,(void*)&inf,sides++,sides_todo);
2552          inf.page1 = pages_todo[++p]; 
2553          inf.page2 = pages_todo[++p]; 
2554          inf.offset = -inf.offset;
2555          if (options.get_bookletmode() != Options::RECTO)
2556            process_double_page(str,doc,(void*)&inf,sides++,sides_todo);
2557          sheets_left -= 1;
2558          if (sheetpos <= 0)
2559            sheetpos = ((sheets_max<sheets_left) ? sheets_max : sheets_left);
2560        }
2561      store_doc_trailer(str);
2562    }
2563}
2564
2565
2566void
2567DjVuToPS::
2568print(ByteStream &str, GP<DjVuDocument> doc)
2569{
2570  GUTF8String dummy;
2571  print(str,doc,dummy);
2572}
2573
2574
2575
2576#ifdef HAVE_NAMESPACES
2577}
2578# ifndef NOT_USING_DJVU_NAMESPACE
2579using namespace DJVU;
2580# endif
2581#endif
2582
Note: See TracBrowser for help on using the repository browser.