Blame tools/djvumake.cpp

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 djvumake
Packit df99a1
Packit df99a1
    {\bf Synopsis}
Packit df99a1
    \begin{verbatim}
Packit df99a1
       % djvumake <djvufile> [...<arguments>...]
Packit df99a1
    \end{verbatim}
Packit df99a1
Packit df99a1
    {\bf Description}
Packit df99a1
Packit df99a1
    This command assembles a single-page DjVu file by copying or creating
Packit df99a1
    chunks according to the provided <arguments>. Supported syntaxes for
Packit df99a1
    <arguments> are as follows:
Packit df99a1
    \begin{tabular}{ll}
Packit df99a1
    {#INFO=<w>,<h>,<dpi>#} &
Packit df99a1
      Creates the initial ``INFO'' chunk.  Arguments #w#, #h# and #dpi#
Packit df99a1
      describe the width, height and resolution of the image.  All arguments
Packit df99a1
      may be omitted.  The default resolution is 300 dpi.  The default width
Packit df99a1
      and height will be retrieved from the first mask chunk specified in the
Packit df99a1
      command line options.\\
Packit df99a1
      {#Sjbz=<jb2file>#} &
Packit df99a1
      Creates a JB2 mask chunk.  File #jb2file# may
Packit df99a1
      contain raw JB2 data or be a DjVu file containing JB2 data, such as the
Packit df99a1
      files produced by \Ref{cjb2}.\\
Packit df99a1
      {#Smmr=<mmrfile>] #} &
Packit df99a1
      Creates a mask chunk containing MMR/G4 data.  File #mmrfile#
Packit df99a1
      may contain raw MMR data or be a DjVu file containing MMR data.\\
Packit df99a1
      {#BG44=<iw44file>:<n>#} &
Packit df99a1
      Creates one or more IW44 background chunks.  File #iw44file# must be an
Packit df99a1
      IW44 file such as the files created by \Ref{c44}.  The optional argument
Packit df99a1
      #n# indicates the number of chunks to copy from the IW44 file.\\
Packit df99a1
      {#BGjp=<jpegfile>#} &
Packit df99a1
      Creates a JPEG background chunk.\\
Packit df99a1
      {#BG2k=<jpegfile>#} &
Packit df99a1
      Creates a JPEG-2000 background chunk.\\
Packit df99a1
      {#FG44=<iw44file>#} &
Packit df99a1
      Creates one IW44 foreround chunks.  File #iw44file# must be an
Packit df99a1
      IW44 file such as the files created by \Ref{c44}.  Only the first
Packit df99a1
      chunk will be copied.\\
Packit df99a1
      {FGbz=(bzzfile|\{#color1[:x,y,w,h]\})}
Packit df99a1
      Creates a chunk containing colors for each JB2 encoded object.
Packit df99a1
      Such chunks are created using class \Ref{DjVuPalette}.
Packit df99a1
      See program \Ref{cpaldjvu} for an example.\\
Packit df99a1
      {#FGjp=<jpegfile>#} &
Packit df99a1
      Creates a JPEG foreground chunk.\\
Packit df99a1
      {#FG2k=<jpegfile>#} &
Packit df99a1
      Creates a JPEG-2000 foreground chunk.\\
Packit df99a1
      {#INCL=<fileid>#} &
Packit df99a1
      Creates an include chunk pointing to <fileid>.
Packit df99a1
      The resulting file should then be included into a 
Packit df99a1
      multipage document.\\
Packit df99a1
      {#PPM=<ppmfile>#} (psuedo-chunk) &
Packit df99a1
      Create IW44 foreground and background chunks
Packit df99a1
      by masking and subsampling PPM file #ppmfile#.
Packit df99a1
      This is used by program \Ref{cdjvu}.\\
Packit df99a1
      {#chunk=<rawdatafile>#} &
Packit df99a1
      Creates the specified chunk with the specified raw data.
Packit df99a1
    \end{tabular}
Packit df99a1
Packit df99a1
    Let us assume now that you have a PPM image #"myimage.ppm"# and a PBM
Packit df99a1
    bitonal image #"mymask.pbm"# whose black pixels indicate which pixels
Packit df99a1
    belong to the foreground.  Such a bitonal file may be obtained by
Packit df99a1
    thresholding, although more sophisticated techniques can give better
Packit df99a1
    results.  You can then generate a Compound DjVu File by typing:
Packit df99a1
    \begin{verbatim}
Packit df99a1
       % cjb2 mymask.pbm mymask.djvu
Packit df99a1
       % djvumake mycompound.djvu Sjbz=mymask.djvu PPM=myimage.ppm
Packit df99a1
    \end{verbatim}
Packit df99a1
Packit df99a1
    @memo
Packit df99a1
    Assemble DjVu files.
Packit df99a1
    @author
Packit df99a1
    L\'eon Bottou <leonb@research.att.com> \\
Packit df99a1
    Patrick Haffner <haffner@research.att.com>
Packit df99a1
*/
Packit df99a1
//@{
Packit df99a1
//@}
Packit df99a1
Packit df99a1
#include "GString.h"
Packit df99a1
#include "GException.h"
Packit df99a1
#include "DjVuImage.h"
Packit df99a1
#include "MMRDecoder.h"
Packit df99a1
#include "IFFByteStream.h"
Packit df99a1
#include "JB2Image.h"
Packit df99a1
#include "IW44Image.h"
Packit df99a1
Packit df99a1
#include "GPixmap.h"
Packit df99a1
#include "GBitmap.h"
Packit df99a1
#include "GContainer.h"
Packit df99a1
#include "GRect.h"
Packit df99a1
#include "DjVuMessage.h"
Packit df99a1
Packit df99a1
#include "common.h"
Packit df99a1
Packit df99a1
int flag_contains_fg      = 0;
Packit df99a1
int flag_contains_bg      = 0;
Packit df99a1
int flag_contains_stencil = 0;
Packit df99a1
int flag_contains_incl    = 0;
Packit df99a1
int flag_fg_needs_palette = 0;
Packit df99a1
Packit df99a1
struct DJVUMAKEGlobal 
Packit df99a1
{
Packit df99a1
  // Globals that need static initialization
Packit df99a1
  // are grouped here to work around broken compilers.
Packit df99a1
  GP<ByteStream> jb2stencil;
Packit df99a1
  GP<ByteStream> mmrstencil;
Packit df99a1
  GP<JB2Image> stencil;
Packit df99a1
  GP<JB2Dict> dictionary;
Packit df99a1
  GTArray<GRect> colorzones;
Packit df99a1
  GP<ByteStream> colorpalette;
Packit df99a1
};
Packit df99a1
Packit df99a1
static DJVUMAKEGlobal& g(void)
Packit df99a1
{
Packit df99a1
  static DJVUMAKEGlobal g;
Packit df99a1
  return g;
Packit df99a1
}
Packit df99a1
Packit df99a1
int w = -1;
Packit df99a1
int h = -1;
Packit df99a1
int dpi = 300;
Packit df99a1
int blit_count = -1;
Packit df99a1
Packit df99a1
// -- Display brief usage information
Packit df99a1
Packit df99a1
void 
Packit df99a1
usage()
Packit df99a1
{
Packit df99a1
  DjVuPrintErrorUTF8(
Packit df99a1
#ifdef DJVULIBRE_VERSION
Packit df99a1
         "DJVUMAKE --- DjVuLibre-" DJVULIBRE_VERSION "\n"
Packit df99a1
#endif
Packit df99a1
         "Utility for manually assembling DjVu files\n\n"
Packit df99a1
         "Usage: djvumake djvufile ...arguments...\n"
Packit df99a1
         "\n"
Packit df99a1
         "The arguments describe the successive chunks of the DJVU file.\n"
Packit df99a1
         "Possible arguments are:\n"
Packit df99a1
         "\n"
Packit df99a1
         "   INFO=w[,[h[,[dpi]]]]     --  Create the initial information chunk\n"
Packit df99a1
         "   Sjbz=jb2file             --  Create a JB2 mask chunk\n"
Packit df99a1
         "   Djbz=jb2file             --  Create a JB2 shape dictionary\n"
Packit df99a1
         "   Smmr=mmrfile             --  Create a MMR mask chunk\n"
Packit df99a1
         "   BG44=[iw4file][:nchunks] --  Create one or more IW44 background chunks\n"
Packit df99a1
         "   BGjp=jpegfile            --  Create a JPEG background chunk\n"
Packit df99a1
         "   BG2k=jpeg2000file        --  Create a JP2K background chunk\n"
Packit df99a1
         "   FG44=iw4file             --  Create an IW44 foreground chunk\n"
Packit df99a1
         "   FGbz=bzzfile             --  Create a foreground color chunk from a file\n"
Packit df99a1
         "   FGbz={#color:x,y,w,h}    --  Create a foreground color chunk from zones\n"
Packit df99a1
         "   FGjp=jpegfile            --  Create a JPEG foreground image chunk\n"
Packit df99a1
         "   FG2k=jpeg2000file        --  Create a JP2K foreground image chunk\n"
Packit df99a1
         "   INCL=fileid              --  Create an INCL chunk\n"
Packit df99a1
         "   chunk=rawdatafile        --  Create the specified chunk from the raw data file\n"
Packit df99a1
         "   PPM=ppmfile              --  Create IW44 foreground and background chunks\n"
Packit df99a1
         "                                by masking and subsampling a PPM file.\n"
Packit df99a1
         "\n"
Packit df99a1
         "You may omit the specification of the information chunk. An information\n"
Packit df99a1
         "chunk will be created using the image size of the first mask chunk\n"
Packit df99a1
         "This program is sometimes able  to issue a warning when you are building an\n"
Packit df99a1
         "incorrect djvu file.\n"
Packit df99a1
         "\n");
Packit df99a1
  exit(1);
Packit df99a1
}
Packit df99a1
Packit df99a1
// -- Obtain image size from mmr chunk
Packit df99a1
Packit df99a1
void
Packit df99a1
analyze_mmr_chunk(const GURL &url)
Packit df99a1
{
Packit df99a1
  if (!g().mmrstencil || !g().mmrstencil->size())
Packit df99a1
    {
Packit df99a1
      GP<ByteStream> gbs=ByteStream::create(url,"rb");
Packit df99a1
      ByteStream &bs=*gbs;
Packit df99a1
      g().mmrstencil = ByteStream::create();
Packit df99a1
      // Check if file is an IFF file
Packit df99a1
      char magic[4];
Packit df99a1
      memset(magic,0,sizeof(magic));
Packit df99a1
      bs.readall(magic,sizeof(magic));
Packit df99a1
      if (!GStringRep::cmp(magic,"AT&T",4))
Packit df99a1
        bs.readall(magic,sizeof(magic));
Packit df99a1
      if (GStringRep::cmp(magic,"FORM",4))
Packit df99a1
        {
Packit df99a1
          // Must be a raw file
Packit df99a1
          bs.seek(0);
Packit df99a1
          g().mmrstencil->copy(bs);
Packit df99a1
        }
Packit df99a1
      else
Packit df99a1
        {
Packit df99a1
          // Search Smmr chunk
Packit df99a1
          bs.seek(0);
Packit df99a1
          GUTF8String chkid;
Packit df99a1
          GP<IFFByteStream> giff=IFFByteStream::create(gbs);
Packit df99a1
          IFFByteStream &iff=*giff;
Packit df99a1
          if (iff.get_chunk(chkid)==0 || chkid!="FORM:DJVU")
Packit df99a1
            G_THROW("Expecting a DjVu file!");
Packit df99a1
          for(; iff.get_chunk(chkid); iff.close_chunk())
Packit df99a1
            if (chkid=="Smmr") { g().mmrstencil->copy(bs); break; }
Packit df99a1
        }
Packit df99a1
      // Check result
Packit df99a1
      g().mmrstencil->seek(0);
Packit df99a1
      if (!g().mmrstencil->size())
Packit df99a1
        G_THROW("Could not find MMR data");
Packit df99a1
      // Decode
Packit df99a1
      g().stencil = MMRDecoder::decode(g().mmrstencil);
Packit df99a1
      int jw = g().stencil->get_width();
Packit df99a1
      int jh = g().stencil->get_height();
Packit df99a1
      if (w < 0) w = jw;
Packit df99a1
      if (h < 0) h = jh;
Packit df99a1
      if (jw!=w || jh!=h)
Packit df99a1
        DjVuPrintErrorUTF8("djvumake: mask size (%s) does not match info size\n", (const char *)url);
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Obtain shape dictionary
Packit df99a1
Packit df99a1
void 
Packit df99a1
analyze_djbz_chunk(GP<ByteStream> gbs)
Packit df99a1
{
Packit df99a1
  if (g().dictionary)
Packit df99a1
    G_THROW("Duplicate Djbz dictionary");
Packit df99a1
  g().dictionary = JB2Dict::create();
Packit df99a1
  g().dictionary->decode(gbs);
Packit df99a1
}
Packit df99a1
Packit df99a1
void 
Packit df99a1
analyze_djbz_chunk(const GURL &url)
Packit df99a1
{
Packit df99a1
  GP<ByteStream> gbs = ByteStream::create(url, "rb");
Packit df99a1
  analyze_djbz_chunk(gbs);
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Obtain image size and blit count from jb2 chunk
Packit df99a1
Packit df99a1
GP<JB2Dict> 
Packit df99a1
provide_shared_dict( void* )
Packit df99a1
{
Packit df99a1
  return(g().dictionary);
Packit df99a1
}
Packit df99a1
Packit df99a1
void 
Packit df99a1
analyze_jb2_chunk(const GURL &url)
Packit df99a1
{
Packit df99a1
  if (!g().jb2stencil || !g().jb2stencil->size())
Packit df99a1
    {
Packit df99a1
      GP<ByteStream> gbs=ByteStream::create(url,"rb");
Packit df99a1
      ByteStream &bs=*gbs;
Packit df99a1
      g().jb2stencil = ByteStream::create();
Packit df99a1
      // Check if file is an IFF file
Packit df99a1
      char magic[4];
Packit df99a1
      memset(magic,0,sizeof(magic));
Packit df99a1
      bs.readall(magic,sizeof(magic));
Packit df99a1
      if (!GStringRep::cmp(magic,"AT&T",4))
Packit df99a1
        bs.readall(magic,sizeof(magic));
Packit df99a1
      if (GStringRep::cmp(magic,"FORM",4))
Packit df99a1
        {
Packit df99a1
          // Must be a raw file
Packit df99a1
          bs.seek(0);
Packit df99a1
          g().jb2stencil->copy(bs);
Packit df99a1
        }
Packit df99a1
      else
Packit df99a1
        {
Packit df99a1
          // Search Sjbz chunk
Packit df99a1
          bs.seek(0);
Packit df99a1
          GUTF8String chkid;
Packit df99a1
          GP<IFFByteStream> giff=IFFByteStream::create(gbs);
Packit df99a1
          IFFByteStream &iff=*giff;
Packit df99a1
          if (iff.get_chunk(chkid)==0 || chkid!="FORM:DJVU")
Packit df99a1
            G_THROW("Expecting a DjVu file!");
Packit df99a1
          for(; iff.get_chunk(chkid); iff.close_chunk())
Packit df99a1
            if (chkid=="Sjbz") { g().jb2stencil->copy(bs); break; }
Packit df99a1
        }
Packit df99a1
      // Check result
Packit df99a1
      g().jb2stencil->seek(0);
Packit df99a1
      if (!g().jb2stencil->size())
Packit df99a1
        G_THROW("Could not find JB2 data");
Packit df99a1
      // Decode
Packit df99a1
      g().stencil=JB2Image::create();
Packit df99a1
      g().stencil->decode(g().jb2stencil,&provide_shared_dict,NULL);
Packit df99a1
      int jw = g().stencil->get_width();
Packit df99a1
      int jh = g().stencil->get_height();
Packit df99a1
      if (w < 0) 
Packit df99a1
        w = jw;
Packit df99a1
      if (h < 0) 
Packit df99a1
        h = jh;
Packit df99a1
      if (blit_count < 0) 
Packit df99a1
        blit_count = g().stencil->get_blit_count();
Packit df99a1
      if (jw!=w || jh!=h)
Packit df99a1
        DjVuPrintErrorUTF8("djvumake: mask size (%s) does not match info size\n", (const char *)url);
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
// -- Load dictionary from an INCL chunk
Packit df99a1
Packit df99a1
void
Packit df99a1
analyze_incl_chunk(const GURL &url)
Packit df99a1
{
Packit df99a1
  GP<ByteStream> gbs = ByteStream::create(url,"rb");
Packit df99a1
  char buffer[24];
Packit df99a1
  memset(buffer, 0, sizeof(buffer));
Packit df99a1
  gbs->read(buffer,sizeof(buffer));
Packit df99a1
  char *s = buffer;
Packit df99a1
  if (!strncmp(s, "AT&T", 4))
Packit df99a1
    s += 4;
Packit df99a1
  if (strncmp(s, "FORM", 4) || strncmp(s+8, "DJVI", 4))
Packit df99a1
    G_THROW("Expecting a valid FORM:DJVI chunk in the included file");
Packit df99a1
  gbs->seek(0);
Packit df99a1
  GP<IFFByteStream> giff=IFFByteStream::create(gbs);
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  giff->get_chunk(chkid); // FORM:DJVI
Packit df99a1
  for(; giff->get_chunk(chkid); giff->close_chunk())
Packit df99a1
    if (chkid=="Djbz") 
Packit df99a1
      analyze_djbz_chunk(giff->get_bytestream());
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
check_for_shared_dict(GArray<GUTF8String> &argv)
Packit df99a1
{
Packit df99a1
  const int argc=argv.hbound()+1;
Packit df99a1
  for (int i=2; i
Packit df99a1
    if (!argv[i].cmp("INCL=",5))
Packit df99a1
      analyze_incl_chunk(GURL::Filename::UTF8(5+(const char *)argv[i]));
Packit df99a1
    else if (!argv[i].cmp("Djbz=", 5))
Packit df99a1
      analyze_djbz_chunk(GURL::Filename::UTF8(5+(const char *)argv[i]));
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
// -- Create info chunk from specification or mask
Packit df99a1
Packit df99a1
void
Packit df99a1
create_info_chunk(IFFByteStream &iff, GArray<GUTF8String> &argv)
Packit df99a1
{
Packit df99a1
  const int argc=argv.hbound()+1;
Packit df99a1
  // Process info specification
Packit df99a1
  for (int i=2; i
Packit df99a1
    if (!argv[i].cmp("INFO=",5))
Packit df99a1
      {
Packit df99a1
        int   narg = 0;
Packit df99a1
        const char *ptr = 5+(const char *)argv[i];
Packit df99a1
        while (*ptr)
Packit df99a1
          {
Packit df99a1
            if (*ptr != ',')
Packit df99a1
              {
Packit df99a1
                int x = strtol((char *)ptr, (char **)&ptr, 10);
Packit df99a1
                switch(narg)
Packit df99a1
                  {
Packit df99a1
                  case 0: 
Packit df99a1
                    w = x; break;
Packit df99a1
                  case 1: 
Packit df99a1
                    h = x; break;
Packit df99a1
                  case 2: 
Packit df99a1
                    dpi = x; break;
Packit df99a1
                  default:  
Packit df99a1
                    G_THROW("djvumake: incorrect 'INFO' chunk specification\n");
Packit df99a1
                  }
Packit df99a1
              }
Packit df99a1
            narg++;
Packit df99a1
            if (*ptr && *ptr++!=',')
Packit df99a1
              G_THROW("djvumake: comma expected in 'INFO' chunk specification\n");
Packit df99a1
          }
Packit df99a1
          break;
Packit df99a1
      }
Packit df99a1
  if (w>0 && (w<=0 || w>=32768))
Packit df99a1
    G_THROW("djvumake: incorrect width in 'INFO' chunk specification\n");
Packit df99a1
  if (h>0 && (h<=0 || h>=32768))
Packit df99a1
    G_THROW("djvumake: incorrect height in 'INFO' chunk specification\n");
Packit df99a1
  if (dpi>0 && (dpi<25 || dpi>6000))
Packit df99a1
    G_THROW("djvumake: incorrect dpi in 'INFO' chunk specification\n");
Packit df99a1
  // Search first mask chunks if size is still unknown
Packit df99a1
  if (h<0 || w<0)
Packit df99a1
    {
Packit df99a1
      for (int i=2; i
Packit df99a1
        if (!argv[i].cmp("Sjbz=",5))
Packit df99a1
          {
Packit df99a1
            analyze_jb2_chunk(GURL::Filename::UTF8(5+(const char *)argv[i]));
Packit df99a1
            break;
Packit df99a1
          }
Packit df99a1
        else if (!argv[i].cmp("Smmr=",5))
Packit df99a1
          {
Packit df99a1
            analyze_mmr_chunk(GURL::Filename::UTF8(5+(const char *)argv[i]));
Packit df99a1
            break;
Packit df99a1
          }
Packit df99a1
    }
Packit df99a1
  
Packit df99a1
  // Check that we have everything
Packit df99a1
  if (w<0 || h<0)
Packit df99a1
    G_THROW("djvumake: cannot determine image size\n");
Packit df99a1
  // write info chunk
Packit df99a1
  GP<DjVuInfo> ginfo=DjVuInfo::create();
Packit df99a1
  DjVuInfo &info=*ginfo;
Packit df99a1
  info.width = w;
Packit df99a1
  info.height = h;
Packit df99a1
  info.dpi = dpi;
Packit df99a1
  iff.put_chunk("INFO");
Packit df99a1
  info.encode(*iff.get_bytestream());
Packit df99a1
  iff.close_chunk();
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Create MMR mask chunk
Packit df99a1
Packit df99a1
void 
Packit df99a1
create_mmr_chunk(IFFByteStream &iff, const char *chkid, const GURL &url)
Packit df99a1
{
Packit df99a1
  analyze_mmr_chunk(url);
Packit df99a1
  g().mmrstencil->seek(0);
Packit df99a1
  iff.put_chunk(chkid);
Packit df99a1
  iff.copy(*g().mmrstencil);
Packit df99a1
  iff.close_chunk();
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Create FGbz palette chunk
Packit df99a1
Packit df99a1
void 
Packit df99a1
create_fgbz_chunk(IFFByteStream &iff)
Packit df99a1
{
Packit df99a1
  int nzones = g().colorzones.size();
Packit df99a1
  int npalette = g().colorpalette->size() / 3;
Packit df99a1
  GP<DjVuPalette> pal = DjVuPalette::create();
Packit df99a1
  g().colorpalette->seek(0);
Packit df99a1
  pal->decode_rgb_entries(*g().colorpalette, npalette);
Packit df99a1
  pal->colordata.resize(0,blit_count-1);
Packit df99a1
  for (int d=0; d
Packit df99a1
    {
Packit df99a1
      JB2Blit *blit = g().stencil->get_blit(d);
Packit df99a1
      const JB2Shape &shape = g().stencil->get_shape(blit->shapeno);
Packit df99a1
      GRect brect(blit->left, blit->bottom, shape.bits->columns(), shape.bits->rows());
Packit df99a1
      int index = nzones;
Packit df99a1
      for (int i=0; i
Packit df99a1
        {
Packit df99a1
          GRect zrect = g().colorzones[i];
Packit df99a1
          if (zrect.isempty() || zrect.intersect(brect, zrect))
Packit df99a1
            index = i;
Packit df99a1
        }
Packit df99a1
      if (index >= npalette)
Packit df99a1
        G_THROW("create_fgbz_chunk: internal error");
Packit df99a1
      pal->colordata[d] = index;
Packit df99a1
    }
Packit df99a1
  iff.put_chunk("FGbz");
Packit df99a1
  pal->encode(iff.get_bytestream());
Packit df99a1
  iff.close_chunk();
Packit df99a1
}
Packit df99a1
Packit df99a1
// -- Create JB2 mask chunk
Packit df99a1
Packit df99a1
void 
Packit df99a1
create_jb2_chunk(IFFByteStream &iff, const char *chkid, const GURL &url)
Packit df99a1
{
Packit df99a1
  analyze_jb2_chunk(url);
Packit df99a1
  g().jb2stencil->seek(0);
Packit df99a1
  iff.put_chunk(chkid);
Packit df99a1
  iff.copy(*g().jb2stencil);
Packit df99a1
  iff.close_chunk();
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Create inclusion chunk
Packit df99a1
Packit df99a1
void 
Packit df99a1
create_incl_chunk(IFFByteStream &iff, const char *chkid, const char *fileid)
Packit df99a1
{
Packit df99a1
  iff.put_chunk("INCL");
Packit df99a1
  iff.write(fileid, strlen(fileid));
Packit df99a1
  iff.close_chunk();
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Create chunk by copying file contents
Packit df99a1
Packit df99a1
void 
Packit df99a1
create_raw_chunk(IFFByteStream &iff, const GUTF8String &chkid, const GURL &url)
Packit df99a1
{
Packit df99a1
  iff.put_chunk(chkid);
Packit df99a1
  GP<ByteStream> ibs=ByteStream::create(url,"rb");
Packit df99a1
  iff.copy(*ibs);
Packit df99a1
  iff.close_chunk();
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Internal headers for IW44
Packit df99a1
Packit df99a1
struct PrimaryHeader {
Packit df99a1
  unsigned char serial;
Packit df99a1
  unsigned char slices;
Packit df99a1
} primary;
Packit df99a1
Packit df99a1
struct SecondaryHeader {
Packit df99a1
  unsigned char major;
Packit df99a1
  unsigned char minor;
Packit df99a1
  unsigned char xhi, xlo;
Packit df99a1
  unsigned char yhi, ylo;
Packit df99a1
} secondary;
Packit df99a1
Packit df99a1
// -- Create and check FG44 chunk
Packit df99a1
Packit df99a1
void 
Packit df99a1
create_fg44_chunk(IFFByteStream &iff, const char *ckid, const GURL &url)
Packit df99a1
{
Packit df99a1
  GP<ByteStream> gbs=ByteStream::create(url,"rb");
Packit df99a1
  GP<IFFByteStream> gbsi=IFFByteStream::create(gbs);
Packit df99a1
  IFFByteStream &bsi=*gbsi;
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  bsi.get_chunk(chkid);
Packit df99a1
  if (chkid != "FORM:PM44" && chkid != "FORM:BM44")
Packit df99a1
    G_THROW("djvumake: FG44 file has incorrect format (wrong IFF header)");
Packit df99a1
  bsi.get_chunk(chkid);
Packit df99a1
  if (chkid!="PM44" && chkid!="BM44")
Packit df99a1
    G_THROW("djvumake: FG44 file has incorrect format (wring IFF header)");
Packit df99a1
  GP<ByteStream> gmbs=ByteStream::create();
Packit df99a1
  ByteStream &mbs=*gmbs;
Packit df99a1
  mbs.copy(*bsi.get_bytestream());
Packit df99a1
  bsi.close_chunk();  
Packit df99a1
  if (bsi.get_chunk(chkid))
Packit df99a1
    DjVuPrintErrorUTF8("%s","djvumake: FG44 file contains more than one chunk\n");
Packit df99a1
  bsi.close_chunk();  
Packit df99a1
  mbs.seek(0);
Packit df99a1
  if (mbs.readall((void*)&primary, sizeof(primary)) != sizeof(primary))
Packit df99a1
    G_THROW("djvumake: FG44 file is corrupted (cannot read primary header)");    
Packit df99a1
  if (primary.serial != 0)
Packit df99a1
    G_THROW("djvumake: FG44 file is corrupted (wrong serial number)");
Packit df99a1
  if (mbs.readall((void*)&secondary, sizeof(secondary)) != sizeof(secondary))
Packit df99a1
    G_THROW("djvumake: FG44 file is corrupted (cannot read secondary header)");    
Packit df99a1
  int iw = (secondary.xhi<<8) + secondary.xlo;
Packit df99a1
  int ih = (secondary.yhi<<8) + secondary.ylo;
Packit df99a1
  int red;
Packit df99a1
  for (red=1; red<=12; red++)
Packit df99a1
    if (iw==(w+red-1)/red && ih==(h+red-1)/red)
Packit df99a1
      break;
Packit df99a1
  flag_contains_fg = red;
Packit df99a1
  if (red>12)
Packit df99a1
    DjVuPrintErrorUTF8("%s","djvumake: FG44 subsampling is not in [1..12] range\n");
Packit df99a1
  mbs.seek(0);
Packit df99a1
  iff.put_chunk(ckid);
Packit df99a1
  iff.copy(mbs);
Packit df99a1
  iff.close_chunk();
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
// -- Create and check BG44 chunk
Packit df99a1
Packit df99a1
void 
Packit df99a1
create_bg44_chunk(IFFByteStream &iff, const char *ckid, GUTF8String filespec)
Packit df99a1
{
Packit df99a1
  static GP<IFFByteStream> bg44iff;
Packit df99a1
  if (! bg44iff)
Packit df99a1
    {
Packit df99a1
      if (flag_contains_bg)
Packit df99a1
        DjVuPrintErrorUTF8("%s","djvumake: Duplicate BGxx chunk\n");
Packit df99a1
      int i=filespec.rsearch(':');
Packit df99a1
      for (int j=i+1; i>0 && j<(int)filespec.length(); j++)
Packit df99a1
        if (filespec[j] < '0' || filespec[j] > '9')
Packit df99a1
          i = -1;
Packit df99a1
      if (!i)
Packit df99a1
        G_THROW("djvumake: no filename specified in first BG44 specification");
Packit df99a1
      GUTF8String filename=(i<0)?filespec:GUTF8String(filespec, i);
Packit df99a1
      const GURL::Filename::UTF8 url(filename);
Packit df99a1
      const GP<ByteStream> gbs(ByteStream::create(url,"rb"));
Packit df99a1
      if(!gbs)
Packit df99a1
      {
Packit df99a1
        G_THROW("djvumake: no such file as"+filename);
Packit df99a1
      }
Packit df99a1
      bg44iff = IFFByteStream::create(gbs);
Packit df99a1
      GUTF8String chkid;
Packit df99a1
      bg44iff->get_chunk(chkid);
Packit df99a1
      if (chkid != "FORM:PM44" && chkid != "FORM:BM44")
Packit df99a1
        G_THROW("djvumake: BG44 file has incorrect format (wrong IFF header)");        
Packit df99a1
      if (i>=0)
Packit df99a1
        filespec = i+1+(const char *)filespec;
Packit df99a1
      else 
Packit df99a1
        filespec = "99";
Packit df99a1
    }
Packit df99a1
  else
Packit df99a1
    {
Packit df99a1
      if (filespec.length() && filespec[0]!=':')
Packit df99a1
        G_THROW("djvumake: filename specified in BG44 refinement");
Packit df99a1
      filespec = 1+(const char *)filespec;
Packit df99a1
    }
Packit df99a1
  const char *s=filespec;
Packit df99a1
  int nchunks = strtol((char *)s, (char **)&s, 10);
Packit df99a1
  if (nchunks<1 || nchunks>99)
Packit df99a1
    G_THROW("djvumake: invalid number of chunks in BG44 specification");    
Packit df99a1
  if (*s)
Packit df99a1
    G_THROW("djvumake: invalid BG44 specification (syntax error)");
Packit df99a1
  
Packit df99a1
  int flag = (nchunks>=99);
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  while (nchunks-->0 && bg44iff->get_chunk(chkid))
Packit df99a1
    {
Packit df99a1
      if (chkid!="PM44" && chkid!="BM44")
Packit df99a1
        {
Packit df99a1
          DjVuPrintErrorUTF8("%s","djvumake: BG44 file contains unrecognized chunks (fixed)\n");
Packit df99a1
          nchunks += 1;
Packit df99a1
          bg44iff->close_chunk();
Packit df99a1
          continue;
Packit df99a1
        }
Packit df99a1
      GP<ByteStream> gmbs=ByteStream::create();
Packit df99a1
      ByteStream &mbs=*gmbs;
Packit df99a1
      mbs.copy(*(bg44iff->get_bytestream()));
Packit df99a1
      bg44iff->close_chunk();  
Packit df99a1
      mbs.seek(0);
Packit df99a1
      if (mbs.readall((void*)&primary, sizeof(primary)) != sizeof(primary))
Packit df99a1
        G_THROW("djvumake: BG44 file is corrupted (cannot read primary header)\n");    
Packit df99a1
      if (primary.serial == 0)
Packit df99a1
        {
Packit df99a1
          if (mbs.readall((void*)&secondary, sizeof(secondary)) != sizeof(secondary))
Packit df99a1
            G_THROW("djvumake: BG44 file is corrupted (cannot read secondary header)\n");    
Packit df99a1
          int iw = (secondary.xhi<<8) + secondary.xlo;
Packit df99a1
          int ih = (secondary.yhi<<8) + secondary.ylo;
Packit df99a1
          int red;
Packit df99a1
          for (red=1; red<=12; red++)
Packit df99a1
            if (iw==(w+red-1)/red && ih==(h+red-1)/red)
Packit df99a1
              break;
Packit df99a1
          flag_contains_bg = red;
Packit df99a1
          if (red>12)
Packit df99a1
            DjVuPrintErrorUTF8("%s","djvumake: BG44 subsampling is not in [1..12] range\n");
Packit df99a1
        }
Packit df99a1
      mbs.seek(0);
Packit df99a1
      iff.put_chunk(ckid);
Packit df99a1
      iff.copy(mbs);
Packit df99a1
      iff.close_chunk();
Packit df99a1
      flag = 1;
Packit df99a1
    }
Packit df99a1
  if (!flag)
Packit df99a1
    DjVuPrintErrorUTF8("%s","djvumake: no more chunks in BG44 file\n");
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
// -- Forward declarations
Packit df99a1
Packit df99a1
void processForeground(const GPixmap* image, const JB2Image *mask,
Packit df99a1
                       GPixmap& subsampled_image, GBitmap& subsampled_mask);
Packit df99a1
void processBackground(const GPixmap* image, const JB2Image *mask,
Packit df99a1
                       GPixmap& subsampled_image, GBitmap& subsampled_mask);
Packit df99a1
Packit df99a1
Packit df99a1
// -- Create both foreground and background by masking and subsampling
Packit df99a1
Packit df99a1
void 
Packit df99a1
create_masksub_chunks(IFFByteStream &iff, const GURL &url)
Packit df99a1
{
Packit df99a1
  // Check and load pixmap file
Packit df99a1
  if (!g().stencil)
Packit df99a1
    G_THROW("The use of a raw ppm image requires a stencil");
Packit df99a1
  GP<ByteStream> gibs=ByteStream::create(url, "rb");
Packit df99a1
  ByteStream &ibs=*gibs;
Packit df99a1
  GP<GPixmap> graw_pm=GPixmap::create(ibs);
Packit df99a1
  GPixmap &raw_pm=*graw_pm;
Packit df99a1
  if ((int) g().stencil->get_width() != (int) raw_pm.columns())
Packit df99a1
    G_THROW("Stencil and raw image have different widths!");
Packit df99a1
  if ((int) g().stencil->get_height() != (int) raw_pm.rows())
Packit df99a1
    G_THROW("Stencil and raw image have different heights!");
Packit df99a1
  // Encode foreground
Packit df99a1
  {
Packit df99a1
    GP<GPixmap> gfg_img=GPixmap::create();
Packit df99a1
    GPixmap &fg_img=*gfg_img;
Packit df99a1
    GP<GBitmap> fg_mask=GBitmap::create();
Packit df99a1
    processForeground(&raw_pm, g().stencil, fg_img, *fg_mask);
Packit df99a1
    GP<IW44Image> fg_pm = IW44Image::create_encode(fg_img, fg_mask, IW44Image::CRCBfull);
Packit df99a1
    IWEncoderParms parms[8];
Packit df99a1
    iff.put_chunk("FG44");
Packit df99a1
    parms[0].slices = 100;
Packit df99a1
    fg_pm->encode_chunk(iff.get_bytestream(), parms[0]);
Packit df99a1
    iff.close_chunk();
Packit df99a1
  }
Packit df99a1
  // Encode backgound 
Packit df99a1
  {
Packit df99a1
    GP<GPixmap> gbg_img=GPixmap::create();
Packit df99a1
    GPixmap &bg_img=*gbg_img;
Packit df99a1
    GP<GBitmap> bg_mask=GBitmap::create();
Packit df99a1
    processBackground(&raw_pm, g().stencil, bg_img, *bg_mask);
Packit df99a1
    GP<IW44Image> bg_pm = IW44Image::create_encode(bg_img, bg_mask, IW44Image::CRCBnormal);
Packit df99a1
    IWEncoderParms parms[4];
Packit df99a1
    parms[0].bytes = 10000;
Packit df99a1
    parms[0].slices = 74;
Packit df99a1
    iff.put_chunk("BG44");
Packit df99a1
    bg_pm->encode_chunk(iff.get_bytestream(), parms[0]);
Packit df99a1
    iff.close_chunk();
Packit df99a1
    parms[1].slices = 84;
Packit df99a1
    iff.put_chunk("BG44");
Packit df99a1
    bg_pm->encode_chunk(iff.get_bytestream(), parms[1]);
Packit df99a1
    iff.close_chunk();
Packit df99a1
    parms[2].slices = 90;
Packit df99a1
    iff.put_chunk("BG44");
Packit df99a1
    bg_pm->encode_chunk(iff.get_bytestream(), parms[2]);
Packit df99a1
    iff.close_chunk();
Packit df99a1
    parms[3].slices = 97;
Packit df99a1
    iff.put_chunk("BG44");
Packit df99a1
    bg_pm->encode_chunk(iff.get_bytestream(), parms[3]);
Packit df99a1
    iff.close_chunk();
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
const char *
Packit df99a1
parse_color_name(const char *s, char *rgb)
Packit df99a1
{
Packit df99a1
  static struct { 
Packit df99a1
    const char *name; 
Packit df99a1
    unsigned char r, g, b; 
Packit df99a1
  } stdcols[] = { 
Packit df99a1
    {"aqua",    0x00, 0xFF, 0xFF},
Packit df99a1
    {"black",   0x00, 0x00, 0x00},
Packit df99a1
    {"blue",    0x00, 0x00, 0xFF},
Packit df99a1
    {"fuchsia", 0xFF, 0x00, 0xFF},
Packit df99a1
    {"gray",    0x80, 0x80, 0x80},
Packit df99a1
    {"green",   0x00, 0x80, 0x00},
Packit df99a1
    {"lime",    0x00, 0xFF, 0x00},
Packit df99a1
    {"maroon",  0x80, 0x00, 0x00},
Packit df99a1
    {"navy",    0x00, 0x00, 0x80},
Packit df99a1
    {"olive",   0x80, 0x80, 0x00},
Packit df99a1
    {"purple",  0x80, 0x00, 0x80},
Packit df99a1
    {"red",     0xFF, 0x00, 0x00},
Packit df99a1
    {"silver",  0xC0, 0xC0, 0xC0},
Packit df99a1
    {"teal",    0x00, 0x80, 0x80},
Packit df99a1
    {"white",   0xFF, 0xFF, 0xFF},
Packit df99a1
    {"yellow",  0xFF, 0xFF, 0x00},
Packit df99a1
    {0}
Packit df99a1
  };
Packit df99a1
  // potential color names
Packit df99a1
  int len = 0;
Packit df99a1
  while (s[len] && s[len]!=':' && s[len]!='#')
Packit df99a1
    len += 1;
Packit df99a1
  GUTF8String name(s, len);
Packit df99a1
  name = name.downcase();
Packit df99a1
  for (int i=0; stdcols[i].name; i++)
Packit df99a1
    if (name == stdcols[i].name)
Packit df99a1
      {
Packit df99a1
        rgb[0] = stdcols[i].r;
Packit df99a1
        rgb[1] = stdcols[i].g;
Packit df99a1
        rgb[2] = stdcols[i].b;
Packit df99a1
        return s+len;
Packit df99a1
      }
Packit df99a1
  // potential hex specifications
Packit df99a1
  unsigned int r,g,b;
Packit df99a1
  if (sscanf(s,"%2x%2x%2x",&r,&g,&b) == 3)
Packit df99a1
    {
Packit df99a1
      rgb[0] = r;
Packit df99a1
      rgb[1] = g;
Packit df99a1
      rgb[2] = b;
Packit df99a1
      return s+6;
Packit df99a1
    }
Packit df99a1
  G_THROW("Unrecognized color name in FGbz chunk specification");
Packit df99a1
  return 0; // win
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
void
Packit df99a1
parse_color_zones(const char *s)
Packit df99a1
{
Packit df99a1
  bool fullpage = false;
Packit df99a1
  int zones = 0;
Packit df99a1
  g().colorzones.empty();
Packit df99a1
  g().colorpalette = ByteStream::create();
Packit df99a1
  // zones
Packit df99a1
  while (s[0] == '#')
Packit df99a1
    {
Packit df99a1
      char rgb[3];
Packit df99a1
      GRect rect;
Packit df99a1
      s = parse_color_name(s+1, rgb);
Packit df99a1
      if (s[0] == ':')
Packit df99a1
        {
Packit df99a1
          int c[4];
Packit df99a1
          for (int i=0; i<4; i++)
Packit df99a1
            {
Packit df99a1
              char *e = 0;
Packit df99a1
              c[i] = strtol(s+1, &e, 10);
Packit df99a1
              if (e <= s || (i>=2 && c[i]<0) || (i<3 && e[0]!=','))
Packit df99a1
                G_THROW("Invalid coordinates in FGbz chunk specification");
Packit df99a1
              s = e;
Packit df99a1
            }
Packit df99a1
          rect = GRect(c[0],c[1],c[2],c[3]);
Packit df99a1
        }
Packit df99a1
      if (rect.isempty())
Packit df99a1
        fullpage = true;
Packit df99a1
      g().colorpalette->writall(rgb, 3);
Packit df99a1
      g().colorzones.touch(zones);
Packit df99a1
      g().colorzones[zones] = rect;
Packit df99a1
      zones++;
Packit df99a1
    }
Packit df99a1
  if (s[0])
Packit df99a1
    G_THROW("Syntax error in FGbz chunk specification");
Packit df99a1
  // add extra black palette entry
Packit df99a1
  if (! fullpage)
Packit df99a1
    {
Packit df99a1
      char rgb[3] = {0,0,0};
Packit df99a1
      g().colorpalette->writall(rgb, 3);
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
// -- Main
Packit df99a1
Packit df99a1
int
Packit df99a1
main(int argc, 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
      // Print usage when called without enough arguments
Packit df99a1
      if (argc <= 2)
Packit df99a1
        usage();
Packit df99a1
      // Open djvu file
Packit df99a1
      remove(dargv[1]);
Packit df99a1
      GP<IFFByteStream> giff = 
Packit df99a1
        IFFByteStream::create(ByteStream::create(GURL::Filename::UTF8(dargv[1]),"wb"));
Packit df99a1
      IFFByteStream &iff=*giff;
Packit df99a1
      // Create header
Packit df99a1
      iff.put_chunk("FORM:DJVU", 1);
Packit df99a1
      // Check if shared dicts are present
Packit df99a1
      check_for_shared_dict(dargv);
Packit df99a1
      // Create information chunk
Packit df99a1
      create_info_chunk(iff, dargv);
Packit df99a1
      // Parse all arguments
Packit df99a1
      for (int i=2; i
Packit df99a1
        {
Packit df99a1
          if (!dargv[i].cmp("INFO=",5))
Packit df99a1
            {
Packit df99a1
              if (i>2)
Packit df99a1
                DjVuPrintErrorUTF8("%s","djvumake: 'INFO' chunk should appear first (ignored)\n");
Packit df99a1
            }
Packit df99a1
          else if (!dargv[i].cmp("Sjbz=",5))
Packit df99a1
            {
Packit df99a1
              if (flag_contains_stencil)
Packit df99a1
                DjVuPrintErrorUTF8("%s","djvumake: duplicate stencil chunk\n");
Packit df99a1
              create_jb2_chunk(iff, "Sjbz", GURL::Filename::UTF8(5+(const char *)dargv[i]));
Packit df99a1
              flag_contains_stencil = 1;
Packit df99a1
              if (flag_fg_needs_palette && blit_count >= 0)
Packit df99a1
                create_fgbz_chunk(iff);
Packit df99a1
              flag_fg_needs_palette = 0;
Packit df99a1
            }
Packit df99a1
          else if (!dargv[i].cmp("Smmr=",5))
Packit df99a1
            {
Packit df99a1
              create_mmr_chunk(iff, "Smmr", 
Packit df99a1
			       GURL::Filename::UTF8(5+(const char *)dargv[i]));
Packit df99a1
              if (flag_contains_stencil)
Packit df99a1
                DjVuPrintErrorUTF8("%s","djvumake: duplicate stencil chunk\n");
Packit df99a1
              flag_contains_stencil = 1;
Packit df99a1
            }
Packit df99a1
          else if (!dargv[i].cmp("FGbz=",5))
Packit df99a1
            {
Packit df99a1
              const char *c = 5 + (const char*)dargv[i];
Packit df99a1
              if (flag_contains_fg)
Packit df99a1
                DjVuPrintErrorUTF8("%s","djvumake: duplicate 'FGxx' chunk\n");
Packit df99a1
              if (c[0] != '#')
Packit df99a1
                {
Packit df99a1
                  create_raw_chunk(iff, "FGbz", GURL::Filename::UTF8(c));
Packit df99a1
                }
Packit df99a1
              else
Packit df99a1
                {
Packit df99a1
                  parse_color_zones(c);
Packit df99a1
                  if (flag_contains_stencil && blit_count >= 0)
Packit df99a1
                    create_fgbz_chunk(iff);
Packit df99a1
                  else
Packit df99a1
                    flag_fg_needs_palette = 1;
Packit df99a1
                }
Packit df99a1
              flag_contains_fg = 1;
Packit df99a1
            }
Packit df99a1
          else if (!dargv[i].cmp("FG44=",5))
Packit df99a1
            {
Packit df99a1
              if (flag_contains_fg)
Packit df99a1
                DjVuPrintErrorUTF8("%s","djvumake: duplicate 'FGxx' chunk\n");
Packit df99a1
              create_fg44_chunk(iff, "FG44", 
Packit df99a1
				GURL::Filename::UTF8(5+(const char *)dargv[i]));
Packit df99a1
            }
Packit df99a1
          else if (!dargv[i].cmp("BG44=",5))
Packit df99a1
            {
Packit df99a1
              create_bg44_chunk(iff, "BG44", 5+(const char *)dargv[i]);
Packit df99a1
            }
Packit df99a1
          else if (!dargv[i].cmp("BGjp=",5) ||
Packit df99a1
                   !dargv[i].cmp("BG2k=",5)  )
Packit df99a1
            {
Packit df99a1
              if (flag_contains_bg)
Packit df99a1
                DjVuPrintErrorUTF8("%s","djvumake: Duplicate BGxx chunk\n");
Packit df99a1
              GUTF8String chkid = dargv[i].substr(0,4);
Packit df99a1
              create_raw_chunk(iff, chkid, GURL::Filename::UTF8(5+(const char *)dargv[i]));
Packit df99a1
              flag_contains_bg = 1;
Packit df99a1
            }
Packit df99a1
          else if (!dargv[i].cmp("FGjp=",5) ||  !dargv[i].cmp("FG2k=",5))
Packit df99a1
            {
Packit df99a1
              if (flag_contains_fg)
Packit df99a1
                DjVuPrintErrorUTF8("%s","djvumake: duplicate 'FGxx' chunk\n");
Packit df99a1
              GUTF8String chkid = dargv[i].substr(0,4);
Packit df99a1
              create_raw_chunk(iff, chkid, GURL::Filename::UTF8(5+(const char *)dargv[i]));
Packit df99a1
              flag_contains_fg = 1;
Packit df99a1
            }
Packit df99a1
          else if (!dargv[i].cmp("INCL=",5))
Packit df99a1
            {
Packit df99a1
              create_incl_chunk(iff, "INCL", GURL::Filename::UTF8(5+(const char *)dargv[i]).fname());
Packit df99a1
              flag_contains_incl = 1;
Packit df99a1
            }
Packit df99a1
          else if (!dargv[i].cmp("PPM=",4))
Packit df99a1
            {
Packit df99a1
              if (flag_contains_bg || flag_contains_fg)
Packit df99a1
                DjVuPrintErrorUTF8("%s","djvumake: Duplicate 'FGxx' or 'BGxx' chunk\n");
Packit df99a1
              create_masksub_chunks(iff, GURL::Filename::UTF8(4+(const char *)dargv[i]));
Packit df99a1
              flag_contains_bg = 1;
Packit df99a1
              flag_contains_fg = 1;
Packit df99a1
            }
Packit df99a1
          else if (dargv[i].length() > 4 && dargv[i][4] == '=')
Packit df99a1
            {
Packit df99a1
              GNativeString chkid = dargv[i].substr(0,4);
Packit df99a1
              if (chkid != "TXTz" && chkid != "TXTa" 
Packit df99a1
                  && chkid != "ANTz" && chkid != "ANTa"
Packit df99a1
                  && chkid != "Djbz" )
Packit df99a1
                DjVuPrintErrorUTF8("djvumake: creating chunk of unknown type ``%s''.\n",
Packit df99a1
                                   (const char*)chkid);
Packit df99a1
              create_raw_chunk(iff, chkid, GURL::Filename::UTF8(5+(const char *)dargv[i]));
Packit df99a1
            }
Packit df99a1
          else 
Packit df99a1
            {
Packit df99a1
              DjVuPrintErrorUTF8("djvumake: illegal argument : ``%s'' (ignored)\n", 
Packit df99a1
                                 (const char *)dargv[i]);
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
      // Common cases for missing chunks
Packit df99a1
      if (flag_contains_stencil)
Packit df99a1
        {
Packit df99a1
          if (flag_contains_bg && ! flag_contains_fg)
Packit df99a1
            {  
Packit df99a1
              DjVuPrintErrorUTF8("%s","djvumake: generating black FGbz chunk\n");
Packit df99a1
              g().colorzones.empty();
Packit df99a1
              g().colorpalette = ByteStream::create();
Packit df99a1
              char rgb[3] = {0,0,0};
Packit df99a1
              g().colorpalette->writall(rgb, 3);
Packit df99a1
              create_fgbz_chunk(iff);
Packit df99a1
              flag_contains_fg = 1;
Packit df99a1
            }
Packit df99a1
          if (flag_contains_fg && !flag_contains_bg)
Packit df99a1
            {
Packit df99a1
              DjVuPrintErrorUTF8("%s","djvumake: generating white BG44 chunk\n");
Packit df99a1
              GPixel bgcolor = GPixel::WHITE;
Packit df99a1
              GP<GPixmap> inputsub=GPixmap::create((h+11)/12, (w+11)/12, &bgcolor);
Packit df99a1
              GP<IW44Image> iw = IW44Image::create_encode(*inputsub, 0, IW44Image::CRCBnone);
Packit df99a1
              IWEncoderParms iwparms;
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
              flag_contains_bg = 1;
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
      // Close
Packit df99a1
      iff.close_chunk();
Packit df99a1
      // Sanity checks
Packit df99a1
      if (flag_contains_stencil)
Packit df99a1
        {
Packit df99a1
          // Compound or Bilevel
Packit df99a1
          if (flag_contains_bg && ! flag_contains_fg)
Packit df99a1
            DjVuPrintErrorUTF8("%s","djvumake: djvu file contains a BGxx chunk but no FGxx chunk\n");
Packit df99a1
          if (flag_contains_fg && ! flag_contains_bg)
Packit df99a1
            DjVuPrintErrorUTF8("%s","djvumake: djvu file contains a FGxx chunk but no BGxx chunk\n");
Packit df99a1
        }
Packit df99a1
      else if (flag_contains_bg)
Packit df99a1
        {
Packit df99a1
          // Photo DjVu Image
Packit df99a1
          if (flag_contains_bg!=1)
Packit df99a1
            DjVuPrintErrorUTF8("%s","djvumake: photo djvu image has subsampled BGxx chunk\n"); 
Packit df99a1
          if (flag_fg_needs_palette)
Packit df99a1
            DjVuPrintErrorUTF8("%s","djvumake: could not generate FGbz chunk, as stencil is not available\n");
Packit df99a1
          else if (flag_contains_fg)
Packit df99a1
            DjVuPrintErrorUTF8("%s","djvumake: photo djvu file contains FGxx chunk\n");            
Packit df99a1
        }
Packit df99a1
      else
Packit df99a1
        DjVuPrintErrorUTF8("%s","djvumake: djvu file contains neither Sxxx nor BGxx chunks\n");
Packit df99a1
    }
Packit df99a1
  G_CATCH(ex)
Packit df99a1
    {
Packit df99a1
      remove(dargv[1]);
Packit df99a1
      ex.perror();
Packit df99a1
      exit(1);
Packit df99a1
    }
Packit df99a1
  G_ENDCATCH;
Packit df99a1
  return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
////////////////////////////////////////
Packit df99a1
// MASKING AND SUBSAMPLING
Packit df99a1
////////////////////////////////////////
Packit df99a1
Packit df99a1
Packit df99a1
// -- Returns a dilated version of a bitmap with the same size
Packit df99a1
Packit df99a1
static GP<GBitmap> 
Packit df99a1
dilate8(const GBitmap *p_bm)
Packit df99a1
{
Packit df99a1
  const GBitmap& bm = *p_bm;
Packit df99a1
  GP<GBitmap> p_newbm = GBitmap::create(bm.rows(),bm.columns(),1);
Packit df99a1
  GBitmap& newbm = *p_newbm;
Packit df99a1
  int nrows = bm.rows();
Packit df99a1
  int ncols = bm.columns();
Packit df99a1
  for(int y=0; y
Packit df99a1
    {
Packit df99a1
      const unsigned char *bmrow = bm[y];
Packit df99a1
      unsigned char *nbmprow = (y-1>=0) ? newbm[y-1] : 0;
Packit df99a1
      unsigned char *nbmrow = newbm[y];
Packit df99a1
      unsigned char *nbmnrow = (y+1
Packit df99a1
      for(int x=0; x
Packit df99a1
        {
Packit df99a1
          if(bmrow[x]) 
Packit df99a1
            {
Packit df99a1
              // Set all the 8-neighborhood to black
Packit df99a1
              if (nbmprow)
Packit df99a1
                {
Packit df99a1
                  nbmprow[x-1]=1;
Packit df99a1
                  nbmprow[x]=1;
Packit df99a1
                  nbmprow[x+1]=1;
Packit df99a1
                }
Packit df99a1
              nbmrow[x-1]=1;
Packit df99a1
              nbmrow[x]=1;
Packit df99a1
              nbmrow[x+1]=1;
Packit df99a1
              if (nbmnrow)
Packit df99a1
                {
Packit df99a1
                  nbmnrow[x-1]=1;
Packit df99a1
                  nbmnrow[x]=1;
Packit df99a1
                  nbmnrow[x+1]=1;
Packit df99a1
                }
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  return p_newbm;
Packit df99a1
}
Packit df99a1
Packit df99a1
// -- Returns a smaller eroded version of a bitmap
Packit df99a1
Packit df99a1
static GP<GBitmap> 
Packit df99a1
erode8(const GBitmap *p_bm)
Packit df99a1
{
Packit df99a1
  const GBitmap& bm = *p_bm;
Packit df99a1
  int newnrows = bm.rows()-2;
Packit df99a1
  int newncolumns = bm.columns()-2;
Packit df99a1
  if(newnrows<=0 || newncolumns<=0) // then return an empty GBitmap 
Packit df99a1
    return GBitmap::create();
Packit df99a1
  GP<GBitmap> p_newbm = GBitmap::create(newnrows,newncolumns); 
Packit df99a1
  GBitmap& newbm = *p_newbm;
Packit df99a1
  for(int y=0; y
Packit df99a1
    {
Packit df99a1
      for(int x=0; x
Packit df99a1
        {
Packit df99a1
          // Check if there's a white pixel in the 8-neighborhood
Packit df99a1
          if(   !( bm[y  ][x] && bm[y  ][x+1] && bm[y  ][x+2]
Packit df99a1
                && bm[y+1][x] && bm[y+1][x+1] && bm[y+1][x+2]
Packit df99a1
                && bm[y+2][x] && bm[y+2][x+1] && bm[y+2][x+2]))
Packit df99a1
            newbm[y][x] = 0; // then set current to white
Packit df99a1
          else
Packit df99a1
            newbm[y][x] = 1; // else set current to black
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  return p_newbm;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Returns a smaller eroded version of a jb2image
Packit df99a1
Packit df99a1
GP<JB2Image> 
Packit df99a1
erode8(const JB2Image *im)
Packit df99a1
{
Packit df99a1
  int i;
Packit df99a1
  GP<JB2Image> newim = JB2Image::create();
Packit df99a1
  newim->set_dimension(im->get_width(),im->get_height());
Packit df99a1
  for(i=0; i<im->get_shape_count(); i++)
Packit df99a1
    {
Packit df99a1
      const JB2Shape &shape = im->get_shape(i);
Packit df99a1
      JB2Shape newshape;
Packit df99a1
      newshape.parent = shape.parent;
Packit df99a1
      if (shape.bits) 
Packit df99a1
        newshape.bits = erode8(shape.bits);
Packit df99a1
      else
Packit df99a1
        newshape.bits = 0;
Packit df99a1
      newim->add_shape(newshape);
Packit df99a1
    }
Packit df99a1
  for(i=0; i<im->get_blit_count(); i++)
Packit df99a1
    {
Packit df99a1
      const JB2Blit* blit = im->get_blit(i);
Packit df99a1
      JB2Blit newblit;
Packit df99a1
      newblit.bottom = blit->bottom + 1;
Packit df99a1
      newblit.left = blit->left + 1;
Packit df99a1
      newblit.shapeno = blit->shapeno;
Packit df99a1
      newim->add_blit(newblit);
Packit df99a1
    }
Packit df99a1
  return newim;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// Subsamples only the pixels of <image> that are not masked (<mask>).  This
Packit df99a1
// call resizes and fills the resulting <subsampled_image> and
Packit df99a1
// <subsampled_mask>.  Their dimension is the dimension of the original
Packit df99a1
// <image> divided by <gridwidth> and rounded to the superior integer.  For
Packit df99a1
// each square grid (gridwidth times gridwidth) of the subsampling mesh that
Packit df99a1
// contains at least <minpixels> non-masked pixels, their value is averaged to
Packit df99a1
// give the value of the corresponding <subsampled_image> pixel, and the
Packit df99a1
// <subsampled_mask> is cleared at this position.  If <inverted_mask> is true,
Packit df99a1
// then pixels are considered to be masked when mask==0
Packit df99a1
 
Packit df99a1
Packit df99a1
static void
Packit df99a1
maskedSubsample(const GPixmap* img,
Packit df99a1
                const GBitmap *p_mask,
Packit df99a1
                GPixmap& subsampled_image,
Packit df99a1
                GBitmap& subsampled_mask,
Packit df99a1
                int gridwidth, int inverted_mask, 
Packit df99a1
                int minpixels=1
Packit df99a1
                )
Packit df99a1
{
Packit df99a1
  const GPixmap& image= *img;
Packit df99a1
  const GBitmap& mask = *p_mask;
Packit df99a1
  int imageheight = image.rows();
Packit df99a1
  int imagewidth = image.columns();
Packit df99a1
  // compute the size of the resulting subsampled image
Packit df99a1
  int subheight = imageheight/gridwidth;
Packit df99a1
  if(imageheight%gridwidth)
Packit df99a1
    subheight++;
Packit df99a1
  int subwidth = imagewidth/gridwidth;
Packit df99a1
  if(imagewidth%gridwidth)
Packit df99a1
    subwidth++;
Packit df99a1
  // set the sizes unless in incremental mode
Packit df99a1
  subsampled_image.init(subheight, subwidth);
Packit df99a1
  subsampled_mask.init(subheight, subwidth);
Packit df99a1
  // go subsampling
Packit df99a1
  int row, col;  // row and col in the subsampled image
Packit df99a1
  int posx, posxend, posy, posyend; // corresponding square in the original image
Packit df99a1
  for(row=0, posy=0; row
Packit df99a1
    {
Packit df99a1
      GPixel* subsampled_image_row = subsampled_image[row]; // row row of subsampled image
Packit df99a1
      unsigned char* subsampled_mask_row = subsampled_mask[row]; // row row of subsampled mask
Packit df99a1
      posyend = posy+gridwidth;
Packit df99a1
      if(posyend>imageheight)
Packit df99a1
        posyend = imageheight;
Packit df99a1
      for(col=0, posx=0; col
Packit df99a1
        {
Packit df99a1
          posxend = posx+gridwidth;
Packit df99a1
          if(posxend>imagewidth)
Packit df99a1
            posxend = imagewidth;
Packit df99a1
          int count = 0;
Packit df99a1
          int r = 0;
Packit df99a1
          int g = 0;
Packit df99a1
          int b = 0;
Packit df99a1
          for(int y=posy; y
Packit df99a1
            {
Packit df99a1
              const unsigned char* mask_y = mask[y]; // Row y of the mask
Packit df99a1
              for(int x=posx; x
Packit df99a1
                {
Packit df99a1
                  unsigned char masked = (inverted_mask ? !mask_y[x] :mask_y[x]);
Packit df99a1
                  if(!masked)
Packit df99a1
                    {
Packit df99a1
                      GPixel p = image[y][x];
Packit df99a1
                      r += p.r;
Packit df99a1
                      g += p.g;
Packit df99a1
                      b += p.b;
Packit df99a1
                      count ++;
Packit df99a1
                    }
Packit df99a1
                }
Packit df99a1
            }
Packit df99a1
          /* minpixels pixels are enough to give the color */
Packit df99a1
          /* so set it, and do not mask this point */
Packit df99a1
          if(count >= minpixels)   
Packit df99a1
            {
Packit df99a1
              GPixel p;
Packit df99a1
              p.r = r/count;
Packit df99a1
              p.g = g/count;
Packit df99a1
              p.b = b/count;
Packit df99a1
              subsampled_image_row[col] = p;
Packit df99a1
              subsampled_mask_row[col] = 0;
Packit df99a1
            } 
Packit df99a1
          else /* make it bright red and masked */ 
Packit df99a1
            {
Packit df99a1
              subsampled_image_row[col] = GPixel::RED;
Packit df99a1
              subsampled_mask_row[col] = 1;
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Computes foreground image and mask
Packit df99a1
Packit df99a1
void 
Packit df99a1
processForeground(const GPixmap* image, const JB2Image *mask,
Packit df99a1
                  GPixmap& subsampled_image, GBitmap& subsampled_mask)
Packit df99a1
{
Packit df99a1
  GP<JB2Image> eroded_mask = erode8(mask);
Packit df99a1
  maskedSubsample(image, eroded_mask->get_bitmap(), 
Packit df99a1
                  subsampled_image, subsampled_mask, 
Packit df99a1
                  6, 1);   // foreground subsample is 6 (300dpi->50dpi)
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Computes background image and mask
Packit df99a1
Packit df99a1
void 
Packit df99a1
processBackground(const GPixmap* image, const JB2Image *mask,
Packit df99a1
                  GPixmap& subsampled_image, GBitmap& subsampled_mask)
Packit df99a1
{
Packit df99a1
  GP<GBitmap> b = mask->get_bitmap();
Packit df99a1
  b = dilate8(b);
Packit df99a1
  b = dilate8(b);
Packit df99a1
  maskedSubsample(image, b, subsampled_image, subsampled_mask, 3, 0);
Packit df99a1
}
Packit df99a1
Packit df99a1