diff options
Diffstat (limited to 'kviewshell/plugins/djvu/libdjvu/DjVuToPS.cpp')
-rw-r--r-- | kviewshell/plugins/djvu/libdjvu/DjVuToPS.cpp | 2582 |
1 files changed, 2582 insertions, 0 deletions
diff --git a/kviewshell/plugins/djvu/libdjvu/DjVuToPS.cpp b/kviewshell/plugins/djvu/libdjvu/DjVuToPS.cpp new file mode 100644 index 00000000..beaa01bc --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/DjVuToPS.cpp @@ -0,0 +1,2582 @@ +//C- -*- C++ -*- +//C- ------------------------------------------------------------------- +//C- DjVuLibre-3.5 +//C- Copyright (c) 2002-2003 Leon Bottou and Yann Le Cun. +//C- Copyright (c) 2001 AT&T +//C- +//C- This software is subject to, and may be distributed under, the +//C- GNU General Public License, Version 2. The license should have +//C- accompanied the software or you may obtain a copy of the license +//C- from the Free Software Foundation at http://www.fsf.org . +//C- +//C- This program is distributed in the hope that it will be useful, +//C- but WITHOUT ANY WARRANTY; without even the implied warranty of +//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//C- GNU General Public License for more details. +//C- +//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.djvu): +//C- +//C- ------------------------------------------------------------------ +//C- | DjVu (r) Reference Library (v. 3.5) +//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. +//C- | The DjVu Reference Library is protected by U.S. Pat. No. +//C- | 6,058,214 and patents pending. +//C- | +//C- | This software is subject to, and may be distributed under, the +//C- | GNU General Public License, Version 2. The license should have +//C- | accompanied the software or you may obtain a copy of the license +//C- | from the Free Software Foundation at http://www.fsf.org . +//C- | +//C- | The computer code originally released by LizardTech under this +//C- | license and unmodified by other parties is deemed "the LIZARDTECH +//C- | ORIGINAL CODE." Subject to any third party intellectual property +//C- | claims, LizardTech grants recipient a worldwide, royalty-free, +//C- | non-exclusive license to make, use, sell, or otherwise dispose of +//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the +//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU +//C- | General Public License. This grant only confers the right to +//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to +//C- | the extent such infringement is reasonably necessary to enable +//C- | recipient to make, have made, practice, sell, or otherwise dispose +//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to +//C- | any greater extent that may be necessary to utilize further +//C- | modifications or combinations. +//C- | +//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY +//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF +//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +//C- +------------------------------------------------------------------ +// +// $Id: DjVuToPS.cpp,v 1.23 2003/11/07 22:08:21 leonb Exp $ +// $Name: release_3_5_15 $ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#if NEED_GNUG_PRAGMAS +# pragma implementation +#endif + +#include "DjVuToPS.h" +#include "IFFByteStream.h" +#include "BSByteStream.h" +#include "DjVuImage.h" +#include "DjVuText.h" +#include "DataPool.h" +#include "IW44Image.h" +#include "JB2Image.h" +#include "GBitmap.h" +#include "GPixmap.h" +#include "debug.h" +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <math.h> +#ifdef UNIX +#include <pwd.h> +#include <grp.h> +#include <unistd.h> +#endif + + +#ifdef HAVE_NAMESPACES +namespace DJVU { +# ifdef NOT_DEFINED // Just to fool emacs c++ mode +} +#endif +#endif + + +static const size_t ps_string_size=15000; + +// *************************************************************************** +// ****************************** Options ************************************ +// *************************************************************************** + +DjVuToPS::Options:: +Options(void) +: format(PS), + level(2), + orientation(AUTO), + mode(COLOR), + zoom(0), + color(true), + calibrate(true), + text(false), + gamma((double)2.2), + copies(1), + frame(false), + cropmarks(false), + bookletmode(OFF), + bookletmax(0), + bookletalign(0), + bookletfold(18), + bookletxfold(200) +{} + +void +DjVuToPS::Options:: +set_format(Format xformat) +{ + if (xformat != EPS && xformat != PS) + G_THROW(ERR_MSG("DjVuToPS.bad_format")); + format=xformat; +} + +void +DjVuToPS::Options:: +set_level(int xlevel) +{ + if (xlevel<1 || xlevel>3) + G_THROW(ERR_MSG("DjVuToPS.bad_level") + + GUTF8String("\t") + GUTF8String(xlevel)); + level=xlevel; +} + +void +DjVuToPS::Options:: +set_orientation(Orientation xorientation) +{ + if (xorientation!=PORTRAIT && + xorientation!=LANDSCAPE && + xorientation!=AUTO ) + G_THROW(ERR_MSG("DjVuToPS.bad_orient")); + orientation=xorientation; +} + +void +DjVuToPS::Options:: +set_mode(Mode xmode) +{ + if (xmode!=COLOR && xmode!=FORE && xmode!=BACK && xmode!=BW) + G_THROW(ERR_MSG("DjVuToPS.bad_mode")); + mode=xmode; +} + +void +DjVuToPS::Options:: +set_zoom(int xzoom) +{ + if (xzoom!=0 && !(xzoom>=5 && xzoom<=999)) + G_THROW(ERR_MSG("DjVuToPS.bad_zoom")); + zoom=xzoom; +} + +void +DjVuToPS::Options:: +set_color(bool xcolor) +{ + color=xcolor; +} + +void +DjVuToPS::Options:: +set_sRGB(bool xcalibrate) +{ + calibrate=xcalibrate; +} + +void +DjVuToPS::Options:: +set_gamma(double xgamma) +{ + if (xgamma<(double)(0.3-0.0001) || xgamma>(double)(5.0+0.0001)) + G_THROW(ERR_MSG("DjVuToPS.bad_gamma")); + gamma=xgamma; +} + +void +DjVuToPS::Options:: +set_copies(int xcopies) +{ + if (xcopies<=0) + G_THROW(ERR_MSG("DjVuToPS.bad_number")); + copies=xcopies; +} + +void +DjVuToPS::Options:: +set_frame(bool xframe) +{ + frame=xframe; +} + +void +DjVuToPS::Options:: +set_cropmarks(bool xmarks) +{ + cropmarks=xmarks; +} + +void +DjVuToPS::Options:: +set_text(bool xtext) +{ + text=xtext; +} + +void +DjVuToPS::Options:: +set_bookletmode(BookletMode m) +{ + bookletmode = m; +} + +void +DjVuToPS::Options:: +set_bookletmax(int m) +{ + bookletmax = 0; + if (m > 0) + bookletmax = (m+3)/4; + bookletmax *= 4; +} + +void +DjVuToPS::Options:: +set_bookletalign(int m) +{ + bookletalign = m; +} + +void +DjVuToPS::Options:: +set_bookletfold(int fold, int xfold) +{ + if (fold >= 0) + bookletfold = fold; + if (xfold >= 0) + bookletxfold = xfold; +} + + +// *************************************************************************** +// ******************************* DjVuToPS ********************************** +// *************************************************************************** + +static char bin2hex[256][2]; + +DjVuToPS::DjVuToPS(void) +{ + DEBUG_MSG("DjVuToPS::DjVuToPS(): initializing...\n"); + DEBUG_MAKE_INDENT(3); + DEBUG_MSG("Initializing dig2hex[]\n"); + // Creating tables for bin=>text translation + static char * dig2hex="0123456789ABCDEF"; + int i; + for(i=0;i<256;i++) + { + bin2hex[i][0]=dig2hex[i/16]; + bin2hex[i][1]=dig2hex[i%16]; + } + refresh_cb=0; + refresh_cl_data=0; + prn_progress_cb=0; + prn_progress_cl_data=0; + dec_progress_cb=0; + dec_progress_cl_data=0; + info_cb=0; + info_cl_data=0; +} + +#ifdef __GNUC__ +static void +write(ByteStream &str, const char *format, ...) +__attribute__((format (printf, 2, 3))); +#endif + +static void +write(ByteStream &str, const char *format, ...) +{ + /* Will output the formated string to the specified \Ref{ByteStream} + like #fprintf# would do it for a #FILE#. */ + va_list args; + va_start(args, format); + GUTF8String tmp; + tmp.vformat(format, args); + str.writall((const char *) tmp, tmp.length()); +} + +// ************************* DOCUMENT LEVEL ********************************* + +void +DjVuToPS:: +store_doc_prolog(ByteStream &str, int pages, int dpi, GRect *grect) +{ + /* Will store the {\em document prolog}, which is basically a + block of document-level comments in PS DSC 3.0 format. + @param str Stream where PostScript data should be written + @param pages Total number of pages + @param dpi (EPS mode only) + @param grect (EPS mode only) */ + DEBUG_MSG("storing the document prolog\n"); + DEBUG_MAKE_INDENT(3); + if (options.get_format()==Options::EPS) + write(str, + "%%!PS-Adobe-3.0 EPSF 3.0\n" + "%%%%BoundingBox: 0 0 %d %d\n", + (grect->width()*100+dpi-1)/dpi, + (grect->height()*100+dpi-1)/dpi ); + else + write(str, "%%!PS-Adobe-3.0\n"); + write(str, + "%%%%Title: DjVu PostScript document\n" + "%%%%Copyright: Copyright (c) 1998-1999 AT&T\n" + "%%%%Creator: DjVu (code by Andrei Erofeev)\n" + "%%%%DocumentData: Clean7Bit\n"); + // Date + time_t tm=time(0); + write(str, "%%%%CreationDate: %s", ctime(&tm)); + // For +#ifdef UNIX + passwd *pswd = getpwuid(getuid()); + if (pswd) + { + char *s = strchr(pswd->pw_gecos, ','); + if (s) + *s = 0; + s = 0; + if (pswd->pw_gecos && strlen(pswd->pw_gecos)) + s = pswd->pw_gecos; + else if (pswd->pw_name && strlen(pswd->pw_name)) + s = pswd->pw_name; + if (s) + write(str, "%%%%For: %s\n", s); + } +#endif + // Language + write(str, "%%%%LanguageLevel: %d\n", options.get_level()); + if (options.get_level()<2 && options.get_color()) + write(str, "%%%%Extensions: CMYK\n"); + // Pages + write(str, "%%%%Pages: %d\n",pages ); + write(str, "%%%%PageOrder: Ascend\n"); + // Orientation + if (options.get_orientation() != Options::AUTO) + write(str, "%%%%Orientation: %s\n", + options.get_orientation()==Options::PORTRAIT ? + "Portrait" : "Landscape" ); + // Requirements + if (options.get_format() == Options::PS) + { + write(str, "%%%%Requirements:"); + if (options.get_color()) + write(str, " color"); + if (options.get_copies()>1) + write(str, " numcopies(%d)", options.get_copies()); + if (options.get_level()>=2) + { + if (options.get_copies()>1) + write(str, " collate"); + if (options.get_bookletmode() == Options::RECTOVERSO) + write(str, " duplex(tumble)"); + } + write(str, "\n"); + } + // End + write(str, + "%%%%EndComments\n" + "%%%%EndProlog\n" + "\n"); +} + +void +DjVuToPS:: +store_doc_setup(ByteStream &str) +{ + /* Will store the {\em document setup}, which is a set of + PostScript commands and functions used to inspect and prepare + the PostScript interpreter environment before displaying images. */ + write(str, + "%%%%BeginSetup\n" + "/doc-origstate save def\n"); + if (options.get_level()>=2) + { + if (options.get_format() == Options::PS) + { + if (options.get_copies()>1) + write(str, + "[{\n" + "%%%%BeginFeature: NumCopies %d\n" + "<< /NumCopies %d >> setpagedevice\n" + "%%%%EndFeature\n" + "} stopped cleartomark\n" + "[{\n" + "%%%%BeginFeature: Collate\n" + "<< /Collate true >> setpagedevice\n" + "%%%%EndFeature\n" + "} stopped cleartomark\n", + options.get_copies(), + options.get_copies() ); + if (options.get_bookletmode()==Options::RECTOVERSO) + write(str, + "[{\n" + "%%%%BeginFeature: Duplex DuplexTumble\n" + "<< /Duplex true /Tumble true >> setpagedevice\n" + "%%%%EndFeature\n" + "} stopped cleartomark\n"); + } + if (options.get_color()) + write(str, + "%% -- procs for reading color image\n" + "/readR () def\n" + "/readG () def\n" + "/readB () def\n" + "/ReadData {\n" + " currentfile /ASCII85Decode filter dup\n" + " /RunLengthDecode filter\n" + " bufferR readstring pop /readR exch def\n" + " dup status { flushfile } { pop } ifelse\n" + " currentfile /ASCII85Decode filter dup\n" + " /RunLengthDecode filter\n" + " bufferG readstring pop /readG exch def\n" + " dup status { flushfile } { pop } ifelse\n" + " currentfile /ASCII85Decode filter dup\n" + " /RunLengthDecode filter\n" + " bufferB readstring pop /readB exch def\n" + " dup status { flushfile } { pop } ifelse\n" + "} bind def\n" + "/ReadR {\n" + " readR length 0 eq { ReadData } if\n" + " readR /readR () def\n" + "} bind def\n" + "/ReadG {\n" + " readG length 0 eq { ReadData } if\n" + " readG /readG () def\n" + "} bind def\n" + "/ReadB {\n" + " readB length 0 eq { ReadData } if\n" + " readB /readB () def\n" + "} bind def\n"); + write(str, + "%% -- procs for foreground layer\n" + "/g {gsave 0 0 0 0 5 index 5 index setcachedevice\n" + " true [1 0 0 1 0 0] 5 4 roll imagemask grestore\n" + "} bind def\n" + "/gn {gsave 0 0 0 0 6 index 6 index setcachedevice\n" + " true [1 0 0 1 0 0] 3 2 roll 5 1 roll \n" + " { 1 sub 0 index 2 add 1 index 1 add roll\n" + " } imagemask grestore pop \n" + "} bind def\n" + "/c {setcolor rmoveto glyphshow} bind def\n" + "/s {rmoveto glyphshow} bind def\n" + "/S {rmoveto gsave show grestore} bind def\n" + "/F {(Helvetica) findfont exch scalefont setfont} bind def\n" + "%% -- emulations\n" + "systemdict /rectstroke known not {\n" + " /rectstroke %% stack : x y width height \n" + " { newpath 4 2 roll moveto 1 index 0 rlineto\n" + " 0 exch rlineto neg 0 rlineto closepath stroke\n" + " } bind def } if\n" + "systemdict /rectclip known not {\n" + " /rectclip %% stack : x y width height \n" + " { newpath 4 2 roll moveto 1 index 0 rlineto\n" + " 0 exch rlineto neg 0 rlineto closepath clip\n" + " } bind def } if\n" + "%% -- color space\n" ); + if (options.get_sRGB()) + write(str, + "/DjVuColorSpace [ %s\n" + "<< /DecodeLMN [ { dup 0.03928 le {\n" + " 12.92321 div\n" + " } {\n" + " 0.055 add 1.055 div 2.4 exp\n" + " } ifelse } bind dup dup ]\n" + " /MatrixLMN [\n" + " 0.412457 0.212673 0.019334\n" + " 0.357576 0.715152 0.119192\n" + " 0.180437 0.072175 0.950301 ]\n" + " /WhitePoint [ 0.9505 1 1.0890 ] %% D65 \n" + " /BlackPoint[0 0 0] >> ] def\n", + (options.get_color()) ? "/CIEBasedABC" : "/CIEBasedA" ); + else if (options.get_color()) + write(str,"/DjVuColorSpace /DeviceRGB def\n"); + else + write(str,"/DjVuColorSpace /DeviceGray def\n"); + } + else + { + // level<2 + if (options.get_format() == Options::PS) + if (options.get_copies() > 1) + write(str,"/#copies %d def\n", options.get_copies()); + if (options.get_color()) + write(str, + "%% -- buffers for reading image\n" + "/buffer8 () def\n" + "/buffer24 () def\n" + "%% -- colorimage emulation\n" + "systemdict /colorimage known {\n" + " /ColorProc {\n" + " currentfile buffer24 readhexstring pop\n" + " } bind def\n" + " /ColorImage {\n" + " colorimage\n" + " } bind def\n" + "} {\n" + " /ColorProc {\n" + " currentfile buffer24 readhexstring pop\n" + " /data exch def /datalen data length def\n" + " /cnt 0 def\n" + " 0 1 datalen 3 idiv 1 sub {\n" + " buffer8 exch\n" + " data cnt get 20 mul /cnt cnt 1 add def\n" + " data cnt get 32 mul /cnt cnt 1 add def\n" + " data cnt get 12 mul /cnt cnt 1 add def\n" + " add add 64 idiv put\n" + " } for\n" + " buffer8 0 datalen 3 idiv getinterval\n" + " } bind def\n" + " /ColorImage {\n" + " pop pop image\n" + " } bind def\n" + "} ifelse\n"); + } // level<2 + write(str, "%%%%EndSetup\n\n"); +} + +void +DjVuToPS:: +store_doc_trailer(ByteStream &str) +{ + /* Will store the {\em document trailer}, which is a clean-up code + used to return the PostScript interpeter back to the state, in which + it was before displaying this document. */ + write(str, + "%%%%Trailer\n" + "doc-origstate restore\n" + "%%%%EOF\n"); +} + +// *********************************************************************** +// ***************************** PAGE LEVEL ****************************** +// *********************************************************************** + +static unsigned char * +ASCII85_encode(unsigned char * dst, + const unsigned char * src_start, + const unsigned char * src_end) +{ + /* Will read data between #src_start# and #src_end# pointers (excluding byte + pointed by #src_end#), encode it using {\bf ASCII85} algorithm, and + output the result into the destination buffer pointed by #dst#. The + function returns pointer to the first unused byte in the destination + buffer. */ + int symbols=0; + const unsigned char * ptr; + for(ptr=src_start;ptr<src_end;ptr+=4) + { + unsigned int num=0; + if (ptr+3<src_end) + { + num |= ptr[0] << 24; + num |= ptr[1] << 16; + num |= ptr[2] << 8; + num |= ptr[3]; + } + else + { + num |= ptr[0] << 24; + if (ptr+1<src_end) + num |= ptr[1] << 16; + if (ptr+2<src_end) + num |= ptr[2] << 8; + } + int a1, a2, a3, a4, a5; + a5=num % 85; num/=85; + a4=num % 85; num/=85; + a3=num % 85; num/=85; + a2=num % 85; + a1=num / 85; + *dst++ = a1+33; + *dst++ = a2+33; + if (ptr+1<src_end) + *dst++ = a3+33; + if (ptr+2<src_end) + *dst++ = a4+33; + if (ptr+3<src_end) + *dst++ = a5+33; + symbols += 5; + if (symbols > 70 && ptr+4<src_end) + { + *dst++='\n'; + symbols=0; + } + } + return dst; +} + +static unsigned char * +RLE_encode(unsigned char * dst, + const unsigned char * src_start, + const unsigned char * src_end) +{ + /* Will read data between #src_start# and #src_end# pointers (excluding byte + pointed by #src_end#), RLE encode it, and output the result into the + destination buffer pointed by #dst#. #counter# is used to count the + number of output bytes. The function returns pointer to the first unused + byte in the destination buffer. */ + const unsigned char * ptr; + for(ptr=src_start;ptr<src_end;ptr++) + { + if (ptr==src_end-1) + { + *dst++=0; *dst++=*ptr; + } + else if (ptr[0]!=ptr[1]) + { + // Guess how many non repeating bytes we have + const unsigned char * ptr1; + for(ptr1=ptr+1;ptr1<src_end-1;ptr1++) + if (ptr1[0]==ptr1[1] || ptr1-ptr>=128) break; + int pixels=ptr1-ptr; + *dst++=pixels-1; + for(int cnt=0;cnt<pixels;cnt++) + *dst++=*ptr++; + ptr--; + } + else + { + // Get the number of repeating bytes + const unsigned char * ptr1; + for(ptr1=ptr+1;ptr1<src_end-1;ptr1++) + if (ptr1[0]!=ptr1[1] || ptr1-ptr+1>=128) break; + int pixels=ptr1-ptr+1; + *dst++=257-pixels; + *dst++=*ptr; + ptr=ptr1; + } + } + return dst; +} + +#define GRAY(r,g,b) (((r)*20+(g)*32+(b)*12)/64) + +void +DjVuToPS:: +store_page_setup(ByteStream &str, + int dpi, + const GRect &grect, + int align ) +{ + /* Will store PostScript code necessary to prepare page for + the coming \Ref{DjVuImage}. This is basically a scaling + code plus initialization of some buffers. */ + if (options.get_format() == Options::EPS) + write(str, + "/page-origstate save def\n" + "%% -- coordinate system\n" + "/image-dpi %d def\n" + "/image-x 0 def\n" + "/image-y 0 def\n" + "/image-width %d def\n" + "/image-height %d def\n" + "/coeff 100 image-dpi div def\n" + "/a11 coeff def\n" + "/a12 0 def\n" + "/a13 0 def\n" + "/a21 0 def\n" + "/a22 coeff def\n" + "/a23 0 def\n" + "[a11 a21 a12 a22 a13 a23] concat\n" + "gsave 0 0 image-width image-height rectclip\n" + "%% -- begin printing\n", + dpi, grect.width(), grect.height() ); + else + { + int margin = 0; + const char *xauto = "false"; + const char *xportrait = "false"; + const char *xfit = "false"; + if (options.get_orientation()==Options::AUTO) + xauto = "true"; + if (options.get_orientation()==Options::PORTRAIT) + xportrait = "true"; + if (options.get_zoom()<=0) + xfit = "true"; + if (options.get_cropmarks()) + margin = 36; + else if (options.get_frame()) + margin = 6; + write(str, + "/page-origstate save def\n" + "%% -- coordinate system\n" + "/auto-orient %s def\n" + "/portrait %s def\n" + "/fit-page %s def\n" + "/zoom %d def\n" + "/image-dpi %d def\n" + "clippath pathbbox newpath\n" + "2 index sub exch 3 index sub\n" + "/page-width exch def\n" + "/page-height exch def\n" + "/page-y exch def\n" + "/page-x exch def\n" + "/image-x 0 def\n" + "/image-y 0 def\n" + "/image-width %d def\n" + "/image-height %d def\n" + "/margin %d def\n" + "/halign %d def\n" + "/valign 0 def\n", + xauto, xportrait, xfit, options.get_zoom(), + dpi, grect.width(), grect.height(), + margin, align ); + write(str, + "%% -- position page\n" + "auto-orient {\n" + " image-height image-width sub\n" + " page-height page-width sub\n" + " mul 0 ge /portrait exch def\n" + "} if\n" + "fit-page {\n" + " /page-width page-width margin sub\n" + " halign 0 eq { margin sub } if def\n" + " /page-height page-height margin sub\n" + " valign 0 eq { margin sub } if def\n" + " /page-x page-x halign 0 ge { margin add } if def\n" + " /page-y page-y valign 0 ge { margin add } if def\n" + "} if\n" + "portrait {\n" + " fit-page {\n" + " image-height page-height div\n" + " image-width page-width div\n" + " gt {\n" + " page-height image-height div /coeff exch def\n" + " } {\n" + " page-width image-width div /coeff exch def\n" + " } ifelse\n" + " } {\n" + " /coeff 72 image-dpi div zoom mul 100 div def\n" + " } ifelse\n" + " /start-x page-x page-width image-width\n" + " coeff mul sub 2 div halign 1 add mul add def\n" + " /start-y page-y page-height image-height\n" + " coeff mul sub 2 div valign 1 add mul add def\n" + " /a11 coeff def\n" + " /a12 0 def\n" + " /a13 start-x def\n" + " /a21 0 def\n" + " /a22 coeff def\n" + " /a23 start-y def\n" + "} { %% landscape\n" + " fit-page {\n" + " image-height page-width div\n" + " image-width page-height div\n" + " gt {\n" + " page-width image-height div /coeff exch def\n" + " } {\n" + " page-height image-width div /coeff exch def\n" + " } ifelse\n" + " } {\n" + " /coeff 72 image-dpi div zoom mul 100 div def\n" + " } ifelse\n" + " /start-x page-x page-width add page-width image-height\n" + " coeff mul sub 2 div valign 1 add mul sub def\n" + " /start-y page-y page-height image-width\n" + " coeff mul sub 2 div halign 1 add mul add def\n" + " /a11 0 def\n" + " /a12 coeff neg def\n" + " /a13 start-x image-y coeff neg mul sub def\n" + " /a21 coeff def\n" + " /a22 0 def\n" + " /a23 start-y image-x coeff mul add def \n" + "} ifelse\n" + "[a11 a21 a12 a22 a13 a23] concat\n" + "gsave 0 0 image-width image-height rectclip\n" + "%% -- begin print\n"); + } +} + +void +DjVuToPS:: +store_page_trailer(ByteStream &str) +{ + write(str, + "%% -- end print\n" + "grestore\n"); + if (options.get_frame()) + write(str, + "%% Drawing frame\n" + "gsave 0.7 setgray 0.5 coeff div setlinewidth 0 0\n" + "image-width image-height rectstroke\n" + "grestore\n"); + if (options.get_cropmarks() && + options.get_format() != Options::EPS ) + write(str, + "%% Drawing crop marks\n" + "/cm { gsave translate rotate 1 coeff div dup scale\n" + " 0 setgray 0.5 setlinewidth -36 0 moveto 0 0 lineto\n" + " 0 -36 lineto stroke grestore } bind def\n" + "0 0 0 cm 180 image-width image-height cm\n" + "90 image-width 0 cm 270 0 image-height cm\n"); + write(str, + "page-origstate restore\n"); +} + +static int +compute_red(int w, int h, int rw, int rh) +{ + for (int red=1; red<16; red++) + if (((w+red-1)/red==rw) && ((h+red-1)/red==rh)) + return red; + return 16; +} + +static int +get_bg_red(GP<DjVuImage> dimg) +{ + GP<GPixmap> pm = 0; + // Access image size + int width = dimg->get_width(); + int height = dimg->get_height(); + if (width<=0 || height<=0) return 0; + // CASE1: Incremental BG IW44Image + GP<IW44Image> bg44 = dimg->get_bg44(); + if (bg44) + { + int w = bg44->get_width(); + int h = bg44->get_height(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + return compute_red(width,height,w,h); + } + // CASE 2: Raw background pixmap + GP<GPixmap> bgpm = dimg->get_bgpm(); + if (bgpm) + { + int w = bgpm->columns(); + int h = bgpm->rows(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + return compute_red(width,height,w,h); + } + return 0; +} + +static GP<GPixmap> +get_bg_pixmap(GP<DjVuImage> dimg, const GRect &rect) +{ + GP<GPixmap> pm = 0; + // Access image size + int width = dimg->get_width(); + int height = dimg->get_height(); + GP<DjVuInfo> info = dimg->get_info(); + if (width<=0 || height<=0 || !info) return 0; + // CASE1: Incremental BG IW44Image + GP<IW44Image> bg44 = dimg->get_bg44(); + if (bg44) + { + int w = bg44->get_width(); + int h = bg44->get_height(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + pm = bg44->get_pixmap(1,rect); + return pm; + } + // CASE 2: Raw background pixmap + GP<GPixmap> bgpm = dimg->get_bgpm(); + if (bgpm) + { + int w = bgpm->columns(); + int h = bgpm->rows(); + // Avoid silly cases + if (w==0 || h==0 || width==0 || height==0) + return 0; + pm->init(*bgpm, rect); + return pm; + } + // FAILURE + return 0; +} + +void +DjVuToPS:: +make_gamma_ramp(GP<DjVuImage> dimg) +{ + double targetgamma = options.get_gamma(); + double whitepoint = (options.get_sRGB() ? 255 : 280); + for (int i=0; i<256; i++) + ramp[i] = i; + if (! dimg->get_info()) + return; + if (targetgamma < 0.1) + return; + double filegamma = dimg->get_info()->gamma; + double correction = filegamma / targetgamma; + if (correction<0.1 || correction>10) + return; + { + for (int i=0; i<256; i++) + { + double x = (double)(i)/255.0; + if (correction != 1.0) + x = pow(x, correction); + int j = (int) floor(whitepoint * x + 0.5); + ramp[i] = (j>255) ? 255 : (j<0) ? 0 : j; + } + } +} + +void +DjVuToPS:: +print_fg_2layer(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect, + unsigned char *blit_list) +{ + // Pure-jb2 or color-jb2 case. + GPixel p; + int currentx=0; + int currenty=0; + GP<DjVuPalette> pal = dimg->get_fgbc(); + GP<JB2Image> jb2 = dimg->get_fgjb(); + if (! jb2) return; + int num_blits = jb2->get_blit_count(); + int current_blit; + for(current_blit=0; current_blit<num_blits; current_blit++) + { + if (blit_list[current_blit]) + { + JB2Blit *blit = jb2->get_blit(current_blit); + if ((pal) && !(options.get_mode()==Options::BW)) + { + pal->index_to_color(pal->colordata[current_blit], p); + if (options.get_color()) + { + write(str,"/%d %d %d %f %f %f c\n", + blit->shapeno, + blit->left-currentx, blit->bottom-currenty, + ramp[p.r]/255.0, ramp[p.g]/255.0, ramp[p.b]/255.0); + } + else + { + write(str,"/%d %d %d %f c\n", + blit->shapeno, + blit->left-currentx, blit->bottom-currenty, + ramp[GRAY(p.r, p.g, p.b)]/255.0); + } + } + else + { + write(str,"/%d %d %d s\n", + blit->shapeno, + blit->left-currentx, blit->bottom-currenty); + } + currentx = blit->left; + currenty = blit->bottom; + } + } +} + +void +DjVuToPS:: +print_fg_3layer(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &cprn_rect, + unsigned char *blit_list ) +{ + GRect prn_rect; + GP<GPixmap> brush = dimg->get_fgpm(); + if (! brush) return; + int br = brush->rows(); + int bc = brush->columns(); + int red = compute_red(dimg->get_width(),dimg->get_height(),bc,br); + prn_rect.ymin = (cprn_rect.ymin)/red; + prn_rect.xmin = (cprn_rect.xmin)/red; + prn_rect.ymax = (cprn_rect.ymax+red-1)/red; + prn_rect.xmax = (cprn_rect.xmax+red-1)/red; + int color_nb = ((options.get_color()) ? 3 : 1); + GP<JB2Image> jb2 = dimg->get_fgjb(); + if (! jb2) return; + int pw = bc; + int ph = 2; + + write(str, + "/P {\n" + " 11 dict dup begin 4 1 roll\n" + " /PatternType 1 def\n" + " /PaintType 1 def\n" + " /TilingType 1 def\n" + " /H exch def\n" + " /W exch def\n" + " /Red %d def\n" + " /PatternString exch def\n" + " /XStep W Red mul def\n" + " /YStep H Red mul def\n" + " /BBox [0 0 XStep YStep] def\n" + " /PaintProc { begin\n" + " Red dup scale\n" + " << /ImageType 1 /Width W /Height H\n" + " /BitsPerComponent 8 /Interpolate false\n" + " /Decode [%s] /ImageMatrix [1 0 0 1 0 0]\n" + " /DataSource PatternString >> image\n" + " end } bind def\n" + " 0 0 XStep YStep rectclip\n" + " end matrix makepattern\n" + " /Pattern setcolorspace setpattern\n" + " 0 0 moveto\n" + "} def\n", red, (color_nb == 1) ? "0 1" : "0 1 0 1 0 1" ); + + unsigned char *s; + GPBuffer<unsigned char> gs(s,pw*ph*color_nb); + unsigned char *s_ascii_encoded; + GPBuffer<unsigned char> gs_ascii_encoded(s_ascii_encoded,pw*ph*2*color_nb); + { + for (int y=prn_rect.ymin; y<prn_rect.ymax; y+=ph) + for (int x=prn_rect.xmin; x<prn_rect.xmax; x+=pw) + { + int w = ((x+pw > prn_rect.xmax) ? prn_rect.xmax-x : pw); + int h = ((y+ph > prn_rect.ymax) ? prn_rect.ymax-y : ph); + int currentx = x * red; + int currenty = y * red; + // Find first intersecting blit + int current_blit; + int num_blits = jb2->get_blit_count(); + GRect rect1(currentx,currenty, w*red, h*red); + for(current_blit=0; current_blit<num_blits; current_blit++) + if (blit_list[current_blit]) + { + JB2Blit *blit = jb2->get_blit(current_blit); + GRect rect2(blit->left, blit->bottom, + jb2->get_shape(blit->shapeno).bits->columns(), + jb2->get_shape(blit->shapeno).bits->rows()); + if (rect2.intersect(rect1,rect2)) + break; + } + if (current_blit >= num_blits) + continue; + // Setup pattern + write(str,"gsave %d %d translate\n", currentx, currenty); + write(str,"<~"); + unsigned char *q = s; + for(int current_row = y; current_row<y+h; current_row++) + { + GPixel *row_pix = (*brush)[current_row]; + for(int current_col = x; current_col<x+w; current_col++) + { + GPixel &p = row_pix[current_col]; + if (color_nb>1) + { + *q++ = ramp[p.r]; + *q++ = ramp[p.g]; + *q++ = ramp[p.b]; + } + else + { + *q++ = ramp[GRAY(p.r,p.g,p.b)]; + } + } + } + unsigned char *stop_ascii = + ASCII85_encode(s_ascii_encoded,s,s+w*h*color_nb); + *stop_ascii++='\0'; + write(str,"%s",s_ascii_encoded); + write(str,"~> %d %d P\n", w, h); + // Keep performing blits + for(; current_blit<num_blits; current_blit++) + if (blit_list[current_blit]) + { + JB2Blit *blit = jb2->get_blit(current_blit); + GRect rect2(blit->left, blit->bottom, + jb2->get_shape(blit->shapeno).bits->columns(), + jb2->get_shape(blit->shapeno).bits->rows()); + if (rect2.intersect(rect1,rect2)) + { + write(str,"/%d %d %d s\n", + blit->shapeno, + blit->left-currentx, blit->bottom-currenty); + currentx = blit->left; + currenty = blit->bottom; + } + } + write(str,"grestore\n"); + } + // Cleanup + } +} + +void +DjVuToPS:: +print_fg(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect ) +{ + GP<JB2Image> jb2=dimg->get_fgjb(); + if (! jb2) return; + int num_blits = jb2->get_blit_count(); + int num_shapes = jb2->get_shape_count(); + unsigned char *dict_shapes = 0; + unsigned char *blit_list = 0; + GPBuffer<unsigned char> gdict_shapes(dict_shapes,num_shapes); + GPBuffer<unsigned char> gblit_list(blit_list,num_blits); + for(int i=0; i<num_shapes; i++) + { + dict_shapes[i]=0; + } + for(int current_blit=0; current_blit<num_blits; current_blit++) + { + JB2Blit *blit = jb2->get_blit(current_blit); + JB2Shape *shape = & jb2->get_shape(blit->shapeno); + blit_list[current_blit] = 0; + if (! shape->bits) + continue; + GRect rect2(blit->left, blit->bottom, + shape->bits->columns(), shape->bits->rows()); + if (rect2.intersect(rect2, prn_rect)) + { + dict_shapes[blit->shapeno] = 1; + blit_list[current_blit] = 1; + } + } + write(str, + "%% --- now doing the foreground\n" + "gsave DjVuColorSpace setcolorspace\n" ); + // Define font + write(str, + "/$DjVuLocalFont 7 dict def\n" + "$DjVuLocalFont begin\n" + "/FontType 3 def \n" + "/FontMatrix [1 0 0 1 0 0] def\n" + "/FontBBox [0 0 1 .5] def\n" + "/CharStrings %d dict def\n" + "/Encoding 2 array def\n" + "0 1 1 {Encoding exch /.notdef put} for \n" + "CharStrings begin\n" + "/.notdef {} def\n", + num_shapes+1); + for(int current_shape=0; current_shape<num_shapes; current_shape++) + { + if (dict_shapes[current_shape]) + { + JB2Shape *shape = & jb2->get_shape(current_shape); + GP<GBitmap> bitmap = shape->bits; + int rows = bitmap->rows(); + int columns = bitmap->columns(); + int nbytes = (columns+7)/8*rows+1; + int nrows = rows; + int nstrings=0; + if (nbytes>(int)ps_string_size) //max string length + { + nrows=ps_string_size/((columns+7)/8); + nbytes=(columns+7)/8*nrows+1; + } + unsigned char *s_start; + GPBuffer<unsigned char> gs_start(s_start,nbytes); + unsigned char *s_ascii; + GPBuffer<unsigned char> gs_ascii(s_ascii,nbytes*2); + write(str,"/%d {",current_shape); + + unsigned char *s = s_start; + for(int current_row=0; current_row<rows; current_row++) + { + unsigned char * row_bits = (*bitmap)[current_row]; + unsigned char acc = 0; + unsigned char mask = 0; + for(int current_col=0; current_col<columns; current_col++) + { + if (mask == 0) + mask = 0x80; + if (row_bits[current_col]) + acc |= mask; + mask >>= 1; + if (mask == 0) + { + *s=acc; + s++; + acc = mask = 0; + } + } + if (mask != 0) + { + *s=acc; + s++; + } + if (!((current_row+1)%nrows)) + { + unsigned char *stop_ascii = ASCII85_encode(s_ascii,s_start,s); + *stop_ascii++='\0'; + write(str,"<~%s~> ",s_ascii); + s=s_start; + nstrings++; + } + } + if (s!=s_start) + { + unsigned char *stop_ascii = ASCII85_encode(s_ascii,s_start,s); + *stop_ascii++='\0'; + write(str,"<~%s~> ",s_ascii); + nstrings++; + } + if (nstrings==1) + write(str," %d %d g} def\n", columns, rows); + else + write(str," %d %d %d gn} def\n", columns, rows,nstrings); + } + } + write(str, + "end\n" + "/BuildGlyph {\n" + " exch /CharStrings get exch\n" + " 2 copy known not\n" + " {pop /.notdef} if\n" + " get exec \n" + "} bind def\n" + "end\n" + "/LocalDjVuFont $DjVuLocalFont definefont pop\n" + "/LocalDjVuFont findfont setfont\n" ); + write(str, + "-%d -%d translate\n" + "0 0 moveto\n", + prn_rect.xmin, prn_rect.ymin); + // Print the foreground layer + if (dimg->get_fgpm() && !(options.get_mode()==Options::BW)) + print_fg_3layer(str, dimg, prn_rect, blit_list); + else + print_fg_2layer(str, dimg, prn_rect, blit_list); + write(str, "/LocalDjVuFont undefinefont grestore\n"); +} + + +void +DjVuToPS:: +print_bg(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &cprn_rect) +{ + GP<GPixmap> pm; + GRect prn_rect; + double print_done = 0; + int red = 0; + write(str, "%% --- now doing the background\n"); + if (! (red = get_bg_red(dimg))) + return; + write(str, + "gsave -%d -%d translate\n" + "/bgred %d def bgred bgred scale\n", + cprn_rect.xmin % red, + cprn_rect.ymin % red, + red); + prn_rect.ymin = (cprn_rect.ymin)/red; + prn_rect.ymax = (cprn_rect.ymax+red-1)/red; + prn_rect.xmin = (cprn_rect.xmin)/red; + prn_rect.xmax = (cprn_rect.xmax+red-1)/red; + // Display image + int band_bytes = 125000; + int band_height = band_bytes/prn_rect.width(); + int buffer_size = band_height*prn_rect.width(); + int ps_chunk_height = 30960/prn_rect.width()+1; + buffer_size = buffer_size*23/10; + bool do_color = options.get_color(); + if (!dimg->is_legal_photo() && + !dimg->is_legal_compound() || + options.get_mode()==Options::BW) + do_color = false; + if (do_color) + buffer_size *= 3; + if (do_color) + write(str, + "/bufferR %d string def\n" + "/bufferG %d string def\n" + "/bufferB %d string def\n" + "DjVuColorSpace setcolorspace\n" + "<< /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [0 1 0 1 0 1]\n" + " /ImageMatrix [1 0 0 1 0 0]\n" + " /MultipleDataSources true\n" + " /DataSource [ { ReadR } { ReadG } { ReadB } ]\n" + " /Interpolate false >> image\n", + ps_chunk_height*prn_rect.width(), + ps_chunk_height*prn_rect.width(), + ps_chunk_height*prn_rect.width(), + prn_rect.width(), prn_rect.height()); + else + write(str, + "DjVuColorSpace setcolorspace\n" + "<< /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [0 1]\n" + " /ImageMatrix [1 0 0 1 0 0]\n" + " /DataSource currentfile /ASCII85Decode\n" + " filter /RunLengthDecode filter\n" + " /Interpolate false >> image\n", + prn_rect.width(), prn_rect.height()); + + unsigned char *buffer; + GPBuffer<unsigned char> gbuffer(buffer,buffer_size); + unsigned char *rle_in; + GPBuffer<unsigned char> grle_in(rle_in,ps_chunk_height*prn_rect.width()); + unsigned char *rle_out; + GPBuffer<unsigned char> grle_out(rle_out,2*ps_chunk_height*prn_rect.width()); + { + // Start storing image in bands + unsigned char * rle_out_end = rle_out; + GRect grectBand = prn_rect; + grectBand.ymax = grectBand.ymin; + while(grectBand.ymax < prn_rect.ymax) + { + GP<GPixmap> pm = 0; + // Compute next band + grectBand.ymin=grectBand.ymax; + grectBand.ymax=grectBand.ymin+band_bytes/grectBand.width(); + if (grectBand.ymax>prn_rect.ymax) + grectBand.ymax=prn_rect.ymax; + pm = get_bg_pixmap(dimg, grectBand); + unsigned char *buf_ptr = buffer; + if (pm) + { + if (do_color) + { + int y=0; + while(y<grectBand.height()) + { + int row, y1; + unsigned char *ptr, *ptr1; + // Doing R component of current chunk + for (row=0,ptr=rle_in,y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->r]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; *buf_ptr++ = '>'; *buf_ptr++ = '\n'; + // Doing G component of current chunk + for (row=0,ptr=rle_in,y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->g]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + // Doing B component of current chunk + for (row=0, ptr=rle_in, y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->b]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + y=y1; + if (refresh_cb) + refresh_cb(refresh_cl_data); + } //while (y>=0) + } + else + { + // Don't use color + int y=0; + while(y<grectBand.height()) + { + unsigned char *ptr = rle_in; + for(int row=0; + row<ps_chunk_height && y<grectBand.height(); + row++,y++) + { + GPixel *pix = (*pm)[y]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[GRAY(pix->r,pix->g,pix->b)]; + } + rle_out_end = RLE_encode(rle_out_end, rle_in, ptr); + unsigned char *encode_to + = rle_out+(rle_out_end-rle_out)/4*4; + int bytes_left = rle_out_end-encode_to; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, encode_to); + *buf_ptr++ = '\n'; + memcpy(rle_out, encode_to, bytes_left); + rle_out_end = rle_out+bytes_left; + if (refresh_cb) + refresh_cb(refresh_cl_data); + } + } + } // if (pm) + str.writall(buffer, buf_ptr-buffer); + if (prn_progress_cb) + { + double done=(double)(grectBand.ymax + - prn_rect.ymin)/prn_rect.height(); + if ((int) (20*print_done)!=(int) (20*done)) + { + print_done=done; + prn_progress_cb(done, prn_progress_cl_data); + } + } + } // while(grectBand.yax<grect.ymax) + if (! do_color) + { + unsigned char * buf_ptr = buffer; + *rle_out_end++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, rle_out_end); + *buf_ptr++='~'; + *buf_ptr++='>'; + *buf_ptr++='\n'; + str.writall(buffer, buf_ptr-buffer); + } + } + //restore the scaling + write(str, "grestore\n"); +} + +void +DjVuToPS:: +print_image_lev1(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect) +{ + double print_done=0; + GRect all(0,0, dimg->get_width(),dimg->get_height()); + GP<GPixmap> pm; + GP<GBitmap> bm; + GRect test(0,0,1,1); + if (options.get_mode() == Options::FORE) + pm = dimg->get_fg_pixmap(test, all); + else if (options.get_mode() == Options::BACK) + pm = dimg->get_bg_pixmap(test, all); + else if (options.get_mode() != Options::BW) + pm = dimg->get_pixmap(test, all); + if (! pm) + bm = dimg->get_bitmap(test,all); + if (! pm && ! bm) + return; + write(str, + "%% --- now doing a level 1 image\n" + "gsave\n"); + // Display image + int band_bytes=125000; + int band_height = band_bytes/prn_rect.width(); + int buffer_size = band_height*prn_rect.width(); + buffer_size = buffer_size*21/10; + bool do_color = false; + bool do_color_or_gray = false; + if (pm && (options.get_mode() != Options::BW)) + do_color_or_gray = true; + if (do_color_or_gray && options.get_color()) + do_color = true; + if (do_color) + buffer_size *= 3; + if (do_color) + write(str, "/buffer24 %d string def\n", 3*prn_rect.width()); + if (do_color_or_gray) + write(str, "/buffer8 %d string def\n", prn_rect.width()); + else + write(str, "/buffer8 %d string def\n", (prn_rect.width()+7)/8); + if (do_color) + { + write(str, + "%d %d 8 [ 1 0 0 1 0 0 ]\n" + "{ ColorProc } false 3 ColorImage\n", + prn_rect.width(), prn_rect.height()); + } + else if (do_color_or_gray) + { + write(str, + "%d %d 8 [ 1 0 0 1 0 0 ]\n" + "{ currentfile buffer8 readhexstring pop } image\n", + prn_rect.width(), prn_rect.height()); + } + else + { + write(str, + "%d %d 1 [ 1 0 0 1 0 0 ]\n" + "{ currentfile buffer8 readhexstring pop } image\n", + prn_rect.width(), prn_rect.height()); + } + unsigned char * buffer; + GPBuffer<unsigned char> gbuffer(buffer,buffer_size); + { + // Start storing image in bands + GRect grectBand = prn_rect; + grectBand.ymax = grectBand.ymin; + while(grectBand.ymax < prn_rect.ymax) + { + // Compute next band + grectBand.ymin = grectBand.ymax; + grectBand.ymax = grectBand.ymin+band_bytes/grectBand.width(); + if (grectBand.ymax > prn_rect.ymax) + grectBand.ymax = prn_rect.ymax; + GRect all(0,0, dimg->get_width(),dimg->get_height()); + pm = 0; + bm = 0; + if (do_color_or_gray) + { + if (options.get_mode() == Options::FORE) + pm = dimg->get_fg_pixmap(grectBand, all); + else if (options.get_mode() == Options::BACK) + pm = dimg->get_bg_pixmap(grectBand, all); + else + pm = dimg->get_pixmap(grectBand, all); + } + else + { + bm = dimg->get_bitmap(grectBand, all); + } + // Store next band + unsigned char *buf_ptr = buffer; + int symbols=0; + for (int y=0; y<grectBand.height(); y++) + { + if (pm && do_color_or_gray) + { + GPixel *pix = (*pm)[y]; + for (int x=grectBand.width(); x>0; x--, pix++) + { + if (do_color) + { + char *data; + data = bin2hex[ramp[pix->r]]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + data = bin2hex[ramp[pix->g]]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + data = bin2hex[ramp[pix->b]]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + symbols += 6; + } + else + { + char *data; + data = bin2hex[ramp[GRAY(pix->r,pix->g,pix->b)]]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + symbols += 2; + } + if (symbols>70) + { + *buf_ptr++ = '\n'; + symbols=0; + } + } + } + else if (bm) + { + unsigned char *pix = (*bm)[y]; + unsigned char acc = 0; + unsigned char mask = 0; + char *data; + for (int x=grectBand.width(); x>0; x--, pix++) + { + if (mask == 0) + mask = 0x80; + if (! *pix) + acc |= mask; + mask >>= 1; + if (mask == 0) + { + data = bin2hex[acc]; + acc = 0; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + symbols += 2; + if (symbols>70) + { + *buf_ptr++ = '\n'; + symbols = 0; + } + } + } + if (mask != 0) + { + data = bin2hex[acc]; + *buf_ptr++ = data[0]; + *buf_ptr++ = data[1]; + symbols += 2; + } + } + if (refresh_cb) + refresh_cb(refresh_cl_data); + } + str.writall(buffer, buf_ptr-buffer); + if (prn_progress_cb) + { + double done=(double) (grectBand.ymax + - prn_rect.ymin)/prn_rect.height(); + if ((int) (20*print_done)!=(int) (20*done)) + { + print_done=done; + prn_progress_cb(done, prn_progress_cl_data); + } + } + } + write(str, "\n"); + } + write(str, "grestore\n"); +} + +void +DjVuToPS:: +print_image_lev2(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect) +{ + double print_done=0; + GRect all(0,0, dimg->get_width(),dimg->get_height()); + GP<GPixmap> pm; + GRect test(0,0,1,1); + if (options.get_mode() == Options::FORE) + pm = dimg->get_fg_pixmap(test, all); + else if (options.get_mode() == Options::BACK) + pm = dimg->get_bg_pixmap(test, all); + else if (options.get_mode() != Options::BW) + pm = dimg->get_pixmap(test, all); + if (! pm) + return; + write(str, + "%% --- now doing a level 2 image\n" + "gsave\n"); + // Display image + int band_bytes=125000; + int band_height = band_bytes/prn_rect.width(); + int buffer_size = band_height*prn_rect.width(); + int ps_chunk_height = 30960/prn_rect.width()+1; + buffer_size = buffer_size*21/10 + 32; + bool do_color = options.get_color(); + if (do_color) + { + buffer_size *= 3; + write(str, + "/bufferR %d string def\n" + "/bufferG %d string def\n" + "/bufferB %d string def\n" + "DjVuColorSpace setcolorspace\n" + "<< /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [0 1 0 1 0 1]\n" + " /ImageMatrix [1 0 0 1 0 0]\n" + " /MultipleDataSources true\n" + " /DataSource [ { ReadR } { ReadG } { ReadB } ]\n" + " /Interpolate false >> image\n", + ps_chunk_height*prn_rect.width(), + ps_chunk_height*prn_rect.width(), + ps_chunk_height*prn_rect.width(), + prn_rect.width(), prn_rect.height()); + } + else + { + write(str, + "DjVuColorSpace setcolorspace\n" + "<< /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /BitsPerComponent 8\n" + " /Decode [0 1]\n" + " /ImageMatrix [1 0 0 1 0 0]\n" + " /DataSource currentfile /ASCII85Decode\n" + " filter /RunLengthDecode filter\n" + " /Interpolate false >> image\n", + prn_rect.width(), prn_rect.height()); + } + unsigned char *buffer; + GPBuffer<unsigned char> gbuffer(buffer,buffer_size); + unsigned char *rle_in; + GPBuffer<unsigned char> grle_in(rle_in,ps_chunk_height*prn_rect.width()); + unsigned char *rle_out; + GPBuffer<unsigned char> grle_out(rle_out,2*ps_chunk_height*prn_rect.width()); + { + // Start storing image in bands + unsigned char * rle_out_end = rle_out; + GRect grectBand = prn_rect; + grectBand.ymax = grectBand.ymin; + while(grectBand.ymax < prn_rect.ymax) + { + // Compute next band + grectBand.ymin = grectBand.ymax; + grectBand.ymax = grectBand.ymin+band_bytes/grectBand.width(); + if (grectBand.ymax > prn_rect.ymax) + grectBand.ymax = prn_rect.ymax; + GRect all(0,0, dimg->get_width(),dimg->get_height()); + pm = 0; + if (options.get_mode() == Options::FORE) + pm = dimg->get_fg_pixmap(grectBand, all); + else if (options.get_mode() == Options::BACK) + pm = dimg->get_bg_pixmap(grectBand, all); + else + pm = dimg->get_pixmap(grectBand, all); + // Store next band + unsigned char *buf_ptr = buffer; + if (do_color && pm) + { + int y=0; + while(y<grectBand.height()) + { + int row, y1; + unsigned char *ptr, *ptr1; + // Doing R component of current chunk + for (row=0,ptr=rle_in,y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->r]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; *buf_ptr++ = '>'; *buf_ptr++ = '\n'; + // Doing G component of current chunk + for (row=0,ptr=rle_in,y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->g]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + // Doing B component of current chunk + for (row=0, ptr=rle_in, y1=y; + row<ps_chunk_height && y1<grectBand.height(); + row++,y1++) + { + GPixel *pix = (*pm)[y1]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[pix->b]; + } + ptr1 = RLE_encode(rle_out, rle_in, ptr); + *ptr1++ = 0x80; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, ptr1); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + y=y1; + if (refresh_cb) + refresh_cb(refresh_cl_data); + } //while (y>=0) + } + else if (pm) + { + // Don't use color + int y=0; + while(y<grectBand.height()) + { + unsigned char *ptr = rle_in; + for(int row=0; + row<ps_chunk_height && y<grectBand.height(); + row++,y++) + { + GPixel *pix = (*pm)[y]; + for (int x=grectBand.width(); x>0; x--,pix++) + *ptr++ = ramp[GRAY(pix->r,pix->g,pix->b)]; + } + rle_out_end = RLE_encode(rle_out_end, rle_in, ptr); + unsigned char *encode_to = rle_out + + (rle_out_end-rle_out)/4*4; + int bytes_left = rle_out_end-encode_to; + buf_ptr = ASCII85_encode(buf_ptr, rle_out, encode_to); + *buf_ptr++ = '\n'; + memcpy(rle_out, encode_to, bytes_left); + rle_out_end = rle_out+bytes_left; + if (refresh_cb) + refresh_cb(refresh_cl_data); + } + if (grectBand.ymax >= prn_rect.ymax) + { + *rle_out_end++ = 0x80; // Add EOF marker + buf_ptr = ASCII85_encode(buf_ptr, rle_out, rle_out_end); + *buf_ptr++ = '~'; + *buf_ptr++ = '>'; + *buf_ptr++ = '\n'; + } + } + str.writall(buffer, buf_ptr-buffer); + if (prn_progress_cb) + { + double done=(double) (grectBand.ymax + - prn_rect.ymin)/prn_rect.height(); + if ((int) (20*print_done)!=(int) (20*done)) + { + print_done=done; + prn_progress_cb(done, prn_progress_cl_data); + } + } + } + write(str, "\n"); + } + write(str, "grestore\n"); +} + +static void +get_anno_sub(IFFByteStream &iff, IFFByteStream &out) +{ + GUTF8String chkid; + while (iff.get_chunk(chkid)) + { + if (iff.composite()) + get_anno_sub(iff, out); + else if (chkid == "ANTa" || chkid == "ANTz" || + chkid == "TXTa" || chkid == "TXTz" ) + { + out.put_chunk(chkid); + out.copy(*iff.get_bytestream()); + out.close_chunk(); + } + iff.close_chunk(); + } +} + +static GP<ByteStream> +get_anno(GP<DjVuFile> f) +{ + if (! f->anno) + { + GP<ByteStream> bs = f->get_init_data_pool()->get_stream(); + GP<ByteStream> anno = ByteStream::create(); + GP<IFFByteStream> in = IFFByteStream::create(bs); + GP<IFFByteStream> out = IFFByteStream::create(anno); + get_anno_sub(*in, *out); + f->anno = anno; + } + f->anno->seek(0); + return f->anno; +} + +static GP<DjVuTXT> +get_text(GP<DjVuFile> file) +{ + GUTF8String chkid; + GP<IFFByteStream> iff = IFFByteStream::create(get_anno(file)); + while (iff->get_chunk(chkid)) + { + if (chkid == "TXTa") + { + GP<DjVuTXT> txt = DjVuTXT::create(); + txt->decode(iff->get_bytestream()); + return txt; + } + else if (chkid == "TXTz") + { + GP<DjVuTXT> txt = DjVuTXT::create(); + GP<ByteStream> bsiff = BSByteStream::create(iff->get_bytestream()); + txt->decode(bsiff); + return txt; + } + iff->close_chunk(); + } + return 0; +} + +static void +print_ps_string(const char *data, int length, ByteStream &out) +{ + while (*data && length>0) + { + int span = 0; + while (span<length && data[span]>=0x20 && data[span]<0x7f + && data[span]!='(' && data[span]!=')' && data[span]!='\\' ) + span++; + if (span > 0) + { + out.write(data, span); + data += span; + length -= span; + } + else + { + char buffer[5]; + sprintf(buffer,"\\%03o", *data); + out.write(buffer,4); + data += 1; + length -= 1; + } + } +} + +static void +print_txt_sub(DjVuTXT &txt, DjVuTXT::Zone &zone, + ByteStream &out,int &lastx,int &lasty) +{ + // Get separator + char separator = 0; + switch(zone.ztype) + { + case DjVuTXT::COLUMN: + separator = DjVuTXT::end_of_column; break; + case DjVuTXT::REGION: + separator = DjVuTXT::end_of_region; break; + case DjVuTXT::PARAGRAPH: + separator = DjVuTXT::end_of_paragraph; break; + case DjVuTXT::LINE: + separator = DjVuTXT::end_of_line; break; + case DjVuTXT::WORD: + separator = ' '; break; + default: + separator = 0; break; + } + // Zone children + if (zone.children.isempty()) + { + const char *data = (const char*)txt.textUTF8 + zone.text_start; + int length = zone.text_length; + if (data[length-1] == separator) + length -= 1; + out.write("( ",2); + print_ps_string(data,length,out); + out.write(")",1); + GUTF8String message; + int tmpx= zone.rect.xmin-lastx; + int tmpy= zone.rect.ymin-lasty; + message.format(" %d %d S \n", tmpx, tmpy); + lastx=zone.rect.xmin; + lasty=zone.rect.ymin; + out.write((const char*)message, message.length()); + } + else + { + if (zone.ztype==DjVuTXT::LINE) + { + GUTF8String message; + message.format("%d F\n",zone.rect.ymax-zone.rect.ymin); + out.write((const char*)message,message.length()); + } + for (GPosition pos=zone.children; pos; ++pos) + print_txt_sub(txt, zone.children[pos], out,lastx,lasty); + } +} + +static void +print_txt(GP<DjVuTXT> txt, + ByteStream &out ) +{ + if (txt) + { + int lastx=0; + int lasty=0; + GUTF8String message = + "%% -- now doing hidden text\n" + "gsave -1 -1 0 0 clip 0 0 moveto\n"; + out.write((const char*)message,message.length()); + print_txt_sub(*txt, txt->page_zone, out,lastx,lasty); + message = + "grestore \n"; + out.write((const char*)message,message.length()); + } +} + +void +DjVuToPS:: +print_image(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect, + GP<DjVuTXT> txt) +{ + /* Just outputs the specified image. The function assumes, that + all add-ons (like {\em document setup}, {\em page setup}) are + already there. It will just output the image. Since + output of this function will generate PostScript errors when + used without output of auxiliary functions, it should be + used carefully. */ + DEBUG_MSG("DjVuToPS::print_image(): Printing DjVuImage to a stream\n"); + DEBUG_MAKE_INDENT(3); + if (!dimg) + G_THROW(ERR_MSG("DjVuToPS.empty_image")); + if (prn_rect.isempty()) + G_THROW(ERR_MSG("DjVuToPS.empty_rect")); + if (prn_progress_cb) + prn_progress_cb(0, prn_progress_cl_data); + // Compute information for chosen display mode + print_txt(txt, str); + make_gamma_ramp(dimg); + if (options.get_level() < 2) + { + print_image_lev1(str, dimg, prn_rect); + } + else if (options.get_level() < 3 && dimg->get_fgpm()) + { + switch(options.get_mode()) + { + case Options::COLOR: + case Options::FORE: + print_image_lev2(str, dimg, prn_rect); + break; + case Options::BW: + print_fg(str, dimg, prn_rect); + break; + case Options::BACK: + print_bg(str, dimg, prn_rect); + break; + } + } + else + { + switch(options.get_mode()) + { + case Options::COLOR: + print_bg(str, dimg, prn_rect); + print_fg(str, dimg, prn_rect); + break; + case Options::FORE: + case Options::BW: + print_fg(str, dimg, prn_rect); + break; + case Options::BACK: + print_bg(str, dimg, prn_rect); + break; + } + } + if (prn_progress_cb) + prn_progress_cb(1, prn_progress_cl_data); +} + + + + +// *********************************************************************** +// ******* PUBLIC FUNCTION FOR PRINTING A SINGLE PAGE ******************** +// *********************************************************************** + + + + +void +DjVuToPS:: +print(ByteStream &str, + GP<DjVuImage> dimg, + const GRect &prn_rect_in, + const GRect &img_rect, + int override_dpi) +{ + DEBUG_MSG("DjVuToPS::print(): Printing DjVu page to a stream\n"); + DEBUG_MAKE_INDENT(3); + GRect prn_rect; + prn_rect.intersect(prn_rect_in, img_rect); + DEBUG_MSG("prn_rect=(" << prn_rect.xmin << ", " << prn_rect.ymin << ", " << + prn_rect.width() << ", " << prn_rect.height() << ")\n"); + DEBUG_MSG("img_rect=(" << img_rect.xmin << ", " << img_rect.ymin << ", " << + img_rect.width() << ", " << img_rect.height() << ")\n"); + if (!dimg) + G_THROW(ERR_MSG("DjVuToPS.empty_image")); + if (prn_rect.isempty()) + G_THROW(ERR_MSG("DjVuToPS.empty_rect")); + if (img_rect.isempty()) + G_THROW(ERR_MSG("DjVuToPS.bad_scale")); + GRectMapper mapper; + mapper.set_input(img_rect); + GRect full_rect(0, 0, dimg->get_width(), dimg->get_height()); + mapper.set_output(full_rect); + mapper.map(prn_rect); + int image_dpi = dimg->get_dpi(); + if (override_dpi>0) + image_dpi = override_dpi; + if (image_dpi <= 0) + image_dpi = 300; + store_doc_prolog(str, 1, (int)(image_dpi), &prn_rect); + store_doc_setup(str); + write(str,"%%%%Page: 1 1\n"); + store_page_setup(str, (int)(image_dpi), prn_rect); + print_image(str, dimg, prn_rect, 0); + store_page_trailer(str); + write(str,"showpage\n"); + store_doc_trailer(str); +} + + + + +// *********************************************************************** +// *************************** DOCUMENT LEVEL **************************** +// *********************************************************************** + + +void +DjVuToPS:: +parse_range(GP<DjVuDocument> doc, + GUTF8String page_range, + GList<int> &pages_todo) +{ + int doc_pages = doc->get_pages_num(); + if (!page_range.length()) + page_range.format("1-%d", doc_pages); + DEBUG_MSG("page_range='" << (const char *)page_range << "'\n"); + int spec = 0; + int both = 1; + int start_page = 1; + int end_page = doc_pages; + const char *q = (const char*)page_range; + char *p = (char*)q; + while (*p) + { + while (*p==' ') + p += 1; + if (! *p) + break; + if (*p>='0' && *p<='9') + { + end_page = strtol(p, &p, 10); + spec = 1; + } + else if (*p=='$') + { + spec = 1; + end_page = doc_pages; + p += 1; + } + else if (both) + { + end_page = 1; + } + else + { + end_page = doc_pages; + } + while (*p==' ') + p += 1; + if (both) + { + start_page = end_page; + if (*p == '-') + { + p += 1; + both = 0; + continue; + } + } + both = 1; + while (*p==' ') + p += 1; + if (*p && *p != ',') + G_THROW(ERR_MSG("DjVuToPS.bad_range") + + GUTF8String("\t") + GUTF8String(p) ); + if (*p == ',') + p += 1; + if (! spec) + G_THROW(ERR_MSG("DjVuToPS.bad_range") + + GUTF8String("\t") + page_range ); + spec = 0; + if (end_page < 0) + end_page = 0; + if (start_page < 0) + start_page = 0; + if (end_page > doc_pages) + end_page = doc_pages; + if (start_page > doc_pages) + start_page = doc_pages; + if (start_page <= end_page) + for(int page_num=start_page; page_num<=end_page; page_num++) + pages_todo.append(page_num-1); + else + for(int page_num=start_page; page_num>=end_page; page_num--) + pages_todo.append(page_num-1); + } +} + +class DjVuToPS::DecodePort : public DjVuPort +{ +protected: + DecodePort(void); +public: + static GP<DecodePort> create(void); + GEvent decode_event; + bool decode_event_received; + double decode_done; + GURL decode_page_url; + virtual void notify_file_flags_changed(const DjVuFile*,long,long); + virtual void notify_decode_progress(const DjVuPort*,double); +}; + +DjVuToPS::DecodePort:: +DecodePort(void) + : decode_event_received(false), + decode_done((double)0) +{ +} + +GP<DjVuToPS::DecodePort> +DjVuToPS::DecodePort:: +create(void) +{ + return new DecodePort; +} + +void +DjVuToPS::DecodePort:: +notify_file_flags_changed(const DjVuFile *source, + long set_mask, long clr_mask) +{ + // WARNING! This function is called from another thread + if (set_mask & (DjVuFile::DECODE_OK | + DjVuFile::DECODE_FAILED | + DjVuFile::DECODE_STOPPED )) + { + if (source->get_url() == decode_page_url) + { + decode_event_received=true; + decode_event.set(); + } + } +} + +void +DjVuToPS::DecodePort:: +notify_decode_progress(const DjVuPort *source, double done) +{ + // WARNING! This function is called from another thread + if (source->inherits("DjVuFile")) + { + DjVuFile * file=(DjVuFile *) source; + if (file->get_url()==decode_page_url) + if ((int) (decode_done*20)!=(int) (done*20)) + { + decode_done=done; + decode_event_received=true; + decode_event.set(); + } + } +} + +void +DjVuToPS:: +set_refresh_cb(void (*_refresh_cb)(void*), void *_refresh_cl_data) +{ + refresh_cb = _refresh_cb; + refresh_cl_data = _refresh_cl_data; +} + +void +DjVuToPS:: +set_prn_progress_cb(void (*_prn_progress_cb)(double, void *), + void *_prn_progress_cl_data) +{ + prn_progress_cb=_prn_progress_cb; + prn_progress_cl_data=_prn_progress_cl_data; +} + +void +DjVuToPS:: +set_dec_progress_cb(void (*_dec_progress_cb)(double, void *), + void *_dec_progress_cl_data) +{ + dec_progress_cb=_dec_progress_cb; + dec_progress_cl_data=_dec_progress_cl_data; +} + +void +DjVuToPS:: +set_info_cb(void (*_info_cb)(int, int, int, Stage, void*), + void *_info_cl_data) +{ + info_cb=_info_cb; + info_cl_data=_info_cl_data; +} + +GP<DjVuImage> +DjVuToPS:: +decode_page(GP<DjVuDocument> doc, + int page_num, int cnt, int todo) +{ + DEBUG_MSG("processing page #" << page_num << "\n"); + if (! port) + { + port = DecodePort::create(); + DjVuPort::get_portcaster()->add_route((DjVuDocument*)doc, port); + } + port->decode_event_received = false; + port->decode_done = 0; + GP<DjVuFile> djvu_file; + GP<DjVuImage> dimg; + if (page_num >= 0 && page_num < doc->get_pages_num()) + djvu_file = doc->get_djvu_file(page_num); + if (! djvu_file ) + return 0; + if (djvu_file->is_decode_ok()) + return doc->get_page(page_num, false); + // This is the best place to call info_cb(). Note, that + // get_page() will start decoding if necessary, and will not + // return until the decoding is over in a single threaded + // environment. That's why we call get_djvu_file() first. + if (info_cb) + info_cb(page_num, cnt, todo, DECODING, info_cl_data); + // Do NOT decode the page synchronously here!!! + // The plugin will deadlock otherwise. + dimg = doc->get_page(page_num, false); + djvu_file = dimg->get_djvu_file(); + port->decode_page_url = djvu_file->get_url(); + if (djvu_file->is_decode_ok()) + return dimg; + DEBUG_MSG("decoding\n"); + if (dec_progress_cb) + dec_progress_cb(0, dec_progress_cl_data); + while(! djvu_file->is_decode_ok()) + { + while(!port->decode_event_received && + !djvu_file->is_decode_ok()) + { + port->decode_event.wait(250); + if (refresh_cb) + refresh_cb(refresh_cl_data); + } + port->decode_event_received = false; + if (djvu_file->is_decode_failed() || + djvu_file->is_decode_stopped()) + G_THROW(ERR_MSG("DjVuToPS.no_image") + + GUTF8String("\t") + + GUTF8String(page_num)); + if (dec_progress_cb) + dec_progress_cb(port->decode_done, dec_progress_cl_data); + } + if (dec_progress_cb) + dec_progress_cb(1, dec_progress_cl_data); + return dimg; +} + +void +DjVuToPS:: +process_single_page(ByteStream &str, + GP<DjVuDocument> doc, + int page_num, int cnt, int todo, + int magic) +{ + GP<DjVuTXT> txt; + GP<DjVuImage> dimg; + dimg = decode_page(doc, page_num, cnt, todo); + if (options.get_text()) + txt = get_text(dimg->get_djvu_file()); + if (info_cb) + info_cb(page_num, cnt, todo, PRINTING, info_cl_data); + if (!magic) + write(str, "%%%%Page: %d %d\n", page_num+1, cnt+1); + if (dimg) + { + int dpi = dimg->get_dpi(); + dpi = ((dpi <= 0) ? 300 : dpi); + GRect img_rect(0, 0, dimg->get_width(), dimg->get_height()); + store_page_setup(str, dpi, img_rect, magic); + print_image(str, dimg, img_rect,txt); + store_page_trailer(str); + } + if (!magic) + write(str,"showpage\n"); +} + + +struct pdata { + int page1, page2; + int smax, spos; + int offset; +}; + +void +DjVuToPS:: +process_double_page(ByteStream &str, + GP<DjVuDocument> doc, + void *v, int cnt, int todo) +{ + const pdata *inf = (const pdata*)v; + int off = abs(inf->offset); + write(str, + "%%%%Page: (%d,%d) %d\n" + "gsave\n" + "/fold-dict 8 dict dup 3 1 roll def begin\n" + " clippath pathbbox newpath pop pop translate\n" + " clippath pathbbox newpath 4 2 roll pop pop\n" + " /ph exch def\n" + " /pw exch def\n" + " /w ph %d sub 2 div def\n" + " /m1 %d def\n" + " /m2 %d def\n" + "end\n", + inf->page1 + 1, inf->page2 + 1, cnt, + 2 * (off + options.get_bookletfold(inf->smax-1)), + inf->offset + options.get_bookletfold(inf->spos), + inf->offset - options.get_bookletfold(inf->spos)); + if (options.get_cropmarks()) + write(str, + "%% -- folding marks\n" + "fold-dict begin\n" + " 0 setgray 0.5 setlinewidth\n" + " ph m1 m2 add add 2 div dup\n" + " 0 exch moveto 36 0 rlineto stroke\n" + " pw exch moveto -36 0 rlineto stroke\n" + "end\n"); + write(str, + "%% -- first page\n" + "gsave fold-dict begin\n" + " 0 ph 2 div w add m1 add translate 270 rotate\n" + " 0 0 w pw rectclip end\n"); + if (inf->page1 >= 0) + process_single_page(str, doc, inf->page1, cnt*2, todo*2, +1); + write(str, + "grestore\n" + "%% -- second page\n" + "gsave fold-dict begin\n" + " 0 ph 2 div m2 add translate 270 rotate\n" + " 0 0 w pw rectclip end\n"); + if (inf->page2 >= 0) + process_single_page(str, doc, inf->page2, cnt*2+1, todo*2, -1); + write(str, + "grestore\n" + "grestore\n" + "showpage\n"); +} + +static void +booklet_order(GList<int>& pages, int smax) +{ + // -- make a multiple of four + while (pages.size() & 0x3) + pages.append(-1); + // -- copy to array + int i = 0; + int n = pages.size(); + GTArray<int> p(0,n-1); + for (GPosition pos=pages; pos; ++pos) + p[i++] = pages[pos]; + // -- rebuild + pages.empty(); + for (i=0; i<n; i+=smax) + { + int lo = i; + int hi = i+smax-1; + if (hi >= n) + hi = n-1; + while (lo < hi) + { + pages.append(p[hi--]); + pages.append(p[lo++]); + pages.append(p[lo++]); + pages.append(p[hi--]); + } + } +} + + +// *********************************************************************** +// ******* PUBLIC FUNCTIONS FOR PRINTING MULTIPLE PAGES ****************** +// *********************************************************************** + + + +void +DjVuToPS:: +print(ByteStream &str, + GP<DjVuDocument> doc, + GUTF8String page_range) +{ + DEBUG_MSG("DjVuToPS::print(): Printing DjVu document\n"); + DEBUG_MAKE_INDENT(3); + // Get page range + GList<int> pages_todo; + parse_range(doc, page_range, pages_todo); + int todo = pages_todo.size(); + if (options.get_format()==Options::EPS) + { + /* Encapsulated Postscript mode */ + if (todo != 1) + G_THROW(ERR_MSG("DjVuToPS.only_one_page")); + GPosition pos = pages_todo; + int page_num = pages_todo[pos]; + GP<DjVuImage> dimg = decode_page(doc,page_num,0,todo); + if (! dimg) + G_THROW(ERR_MSG("DjVuToPS.no_image") + GUTF8String("\t1")); + GRect bbox(0, 0, dimg->get_width(), dimg->get_height()); + store_doc_prolog(str, 1, dimg->get_dpi(), &bbox); + store_doc_setup(str); + process_single_page(str, doc, page_num, 0, todo, 0); + } + else if (options.get_bookletmode()==Options::OFF) + { + /* Normal mode */ + int cnt = 0; + store_doc_prolog(str, todo, 0, 0); + store_doc_setup(str); + for(GPosition pos = pages_todo; pos; ++pos) + process_single_page(str,doc,pages_todo[pos],cnt++,todo,0); + store_doc_trailer(str); + } + else + { + /* Booklet mode */ + int sheets_left = (todo+3)/4; + int sides_todo = sheets_left; + if (options.get_bookletmode() == Options::RECTOVERSO) + sides_todo *= 2; + int sheets_max = (options.get_bookletmax()+3)/4; + if (! sheets_max) + sheets_max = sheets_left; + // -- reorder pages + booklet_order(pages_todo, sheets_max*4); + // -- print + int sides = 0; + int sheetpos = sheets_max; + store_doc_prolog(str, sides_todo, 0, 0); + store_doc_setup(str); + for (GPosition p=pages_todo; p; ++p) + { + struct pdata inf; + inf.page1 = pages_todo[p]; + inf.page2 = pages_todo[++p]; + inf.smax = sheets_max; + inf.spos = --sheetpos; + inf.offset = options.get_bookletalign(); + if (options.get_bookletmode() != Options::VERSO) + process_double_page(str,doc,(void*)&inf,sides++,sides_todo); + inf.page1 = pages_todo[++p]; + inf.page2 = pages_todo[++p]; + inf.offset = -inf.offset; + if (options.get_bookletmode() != Options::RECTO) + process_double_page(str,doc,(void*)&inf,sides++,sides_todo); + sheets_left -= 1; + if (sheetpos <= 0) + sheetpos = ((sheets_max<sheets_left) ? sheets_max : sheets_left); + } + store_doc_trailer(str); + } +} + + +void +DjVuToPS:: +print(ByteStream &str, GP<DjVuDocument> doc) +{ + GUTF8String dummy; + print(str,doc,dummy); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + |