|
Packit |
df99a1 |
//C- -*- C++ -*-
|
|
Packit |
df99a1 |
//C- -------------------------------------------------------------------
|
|
Packit |
df99a1 |
//C- DjVuLibre-3.5
|
|
Packit |
df99a1 |
//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun.
|
|
Packit |
df99a1 |
//C- Copyright (c) 2001 AT&T
|
|
Packit |
df99a1 |
//C-
|
|
Packit |
df99a1 |
//C- This software is subject to, and may be distributed under, the
|
|
Packit |
df99a1 |
//C- GNU General Public License, either Version 2 of the license,
|
|
Packit |
df99a1 |
//C- or (at your option) any later version. The license should have
|
|
Packit |
df99a1 |
//C- accompanied the software or you may obtain a copy of the license
|
|
Packit |
df99a1 |
//C- from the Free Software Foundation at http://www.fsf.org .
|
|
Packit |
df99a1 |
//C-
|
|
Packit |
df99a1 |
//C- This program is distributed in the hope that it will be useful,
|
|
Packit |
df99a1 |
//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
df99a1 |
//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
df99a1 |
//C- GNU General Public License for more details.
|
|
Packit |
df99a1 |
//C-
|
|
Packit |
df99a1 |
//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from
|
|
Packit |
df99a1 |
//C- Lizardtech Software. Lizardtech Software has authorized us to
|
|
Packit |
df99a1 |
//C- replace the original DjVu(r) Reference Library notice by the following
|
|
Packit |
df99a1 |
//C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu):
|
|
Packit |
df99a1 |
//C-
|
|
Packit |
df99a1 |
//C- ------------------------------------------------------------------
|
|
Packit |
df99a1 |
//C- | DjVu (r) Reference Library (v. 3.5)
|
|
Packit |
df99a1 |
//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
|
|
Packit |
df99a1 |
//C- | The DjVu Reference Library is protected by U.S. Pat. No.
|
|
Packit |
df99a1 |
//C- | 6,058,214 and patents pending.
|
|
Packit |
df99a1 |
//C- |
|
|
Packit |
df99a1 |
//C- | This software is subject to, and may be distributed under, the
|
|
Packit |
df99a1 |
//C- | GNU General Public License, either Version 2 of the license,
|
|
Packit |
df99a1 |
//C- | or (at your option) any later version. The license should have
|
|
Packit |
df99a1 |
//C- | accompanied the software or you may obtain a copy of the license
|
|
Packit |
df99a1 |
//C- | from the Free Software Foundation at http://www.fsf.org .
|
|
Packit |
df99a1 |
//C- |
|
|
Packit |
df99a1 |
//C- | The computer code originally released by LizardTech under this
|
|
Packit |
df99a1 |
//C- | license and unmodified by other parties is deemed "the LIZARDTECH
|
|
Packit |
df99a1 |
//C- | ORIGINAL CODE." Subject to any third party intellectual property
|
|
Packit |
df99a1 |
//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
|
|
Packit |
df99a1 |
//C- | non-exclusive license to make, use, sell, or otherwise dispose of
|
|
Packit |
df99a1 |
//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
|
|
Packit |
df99a1 |
//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
|
|
Packit |
df99a1 |
//C- | General Public License. This grant only confers the right to
|
|
Packit |
df99a1 |
//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
|
|
Packit |
df99a1 |
//C- | the extent such infringement is reasonably necessary to enable
|
|
Packit |
df99a1 |
//C- | recipient to make, have made, practice, sell, or otherwise dispose
|
|
Packit |
df99a1 |
//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
|
|
Packit |
df99a1 |
//C- | any greater extent that may be necessary to utilize further
|
|
Packit |
df99a1 |
//C- | modifications or combinations.
|
|
Packit |
df99a1 |
//C- |
|
|
Packit |
df99a1 |
//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
|
|
Packit |
df99a1 |
//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
Packit |
df99a1 |
//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
|
|
Packit |
df99a1 |
//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
|
Packit |
df99a1 |
//C- +------------------------------------------------------------------
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
#ifdef HAVE_CONFIG_H
|
|
Packit |
df99a1 |
# include "config.h"
|
|
Packit |
df99a1 |
#endif
|
|
Packit |
df99a1 |
#if NEED_GNUG_PRAGMAS
|
|
Packit |
df99a1 |
# pragma implementation
|
|
Packit |
df99a1 |
#endif
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
/** @name csepdjvu
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
{\bf Synopsis}
|
|
Packit |
df99a1 |
\begin{verbatim}
|
|
Packit |
df99a1 |
csepdjvu <....options-or-separated_files...> <outputdjvufile>
|
|
Packit |
df99a1 |
\end{verbatim}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
{\bf Description}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
File #"csepdjvu.cpp"# demonstrates a complete back-end encoder that takes
|
|
Packit |
df99a1 |
{\em separated files} as input and produces a djvu file as output.
|
|
Packit |
df99a1 |
Each {\em separated file} contains a concatenation of pages.
|
|
Packit |
df99a1 |
Each page contains the following components:
|
|
Packit |
df99a1 |
\begin{itemize}
|
|
Packit |
df99a1 |
\item A run-length encoded file representing the foreground.
|
|
Packit |
df99a1 |
Two kind of run-length encoded files are accepted.
|
|
Packit |
df99a1 |
The Black-And-White RLE format is described in section
|
|
Packit |
df99a1 |
\Ref{PNM and RLE file formats}. The Color RLE format
|
|
Packit |
df99a1 |
is described below.
|
|
Packit |
df99a1 |
\item An optional PPM image representing the background.
|
|
Packit |
df99a1 |
The size (width and height) of the PPM image must be obtained by
|
|
Packit |
df99a1 |
rounding up the quotient of the foreground image size by
|
|
Packit |
df99a1 |
an integer reduction factor ranging from 1 to 12.
|
|
Packit |
df99a1 |
\item An arbitrary number of comment lines starting with
|
|
Packit |
df99a1 |
character '#'.
|
|
Packit |
df99a1 |
\end{itemize}
|
|
Packit |
df99a1 |
All the provided pages will be converted to Compound DjVu Images.
|
|
Packit |
df99a1 |
Foreground colors will be encoded using a single solid color per component
|
|
Packit |
df99a1 |
(see \Ref{DjVu Image files}). Multiple pages will be gathered into a
|
|
Packit |
df99a1 |
single bundled file. Use \Ref{djvmcvt} or \Ref{djvujoin} to create an
|
|
Packit |
df99a1 |
indirect file.
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
{\bf Options}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
\begin{description}
|
|
Packit |
df99a1 |
\item[-d n] Resolution written into the output file (default: 300).
|
|
Packit |
df99a1 |
\item[-q <spec>] Quality level for background (default: 72+11+10+10).
|
|
Packit |
df99a1 |
See option #"-slice"# in program \Ref{c44} for details.
|
|
Packit |
df99a1 |
\item[-v] Displays a brief message per page.
|
|
Packit |
df99a1 |
\item[-vv] Displays lots of additional messages.
|
|
Packit |
df99a1 |
\end{description}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
{\bf Color RLE Images}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
The Color RLE file format is a simple run-length encoding scheme for color
|
|
Packit |
df99a1 |
images with a limited number of colors. Color RLE files always begin with
|
|
Packit |
df99a1 |
a text header composed of:
|
|
Packit |
df99a1 |
- the two characters #"R6"#,
|
|
Packit |
df99a1 |
- the number of columns in decimal,\\
|
|
Packit |
df99a1 |
- the number of rows in decimal,\\
|
|
Packit |
df99a1 |
- the number of palette entries in decimal.\\
|
|
Packit |
df99a1 |
These four items are separated by blank characters (space, tab, cr, or nl)
|
|
Packit |
df99a1 |
or by comment lines introduced by character ``\#''. The last number is
|
|
Packit |
df99a1 |
followed by exactly one character (usually a nl character). This header
|
|
Packit |
df99a1 |
is followed by a palette containing three bytes per color. The bytes
|
|
Packit |
df99a1 |
represent the red, green, and blue components of the color.
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
The palette is followed by four bytes integers (MSB first) representing
|
|
Packit |
df99a1 |
runs. The twelve upper bits of this integer indicate the index of the run
|
|
Packit |
df99a1 |
color in the palette entry. The twenty lower bits of the integer indicate
|
|
Packit |
df99a1 |
the run length. Color indices greater than 0xff0 are reserved for pixels
|
|
Packit |
df99a1 |
belonging to the background layer. Color index 0xfff is used for
|
|
Packit |
df99a1 |
transparent runs. Color index 0xffe is used for don't-care runs
|
|
Packit |
df99a1 |
(i.e. pixels whose values should be taken by smoothly completing the
|
|
Packit |
df99a1 |
background using the wavelet masking algorithm). Each row is represented
|
|
Packit |
df99a1 |
by a sequence of runs whose lengths add up to the image width. Rows are
|
|
Packit |
df99a1 |
encoded starting with the top row and progressing towards the bottom row.
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
@memo
|
|
Packit |
df99a1 |
Creates DjVu files from Separated files.
|
|
Packit |
df99a1 |
@author
|
|
Packit |
df99a1 |
L\'eon Bottou <leonb@research.att.com>
|
|
Packit |
df99a1 |
*/
|
|
Packit |
df99a1 |
//@{
|
|
Packit |
df99a1 |
//@}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
#include "DjVuGlobal.h"
|
|
Packit |
df99a1 |
#include "GException.h"
|
|
Packit |
df99a1 |
#include "GSmartPointer.h"
|
|
Packit |
df99a1 |
#include "GContainer.h"
|
|
Packit |
df99a1 |
#include "ByteStream.h"
|
|
Packit |
df99a1 |
#include "IFFByteStream.h"
|
|
Packit |
df99a1 |
#include "GRect.h"
|
|
Packit |
df99a1 |
#include "GBitmap.h"
|
|
Packit |
df99a1 |
#include "JB2Image.h"
|
|
Packit |
df99a1 |
#include "DjVuPalette.h"
|
|
Packit |
df99a1 |
#include "IW44Image.h"
|
|
Packit |
df99a1 |
#include "DjVuInfo.h"
|
|
Packit |
df99a1 |
#include "DjVmDoc.h"
|
|
Packit |
df99a1 |
#include "DjVmNav.h"
|
|
Packit |
df99a1 |
#include "GOS.h"
|
|
Packit |
df99a1 |
#include "GURL.h"
|
|
Packit |
df99a1 |
#include "DjVuMessage.h"
|
|
Packit |
df99a1 |
#include "DjVuText.h"
|
|
Packit |
df99a1 |
#include "BSByteStream.h"
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
#include "miniexp.h"
|
|
Packit |
df99a1 |
#include "jb2tune.h"
|
|
Packit |
df99a1 |
#include "common.h"
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
#undef MIN
|
|
Packit |
df99a1 |
#undef MAX
|
|
Packit |
df99a1 |
inline int MIN(int a, int b) { return ( a
|
|
Packit |
df99a1 |
inline int MAX(int a, int b) { return ( a>b ?a :b); }
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// --------------------------------------------------
|
|
Packit |
df99a1 |
// OPTIONS
|
|
Packit |
df99a1 |
// --------------------------------------------------
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
struct csepdjvuopts
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int dpi; // resolution
|
|
Packit |
df99a1 |
int verbose; // verbosity level
|
|
Packit |
df99a1 |
DjVuTXT::ZoneType text; // level of text detail
|
|
Packit |
df99a1 |
unsigned char slice[16]; // background quality spec
|
|
Packit |
df99a1 |
csepdjvuopts();
|
|
Packit |
df99a1 |
};
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
csepdjvuopts::csepdjvuopts()
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
dpi = 300;
|
|
Packit |
df99a1 |
verbose = 0;
|
|
Packit |
df99a1 |
text = DjVuTXT::WORD;
|
|
Packit |
df99a1 |
slice[0] = 72;
|
|
Packit |
df99a1 |
slice[1] = 83;
|
|
Packit |
df99a1 |
slice[2] = 93;
|
|
Packit |
df99a1 |
slice[3] = 103;
|
|
Packit |
df99a1 |
slice[4] = 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// --------------------------------------------------
|
|
Packit |
df99a1 |
// BUFFERED BYTESTREAM
|
|
Packit |
df99a1 |
// --------------------------------------------------
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- A bytestream that performs buffering and
|
|
Packit |
df99a1 |
// offers a stdio-like interface for parsing files.
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
class BufferByteStream : public ByteStream
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
public:
|
|
Packit |
df99a1 |
enum {bufsize=512};
|
|
Packit |
df99a1 |
private:
|
|
Packit |
df99a1 |
ByteStream &bs;
|
|
Packit |
df99a1 |
unsigned char buffer[bufsize];
|
|
Packit |
df99a1 |
int bufpos;
|
|
Packit |
df99a1 |
int bufend;
|
|
Packit |
df99a1 |
public:
|
|
Packit |
df99a1 |
BufferByteStream(ByteStream &lbs;;
|
|
Packit |
df99a1 |
size_t read(void *buffer, size_t size);
|
|
Packit |
df99a1 |
size_t write(const void *buffer, size_t size);
|
|
Packit |
df99a1 |
virtual long tell(void) const;
|
|
Packit |
df99a1 |
int eof(void);
|
|
Packit |
df99a1 |
int unget(int c);
|
|
Packit |
df99a1 |
inline int get(void);
|
|
Packit |
df99a1 |
// parsing helpers
|
|
Packit |
df99a1 |
bool skip(const char *s = " \t\n\r");
|
|
Packit |
df99a1 |
bool expect(int &c, const char *s);
|
|
Packit |
df99a1 |
bool read_integer(int &x);
|
|
Packit |
df99a1 |
bool read_pair(int &x, int &y);
|
|
Packit |
df99a1 |
bool read_geometry(GRect &r);
|
|
Packit |
df99a1 |
bool read_ps_string(GUTF8String &s);
|
|
Packit |
df99a1 |
};
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
BufferByteStream::BufferByteStream(ByteStream &bs)
|
|
Packit |
df99a1 |
: bs(bs), bufpos(1), bufend(1)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
int
|
|
Packit |
df99a1 |
BufferByteStream::eof(void) // aka. feof
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (bufpos < bufend)
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
bufend = bufpos = 1;
|
|
Packit |
df99a1 |
bufend += bs.read(buffer+bufend, bufsize-bufend);
|
|
Packit |
df99a1 |
return (bufend == bufpos);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
size_t
|
|
Packit |
df99a1 |
BufferByteStream::read(void *buf, size_t size)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (size < 1)
|
|
Packit |
df99a1 |
return 0;
|
|
Packit |
df99a1 |
if (bufend == bufpos)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (size >= bufsize)
|
|
Packit |
df99a1 |
return bs.read(buf, size);
|
|
Packit |
df99a1 |
if (eof())
|
|
Packit |
df99a1 |
return 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (bufpos + (int)size > bufend)
|
|
Packit |
df99a1 |
size = bufend - bufpos;
|
|
Packit |
df99a1 |
memcpy(buf, buffer+bufpos, size);
|
|
Packit |
df99a1 |
bufpos += size;
|
|
Packit |
df99a1 |
return size;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
size_t
|
|
Packit |
df99a1 |
BufferByteStream::write(const void *, size_t )
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
G_THROW("Cannot write into a BufferByteStream");
|
|
Packit |
df99a1 |
return 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
long
|
|
Packit |
df99a1 |
BufferByteStream::tell(void) const
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
return bs.tell() + bufpos - bufend;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
inline int
|
|
Packit |
df99a1 |
BufferByteStream::get(void) // aka. getc()
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (bufpos < bufend || !eof())
|
|
Packit |
df99a1 |
return buffer[bufpos++];
|
|
Packit |
df99a1 |
return EOF;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
int
|
|
Packit |
df99a1 |
BufferByteStream::unget(int c) // aka. ungetc()
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (bufpos > 0 && c != EOF)
|
|
Packit |
df99a1 |
return buffer[--bufpos] = (unsigned char)c;
|
|
Packit |
df99a1 |
return EOF;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
bool
|
|
Packit |
df99a1 |
BufferByteStream::expect(int &c, const char *s)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
c = get();
|
|
Packit |
df99a1 |
if (strchr(s, c))
|
|
Packit |
df99a1 |
return true;
|
|
Packit |
df99a1 |
unget(c);
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
bool
|
|
Packit |
df99a1 |
BufferByteStream::skip(const char *s)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int c;
|
|
Packit |
df99a1 |
while (expect(c, s)) { }
|
|
Packit |
df99a1 |
return true;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
bool
|
|
Packit |
df99a1 |
BufferByteStream::read_integer(int &x)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
x = 0;
|
|
Packit |
df99a1 |
int c = get();
|
|
Packit |
df99a1 |
if (c<'0' || c>'9')
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
while (c>='0' && c<='9')
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
x = x*10 + c - '0';
|
|
Packit |
df99a1 |
c = get();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
unget(c);
|
|
Packit |
df99a1 |
return true;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
bool
|
|
Packit |
df99a1 |
BufferByteStream::read_pair(int &x, int &y)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int c;
|
|
Packit |
df99a1 |
x = y = 0;
|
|
Packit |
df99a1 |
expect(c, "-");
|
|
Packit |
df99a1 |
if (! read_integer(x))
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
if (c == '-')
|
|
Packit |
df99a1 |
x = -x;
|
|
Packit |
df99a1 |
if (! expect(c, ":"))
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
expect(c, "-");
|
|
Packit |
df99a1 |
if (! read_integer(y))
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
if (c == '-')
|
|
Packit |
df99a1 |
y = -y;
|
|
Packit |
df99a1 |
return true;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
bool
|
|
Packit |
df99a1 |
BufferByteStream::read_geometry(GRect &r)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int x,y,w,h,c;
|
|
Packit |
df99a1 |
x = y = w = h = 0;
|
|
Packit |
df99a1 |
if (read_integer(w) && expect(c, "x") && read_integer(h))
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (expect(c,"+-"))
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (c == '+')
|
|
Packit |
df99a1 |
expect(c,"-");
|
|
Packit |
df99a1 |
if (! read_integer(x))
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
if (c == '-')
|
|
Packit |
df99a1 |
x = -x;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (expect(c,"+-"))
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (c == '+')
|
|
Packit |
df99a1 |
expect(c,"-");
|
|
Packit |
df99a1 |
if (! read_integer(y))
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
if (c == '-')
|
|
Packit |
df99a1 |
y = -y;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
r = GRect(x,y,w,h);
|
|
Packit |
df99a1 |
return true;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
static void
|
|
Packit |
df99a1 |
add_to_string(GUTF8String &s, char *buffer, int len, int &bom)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (!s && !bom && len>=2)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (buffer[0]==(char)0xfe && buffer[1]==(char)0xff)
|
|
Packit |
df99a1 |
bom = 0xfeff;
|
|
Packit |
df99a1 |
if (buffer[0]==(char)0xff && buffer[1]==(char)0xfe)
|
|
Packit |
df99a1 |
bom = 0xfffe;
|
|
Packit |
df99a1 |
if (bom)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
buffer += 2;
|
|
Packit |
df99a1 |
len -= 2;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (bom == 0xfeff)
|
|
Packit |
df99a1 |
for (int i=0; i
|
|
Packit |
df99a1 |
*(uint16_t*)(buffer+i) = ((buffer[i]<<8) | buffer[i+1]);
|
|
Packit |
df99a1 |
if (bom == 0xfffe)
|
|
Packit |
df99a1 |
for (int i=0; i
|
|
Packit |
df99a1 |
*(uint16_t*)(buffer+i) = ((buffer[i+1]<<8) | buffer[i]);
|
|
Packit |
df99a1 |
if (bom)
|
|
Packit |
df99a1 |
s += GUTF8String((const uint16_t*)buffer, len/2);
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
s += GUTF8String((const char*)buffer, len);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
bool
|
|
Packit |
df99a1 |
BufferByteStream::read_ps_string(GUTF8String &s)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int bom = 0;
|
|
Packit |
df99a1 |
unsigned int pos = 0;
|
|
Packit |
df99a1 |
char buffer[512];
|
|
Packit |
df99a1 |
if (get() != '(')
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
s = "";
|
|
Packit |
df99a1 |
for(;;)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int c = get();
|
|
Packit |
df99a1 |
if (c == '\n' || c == '\r')
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
if (c == ')')
|
|
Packit |
df99a1 |
break;
|
|
Packit |
df99a1 |
if (c == '\\')
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
c = get();
|
|
Packit |
df99a1 |
switch (c)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
case 'n':
|
|
Packit |
df99a1 |
c='\n' ; break;
|
|
Packit |
df99a1 |
case 'r':
|
|
Packit |
df99a1 |
c='\r' ; break;
|
|
Packit |
df99a1 |
case 't':
|
|
Packit |
df99a1 |
c='\t' ; break;
|
|
Packit |
df99a1 |
case 'b':
|
|
Packit |
df99a1 |
c='\b' ; break;
|
|
Packit |
df99a1 |
case 'f':
|
|
Packit |
df99a1 |
c='\f' ; break;
|
|
Packit |
df99a1 |
default:
|
|
Packit |
df99a1 |
if (c>='0' && c<='7')
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int n = 0;
|
|
Packit |
df99a1 |
int x = 0;
|
|
Packit |
df99a1 |
while (c>='0' && c<='7' && n<3)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
x = (x * 8) + c - '0';
|
|
Packit |
df99a1 |
c = get();
|
|
Packit |
df99a1 |
n++;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
unget(c);
|
|
Packit |
df99a1 |
c = x;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
break;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (c == EOF)
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
if (pos >= (int)sizeof(buffer))
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
add_to_string(s, buffer, pos, bom);
|
|
Packit |
df99a1 |
pos = 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
buffer[pos++] = c;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
add_to_string(s, buffer, pos, bom);
|
|
Packit |
df99a1 |
return true;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// --------------------------------------------------
|
|
Packit |
df99a1 |
// COLOR CONNECTED COMPONENT ANALYSIS
|
|
Packit |
df99a1 |
// --------------------------------------------------
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- A run of pixels with the same color
|
|
Packit |
df99a1 |
struct Run
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
short y; // vertical coordinate
|
|
Packit |
df99a1 |
short x1; // first horizontal coordinate
|
|
Packit |
df99a1 |
short x2; // last horizontal coordinate
|
|
Packit |
df99a1 |
short color; // color id
|
|
Packit |
df99a1 |
int ccid; // component id
|
|
Packit |
df99a1 |
};
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Compares runs for y-x sorting
|
|
Packit |
df99a1 |
static inline bool
|
|
Packit |
df99a1 |
operator <= (const Run &a, const Run &b)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
return (a.y
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Color component descriptor
|
|
Packit |
df99a1 |
struct CC
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
GRect bb; // bounding box
|
|
Packit |
df99a1 |
int npix; // number of black pixels
|
|
Packit |
df99a1 |
int nrun; // number of runs
|
|
Packit |
df99a1 |
int frun; // first run in cc ordered array of runs
|
|
Packit |
df99a1 |
int color; // color id
|
|
Packit |
df99a1 |
};
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- An image composed of runs
|
|
Packit |
df99a1 |
class CRLEImage
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
public:
|
|
Packit |
df99a1 |
int height; // Height of the image in pixels
|
|
Packit |
df99a1 |
int width; // Width of the image in pixels
|
|
Packit |
df99a1 |
GP<DjVuPalette> pal; // Color palette
|
|
Packit |
df99a1 |
GTArray<Run> runs; // Array of runs
|
|
Packit |
df99a1 |
GTArray<CC> ccs; // Array of component descriptors
|
|
Packit |
df99a1 |
int nregularccs; // Number of regular ccs (set by merge_and_split_ccs)
|
|
Packit |
df99a1 |
char bg_flags; // Comment flags about background.
|
|
Packit |
df99a1 |
char fg_flags; // Comment flags about foreground.
|
|
Packit |
df99a1 |
CRLEImage(BufferByteStream &bs);
|
|
Packit |
df99a1 |
GP<GBitmap> get_bitmap_for_cc(int ccid) const;
|
|
Packit |
df99a1 |
void make_ccids_by_analysis();
|
|
Packit |
df99a1 |
void make_ccs_from_ccids();
|
|
Packit |
df99a1 |
void merge_and_split_ccs(int smallsize, int largesize);
|
|
Packit |
df99a1 |
void sort_in_reading_order();
|
|
Packit |
df99a1 |
private:
|
|
Packit |
df99a1 |
unsigned int read_integer(BufferByteStream &bs);
|
|
Packit |
df99a1 |
void insert_runs(int y, const short *x1x2color, int nruns);
|
|
Packit |
df99a1 |
};
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Helper for CRLEImage::CRLEImage(ByteStream &bs)
|
|
Packit |
df99a1 |
unsigned int
|
|
Packit |
df99a1 |
CRLEImage::read_integer(BufferByteStream &bs)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int c, x;
|
|
Packit |
df99a1 |
while (bs.skip() && bs.expect(c, "#"))
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
char buffer[256];
|
|
Packit |
df99a1 |
char *s = buffer;
|
|
Packit |
df99a1 |
while (c != EOF && c != '\n' && c != '\r')
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (s - buffer < (int)sizeof(buffer) - 1)
|
|
Packit |
df99a1 |
*s++ = c;
|
|
Packit |
df99a1 |
c = bs.get();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
*s = 0;
|
|
Packit |
df99a1 |
for(s = buffer; *s; s++)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (!strncmp(s, "bg-", 3))
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (!strncmp(s+3,"bw",2) ||
|
|
Packit |
df99a1 |
!strncmp(s+3,"gray",4) ||
|
|
Packit |
df99a1 |
!strncmp(s+3,"color",5) )
|
|
Packit |
df99a1 |
bg_flags = s[3];
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (!strncmp(s, "fg-", 3))
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (!strncmp(s+3,"bw",2) ||
|
|
Packit |
df99a1 |
!strncmp(s+3,"gray",4) ||
|
|
Packit |
df99a1 |
!strncmp(s+3,"color",5) )
|
|
Packit |
df99a1 |
fg_flags = s[3];
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (! bs.read_integer(x) )
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: corrupted input file (bad file header)");
|
|
Packit |
df99a1 |
return x;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Helper for CRLEImage::CRLEImage(ByteStream &bs)
|
|
Packit |
df99a1 |
void
|
|
Packit |
df99a1 |
CRLEImage::insert_runs(int y, const short *x1x2color, int count)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (count > 0)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int index = runs.lbound() - count;
|
|
Packit |
df99a1 |
runs.resize(index, runs.hbound());
|
|
Packit |
df99a1 |
Run *run = &runs[index];
|
|
Packit |
df99a1 |
while (--count>=0) {
|
|
Packit |
df99a1 |
run->y = y;
|
|
Packit |
df99a1 |
run->x1 = *x1x2color++;
|
|
Packit |
df99a1 |
run->x2 = *x1x2color++;
|
|
Packit |
df99a1 |
run->color = *x1x2color++;
|
|
Packit |
df99a1 |
run->ccid = 0;
|
|
Packit |
df99a1 |
run++;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
runs.shift(-index);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Constructs CRLEImage from a run lenght encoded file,
|
|
Packit |
df99a1 |
// making sure that runs are properly sorted.
|
|
Packit |
df99a1 |
CRLEImage::CRLEImage(BufferByteStream &bs)
|
|
Packit |
df99a1 |
: height(0), width(0), nregularccs(0), bg_flags(0), fg_flags(0)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
unsigned int magic = bs.read16();
|
|
Packit |
df99a1 |
width = read_integer(bs);
|
|
Packit |
df99a1 |
height = read_integer(bs);
|
|
Packit |
df99a1 |
if (width<1 || height<1)
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: corrupted input file (bad image size)");
|
|
Packit |
df99a1 |
// An array for the runs and the buffered data
|
|
Packit |
df99a1 |
GTArray<short> ax(3*width+3);
|
|
Packit |
df99a1 |
// File format switch
|
|
Packit |
df99a1 |
if (magic == 0x5234) // Black&White RLE data
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// Skip one character
|
|
Packit |
df99a1 |
bs.get();
|
|
Packit |
df99a1 |
// Setup palette with one color
|
|
Packit |
df99a1 |
pal = DjVuPalette::create();
|
|
Packit |
df99a1 |
static char zeroes[4];
|
|
Packit |
df99a1 |
GP<ByteStream> gzbs=ByteStream::create_static(zeroes,4);
|
|
Packit |
df99a1 |
ByteStream &zbs=*gzbs;
|
|
Packit |
df99a1 |
pal->decode_rgb_entries(zbs, 1);
|
|
Packit |
df99a1 |
// RLE format
|
|
Packit |
df99a1 |
int x, c, n;
|
|
Packit |
df99a1 |
unsigned char p = 0;
|
|
Packit |
df99a1 |
short *px = ax;
|
|
Packit |
df99a1 |
n = height - 1;
|
|
Packit |
df99a1 |
c = 0;
|
|
Packit |
df99a1 |
while (n >= 0)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (bs.eof())
|
|
Packit |
df99a1 |
G_THROW( ByteStream::EndOfFile );
|
|
Packit |
df99a1 |
x = bs.get();
|
|
Packit |
df99a1 |
if (x >= 0xc0)
|
|
Packit |
df99a1 |
x = (bs.get()) + ((x - 0xc0) << 8);
|
|
Packit |
df99a1 |
if (c+x > width)
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: corrupted input file (lost RLE sync.)");
|
|
Packit |
df99a1 |
if (p)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
px[0] = c;
|
|
Packit |
df99a1 |
px[1] = c+x-1;
|
|
Packit |
df99a1 |
px[2] = 0;
|
|
Packit |
df99a1 |
px += 3;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
c += x;
|
|
Packit |
df99a1 |
p = 1 - p;
|
|
Packit |
df99a1 |
if (c >= width)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
insert_runs(n, ax, (px-ax)/3);
|
|
Packit |
df99a1 |
c = 0;
|
|
Packit |
df99a1 |
p = 0;
|
|
Packit |
df99a1 |
n -= 1;
|
|
Packit |
df99a1 |
px = ax;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
} else if (magic == 0x5236) { // Color-RLE data
|
|
Packit |
df99a1 |
// Read ncolors and skip one character.
|
|
Packit |
df99a1 |
int ncolors = read_integer(bs);
|
|
Packit |
df99a1 |
bs.get();
|
|
Packit |
df99a1 |
// Setup palette
|
|
Packit |
df99a1 |
if (ncolors<1 || ncolors>4095)
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: corrupted input file (bad number of colors)");
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
pal = DjVuPalette::create();
|
|
Packit |
df99a1 |
pal->decode_rgb_entries(bs, ncolors);
|
|
Packit |
df99a1 |
// RLE format
|
|
Packit |
df99a1 |
int x, c, n, p;
|
|
Packit |
df99a1 |
n = height - 1;
|
|
Packit |
df99a1 |
c = 0;
|
|
Packit |
df99a1 |
short *px = ax;
|
|
Packit |
df99a1 |
while (n >= 0)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (bs.eof())
|
|
Packit |
df99a1 |
G_THROW( ByteStream::EndOfFile );
|
|
Packit |
df99a1 |
x = (bs.get() << 24);
|
|
Packit |
df99a1 |
x |= (bs.get() << 16);
|
|
Packit |
df99a1 |
x |= (bs.get() << 8);
|
|
Packit |
df99a1 |
x |= (bs.get());
|
|
Packit |
df99a1 |
p = (x >> 20) & 0xfff;
|
|
Packit |
df99a1 |
x = (x & 0xfffff);
|
|
Packit |
df99a1 |
if (c+x > width)
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: corrupted input file (lost RLE sync.)");
|
|
Packit |
df99a1 |
if (p >= 0 && p < ncolors)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
px[0] = c;
|
|
Packit |
df99a1 |
px[1] = c+x-1;
|
|
Packit |
df99a1 |
px[2] = p;
|
|
Packit |
df99a1 |
px += 3;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
c += x;
|
|
Packit |
df99a1 |
if (c >= width)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
insert_runs(n, ax, (px-ax)/3);
|
|
Packit |
df99a1 |
c = 0;
|
|
Packit |
df99a1 |
p = 0;
|
|
Packit |
df99a1 |
n -= 1;
|
|
Packit |
df99a1 |
px = ax;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
} else { // Unrecognized file
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: corrupted input file (bad file header)");
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Performs color connected component analysis
|
|
Packit |
df99a1 |
// assuming that runs are already y-x sorted.
|
|
Packit |
df99a1 |
void
|
|
Packit |
df99a1 |
CRLEImage::make_ccids_by_analysis()
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// runs.sort(); (we know that runs are
|
|
Packit |
df99a1 |
// Single Pass Connected Component Analysis (with unodes)
|
|
Packit |
df99a1 |
int n;
|
|
Packit |
df99a1 |
int p=0;
|
|
Packit |
df99a1 |
GTArray<int> umap;
|
|
Packit |
df99a1 |
for (n=0; n<=runs.hbound(); n++)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int y = runs[n].y;
|
|
Packit |
df99a1 |
int x1 = runs[n].x1 - 1;
|
|
Packit |
df99a1 |
int x2 = runs[n].x2 + 1;
|
|
Packit |
df99a1 |
int color = runs[n].color;
|
|
Packit |
df99a1 |
int id = (umap.hbound() + 1);
|
|
Packit |
df99a1 |
// iterate over previous line runs
|
|
Packit |
df99a1 |
if (p>0) p--;
|
|
Packit |
df99a1 |
for(;runs[p].y < y-1;p++);
|
|
Packit |
df99a1 |
for(;(runs[p].y < y) && (runs[p].x1 <= x2);p++ )
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if ( runs[p].x2 >= x1 )
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (runs[p].color == color)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// previous run touches current run and has same color
|
|
Packit |
df99a1 |
int oid = runs[p].ccid;
|
|
Packit |
df99a1 |
while (umap[oid] < oid)
|
|
Packit |
df99a1 |
oid = umap[oid];
|
|
Packit |
df99a1 |
if ((int)id > umap.hbound()) {
|
|
Packit |
df99a1 |
id = oid;
|
|
Packit |
df99a1 |
} else if (id < oid) {
|
|
Packit |
df99a1 |
umap[oid] = id;
|
|
Packit |
df99a1 |
} else {
|
|
Packit |
df99a1 |
umap[id] = oid;
|
|
Packit |
df99a1 |
id = oid;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// freshen previous run id
|
|
Packit |
df99a1 |
runs[p].ccid = id;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// stop if previous run goes past current run
|
|
Packit |
df99a1 |
if (runs[p].x2 >= x2)
|
|
Packit |
df99a1 |
break;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// create new entry in umap
|
|
Packit |
df99a1 |
runs[n].ccid = id;
|
|
Packit |
df99a1 |
if (id > umap.hbound())
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
umap.touch(id);
|
|
Packit |
df99a1 |
umap[id] = id;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Update umap and ccid
|
|
Packit |
df99a1 |
for (n=0; n<=runs.hbound(); n++)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
Run &run = runs[n];
|
|
Packit |
df99a1 |
int ccid = run.ccid;
|
|
Packit |
df99a1 |
while (umap[ccid] < ccid)
|
|
Packit |
df99a1 |
ccid = umap[ccid];
|
|
Packit |
df99a1 |
umap[run.ccid] = ccid;
|
|
Packit |
df99a1 |
run.ccid = ccid;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Constructs the ``ccs'' array from run's ccids.
|
|
Packit |
df99a1 |
void
|
|
Packit |
df99a1 |
CRLEImage::make_ccs_from_ccids()
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int n;
|
|
Packit |
df99a1 |
Run *pruns = runs;
|
|
Packit |
df99a1 |
// Find maximal ccid
|
|
Packit |
df99a1 |
int maxccid = -1;
|
|
Packit |
df99a1 |
for (n=0; n<=runs.hbound(); n++)
|
|
Packit |
df99a1 |
if (pruns[n].ccid > maxccid)
|
|
Packit |
df99a1 |
maxccid = runs[n].ccid;
|
|
Packit |
df99a1 |
GTArray<int> armap(0,maxccid);
|
|
Packit |
df99a1 |
int *rmap = armap;
|
|
Packit |
df99a1 |
// Renumber ccs
|
|
Packit |
df99a1 |
for (n=0; n<=maxccid; n++)
|
|
Packit |
df99a1 |
armap[n] = -1;
|
|
Packit |
df99a1 |
for (n=0; n<=runs.hbound(); n++)
|
|
Packit |
df99a1 |
if (pruns[n].ccid >= 0)
|
|
Packit |
df99a1 |
rmap[ pruns[n].ccid ] = 1;
|
|
Packit |
df99a1 |
int nid = 0;
|
|
Packit |
df99a1 |
for (n=0; n<=maxccid; n++)
|
|
Packit |
df99a1 |
if (rmap[n] > 0)
|
|
Packit |
df99a1 |
rmap[n] = nid++;
|
|
Packit |
df99a1 |
// Adjust nregularccs (since ccs are renumbered)
|
|
Packit |
df99a1 |
while (nregularccs>0 && rmap[nregularccs-1]<0)
|
|
Packit |
df99a1 |
nregularccs -= 1;
|
|
Packit |
df99a1 |
if (nregularccs>0)
|
|
Packit |
df99a1 |
nregularccs = 1 + rmap[nregularccs-1];
|
|
Packit |
df99a1 |
// Prepare cc descriptors
|
|
Packit |
df99a1 |
ccs.resize(0,nid-1);
|
|
Packit |
df99a1 |
for (n=0; n
|
|
Packit |
df99a1 |
ccs[n].nrun = 0;
|
|
Packit |
df99a1 |
// Relabel runs
|
|
Packit |
df99a1 |
for (n=0; n<=runs.hbound(); n++)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
Run &run = pruns[n];
|
|
Packit |
df99a1 |
if (run.ccid < 0) continue; // runs with negative ccids are destroyed
|
|
Packit |
df99a1 |
int oldccid = run.ccid;
|
|
Packit |
df99a1 |
int newccid = rmap[oldccid];
|
|
Packit |
df99a1 |
CC &cc = ccs[newccid];
|
|
Packit |
df99a1 |
run.ccid = newccid;
|
|
Packit |
df99a1 |
cc.nrun += 1;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Compute positions for runs of cc
|
|
Packit |
df99a1 |
int frun = 0;
|
|
Packit |
df99a1 |
for (n=0; n
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
ccs[n].frun = rmap[n] = frun;
|
|
Packit |
df99a1 |
frun += ccs[n].nrun;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Copy runs
|
|
Packit |
df99a1 |
GTArray<Run> rtmp;
|
|
Packit |
df99a1 |
rtmp.steal(runs);
|
|
Packit |
df99a1 |
Run *ptmp = rtmp;
|
|
Packit |
df99a1 |
runs.resize(0,frun-1);
|
|
Packit |
df99a1 |
pruns = runs;
|
|
Packit |
df99a1 |
for (n=0; n<=rtmp.hbound(); n++)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int id = ptmp[n].ccid;
|
|
Packit |
df99a1 |
if (id < 0) continue;
|
|
Packit |
df99a1 |
int pos = rmap[id]++;
|
|
Packit |
df99a1 |
pruns[pos] = ptmp[n];
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Finalize ccs
|
|
Packit |
df99a1 |
for (n=0; n
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
CC &cc = ccs[n];
|
|
Packit |
df99a1 |
int npix = 0;
|
|
Packit |
df99a1 |
runs.sort(cc.frun, cc.frun+cc.nrun-1);
|
|
Packit |
df99a1 |
Run *run = &runs[cc.frun];
|
|
Packit |
df99a1 |
int xmin = run->x1;
|
|
Packit |
df99a1 |
int xmax = run->x2;
|
|
Packit |
df99a1 |
int ymin = run->y;
|
|
Packit |
df99a1 |
int ymax = run->y;
|
|
Packit |
df99a1 |
cc.color = run->color;
|
|
Packit |
df99a1 |
for (int i=0; i
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (run->x1 < xmin) xmin = run->x1;
|
|
Packit |
df99a1 |
if (run->x2 > xmax) xmax = run->x2;
|
|
Packit |
df99a1 |
if (run->y < ymin) ymin = run->y;
|
|
Packit |
df99a1 |
if (run->y > ymax) ymax = run->y;
|
|
Packit |
df99a1 |
npix += run->x2 - run->x1 + 1;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
cc.npix = npix;
|
|
Packit |
df99a1 |
cc.bb.xmin = xmin;
|
|
Packit |
df99a1 |
cc.bb.ymin = ymin;
|
|
Packit |
df99a1 |
cc.bb.xmax = xmax + 1;
|
|
Packit |
df99a1 |
cc.bb.ymax = ymax + 1;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Helper for merge_and_split_ccs
|
|
Packit |
df99a1 |
struct Grid_x_Color
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
short gridi;
|
|
Packit |
df99a1 |
short gridj;
|
|
Packit |
df99a1 |
int color;
|
|
Packit |
df99a1 |
};
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Helper for merge_and_split_ccs
|
|
Packit |
df99a1 |
static inline unsigned int
|
|
Packit |
df99a1 |
hash(const Grid_x_Color &x)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
return (x.gridi<<16) ^ (x.gridj<<8) ^ x.color;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Helper for merge_and_split_ccs
|
|
Packit |
df99a1 |
static inline bool
|
|
Packit |
df99a1 |
operator==(const Grid_x_Color &x, const Grid_x_Color &y)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
return (x.gridi==y.gridi) && (x.gridj==y.gridj) && (x.color==y.color);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Helper for merge_and_split_ccs
|
|
Packit |
df99a1 |
static int
|
|
Packit |
df99a1 |
makeccid(const Grid_x_Color &x, GMap<Grid_x_Color,int> &map, int &ncc)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
GPosition p = map.contains(x);
|
|
Packit |
df99a1 |
if (p) return map[p];
|
|
Packit |
df99a1 |
return map[x] = ncc++;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Merges small ccs of similar color and splits large ccs
|
|
Packit |
df99a1 |
void
|
|
Packit |
df99a1 |
CRLEImage::merge_and_split_ccs(int smallsize, int largesize)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int ncc = ccs.size();
|
|
Packit |
df99a1 |
int nruns = runs.size();
|
|
Packit |
df99a1 |
int splitsize = largesize;
|
|
Packit |
df99a1 |
if (ncc <= 0) return;
|
|
Packit |
df99a1 |
// Associative map for storing merged ccids
|
|
Packit |
df99a1 |
GMap<Grid_x_Color,int> map;
|
|
Packit |
df99a1 |
nregularccs = ncc;
|
|
Packit |
df99a1 |
// Set the correct ccids for the runs
|
|
Packit |
df99a1 |
for (int ccid=0; ccid
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
CC* cc = &ccs[ccid];
|
|
Packit |
df99a1 |
if (cc->nrun <= 0) continue;
|
|
Packit |
df99a1 |
Grid_x_Color key;
|
|
Packit |
df99a1 |
key.color = cc->color;
|
|
Packit |
df99a1 |
int ccheight = cc->bb.height();
|
|
Packit |
df99a1 |
int ccwidth = cc->bb.width();
|
|
Packit |
df99a1 |
if (ccheight<=smallsize && ccwidth<=smallsize)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
key.gridi = (cc->bb.ymin+cc->bb.ymax)/splitsize/2;
|
|
Packit |
df99a1 |
key.gridj = (cc->bb.xmin+cc->bb.xmax)/splitsize/2;
|
|
Packit |
df99a1 |
int newccid = makeccid(key, map, ncc);
|
|
Packit |
df99a1 |
for(int runid=cc->frun; runid<cc->frun+cc->nrun; runid++)
|
|
Packit |
df99a1 |
runs[runid].ccid = newccid;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
else if (ccheight>=largesize || ccwidth>=largesize)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
for(int runid=cc->frun; runid<cc->frun+cc->nrun; runid++)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
Run *r = & runs[runid];
|
|
Packit |
df99a1 |
key.gridi = r->y/splitsize;
|
|
Packit |
df99a1 |
key.gridj = r->x1/splitsize;
|
|
Packit |
df99a1 |
int gridj_end = r->x2/splitsize;
|
|
Packit |
df99a1 |
int gridj_span = gridj_end - key.gridj;
|
|
Packit |
df99a1 |
r->ccid = makeccid(key, map, ncc);
|
|
Packit |
df99a1 |
if (gridj_span>0)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// truncate current run
|
|
Packit |
df99a1 |
runs.touch(nruns+gridj_span-1);
|
|
Packit |
df99a1 |
r = &runs[runid];
|
|
Packit |
df99a1 |
int x = key.gridj*splitsize + splitsize;
|
|
Packit |
df99a1 |
int x_end = r->x2;
|
|
Packit |
df99a1 |
r->x2 = x-1;
|
|
Packit |
df99a1 |
// append additional runs to the runs array
|
|
Packit |
df99a1 |
while (++key.gridj < gridj_end)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
Run& newrun = runs[nruns++];
|
|
Packit |
df99a1 |
newrun.y = r->y;
|
|
Packit |
df99a1 |
newrun.x1 = x;
|
|
Packit |
df99a1 |
x += splitsize;
|
|
Packit |
df99a1 |
newrun.x2 = x-1;
|
|
Packit |
df99a1 |
newrun.color = key.color;
|
|
Packit |
df99a1 |
newrun.ccid = makeccid(key, map, ncc);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// append last run to the run array
|
|
Packit |
df99a1 |
Run& newrun = runs[nruns++];
|
|
Packit |
df99a1 |
newrun.y = r->y;
|
|
Packit |
df99a1 |
newrun.x1 = x;
|
|
Packit |
df99a1 |
newrun.x2 = x_end;
|
|
Packit |
df99a1 |
newrun.color = key.color;
|
|
Packit |
df99a1 |
newrun.ccid = makeccid(key, map, ncc);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Recompute cc descriptors
|
|
Packit |
df99a1 |
make_ccs_from_ccids();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Helps sorting cc
|
|
Packit |
df99a1 |
static int
|
|
Packit |
df99a1 |
top_edges_descending (const void *pa, const void *pb)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (((CC*) pa)->bb.ymax != ((CC*) pb)->bb.ymax)
|
|
Packit |
df99a1 |
return (((CC*) pb)->bb.ymax - ((CC*) pa)->bb.ymax);
|
|
Packit |
df99a1 |
if (((CC*) pa)->bb.xmin != ((CC*) pb)->bb.xmin)
|
|
Packit |
df99a1 |
return (((CC*) pa)->bb.xmin - ((CC*) pb)->bb.xmin);
|
|
Packit |
df99a1 |
return (((CC*) pa)->frun - ((CC*) pb)->frun);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Helps sorting cc
|
|
Packit |
df99a1 |
static int
|
|
Packit |
df99a1 |
left_edges_ascending (const void *pa, const void *pb)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (((CC*) pa)->bb.xmin != ((CC*) pb)->bb.xmin)
|
|
Packit |
df99a1 |
return (((CC*) pa)->bb.xmin - ((CC*) pb)->bb.xmin);
|
|
Packit |
df99a1 |
if (((CC*) pb)->bb.ymax != ((CC*) pa)->bb.ymax)
|
|
Packit |
df99a1 |
return (((CC*) pb)->bb.ymax - ((CC*) pa)->bb.ymax);
|
|
Packit |
df99a1 |
return (((CC*) pa)->frun - ((CC*) pb)->frun);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Helps sorting cc
|
|
Packit |
df99a1 |
static int
|
|
Packit |
df99a1 |
integer_ascending (const void *pa, const void *pb)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
return ( *(int*)pb - *(int*)pa );
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Sort ccs in approximate reading order
|
|
Packit |
df99a1 |
void
|
|
Packit |
df99a1 |
CRLEImage::sort_in_reading_order()
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (nregularccs<2) return;
|
|
Packit |
df99a1 |
CC *ccarray = new CC[nregularccs];
|
|
Packit |
df99a1 |
// Copy existing ccarray (but segregate special ccs)
|
|
Packit |
df99a1 |
int ccid;
|
|
Packit |
df99a1 |
for(ccid=0; ccid
|
|
Packit |
df99a1 |
ccarray[ccid] = ccs[ccid];
|
|
Packit |
df99a1 |
// Sort the ccarray list into top-to-bottom order.
|
|
Packit |
df99a1 |
qsort (ccarray, nregularccs, sizeof(CC), top_edges_descending);
|
|
Packit |
df99a1 |
// Subdivide the ccarray list roughly into text lines
|
|
Packit |
df99a1 |
int maxtopchange = width / 40;
|
|
Packit |
df99a1 |
if (maxtopchange < 32)
|
|
Packit |
df99a1 |
maxtopchange = 32;
|
|
Packit |
df99a1 |
// - Loop until processing all ccs
|
|
Packit |
df99a1 |
int ccno = 0;
|
|
Packit |
df99a1 |
int *bottoms = new int[nregularccs];
|
|
Packit |
df99a1 |
while (ccno < nregularccs)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// - Gather first line approximation
|
|
Packit |
df99a1 |
int nccno;
|
|
Packit |
df99a1 |
int sublist_top = ccarray[ccno].bb.ymax-1;
|
|
Packit |
df99a1 |
int sublist_bottom = ccarray[ccno].bb.ymin;
|
|
Packit |
df99a1 |
for (nccno=ccno; nccno < nregularccs; nccno++)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (ccarray[nccno].bb.ymax-1 < sublist_bottom) break;
|
|
Packit |
df99a1 |
if (ccarray[nccno].bb.ymax-1 < sublist_top - maxtopchange) break;
|
|
Packit |
df99a1 |
int bottom = ccarray[nccno].bb.ymin;
|
|
Packit |
df99a1 |
bottoms[nccno-ccno] = bottom;
|
|
Packit |
df99a1 |
if (bottom < sublist_bottom)
|
|
Packit |
df99a1 |
sublist_bottom = bottom;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// - If more than one candidate cc for the line
|
|
Packit |
df99a1 |
if (nccno > ccno + 1)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// - Compute median bottom
|
|
Packit |
df99a1 |
qsort(bottoms, nccno-ccno, sizeof(int), integer_ascending);
|
|
Packit |
df99a1 |
int bottom = bottoms[ (nccno-ccno-1)/2 ];
|
|
Packit |
df99a1 |
// - Compose final line
|
|
Packit |
df99a1 |
for (nccno=ccno; nccno < nregularccs; nccno++)
|
|
Packit |
df99a1 |
if (ccarray[nccno].bb.ymax-1 < bottom)
|
|
Packit |
df99a1 |
break;
|
|
Packit |
df99a1 |
// - Sort final line
|
|
Packit |
df99a1 |
qsort (ccarray+ccno, nccno-ccno, sizeof(CC), left_edges_ascending);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// - Next line
|
|
Packit |
df99a1 |
ccno = nccno;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Copy ccarray back and renumber the runs
|
|
Packit |
df99a1 |
for(ccid=0; ccid
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
CC& cc = ccarray[ccid];
|
|
Packit |
df99a1 |
ccs[ccid] = cc;
|
|
Packit |
df99a1 |
for(int r=cc.frun; r
|
|
Packit |
df99a1 |
runs[r].ccid = ccid;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Free memory
|
|
Packit |
df99a1 |
delete [] bottoms;
|
|
Packit |
df99a1 |
delete[] ccarray;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Creates a bitmap for a particular component
|
|
Packit |
df99a1 |
GP<GBitmap>
|
|
Packit |
df99a1 |
CRLEImage::get_bitmap_for_cc(const int ccid) const
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
const CC &cc = ccs[ccid];
|
|
Packit |
df99a1 |
const GRect &bb = cc.bb;
|
|
Packit |
df99a1 |
GP<GBitmap> bits = GBitmap::create(bb.height(), bb.width());
|
|
Packit |
df99a1 |
const Run *prun = & runs[(int)cc.frun];
|
|
Packit |
df99a1 |
for (int i=0; i
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (prun->y<bb.ymin || prun->y>=bb.ymax)
|
|
Packit |
df99a1 |
G_THROW("Internal error (y bounds)");
|
|
Packit |
df99a1 |
if (prun->x1<bb.xmin || prun->x2>=bb.xmax)
|
|
Packit |
df99a1 |
G_THROW("Internal error (x bounds)");
|
|
Packit |
df99a1 |
unsigned char *row = (*bits)[prun->y - bb.ymin];
|
|
Packit |
df99a1 |
for (int x=prun->x1; x<=prun->x2; x++)
|
|
Packit |
df99a1 |
row[x - bb.xmin] = 1;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
return bits;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// --------------------------------------------------
|
|
Packit |
df99a1 |
// PROCESS BACKGROUND PIXMAP
|
|
Packit |
df99a1 |
// --------------------------------------------------
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Tries to read a background pixmap
|
|
Packit |
df99a1 |
GP<GPixmap>
|
|
Packit |
df99a1 |
read_background(BufferByteStream &bs, int w, int h, int &bgred)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// Skip null bytes (why?)
|
|
Packit |
df99a1 |
int lookahead;
|
|
Packit |
df99a1 |
while (! (lookahead = bs.get())) { }
|
|
Packit |
df99a1 |
bs.unget(lookahead);
|
|
Packit |
df99a1 |
// Check pixmap
|
|
Packit |
df99a1 |
if (lookahead != 'P')
|
|
Packit |
df99a1 |
return 0;
|
|
Packit |
df99a1 |
GP<GPixmap> pix = GPixmap::create(bs);
|
|
Packit |
df99a1 |
// Check background reduction
|
|
Packit |
df99a1 |
for (bgred=1; bgred<=12; bgred++)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int subw = (w + bgred - 1) / bgred;
|
|
Packit |
df99a1 |
int subh = (h + bgred - 1) / bgred;
|
|
Packit |
df99a1 |
if (subh == (int)pix->rows() && subw == (int)pix->columns())
|
|
Packit |
df99a1 |
// Found reduction factor
|
|
Packit |
df99a1 |
return pix;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Failure
|
|
Packit |
df99a1 |
G_THROW("Background pixmap size does not match foreground");
|
|
Packit |
df99a1 |
return 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// --------------------------------------------------
|
|
Packit |
df99a1 |
// HANDLE COMMENTS IN SEP FILES
|
|
Packit |
df99a1 |
// --------------------------------------------------
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
class Comments
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
public:
|
|
Packit |
df99a1 |
Comments(int w, int h, const csepdjvuopts &opts);
|
|
Packit |
df99a1 |
void process_comments(BufferByteStream &bs, int verbose=0);
|
|
Packit |
df99a1 |
bool parse_comment_line(BufferByteStream &bs);
|
|
Packit |
df99a1 |
void make_chunks(IFFByteStream &iff);
|
|
Packit |
df99a1 |
GP<DjVmNav> get_djvm_nav();
|
|
Packit |
df99a1 |
protected:
|
|
Packit |
df99a1 |
int w;
|
|
Packit |
df99a1 |
int h;
|
|
Packit |
df99a1 |
GRectMapper mapper;
|
|
Packit |
df99a1 |
DjVuTXT::ZoneType detail;
|
|
Packit |
df99a1 |
int lastx, lasty;
|
|
Packit |
df99a1 |
int lastdirx, lastdiry;
|
|
Packit |
df99a1 |
int lastsize[3];
|
|
Packit |
df99a1 |
struct TxtMark : public GPEnabled {
|
|
Packit |
df99a1 |
int x,y,dx,dy;
|
|
Packit |
df99a1 |
int inter;
|
|
Packit |
df99a1 |
GRect r;
|
|
Packit |
df99a1 |
GUTF8String s;
|
|
Packit |
df99a1 |
};
|
|
Packit |
df99a1 |
GPList<TxtMark> lastline;
|
|
Packit |
df99a1 |
GP<DjVuTXT> txt;
|
|
Packit |
df99a1 |
struct LnkMark : public GPEnabled {
|
|
Packit |
df99a1 |
GRect r;
|
|
Packit |
df99a1 |
GUTF8String s;
|
|
Packit |
df99a1 |
};
|
|
Packit |
df99a1 |
GPList<LnkMark> links;
|
|
Packit |
df99a1 |
GP<DjVmNav> nav;
|
|
Packit |
df99a1 |
protected:
|
|
Packit |
df99a1 |
bool allspace(const TxtMark *mark);
|
|
Packit |
df99a1 |
void textmark(GP<TxtMark> mark);
|
|
Packit |
df99a1 |
void textflush(void);
|
|
Packit |
df99a1 |
};
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
Comments::Comments(int w, int h, const csepdjvuopts &opts)
|
|
Packit |
df99a1 |
: w(w), h(h), detail(opts.text)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
GRect pagerect(0,0,w,h);
|
|
Packit |
df99a1 |
mapper.set_input(pagerect);
|
|
Packit |
df99a1 |
mapper.set_output(pagerect);
|
|
Packit |
df99a1 |
mapper.mirrory();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
void
|
|
Packit |
df99a1 |
Comments::process_comments(BufferByteStream &bs, int verbose)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int c;
|
|
Packit |
df99a1 |
// Skip null bytes
|
|
Packit |
df99a1 |
while (! (c = bs.get())) { }
|
|
Packit |
df99a1 |
// Process comment lines
|
|
Packit |
df99a1 |
while (c == '#')
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
const char *message = 0;
|
|
Packit |
df99a1 |
bs.skip(" \t");
|
|
Packit |
df99a1 |
G_TRY
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (! parse_comment_line(bs) && verbose > 1)
|
|
Packit |
df99a1 |
message = "csepdjvu: unrecognized comment '# ";
|
|
Packit |
df99a1 |
else if (bs.skip(" \t") && bs.expect(c, "\n\r"))
|
|
Packit |
df99a1 |
bs.unget(c);
|
|
Packit |
df99a1 |
else if (verbose > 1)
|
|
Packit |
df99a1 |
message = "csepdjvu: garbage in comments: '";
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
G_CATCH(ex)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
message = 0;
|
|
Packit |
df99a1 |
GUTF8String str = DjVuMessageLite::LookUpUTF8(ex.get_cause());
|
|
Packit |
df99a1 |
if (verbose > 1)
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8("%s\n",(const char *)str);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
G_ENDCATCH;
|
|
Packit |
df99a1 |
if (message)
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8(message);
|
|
Packit |
df99a1 |
c = bs.get();
|
|
Packit |
df99a1 |
while (c != EOF && c != '\r' && c != '\n')
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (message)
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8("%c", c);
|
|
Packit |
df99a1 |
c = bs.get();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (message)
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8("'\n");
|
|
Packit |
df99a1 |
bs.skip();
|
|
Packit |
df99a1 |
c = bs.get();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
bs.unget(c);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
bool
|
|
Packit |
df99a1 |
Comments::parse_comment_line(BufferByteStream &bs)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int c = bs.get();
|
|
Packit |
df99a1 |
// Text comments
|
|
Packit |
df99a1 |
if (c == 'T')
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
GP<TxtMark> mark = new TxtMark;
|
|
Packit |
df99a1 |
if (! (bs.skip(" \t") && bs.read_pair(mark->x,mark->y) &&
|
|
Packit |
df99a1 |
bs.skip(" \t") && bs.read_pair(mark->dx,mark->dy) &&
|
|
Packit |
df99a1 |
bs.skip(" \t") && bs.read_geometry(mark->r) &&
|
|
Packit |
df99a1 |
bs.skip(" \t") && bs.read_ps_string(mark->s) ) )
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: corrupted file (syntax error in text comment)");
|
|
Packit |
df99a1 |
if (mark->r.isempty())
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: corrupted file (empty rectangle in text comment)");
|
|
Packit |
df99a1 |
textmark(mark);
|
|
Packit |
df99a1 |
return true;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Link comments
|
|
Packit |
df99a1 |
if (c == 'L')
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
GP<LnkMark> mark = new LnkMark;
|
|
Packit |
df99a1 |
if (! (bs.skip(" \t") && bs.read_geometry(mark->r) &&
|
|
Packit |
df99a1 |
bs.skip(" \t") && bs.read_ps_string(mark->s) ) )
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: corrupted file (syntax error in link comment)");
|
|
Packit |
df99a1 |
if (mark->r.isempty())
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: corrupted file (empty rectangle in link comment)");
|
|
Packit |
df99a1 |
int ymax = h - mark->r.ymin - 1; // reversed in gsdjvu ?
|
|
Packit |
df99a1 |
int ymin = h - mark->r.ymax - 1; // reversed in gsdjvu ?
|
|
Packit |
df99a1 |
mark->r.ymax = ymax;
|
|
Packit |
df99a1 |
mark->r.ymin = ymin;
|
|
Packit |
df99a1 |
links.append(mark);
|
|
Packit |
df99a1 |
return true;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Bookmark comments
|
|
Packit |
df99a1 |
if (c == 'B')
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int count;
|
|
Packit |
df99a1 |
GUTF8String url;
|
|
Packit |
df99a1 |
GUTF8String title;
|
|
Packit |
df99a1 |
if (! (bs.skip(" \t") && bs.read_integer(count) &&
|
|
Packit |
df99a1 |
bs.skip(" \t") && bs.read_ps_string(title) &&
|
|
Packit |
df99a1 |
bs.skip(" \t") && bs.read_ps_string(url) ) )
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: corrupted file (syntax error in outline comment)");
|
|
Packit |
df99a1 |
GP<DjVmNav::DjVuBookMark> b =
|
|
Packit |
df99a1 |
DjVmNav::DjVuBookMark::create(count, title, url);
|
|
Packit |
df99a1 |
if (b && ! nav)
|
|
Packit |
df99a1 |
nav = DjVmNav::create();
|
|
Packit |
df99a1 |
if (b)
|
|
Packit |
df99a1 |
nav->append(b);
|
|
Packit |
df99a1 |
return true;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Unrecognized
|
|
Packit |
df99a1 |
bs.unget(c);
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
static int
|
|
Packit |
df99a1 |
median3(int *p)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (p[0] > p[1])
|
|
Packit |
df99a1 |
return MAX(p[1],MIN(p[0],p[2]));
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
return MIN(p[1],MAX(p[0],p[2]));
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
static bool
|
|
Packit |
df99a1 |
allspaces(const GUTF8String &s)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
bool ok = true;
|
|
Packit |
df99a1 |
for (int i=0; ok && i<(int)s.length(); i++)
|
|
Packit |
df99a1 |
if (s[i] != ' ')
|
|
Packit |
df99a1 |
ok = false;
|
|
Packit |
df99a1 |
return ok;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
void
|
|
Packit |
df99a1 |
Comments::textmark(GP<TxtMark> mark)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// determine direction
|
|
Packit |
df99a1 |
int dirx = 0;
|
|
Packit |
df99a1 |
int diry = 0;
|
|
Packit |
df99a1 |
int size = 0;
|
|
Packit |
df99a1 |
if (abs(mark->dx) > 8*abs(mark->dy))
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
dirx = (mark->dx > 0) ? +1 : -1;
|
|
Packit |
df99a1 |
size = mark->r.height();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
else if (abs(mark->dy) > 8*abs(mark->dy))
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
diry = (mark->dy > 0) ? +1 : -1;
|
|
Packit |
df99a1 |
size = mark->r.width();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// make mark
|
|
Packit |
df99a1 |
mark->inter = 0;
|
|
Packit |
df99a1 |
// flush previous line
|
|
Packit |
df99a1 |
if (lastline.size())
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (size != lastsize[0])
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
lastsize[2] = lastsize[1];
|
|
Packit |
df99a1 |
lastsize[1] = lastsize[0];
|
|
Packit |
df99a1 |
lastsize[0] = size;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
int fontsize = median3(lastsize) + 1;
|
|
Packit |
df99a1 |
int shx = (mark->x - lastx) * 100 / fontsize;
|
|
Packit |
df99a1 |
int shy = (mark->y - lasty) * 100 / fontsize;
|
|
Packit |
df99a1 |
int inter = dirx * shx + diry * shy;
|
|
Packit |
df99a1 |
if ( (dirx || diry) && (dirx == lastdirx) && (diry == lastdiry) &&
|
|
Packit |
df99a1 |
(inter > -150) && (inter < 300) &&
|
|
Packit |
df99a1 |
abs(diry * shx + dirx * shy) < 80 )
|
|
Packit |
df99a1 |
mark->inter = inter;
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
textflush();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (! lastline.size())
|
|
Packit |
df99a1 |
lastsize[0] = lastsize[1] = lastsize[2] = size;
|
|
Packit |
df99a1 |
lastline.append(mark);
|
|
Packit |
df99a1 |
lastdirx = dirx;
|
|
Packit |
df99a1 |
lastdiry = diry;
|
|
Packit |
df99a1 |
lastx = mark->x + mark->dx;
|
|
Packit |
df99a1 |
lasty = mark->y + mark->dy;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
void
|
|
Packit |
df99a1 |
Comments::textflush(void)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int size = lastline.size();
|
|
Packit |
df99a1 |
if (size > 0)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// compute word spacing
|
|
Packit |
df99a1 |
int i = 0;
|
|
Packit |
df99a1 |
GTArray<int> inter(0,size-1);
|
|
Packit |
df99a1 |
for (GPosition p=lastline; p; ++p)
|
|
Packit |
df99a1 |
inter[i++] = lastline[p]->inter;
|
|
Packit |
df99a1 |
inter.sort();
|
|
Packit |
df99a1 |
int wordsep = MAX(10, 2 * inter[(2*size)/3]);
|
|
Packit |
df99a1 |
// compute word list
|
|
Packit |
df99a1 |
GP<TxtMark> word;
|
|
Packit |
df99a1 |
GPList<TxtMark> words;
|
|
Packit |
df99a1 |
{ // extra nesting for windows
|
|
Packit |
df99a1 |
for (GPosition p=lastline; p; ++p)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
TxtMark *mark = lastline[p];
|
|
Packit |
df99a1 |
if (word && mark->inter > wordsep)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (! allspaces(word->s))
|
|
Packit |
df99a1 |
words.append(word);
|
|
Packit |
df99a1 |
word = 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (! word)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
word = mark;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
word->dx += mark->dx;
|
|
Packit |
df99a1 |
word->dy += mark->dy;
|
|
Packit |
df99a1 |
word->s += mark->s;
|
|
Packit |
df99a1 |
word->r.recthull(word->r, mark->r);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (word)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (! allspaces(word->s))
|
|
Packit |
df99a1 |
words.append(word);
|
|
Packit |
df99a1 |
word = 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// create text data
|
|
Packit |
df99a1 |
int size = words.size();
|
|
Packit |
df99a1 |
if (size)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
DjVuTXT::Zone *lzone = 0;
|
|
Packit |
df99a1 |
for (GPosition p = words; p; ++p)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
word = words[p];
|
|
Packit |
df99a1 |
mapper.map(word->r);
|
|
Packit |
df99a1 |
if (! lzone)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (! txt)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
txt = DjVuTXT::create();
|
|
Packit |
df99a1 |
txt->page_zone.ztype = DjVuTXT::PAGE;
|
|
Packit |
df99a1 |
txt->page_zone.rect = GRect(0,0,w,h);
|
|
Packit |
df99a1 |
txt->page_zone.text_start = 0;
|
|
Packit |
df99a1 |
txt->page_zone.text_length = 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
lzone = txt->page_zone.append_child();
|
|
Packit |
df99a1 |
lzone->ztype = DjVuTXT::LINE;
|
|
Packit |
df99a1 |
lzone->text_start = txt->textUTF8.length();
|
|
Packit |
df99a1 |
lzone->text_length = 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (detail >= DjVuTXT::WORD)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
DjVuTXT::Zone *wzone = lzone->append_child();
|
|
Packit |
df99a1 |
wzone->ztype = DjVuTXT::WORD;
|
|
Packit |
df99a1 |
wzone->text_start = txt->textUTF8.length();
|
|
Packit |
df99a1 |
txt->textUTF8 += word->s;
|
|
Packit |
df99a1 |
wzone->text_length =
|
|
Packit |
df99a1 |
txt->textUTF8.length() - wzone->text_start;
|
|
Packit |
df99a1 |
wzone->rect = word->r;
|
|
Packit |
df99a1 |
lzone->rect.recthull(lzone->rect, word->r);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (lzone->text_length > 0) txt->textUTF8 += " ";
|
|
Packit |
df99a1 |
txt->textUTF8 += word->s;
|
|
Packit |
df99a1 |
lzone->text_length =
|
|
Packit |
df99a1 |
txt->textUTF8.length() - lzone->text_start;
|
|
Packit |
df99a1 |
lzone->rect.recthull(lzone->rect, word->r);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
lastline.empty();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
static int
|
|
Packit |
df99a1 |
bytestream_fputs(miniexp_io_t *io, const char *s)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
ByteStream *outbs = (ByteStream*)io->data[0];
|
|
Packit |
df99a1 |
return (outbs) ? outbs->write((const void*)s, strlen(s)) : -1;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
void
|
|
Packit |
df99a1 |
Comments::make_chunks(IFFByteStream &iff)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// Write text chunk
|
|
Packit |
df99a1 |
textflush();
|
|
Packit |
df99a1 |
if (txt)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
txt->normalize_text();
|
|
Packit |
df99a1 |
iff.put_chunk("TXTz");
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
GP<ByteStream> bsb = BSByteStream::create(iff.get_bytestream(), 50);
|
|
Packit |
df99a1 |
txt->encode(bsb);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
iff.close_chunk();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Create annotation chunk
|
|
Packit |
df99a1 |
if (links.size() > 0)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
iff.put_chunk("ANTz");
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
GP<ByteStream> bsb = BSByteStream::create(iff.get_bytestream(), 50);
|
|
Packit |
df99a1 |
miniexp_io_t io;
|
|
Packit |
df99a1 |
miniexp_io_init(&io);
|
|
Packit |
df99a1 |
io.fputs = bytestream_fputs;
|
|
Packit |
df99a1 |
io.data[0] = (void*)(ByteStream*)bsb;
|
|
Packit |
df99a1 |
minivar_t exor = miniexp_cons(miniexp_symbol("xor"),miniexp_nil);
|
|
Packit |
df99a1 |
minivar_t zstr = miniexp_string("");
|
|
Packit |
df99a1 |
for (GPosition p = links; p; ++p)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
GP<LnkMark> mark = links[p];
|
|
Packit |
df99a1 |
minivar_t url = miniexp_string((const char*)(mark->s));
|
|
Packit |
df99a1 |
minivar_t expr = miniexp_cons(exor, miniexp_nil);
|
|
Packit |
df99a1 |
minivar_t area;
|
|
Packit |
df99a1 |
area = miniexp_cons(miniexp_number(mark->r.height()), area);
|
|
Packit |
df99a1 |
area = miniexp_cons(miniexp_number(mark->r.width()), area);
|
|
Packit |
df99a1 |
area = miniexp_cons(miniexp_number(mark->r.ymin), area);
|
|
Packit |
df99a1 |
area = miniexp_cons(miniexp_number(mark->r.xmin), area);
|
|
Packit |
df99a1 |
area = miniexp_cons(miniexp_symbol("rect"),area);
|
|
Packit |
df99a1 |
expr = miniexp_cons(area, expr);
|
|
Packit |
df99a1 |
expr = miniexp_cons(zstr, expr);
|
|
Packit |
df99a1 |
expr = miniexp_cons(url, expr);
|
|
Packit |
df99a1 |
expr = miniexp_cons(miniexp_symbol("maparea"), expr);
|
|
Packit |
df99a1 |
miniexp_pprint_r(&io, expr, 72);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
iff.close_chunk();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
GP<DjVmNav>
|
|
Packit |
df99a1 |
Comments::get_djvm_nav()
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (nav && nav->getBookMarkCount() && nav->isValidBookmark())
|
|
Packit |
df99a1 |
return nav;
|
|
Packit |
df99a1 |
if (nav)
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8("%s", "csepdjvu: corrupted outline comments.\n");
|
|
Packit |
df99a1 |
return 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// --------------------------------------------------
|
|
Packit |
df99a1 |
// MAIN COMPRESSION ROUTINE
|
|
Packit |
df99a1 |
// --------------------------------------------------
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Compresses one page:
|
|
Packit |
df99a1 |
// - bytestream bs contains the input separated file.
|
|
Packit |
df99a1 |
// - bytestream obs will receive the output djvu file.
|
|
Packit |
df99a1 |
void
|
|
Packit |
df99a1 |
csepdjvu_page(BufferByteStream &bs,
|
|
Packit |
df99a1 |
GP<ByteStream> obs,
|
|
Packit |
df99a1 |
GP<DjVmNav> &nav,
|
|
Packit |
df99a1 |
const csepdjvuopts &opts)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// Read rle data from separation file
|
|
Packit |
df99a1 |
CRLEImage rimg(bs);
|
|
Packit |
df99a1 |
int w = rimg.width;
|
|
Packit |
df99a1 |
int h = rimg.height;
|
|
Packit |
df99a1 |
if (opts.verbose > 1)
|
|
Packit |
df99a1 |
DjVuFormatErrorUTF8( "%s\t%d\t%d\t%d\t%d",
|
|
Packit |
df99a1 |
ERR_MSG("csepdjvu.summary"),
|
|
Packit |
df99a1 |
w, h, rimg.pal->size(), rimg.runs.size());
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// Perform Color Connected Component Analysis
|
|
Packit |
df99a1 |
rimg.make_ccids_by_analysis(); // Obtain ccids
|
|
Packit |
df99a1 |
rimg.make_ccs_from_ccids(); // Compute cc descriptors
|
|
Packit |
df99a1 |
if (opts.verbose > 1)
|
|
Packit |
df99a1 |
DjVuFormatErrorUTF8("%s\t%d", ERR_MSG("csepdjvu.analyzed"),
|
|
Packit |
df99a1 |
rimg.ccs.size());
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// Post-process Color Connected Components
|
|
Packit |
df99a1 |
int largesize = MIN(500, MAX(64, opts.dpi));
|
|
Packit |
df99a1 |
int smallsize = MAX(2, opts.dpi/150);
|
|
Packit |
df99a1 |
rimg.merge_and_split_ccs(smallsize,largesize); // Eliminates gross ccs
|
|
Packit |
df99a1 |
if (opts.verbose > 1)
|
|
Packit |
df99a1 |
DjVuFormatErrorUTF8( "%s\t%d",
|
|
Packit |
df99a1 |
ERR_MSG("csepdjvu.merge_split"),
|
|
Packit |
df99a1 |
rimg.ccs.size());
|
|
Packit |
df99a1 |
rimg.sort_in_reading_order(); // Sort cc descriptors
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// Create JB2Image and fill colordata
|
|
Packit |
df99a1 |
GP<JB2Image> gjimg=JB2Image::create();
|
|
Packit |
df99a1 |
JB2Image &jimg=*gjimg;
|
|
Packit |
df99a1 |
jimg.set_dimension(w, h);
|
|
Packit |
df99a1 |
int nccs = rimg.ccs.size();
|
|
Packit |
df99a1 |
for (int ccid=0; ccid
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
JB2Shape shape;
|
|
Packit |
df99a1 |
JB2Blit blit;
|
|
Packit |
df99a1 |
shape.parent = -1;
|
|
Packit |
df99a1 |
shape.userdata = 0;
|
|
Packit |
df99a1 |
if (ccid >= rimg.nregularccs)
|
|
Packit |
df99a1 |
shape.userdata |= JB2SHAPE_SPECIAL;
|
|
Packit |
df99a1 |
shape.bits = rimg.get_bitmap_for_cc(ccid);
|
|
Packit |
df99a1 |
shape.bits->compress();
|
|
Packit |
df99a1 |
CC& cc = rimg.ccs[ccid];
|
|
Packit |
df99a1 |
blit.shapeno = jimg.add_shape(shape);
|
|
Packit |
df99a1 |
blit.left = cc.bb.xmin;
|
|
Packit |
df99a1 |
blit.bottom = cc.bb.ymin;
|
|
Packit |
df99a1 |
int blitno = jimg.add_blit(blit);
|
|
Packit |
df99a1 |
rimg.pal->colordata.touch(blitno);
|
|
Packit |
df99a1 |
rimg.pal->colordata[blitno] = cc.color;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// Organize JB2Image
|
|
Packit |
df99a1 |
tune_jb2image_lossless(&jimg);
|
|
Packit |
df99a1 |
if (opts.verbose> 1)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int nshape=0, nrefine=0;
|
|
Packit |
df99a1 |
for (int i=0; i
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (!jimg.get_shape(i).bits) continue;
|
|
Packit |
df99a1 |
if (jimg.get_shape(i).parent >= 0) nrefine++;
|
|
Packit |
df99a1 |
nshape++;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
DjVuFormatErrorUTF8( "%s\t%d\t%d",
|
|
Packit |
df99a1 |
ERR_MSG("csepdjvu.cross_code"),
|
|
Packit |
df99a1 |
nshape, nrefine);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// Obtain background image
|
|
Packit |
df99a1 |
int bgred;
|
|
Packit |
df99a1 |
GP<GPixmap> bgpix = read_background(bs, w, h, bgred);
|
|
Packit |
df99a1 |
if (opts.verbose > 1 && bgpix)
|
|
Packit |
df99a1 |
DjVuFormatErrorUTF8( "%s\t%d", ERR_MSG("csepdjvu.reduction"), bgred);
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// Process comments
|
|
Packit |
df99a1 |
Comments coms(w, h, opts);
|
|
Packit |
df99a1 |
coms.process_comments(bs, opts.verbose);
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// Compute flags for simplifying output format
|
|
Packit |
df99a1 |
bool white_background = (bgpix ? false : true);
|
|
Packit |
df99a1 |
bool gray_background = white_background;
|
|
Packit |
df99a1 |
if (rimg.bg_flags == 'g' || rimg.bg_flags=='b')
|
|
Packit |
df99a1 |
gray_background = true;
|
|
Packit |
df99a1 |
bool bitonal = false;
|
|
Packit |
df99a1 |
if (white_background && rimg.pal->size() == 1)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
GPixel fgcolor;
|
|
Packit |
df99a1 |
rimg.pal->index_to_color(0, fgcolor);
|
|
Packit |
df99a1 |
if (fgcolor == GPixel::BLACK)
|
|
Packit |
df99a1 |
bitonal = true;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (opts.verbose > 1)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
if (bitonal)
|
|
Packit |
df99a1 |
DjVuWriteError( ERR_MSG("csepdjvu.bilevel") );
|
|
Packit |
df99a1 |
else if (white_background)
|
|
Packit |
df99a1 |
DjVuWriteError( ERR_MSG("csepdjvu.white_bg") );
|
|
Packit |
df99a1 |
else if (gray_background)
|
|
Packit |
df99a1 |
DjVuWriteError( ERR_MSG("csepdjvu.gray_bg") );
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
DjVuWriteError( ERR_MSG("csepdjvu.color") );
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// Create background image
|
|
Packit |
df99a1 |
GP<IW44Image> iw;
|
|
Packit |
df99a1 |
if (! white_background)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
/* Perform masked compression */
|
|
Packit |
df99a1 |
GP<GBitmap> mask = jimg.get_bitmap(bgred);
|
|
Packit |
df99a1 |
mask->binarize_grays(bgred*bgred-1);
|
|
Packit |
df99a1 |
IW44Image::CRCBMode mode = IW44Image::CRCBnormal;
|
|
Packit |
df99a1 |
if (gray_background) mode = IW44Image::CRCBnone;
|
|
Packit |
df99a1 |
iw = IW44Image::create_encode(*bgpix, mask, mode);
|
|
Packit |
df99a1 |
bgpix = 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
else if (! bitonal)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
/* Compute white background */
|
|
Packit |
df99a1 |
GPixel bgcolor = GPixel::WHITE;
|
|
Packit |
df99a1 |
GP<GPixmap> inputsub=GPixmap::create((h+11)/12, (w+11)/12, &bgcolor);
|
|
Packit |
df99a1 |
iw = IW44Image::create_encode(*inputsub, 0, IW44Image::CRCBnone);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// Assemble DJVU file
|
|
Packit |
df99a1 |
GP<IFFByteStream> giff=IFFByteStream::create(obs);
|
|
Packit |
df99a1 |
IFFByteStream &iff=*giff;
|
|
Packit |
df99a1 |
// -- main composite chunk
|
|
Packit |
df99a1 |
iff.put_chunk("FORM:DJVU", 1);
|
|
Packit |
df99a1 |
// -- ``INFO'' chunk
|
|
Packit |
df99a1 |
iff.put_chunk("INFO");
|
|
Packit |
df99a1 |
GP<DjVuInfo> ginfo=DjVuInfo::create();
|
|
Packit |
df99a1 |
DjVuInfo &info=*ginfo;
|
|
Packit |
df99a1 |
info.height = h;
|
|
Packit |
df99a1 |
info.width = w;
|
|
Packit |
df99a1 |
info.dpi = opts.dpi;
|
|
Packit |
df99a1 |
info.encode(*iff.get_bytestream());
|
|
Packit |
df99a1 |
iff.close_chunk();
|
|
Packit |
df99a1 |
// -- ``Sjbz'' chunk
|
|
Packit |
df99a1 |
iff.put_chunk("Sjbz");
|
|
Packit |
df99a1 |
jimg.encode(iff.get_bytestream());
|
|
Packit |
df99a1 |
iff.close_chunk();
|
|
Packit |
df99a1 |
// -- Color stuff
|
|
Packit |
df99a1 |
if (! bitonal)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// -- ``FGbz'' chunk
|
|
Packit |
df99a1 |
iff.put_chunk("FGbz");
|
|
Packit |
df99a1 |
rimg.pal->encode(iff.get_bytestream());
|
|
Packit |
df99a1 |
iff.close_chunk();
|
|
Packit |
df99a1 |
// -- ``BG44'' chunk
|
|
Packit |
df99a1 |
IWEncoderParms iwparms;
|
|
Packit |
df99a1 |
if (white_background)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
iff.put_chunk("BG44");
|
|
Packit |
df99a1 |
iwparms.slices = 97;
|
|
Packit |
df99a1 |
iw->encode_chunk(iff.get_bytestream(), iwparms);
|
|
Packit |
df99a1 |
iff.close_chunk();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
const unsigned char *slice = opts.slice;
|
|
Packit |
df99a1 |
while ((iwparms.slices = *slice++))
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
iff.put_chunk("BG44");
|
|
Packit |
df99a1 |
iw->encode_chunk(iff.get_bytestream(), iwparms);
|
|
Packit |
df99a1 |
iff.close_chunk();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// -- terminate main composite chunk
|
|
Packit |
df99a1 |
coms.make_chunks(iff);
|
|
Packit |
df99a1 |
iff.close_chunk();
|
|
Packit |
df99a1 |
// -- store outline
|
|
Packit |
df99a1 |
if (! nav)
|
|
Packit |
df99a1 |
nav = coms.get_djvm_nav();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Checks whether there is another page in the same file
|
|
Packit |
df99a1 |
bool
|
|
Packit |
df99a1 |
check_for_another_page(BufferByteStream &bs, const csepdjvuopts &opts)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// Skip null bytes (why?)
|
|
Packit |
df99a1 |
int lookahead;
|
|
Packit |
df99a1 |
while (! (lookahead = bs.get())) { }
|
|
Packit |
df99a1 |
bs.unget(lookahead);
|
|
Packit |
df99a1 |
// Check next header
|
|
Packit |
df99a1 |
if (lookahead == 'R')
|
|
Packit |
df99a1 |
return true;
|
|
Packit |
df99a1 |
if (lookahead != EOF)
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8("%s","csepdjvu: found corrupted data\n");
|
|
Packit |
df99a1 |
return false;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Prints usage message
|
|
Packit |
df99a1 |
void
|
|
Packit |
df99a1 |
usage()
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
const char *msg =
|
|
Packit |
df99a1 |
#ifdef DJVULIBRE_VERSION
|
|
Packit |
df99a1 |
"CSEPDJVU --- DjVuLibre-" DJVULIBRE_VERSION "\n"
|
|
Packit |
df99a1 |
#endif
|
|
Packit |
df99a1 |
"DjVu encoder working with \"separated\" files\n\n"
|
|
Packit |
df99a1 |
"Usage: csepdjvu <...options_or_separatedfiles...> <outputdjvufile>\n"
|
|
Packit |
df99a1 |
"Options are:\n"
|
|
Packit |
df99a1 |
" -v, -vv Select verbosity level.\n"
|
|
Packit |
df99a1 |
" -d <n> Set resolution to <n> dpi (default: 300).\n"
|
|
Packit |
df99a1 |
" -t Restricts text information to lines only.\n"
|
|
Packit |
df99a1 |
" -q <spec> Select quality for background (default: 72+11+10+10);\n"
|
|
Packit |
df99a1 |
" see option -slice in program c44 for more information.\n"
|
|
Packit |
df99a1 |
"Each separated files contain one or more pages\n"
|
|
Packit |
df99a1 |
"Each page is composed of:\n"
|
|
Packit |
df99a1 |
" (1) a B&W-RLE or Color-RLE image representing the foreground,\n"
|
|
Packit |
df99a1 |
" (2) an optional PPM image representing the background layer.\n";
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8(msg);
|
|
Packit |
df99a1 |
exit(10);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Parsing quality spec (borrowed from c44)
|
|
Packit |
df99a1 |
void
|
|
Packit |
df99a1 |
parse_slice(const char *q, csepdjvuopts &opts)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
int count = 0;
|
|
Packit |
df99a1 |
int lastx = 0;
|
|
Packit |
df99a1 |
while (*q)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
char *ptr;
|
|
Packit |
df99a1 |
int x = strtol(q, &ptr, 10);
|
|
Packit |
df99a1 |
if (ptr == q)
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: "
|
|
Packit |
df99a1 |
"illegal quality specification (number expected)");
|
|
Packit |
df99a1 |
if (lastx>0 && q[-1]=='+')
|
|
Packit |
df99a1 |
x += lastx;
|
|
Packit |
df99a1 |
if (x<1 || x>1000 || x
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: "
|
|
Packit |
df99a1 |
"illegal quality specification (number out of range)");
|
|
Packit |
df99a1 |
lastx = x;
|
|
Packit |
df99a1 |
if (*ptr && *ptr!='+' && *ptr!=',')
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: "
|
|
Packit |
df99a1 |
"illegal quality specification (comma expected)");
|
|
Packit |
df99a1 |
q = (*ptr ? ptr+1 : ptr);
|
|
Packit |
df99a1 |
if (count+1 >= (int)(sizeof(opts.slice)/sizeof(opts.slice[0])))
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: "
|
|
Packit |
df99a1 |
"illegal quality specification (too many chunks)");
|
|
Packit |
df99a1 |
opts.slice[count++] = x;
|
|
Packit |
df99a1 |
opts.slice[count] = 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
if (count < 1)
|
|
Packit |
df99a1 |
G_THROW("csepdjvu: "
|
|
Packit |
df99a1 |
"illegal quality specification (no chunks)");
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
|
|
Packit |
df99a1 |
// -- Main routine
|
|
Packit |
df99a1 |
int
|
|
Packit |
df99a1 |
main(int argc, const char **argv)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
DJVU_LOCALE;
|
|
Packit |
df99a1 |
GArray<GUTF8String> dargv(0,argc-1);
|
|
Packit |
df99a1 |
for(int i=0;i
|
|
Packit |
df99a1 |
dargv[i]=GNativeString(argv[i]);
|
|
Packit |
df99a1 |
G_TRY
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
GP<DjVmDoc> gdoc=DjVmDoc::create();
|
|
Packit |
df99a1 |
GP<DjVmNav> gnav;
|
|
Packit |
df99a1 |
DjVmDoc &doc=*gdoc;
|
|
Packit |
df99a1 |
GURL outputurl;
|
|
Packit |
df99a1 |
GP<ByteStream> goutputpage=ByteStream::create();
|
|
Packit |
df99a1 |
csepdjvuopts opts;
|
|
Packit |
df99a1 |
int pageno = 0;
|
|
Packit |
df99a1 |
// Read outputurl name
|
|
Packit |
df99a1 |
if (argc < 3) usage();
|
|
Packit |
df99a1 |
outputurl = GURL::Filename::UTF8(dargv[--argc]);
|
|
Packit |
df99a1 |
// Process arguments
|
|
Packit |
df99a1 |
for (int i=1; i
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
GUTF8String arg = dargv[i];
|
|
Packit |
df99a1 |
if (arg == "-v")
|
|
Packit |
df99a1 |
opts.verbose = 1;
|
|
Packit |
df99a1 |
else if (arg == "-vv")
|
|
Packit |
df99a1 |
opts.verbose = 2;
|
|
Packit |
df99a1 |
else if (arg == "-t")
|
|
Packit |
df99a1 |
opts.text = DjVuTXT::LINE;
|
|
Packit |
df99a1 |
else if (arg == "-d" && i+1
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// Specify resolution
|
|
Packit |
df99a1 |
char *end;
|
|
Packit |
df99a1 |
opts.dpi = strtol(dargv[++i], &end, 10);
|
|
Packit |
df99a1 |
if (*end || opts.dpi<25 || opts.dpi>6000)
|
|
Packit |
df99a1 |
usage();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
else if (arg == "-q" && i+1 < argc)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// Specify background quality
|
|
Packit |
df99a1 |
parse_slice(dargv[++i], opts);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
else if (arg == "-l" || arg == "-t" || arg == "-h")
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8("csepdjvu: option %s not yet supported\n",
|
|
Packit |
df99a1 |
(const char *)arg );
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
else if ((arg == "-j" || arg == "-p") && i+1 < argc)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
i += 1;
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8("csepdjvu: option %s not yet supported\n",
|
|
Packit |
df99a1 |
(const char *)arg );
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// Process separation file
|
|
Packit |
df99a1 |
GP<ByteStream> fbs =
|
|
Packit |
df99a1 |
ByteStream::create(GURL::Filename::UTF8(arg),"rb");
|
|
Packit |
df99a1 |
BufferByteStream ibs(*fbs);
|
|
Packit |
df99a1 |
do {
|
|
Packit |
df99a1 |
char pagename[16];
|
|
Packit |
df99a1 |
sprintf(pagename, "p%04d.djvu", ++pageno);
|
|
Packit |
df99a1 |
if (opts.verbose > 1)
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8("%s","--------------------\n");
|
|
Packit |
df99a1 |
// Compress page
|
|
Packit |
df99a1 |
goutputpage=ByteStream::create();
|
|
Packit |
df99a1 |
ByteStream &outputpage=*goutputpage;
|
|
Packit |
df99a1 |
csepdjvu_page(ibs, goutputpage, gnav, opts);
|
|
Packit |
df99a1 |
if (opts.verbose) {
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8("csepdjvu: %d bytes for page %d",
|
|
Packit |
df99a1 |
outputpage.size(), pageno);
|
|
Packit |
df99a1 |
if (arg == "-")
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8("%s"," (from stdin)\n");
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
DjVuPrintErrorUTF8(" (from file '%s')\n",
|
|
Packit |
df99a1 |
(const char*)arg);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Insert page into document
|
|
Packit |
df99a1 |
outputpage.seek(0);
|
|
Packit |
df99a1 |
doc.insert_file(outputpage, DjVmDir::File::PAGE,
|
|
Packit |
df99a1 |
pagename, pagename);
|
|
Packit |
df99a1 |
} while (check_for_another_page(ibs, opts));
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
// Save file
|
|
Packit |
df99a1 |
if (pageno == 1 && ! gnav)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
ByteStream &outputpage=*goutputpage;
|
|
Packit |
df99a1 |
// Save as a single page
|
|
Packit |
df99a1 |
outputpage.seek(0);
|
|
Packit |
df99a1 |
ByteStream::create(outputurl,"wb")->copy(outputpage);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
else if (pageno >= 1)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
// Save as a bundled file
|
|
Packit |
df99a1 |
doc.set_djvm_nav(gnav);
|
|
Packit |
df99a1 |
doc.write(ByteStream::create(outputurl,"wb"));
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
else
|
|
Packit |
df99a1 |
usage();
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
G_CATCH(ex)
|
|
Packit |
df99a1 |
{
|
|
Packit |
df99a1 |
ex.perror();
|
|
Packit |
df99a1 |
exit(1);
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
G_ENDCATCH;
|
|
Packit |
df99a1 |
return 0;
|
|
Packit |
df99a1 |
}
|
|
Packit |
df99a1 |
|