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