You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2583 lines
79 KiB
2583 lines
79 KiB
15 years ago
|
//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
|
||
|
|