Blame tools/djvused.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
Packit df99a1
#include "IW44Image.h"
Packit df99a1
#include "GOS.h"
Packit df99a1
#include "GString.h"
Packit df99a1
#include "DjVuDocEditor.h"
Packit df99a1
#include "DjVuDumpHelper.h"
Packit df99a1
#include "BSByteStream.h"
Packit df99a1
#include "DjVuText.h"
Packit df99a1
#include "DjVuAnno.h"
Packit df99a1
#include "DjVuInfo.h"
Packit df99a1
#include "IFFByteStream.h"
Packit df99a1
#include "DataPool.h"
Packit df99a1
#include "DjVuPort.h"
Packit df99a1
#include "DjVuFile.h"
Packit df99a1
#include "DjVmNav.h"
Packit df99a1
#include "common.h"
Packit df99a1
Packit df99a1
static bool modified = false;
Packit df99a1
static bool verbose = false;
Packit df99a1
static bool save = false;
Packit df99a1
static bool nosave = false;
Packit df99a1
static bool utf8 = false;
Packit df99a1
Packit df99a1
static unsigned char utf8bom[] = { 0xef, 0xbb, 0xbf };
Packit df99a1
Packit df99a1
struct DJVUSEDGlobal 
Packit df99a1
{
Packit df99a1
  // Globals that need static initialization
Packit df99a1
  // are grouped here to work around broken compilers.
Packit df99a1
  GUTF8String djvufile;
Packit df99a1
  GP<ByteStream> cmdbs;
Packit df99a1
  GP<DjVuDocEditor> doc;
Packit df99a1
  GPList<DjVmDir::File> selected;
Packit df99a1
  GP<DjVuFile> file;
Packit df99a1
  GUTF8String fileid;
Packit df99a1
};
Packit df99a1
Packit df99a1
static DJVUSEDGlobal& g(void)
Packit df99a1
{
Packit df99a1
  static DJVUSEDGlobal g;
Packit df99a1
  return g;
Packit df99a1
}
Packit df99a1
Packit df99a1
static GUTF8String 
Packit df99a1
ToNative(GUTF8String s)
Packit df99a1
{
Packit df99a1
  if (utf8)
Packit df99a1
    return s;
Packit df99a1
  // fake the damn GUTF8/GNative type check
Packit df99a1
  GNativeString n = s;
Packit df99a1
  return GUTF8String((const char*)n);
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
// --------------------------------------------------
Packit df99a1
// PARSING BYTESTREAM
Packit df99a1
// --------------------------------------------------
Packit df99a1
Packit df99a1
// -- A bytestream that performs buffering and 
Packit df99a1
//    offers a stdio-like interface for reading files.
Packit df99a1
Packit df99a1
class ParsingByteStream : public ByteStream 
Packit df99a1
{
Packit df99a1
private:
Packit df99a1
  enum { bufsize=512 };
Packit df99a1
  const GP<ByteStream> &gbs;
Packit df99a1
  ByteStream &bs;
Packit df99a1
  unsigned char buffer[bufsize];
Packit df99a1
  int  bufpos;
Packit df99a1
  int  bufend;
Packit df99a1
  bool goteof;
Packit df99a1
  ParsingByteStream(const GP<ByteStream> &gbs;;
Packit df99a1
  int getbom(int c);
Packit df99a1
public:
Packit df99a1
  static GP<ParsingByteStream> create(const GP<ByteStream> &gbs) 
Packit df99a1
  { return new ParsingByteStream(gbs); }
Packit df99a1
  size_t read(void *buffer, size_t size);
Packit df99a1
  size_t write(const void *buffer, size_t size);
Packit df99a1
  long int tell() const;
Packit df99a1
  int eof();
Packit df99a1
  int unget(int c);
Packit df99a1
  inline int get();
Packit df99a1
  int get_spaces(bool skipseparator=false);
Packit df99a1
  GUTF8String get_token(bool skipseparator=false, bool compat=false);
Packit df99a1
  const char *get_error_context(int c=EOF);
Packit df99a1
};
Packit df99a1
Packit df99a1
ParsingByteStream::ParsingByteStream(const GP<ByteStream> &xgbs)
Packit df99a1
  : gbs(xgbs),bs(*gbs), bufpos(1), bufend(1), goteof(false)
Packit df99a1
{ 
Packit df99a1
}
Packit df99a1
Packit df99a1
int 
Packit df99a1
ParsingByteStream::eof() // aka. feof
Packit df99a1
{
Packit df99a1
  if (bufpos < bufend) 
Packit df99a1
    return false;
Packit df99a1
  if (goteof)
Packit df99a1
    return true;
Packit df99a1
  bufend = bufpos = 1;
Packit df99a1
  while (bs.read(buffer+bufend,1) && ++bufend<(int)bufsize)
Packit df99a1
    if (buffer[bufend-1]=='\r' || buffer[bufend-1]=='\n')
Packit df99a1
      break;
Packit df99a1
  if (bufend == bufpos)
Packit df99a1
    goteof = true;
Packit df99a1
  return goteof;
Packit df99a1
}
Packit df99a1
Packit df99a1
size_t 
Packit df99a1
ParsingByteStream::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
ParsingByteStream::write(const void *, size_t )
Packit df99a1
{
Packit df99a1
  G_THROW("Cannot write() into a ParsingByteStream");
Packit df99a1
  return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
long int
Packit df99a1
ParsingByteStream::tell() const
Packit df99a1
{ 
Packit df99a1
  G_THROW("Cannot tell() a ParsingByteStream");
Packit df99a1
  return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
int 
Packit df99a1
ParsingByteStream::getbom(int c)
Packit df99a1
{
Packit df99a1
  int i = 0;
Packit df99a1
  while (c == utf8bom[i++])
Packit df99a1
    {
Packit df99a1
      if (i >= 3)
Packit df99a1
        i = 0;
Packit df99a1
      if (bufpos < bufend || !eof())
Packit df99a1
        c = buffer[bufpos++];
Packit df99a1
    }
Packit df99a1
  while (--i > 0)
Packit df99a1
    {
Packit df99a1
      unget(c);
Packit df99a1
      c = utf8bom[i-1];
Packit df99a1
    }
Packit df99a1
  return c;
Packit df99a1
}
Packit df99a1
Packit df99a1
inline int 
Packit df99a1
ParsingByteStream::get() // like getc() skipping bom.
Packit df99a1
{
Packit df99a1
  int c = EOF;
Packit df99a1
  if (bufpos < bufend || !eof())
Packit df99a1
    c = buffer[bufpos++];
Packit df99a1
  if (c == utf8bom[0])
Packit df99a1
    return getbom(c);
Packit df99a1
  return c;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
int  
Packit df99a1
ParsingByteStream::unget(int c) // like 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
int
Packit df99a1
ParsingByteStream::get_spaces(bool skipseparator)
Packit df99a1
{
Packit df99a1
   int c = get();
Packit df99a1
   while (c==' ' || c=='\t' || c=='\r' 
Packit df99a1
          || c=='\n' || c=='#' || c==';' )
Packit df99a1
     {
Packit df99a1
       if (c == '#')
Packit df99a1
         do { c=get(); } while (c!=EOF && c!='\n' && c!='\r');
Packit df99a1
       if (!skipseparator && (c=='\n' || c=='\r' || c==';'))
Packit df99a1
         break;
Packit df99a1
       c = get();
Packit df99a1
     }
Packit df99a1
   return c;
Packit df99a1
}
Packit df99a1
  
Packit df99a1
const char *
Packit df99a1
ParsingByteStream::get_error_context(int c)
Packit df99a1
{
Packit df99a1
  static char buffer[22];
Packit df99a1
  unget(c);
Packit df99a1
  int len = read((void*)buffer, sizeof(buffer)-1);
Packit df99a1
  buffer[(len>0)?len:0] = 0;
Packit df99a1
  for (int i=0; i
Packit df99a1
    if (buffer[i]=='\n')
Packit df99a1
      buffer[i] = 0;
Packit df99a1
  return buffer;
Packit df99a1
}
Packit df99a1
Packit df99a1
GUTF8String
Packit df99a1
ParsingByteStream::get_token(bool skipseparator, bool compat)
Packit df99a1
{
Packit df99a1
  GUTF8String str;
Packit df99a1
  int c = get_spaces(skipseparator);
Packit df99a1
  if (c == EOF)
Packit df99a1
    {
Packit df99a1
      return str;
Packit df99a1
    }
Packit df99a1
  if (!skipseparator && (c=='\n' || c=='\r' || c==';'))
Packit df99a1
    {
Packit df99a1
      unget(c);
Packit df99a1
      return str;
Packit df99a1
    }
Packit df99a1
  if (c != '\"' && c != '\'') 
Packit df99a1
    {
Packit df99a1
      while (c!=' ' && c!='\t' && c!='\r' && c!=';'
Packit df99a1
             && c!='\n' && c!='#' && c!=EOF)
Packit df99a1
        {
Packit df99a1
          str += c;
Packit df99a1
          c = get();
Packit df99a1
        }
Packit df99a1
      unget(c);
Packit df99a1
    }
Packit df99a1
  else 
Packit df99a1
    {
Packit df99a1
      int delim = c;
Packit df99a1
      c = get();
Packit df99a1
      while (c != delim && c!=EOF) 
Packit df99a1
        {
Packit df99a1
          if (c == '\\') 
Packit df99a1
            {
Packit df99a1
              c = get();
Packit df99a1
              if (compat && c!='\"')
Packit df99a1
                {
Packit df99a1
                  str += '\\';
Packit df99a1
                }
Packit df99a1
              else if (c>='0' && c<='7')
Packit df99a1
                {
Packit df99a1
                  int x = 0;
Packit df99a1
                  { // extra nesting for windows
Packit df99a1
                    for (int i=0; i<3 && c>='0' && c<='7'; i++) 
Packit df99a1
                    {
Packit df99a1
                      x = x * 8 + c - '0';
Packit df99a1
                      c = get();
Packit df99a1
                    }
Packit df99a1
                  }
Packit df99a1
                  unget(c);
Packit df99a1
                  c = x;
Packit df99a1
                }
Packit df99a1
              else 
Packit df99a1
                {
Packit df99a1
                  const char *tr1 = "tnrbfva";
Packit df99a1
                  const char *tr2 = "\t\n\r\b\f\013\007";
Packit df99a1
                  { // extra nesting for windows
Packit df99a1
                    for (int i=0; tr1[i]; i++)
Packit df99a1
                    {
Packit df99a1
                      if (c == tr1[i])
Packit df99a1
                        c = tr2[i];
Packit df99a1
                    }
Packit df99a1
                  }
Packit df99a1
                }
Packit df99a1
            }
Packit df99a1
          if (c != EOF)
Packit df99a1
            str += c;
Packit df99a1
          c = get();
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  return str;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// --------------------------------------------------
Packit df99a1
// COMMANDS
Packit df99a1
// --------------------------------------------------
Packit df99a1
Packit df99a1
Packit df99a1
static void
Packit df99a1
vprint(const char *fmt, ... )
Packit df99a1
#ifdef __GNUC__
Packit df99a1
  __attribute__ ((format (printf, 1, 2)));
Packit df99a1
static void
Packit df99a1
vprint(const char *fmt, ... )
Packit df99a1
#endif
Packit df99a1
{
Packit df99a1
  if (verbose)
Packit df99a1
    {
Packit df99a1
      GUTF8String msg("");
Packit df99a1
      va_list args;
Packit df99a1
      va_start(args, fmt);
Packit df99a1
      msg.vformat(fmt, args);
Packit df99a1
      fprintf(stderr,"djvused: %s\n", (const char*)ToNative(msg));
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
verror(const char *fmt, ... )
Packit df99a1
#ifdef __GNUC__
Packit df99a1
  __attribute__ ((format (printf, 1, 2)));
Packit df99a1
static void
Packit df99a1
verror(const char *fmt, ... )
Packit df99a1
#endif
Packit df99a1
{
Packit df99a1
  GUTF8String msg;
Packit df99a1
  va_list args;
Packit df99a1
  va_start(args, fmt);
Packit df99a1
  msg.vformat(fmt, args);
Packit df99a1
  G_THROW((const char*)ToNative(msg));
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
get_data_from_file(const char *cmd, ParsingByteStream &pbs, ByteStream &out)
Packit df99a1
{
Packit df99a1
  GUTF8String fname = pbs.get_token();
Packit df99a1
Packit df99a1
  if (! fname)
Packit df99a1
    {
Packit df99a1
      vprint("%s: enter data and terminate with a period on a single line", cmd);
Packit df99a1
      int c = pbs.get_spaces(true);
Packit df99a1
      pbs.unget(c);
Packit df99a1
      char skip[4];
Packit df99a1
      char term0[4] = "\n.\n";
Packit df99a1
      char term1[4] = "\r.\r";
Packit df99a1
      char *s = skip;
Packit df99a1
      int state = 1;
Packit df99a1
      while (state < 3) 
Packit df99a1
        {
Packit df99a1
          c = pbs.get();
Packit df99a1
          if (c == EOF)
Packit df99a1
            break;
Packit df99a1
          if ( c == term0[state] || c == term1[state] )
Packit df99a1
            {
Packit df99a1
              state += 1;
Packit df99a1
              *s++ = c;
Packit df99a1
            }
Packit df99a1
          else
Packit df99a1
            {
Packit df99a1
              { // extra nesting for windows
Packit df99a1
                for (char *m=skip; m
Packit df99a1
                  out.write8(*m);
Packit df99a1
              }
Packit df99a1
              s = skip;
Packit df99a1
              state = 0;
Packit df99a1
              if (c == '\n')
Packit df99a1
                pbs.unget(c);
Packit df99a1
              else if (c != EOF)
Packit df99a1
                out.write8(c);
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
      pbs.unget(c);
Packit df99a1
    }
Packit df99a1
  else
Packit df99a1
    {
Packit df99a1
      GP<ByteStream> in=ByteStream::create(GURL::Filename::UTF8(fname),"rb");
Packit df99a1
      out.copy(*in);
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
static bool
Packit df99a1
char_unquoted(unsigned char c, bool eightbit)
Packit df99a1
{
Packit df99a1
  if (eightbit && c>=0x80)
Packit df99a1
    return true;
Packit df99a1
  if (c==0x7f || c=='\"' || c=='\\')
Packit df99a1
    return false;
Packit df99a1
  if (c>=0x20 && c<0x7f)
Packit df99a1
    return true;
Packit df99a1
  return false;
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
print_c_string(const char *data, int length, ByteStream &out, bool eightbit)
Packit df99a1
{
Packit df99a1
  out.write("\"",1);
Packit df99a1
  while (*data && length>0) 
Packit df99a1
    {
Packit df99a1
      int span = 0;
Packit df99a1
      while (span
Packit df99a1
        span++;
Packit df99a1
      if (span > 0) 
Packit df99a1
        {
Packit df99a1
          out.write(data, span);
Packit df99a1
          data += span;
Packit df99a1
          length -= span;
Packit df99a1
        }
Packit df99a1
      else
Packit df99a1
        {
Packit df99a1
          char buf[5];
Packit df99a1
          static const char *tr1 = "\"\\tnrbf";
Packit df99a1
          static const char *tr2 = "\"\\\t\n\r\b\f";
Packit df99a1
          sprintf(buf,"\\%03o", (int)(((unsigned char*)data)[0]));
Packit df99a1
          { // extra nesting for windows
Packit df99a1
            for (int i=0; tr2[i]; i++)
Packit df99a1
              if (*(char*)data == tr2[i])
Packit df99a1
                buf[1] = tr1[i];
Packit df99a1
          }
Packit df99a1
          if (buf[1]<'0' || buf[1]>'3')
Packit df99a1
            buf[2] = 0;
Packit df99a1
          out.write(buf, ((buf[2]) ? 4 : 2));
Packit df99a1
          data += 1;
Packit df99a1
          length -= 1;
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  out.write("\"",1);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_ls(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  int pagenum = 0;
Packit df99a1
  GPList<DjVmDir::File> lst = g().doc->get_djvm_dir()->get_files_list();
Packit df99a1
  { // extra nesting for windows
Packit df99a1
    for (GPosition p=lst; p; ++p) 
Packit df99a1
    {
Packit df99a1
      GP<DjVmDir::File> f = lst[p];
Packit df99a1
      if (f->is_page())
Packit df99a1
        fprintf(stdout,"%4d P ", ++pagenum);
Packit df99a1
      else if (f->is_include())
Packit df99a1
        fprintf(stdout,"     I ");
Packit df99a1
      else if (f->is_thumbnails())
Packit df99a1
        continue;
Packit df99a1
      else if (f->is_shared_anno())
Packit df99a1
        fprintf(stdout,"     A ");
Packit df99a1
      else
Packit df99a1
        fprintf(stdout,"     ? ");
Packit df99a1
      GUTF8String id = f->get_load_name();
Packit df99a1
      fprintf(stdout,"%8d  %s", f->size, (const char*)ToNative(id));
Packit df99a1
      GUTF8String name = f->get_save_name();
Packit df99a1
      if (name != id)
Packit df99a1
        fprintf(stdout," F=%s", (const char*)ToNative(name));
Packit df99a1
      GUTF8String title = f->get_title();
Packit df99a1
      if (title != id && f->is_page())
Packit df99a1
        fprintf(stdout," T=%s", (const char*)ToNative(title));
Packit df99a1
      fprintf(stdout,"\n");
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  if (g().doc->get_thumbnails_num() == g().doc->get_pages_num())
Packit df99a1
    fprintf(stdout,"     T %8s  %s\n", "", "<thumbnails>");
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_n(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  int pagenum = 0;
Packit df99a1
  GPList<DjVmDir::File> lst = g().doc->get_djvm_dir()->get_files_list();
Packit df99a1
  { // extra nesting for windows
Packit df99a1
    for (GPosition p=lst; p; ++p) 
Packit df99a1
    {
Packit df99a1
      GP<DjVmDir::File> f = lst[p];
Packit df99a1
      if (f->is_page())
Packit df99a1
        ++pagenum;
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  fprintf(stdout,"%d\n", pagenum); 
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_dump(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  GP<DataPool> pool;
Packit df99a1
  // Need to be modified to handle "selected" list.
Packit df99a1
  if (g().file)
Packit df99a1
    pool = g().file->get_djvu_data(false, false);
Packit df99a1
  else
Packit df99a1
    pool = g().doc->get_init_data_pool();
Packit df99a1
  DjVuDumpHelper helper;
Packit df99a1
  GP<ByteStream> bs = helper.dump(pool);
Packit df99a1
  size_t size = bs->size();
Packit df99a1
  GUTF8String str;
Packit df99a1
  char *buf = str.getbuf(size);
Packit df99a1
  bs->seek(0);
Packit df99a1
  bs->readall(buf, size);
Packit df99a1
  GUTF8String ns = ToNative(str);
Packit df99a1
  GP<ByteStream> obs=ByteStream::create("w");
Packit df99a1
  obs->writall((const char*)ns, ns.length());
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
print_size(const GP<DjVuFile> &file)
Packit df99a1
{
Packit df99a1
  GP<DjVuInfo> info = file->info;
Packit df99a1
  if (! info)
Packit df99a1
    {
Packit df99a1
      const GP<ByteStream> pbs(file->get_djvu_bytestream(false, false));
Packit df99a1
      const GP<IFFByteStream> iff(IFFByteStream::create(pbs));
Packit df99a1
      GUTF8String chkid;
Packit df99a1
      if (! iff->get_chunk(chkid))
Packit df99a1
        verror("Selected file contains no data");
Packit df99a1
      if (chkid == "FORM:DJVU")
Packit df99a1
        {
Packit df99a1
          while (iff->get_chunk(chkid) && chkid!="INFO")
Packit df99a1
            iff->close_chunk();
Packit df99a1
          if (chkid == "INFO")
Packit df99a1
            { 
Packit df99a1
              info = DjVuInfo::create();
Packit df99a1
              info->decode(*iff->get_bytestream());
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
      else if (chkid == "FORM:BM44" || chkid == "FORM:PM44")
Packit df99a1
        {
Packit df99a1
          while (iff->get_chunk(chkid) && chkid!="BM44" && chkid!="PM44")
Packit df99a1
            iff->close_chunk();
Packit df99a1
          if (chkid=="BM44" || chkid=="PM44")
Packit df99a1
            {
Packit df99a1
              GP<IW44Image> junk=IW44Image::create_decode(IW44Image::COLOR);
Packit df99a1
              junk->decode_chunk(iff->get_bytestream());
Packit df99a1
              fprintf(stdout,"width=%d height=%d\n", 
Packit df99a1
                      junk->get_width(), junk->get_height());
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  if (info)
Packit df99a1
    {
Packit df99a1
      fprintf(stdout,"width=%d height=%d", info->width, info->height);
Packit df99a1
      if (info->orientation)
Packit df99a1
        fprintf(stdout, " rotation=%d", info->orientation);
Packit df99a1
      fprintf(stdout,"\n");
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
void
Packit df99a1
command_size(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
  { // extra nesting for windows
Packit df99a1
    for (GPosition p=lst; p; ++p)
Packit df99a1
    {
Packit df99a1
      if (lst[p]->is_page())
Packit df99a1
        {
Packit df99a1
          GUTF8String fileid = g().doc->page_to_id(lst[p]->get_page_num());
Packit df99a1
          const GP<DjVuFile> f = g().doc->get_djvu_file(fileid);
Packit df99a1
          print_size(f);
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
select_all(void)
Packit df99a1
{
Packit df99a1
  g().file = 0;
Packit df99a1
  g().fileid = "";
Packit df99a1
  g().selected = g().doc->get_djvm_dir()->get_files_list();
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
select_clear(void)
Packit df99a1
{
Packit df99a1
  g().file = 0;
Packit df99a1
  g().fileid = "<all>";
Packit df99a1
  g().selected.empty();
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
select_add(GP<DjVmDir::File> frec)
Packit df99a1
{
Packit df99a1
  GPosition selp = g().selected;
Packit df99a1
  GPList<DjVmDir::File> all = g().doc->get_djvm_dir()->get_files_list();
Packit df99a1
  GPosition allp = all;
Packit df99a1
  while (allp)
Packit df99a1
    {
Packit df99a1
      if (all[allp] == frec)
Packit df99a1
        break;
Packit df99a1
      if ( selp && all[allp] == g().selected[selp])
Packit df99a1
        ++ selp;
Packit df99a1
      ++ allp;
Packit df99a1
    }
Packit df99a1
  if (allp && (!selp || all[allp] != g().selected[selp]))
Packit df99a1
    {
Packit df99a1
      g().selected.insert_before(selp, frec);
Packit df99a1
      if (! g().file)
Packit df99a1
        {
Packit df99a1
          g().fileid = frec->get_load_name();
Packit df99a1
          g().file = g().doc->get_djvu_file(g().fileid);
Packit df99a1
        }
Packit df99a1
      else
Packit df99a1
        {
Packit df99a1
          g().fileid = "<multiple>";
Packit df99a1
          g().file = 0;
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_select(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  GUTF8String pagid = pbs.get_token();
Packit df99a1
  // Case of NULL
Packit df99a1
  if (pagid == "") 
Packit df99a1
    {
Packit df99a1
      select_all();
Packit df99a1
      vprint("select: selecting entire document");
Packit df99a1
      return;
Packit df99a1
    } 
Packit df99a1
  // Case of a single page number
Packit df99a1
  if (pagid.is_int())
Packit df99a1
    {
Packit df99a1
      int pageno = atoi(pagid);
Packit df99a1
      GP<DjVmDir::File> frec = g().doc->get_djvm_dir()->page_to_file(pageno-1);
Packit df99a1
      if (!frec)
Packit df99a1
        verror("page \"%d\" not found", pageno);
Packit df99a1
      select_clear();
Packit df99a1
      select_add(frec);
Packit df99a1
      vprint("select: selecting \"%s\"", (const char*)ToNative(g().fileid));
Packit df99a1
      return;
Packit df99a1
    }
Packit df99a1
  // Case of a single file id
Packit df99a1
  GP<DjVmDir::File> frec = g().doc->get_djvm_dir()->id_to_file(pagid);
Packit df99a1
  if (!frec)
Packit df99a1
    frec = g().doc->get_djvm_dir()->name_to_file(pagid);
Packit df99a1
  if (!frec)
Packit df99a1
    frec = g().doc->get_djvm_dir()->title_to_file(pagid);
Packit df99a1
  if (!frec)
Packit df99a1
    verror("page \"%s\" not found", (const char*)ToNative(pagid));
Packit df99a1
  select_clear();
Packit df99a1
  select_add(frec);
Packit df99a1
  vprint("select: selecting \"%s\"", (const char*)ToNative(g().fileid));
Packit df99a1
}  
Packit df99a1
Packit df99a1
void
Packit df99a1
command_select_shared_ant(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  GP<DjVmDir::File> frec = g().doc->get_djvm_dir()->get_shared_anno_file();
Packit df99a1
  if (! frec)
Packit df99a1
    verror("select-shared-ant: no shared annotation file"); 
Packit df99a1
  select_clear();
Packit df99a1
  select_add(frec);
Packit df99a1
  vprint("select-shared-ant: selecting shared annotation");
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_create_shared_ant(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  GP<DjVmDir::File> frec = g().doc->get_djvm_dir()->get_shared_anno_file();
Packit df99a1
  if (! frec)
Packit df99a1
    {
Packit df99a1
      vprint("create-shared-ant: creating shared annotation file");
Packit df99a1
      g().doc->create_shared_anno_file();
Packit df99a1
      frec = g().doc->get_djvm_dir()->get_shared_anno_file();
Packit df99a1
      if (!frec) G_THROW("internal error");
Packit df99a1
    }
Packit df99a1
  select_clear();
Packit df99a1
  select_add(frec);
Packit df99a1
  vprint("select-shared-ant: selecting shared annotation");
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_showsel(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  int pagenum = 0;
Packit df99a1
  GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
  { // extra nesting for windows
Packit df99a1
    for (GPosition p=lst; p; ++p) 
Packit df99a1
    {
Packit df99a1
      GP<DjVmDir::File> f = lst[p];
Packit df99a1
      if (f->is_page())
Packit df99a1
        fprintf(stdout,"%4d P ", ++pagenum);
Packit df99a1
      else if (f->is_include())
Packit df99a1
        fprintf(stdout,"     I ");
Packit df99a1
      else if (f->is_thumbnails())
Packit df99a1
        fprintf(stdout,"     T ");
Packit df99a1
      else if (f->is_shared_anno())
Packit df99a1
        fprintf(stdout,"     A ");
Packit df99a1
      else
Packit df99a1
        fprintf(stdout,"     ? ");
Packit df99a1
      GUTF8String id = f->get_load_name();
Packit df99a1
      fprintf(stdout,"%8d  %s", f->size, (const char*)ToNative(id));
Packit df99a1
      GUTF8String name = f->get_save_name();
Packit df99a1
      if (name != id)
Packit df99a1
        fprintf(stdout," F=%s", (const char*)ToNative(name));
Packit df99a1
      GUTF8String title = f->get_title();
Packit df99a1
      if (title != id && f->is_page())
Packit df99a1
        fprintf(stdout," T=%s", (const char*)ToNative(title));
Packit df99a1
      fprintf(stdout,"\n");
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  if (g().doc->get_thumbnails_num() == g().doc->get_pages_num())
Packit df99a1
    fprintf(stdout,"     T %8s  %s\n", "", "<thumbnails>");
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_set_page_title(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  if (! g().file)
Packit df99a1
    verror("must select a single page first");
Packit df99a1
  GUTF8String fname = pbs.get_token();
Packit df99a1
  if (! fname)
Packit df99a1
    verror("must provide a name");
Packit df99a1
  GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
  GPosition pos = lst;
Packit df99a1
  if (! lst[pos]->is_page())
Packit df99a1
    verror("component file is not a page");
Packit df99a1
  g().doc->set_file_title(g().fileid, fname);
Packit df99a1
  vprint("set-page-title: modified \"%s\"", (const char*)ToNative(g().fileid));
Packit df99a1
  modified = true;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuInfo> decode_info(GP<DjVuFile> file)
Packit df99a1
{
Packit df99a1
  GP<DjVuInfo> info = file->info;
Packit df99a1
  if (! info)
Packit df99a1
    {
Packit df99a1
      const GP<ByteStream> pbs(file->get_djvu_bytestream(false, false));
Packit df99a1
      const GP<IFFByteStream> iff(IFFByteStream::create(pbs));
Packit df99a1
      GUTF8String chkid;
Packit df99a1
      if (! iff->get_chunk(chkid))
Packit df99a1
        return 0;
Packit df99a1
      if (chkid == "FORM:DJVU")
Packit df99a1
        {
Packit df99a1
          while (iff->get_chunk(chkid) && chkid!="INFO")
Packit df99a1
            iff->close_chunk();
Packit df99a1
          if (chkid == "INFO")
Packit df99a1
            {
Packit df99a1
              info = DjVuInfo::create();
Packit df99a1
              info->decode(*iff->get_bytestream());
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
      file->info = info;
Packit df99a1
    }
Packit df99a1
  return info;
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
set_rotation(GP<DjVuFile> file, int rot, bool relative)
Packit df99a1
{
Packit df99a1
  // decode info
Packit df99a1
  GP<DjVuInfo> info = decode_info(file);
Packit df99a1
  if (! info)
Packit df99a1
    return false;
Packit df99a1
  if (relative)
Packit df99a1
    rot += info->orientation;
Packit df99a1
  info->orientation = rot & 3;
Packit df99a1
  file->set_modified(true);
Packit df99a1
  modified = true;
Packit df99a1
  return true;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_set_rotation(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  GUTF8String rot = pbs.get_token();
Packit df99a1
  if (! rot.is_int())
Packit df99a1
    verror("usage: set-rotation [+-]<rot>");
Packit df99a1
  int rotation = rot.toInt();
Packit df99a1
  bool relative = (rot[0]=='+' || rot[0]=='-');
Packit df99a1
  if (! relative)
Packit df99a1
    if (rotation < 0 || rotation > 3)
Packit df99a1
      verror("absolute rotation must be in range 0..3");
Packit df99a1
  int rcount = 0;
Packit df99a1
  if (g().file)
Packit df99a1
    {
Packit df99a1
      GUTF8String id = g().fileid;
Packit df99a1
      if (set_rotation(g().file, rotation, relative))
Packit df99a1
        rcount += 1;
Packit df99a1
    }
Packit df99a1
  else
Packit df99a1
    {
Packit df99a1
      GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
      for (GPosition p=lst; p; ++p)
Packit df99a1
        {
Packit df99a1
          GUTF8String id = lst[p]->get_load_name();
Packit df99a1
          const GP<DjVuFile> f(g().doc->get_djvu_file(id));
Packit df99a1
          if (set_rotation(f, rotation, relative))
Packit df99a1
            rcount += 1;
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  vprint("rotated %d pages", rcount);
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
set_dpi(GP<DjVuFile> file, int dpi)
Packit df99a1
{
Packit df99a1
  // decode info
Packit df99a1
  GP<DjVuInfo> info = decode_info(file);
Packit df99a1
  if (! info)
Packit df99a1
    return false;
Packit df99a1
  info->dpi = dpi;
Packit df99a1
  file->set_modified(true);
Packit df99a1
  modified = true;
Packit df99a1
  return true;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_set_dpi(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  GUTF8String sdpi = pbs.get_token();
Packit df99a1
  if (! sdpi.is_int())
Packit df99a1
    verror("usage: set-dpi <dpi>");
Packit df99a1
  int dpi = sdpi.toInt();
Packit df99a1
  if (dpi < 25 || dpi > 6000)
Packit df99a1
    verror("resolution should be in range 25..6000dpi");
Packit df99a1
  int rcount = 0;
Packit df99a1
  if (g().file)
Packit df99a1
    {
Packit df99a1
      GUTF8String id = g().fileid;
Packit df99a1
      if (set_dpi(g().file, dpi))
Packit df99a1
        rcount += 1;
Packit df99a1
    }
Packit df99a1
  else
Packit df99a1
    {
Packit df99a1
      GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
      for (GPosition p=lst; p; ++p)
Packit df99a1
        {
Packit df99a1
          GUTF8String id = lst[p]->get_load_name();
Packit df99a1
          const GP<DjVuFile> f(g().doc->get_djvu_file(id));
Packit df99a1
          if (set_dpi(f, dpi))
Packit df99a1
            rcount += 1;
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  vprint("set dpi on %d pages", rcount);
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
#define DELMETA     1
Packit df99a1
#define DELXMP      8
Packit df99a1
#define CHKCOMPAT   2
Packit df99a1
#define EIGHTBIT    4
Packit df99a1
Packit df99a1
static bool
Packit df99a1
filter_ant(GP<ByteStream> in, 
Packit df99a1
           GP<ByteStream> out, 
Packit df99a1
           int flags)
Packit df99a1
{
Packit df99a1
  int c;
Packit df99a1
  int plevel = 0;
Packit df99a1
  bool copy = true;
Packit df99a1
  bool unchanged = true;
Packit df99a1
  bool compat = false;
Packit df99a1
  GP<ByteStream> mem;
Packit df99a1
  GP<ParsingByteStream> inp;
Packit df99a1
Packit df99a1
  if (flags & CHKCOMPAT)
Packit df99a1
    {
Packit df99a1
      mem = ByteStream::create();
Packit df99a1
      mem->copy(*in);
Packit df99a1
      mem->seek(0);
Packit df99a1
      char c;
Packit df99a1
      int state = 0;
Packit df99a1
      while (!compat && mem->read(&c,1)>0)
Packit df99a1
          {
Packit df99a1
            switch(state)
Packit df99a1
              {
Packit df99a1
              case 0:
Packit df99a1
                if (c == '\"')
Packit df99a1
                  state = '\"';
Packit df99a1
                break;
Packit df99a1
              case '\"':
Packit df99a1
                if (c == '\"')
Packit df99a1
                  state = 0;
Packit df99a1
                else if (c == '\\')
Packit df99a1
                  state = '\\';
Packit df99a1
                else if ((unsigned char)c<0x20 || c==0x7f)
Packit df99a1
                  compat = true;
Packit df99a1
                break;
Packit df99a1
              case '\\':
Packit df99a1
                if (!strchr("01234567tnrbfva\"\\",c))
Packit df99a1
                  compat = true;
Packit df99a1
                state = '\"';
Packit df99a1
                break;
Packit df99a1
              }
Packit df99a1
          }
Packit df99a1
      mem->seek(0);
Packit df99a1
      inp = ParsingByteStream::create(mem);
Packit df99a1
    }
Packit df99a1
  else
Packit df99a1
    {
Packit df99a1
      inp = ParsingByteStream::create(in);
Packit df99a1
    }
Packit df99a1
  
Packit df99a1
  while ((c = inp->get()) != EOF)
Packit df99a1
    if (c!=' ' && c!='\t' && c!='\r' && c!='\n')
Packit df99a1
      break;
Packit df99a1
  inp->unget(c);
Packit df99a1
  while ((c = inp->get()) != EOF)
Packit df99a1
    {
Packit df99a1
      if (plevel == 0)
Packit df99a1
        if (c !=' ' && c!='\t' && c!='\r' && c!='\n')
Packit df99a1
          copy = true;
Packit df99a1
      if (c == '\"')
Packit df99a1
        {
Packit df99a1
          inp->unget(c);
Packit df99a1
          GUTF8String token = inp->get_token(false, compat);
Packit df99a1
          if (copy)
Packit df99a1
	    print_c_string(token, token.length(), *out, !!(flags & EIGHTBIT));
Packit df99a1
          if (compat)
Packit df99a1
            unchanged = false;
Packit df99a1
        }
Packit df99a1
      else if (c == '(')
Packit df99a1
        {
Packit df99a1
          while ((c = inp->get()) != EOF)
Packit df99a1
            if (c!=' ' && c!='\t' && c!='\r' && c!='\n')
Packit df99a1
              break;
Packit df99a1
          inp->unget(c);
Packit df99a1
          if ((flags & DELMETA) && plevel==0 && c=='m')
Packit df99a1
            {
Packit df99a1
              GUTF8String token = inp->get_token();
Packit df99a1
              if (token == "metadata")
Packit df99a1
                copy = unchanged = false;
Packit df99a1
              if (copy) {
Packit df99a1
                out->write8('(');
Packit df99a1
                out->write((const char*)token, token.length());
Packit df99a1
              }
Packit df99a1
            }
Packit df99a1
          if ((flags & DELXMP) && plevel==0 && c=='x')
Packit df99a1
            {
Packit df99a1
              GUTF8String token = inp->get_token();
Packit df99a1
              if (token == "xmp")
Packit df99a1
                copy = unchanged = false;
Packit df99a1
              if (copy) {
Packit df99a1
                out->write8('(');
Packit df99a1
                out->write((const char*)token, token.length());
Packit df99a1
              }
Packit df99a1
            }
Packit df99a1
          else if (copy) 
Packit df99a1
            out->write8('(');
Packit df99a1
          plevel += 1;
Packit df99a1
        }
Packit df99a1
      else if (c == ')')
Packit df99a1
        {
Packit df99a1
          if (copy) 
Packit df99a1
            out->write8(c);
Packit df99a1
          if ( --plevel < 0)
Packit df99a1
            plevel = 0;
Packit df99a1
        }
Packit df99a1
      else if (copy)
Packit df99a1
        out->write8(c);
Packit df99a1
    }
Packit df99a1
  return !unchanged;
Packit df99a1
}
Packit df99a1
Packit df99a1
static bool
Packit df99a1
print_ant(GP<IFFByteStream> iff, 
Packit df99a1
          GP<ByteStream> out, 
Packit df99a1
          int flags=CHKCOMPAT)
Packit df99a1
{
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  bool changed = false;
Packit df99a1
  if (utf8)
Packit df99a1
    flags |= EIGHTBIT;
Packit df99a1
  while (iff->get_chunk(chkid))
Packit df99a1
    {
Packit df99a1
      if (chkid == "ANTa") 
Packit df99a1
        {
Packit df99a1
          changed = filter_ant(iff->get_bytestream(), out, flags);
Packit df99a1
        }
Packit df99a1
      else if (chkid == "ANTz") 
Packit df99a1
        {
Packit df99a1
          GP<ByteStream> bsiff = 
Packit df99a1
	    BSByteStream::create(iff->get_bytestream());
Packit df99a1
          changed = filter_ant(bsiff, out, flags);
Packit df99a1
        }
Packit df99a1
      iff->close_chunk();
Packit df99a1
    }
Packit df99a1
  return changed;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_print_ant(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  if (!g().file)
Packit df99a1
    verror("you must first select a single page");
Packit df99a1
  GP<ByteStream> out=ByteStream::create("w");
Packit df99a1
  GP<ByteStream> anno = g().file->get_anno();
Packit df99a1
  if (! (anno && anno->size())) return;
Packit df99a1
  GP<IFFByteStream> iff=IFFByteStream::create(anno);
Packit df99a1
  print_ant(iff, out);
Packit df99a1
  out->write8('\n');
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_print_merged_ant(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  if (!g().file)
Packit df99a1
    verror("you must first select a single page");
Packit df99a1
  GP<ByteStream> out=ByteStream::create("w");
Packit df99a1
  GP<ByteStream> anno = g().file->get_merged_anno();
Packit df99a1
  if (! (anno && anno->size())) return;
Packit df99a1
  GP<IFFByteStream> iff=IFFByteStream::create(anno);
Packit df99a1
  print_ant(iff, out);
Packit df99a1
  out->write8('\n');
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
static void
Packit df99a1
modify_ant(const GP<DjVuFile> &f, 
Packit df99a1
           const char *newchunkid,
Packit df99a1
           const GP<ByteStream> newchunk )
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> anno(ByteStream::create());
Packit df99a1
  if (newchunkid && newchunk && newchunk->size())
Packit df99a1
    {
Packit df99a1
      const GP<IFFByteStream> out(IFFByteStream::create(anno));
Packit df99a1
      newchunk->seek(0);
Packit df99a1
      out->put_chunk(newchunkid);
Packit df99a1
      out->copy(*newchunk);
Packit df99a1
      out->close_chunk();
Packit df99a1
    }
Packit df99a1
  f->anno = anno;
Packit df99a1
  if (! anno->size())
Packit df99a1
    f->remove_anno();
Packit df99a1
  f->set_modified(true);
Packit df99a1
  modified = true;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
file_remove_ant(const GP<DjVuFile> &f, const char *id)
Packit df99a1
{
Packit df99a1
  if (!f) return;
Packit df99a1
  modify_ant(f, 0, 0);
Packit df99a1
  vprint("remove_ant: modified \"%s\"", id);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_remove_ant(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  GPList<DjVmDir::File> & lst = g().selected;
Packit df99a1
  { // extra nesting for windows
Packit df99a1
    for (GPosition p=lst; p; ++p)
Packit df99a1
    {
Packit df99a1
      GUTF8String id = lst[p]->get_load_name();
Packit df99a1
      const GP<DjVuFile> f(g().doc->get_djvu_file(id));
Packit df99a1
      file_remove_ant(f, id);
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_set_ant(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  if (! g().file)
Packit df99a1
    verror("must select a single page first");
Packit df99a1
  const GP<ByteStream> ant = ByteStream::create();
Packit df99a1
  {
Packit df99a1
    const GP<ByteStream> dsedant = ByteStream::create();
Packit df99a1
    get_data_from_file("set-ant", pbs, *dsedant);
Packit df99a1
    dsedant->seek(0);
Packit df99a1
    GP<ByteStream> bsant = BSByteStream::create(ant,100);
Packit df99a1
    filter_ant(dsedant, bsant, EIGHTBIT);
Packit df99a1
    bsant = 0;
Packit df99a1
  }
Packit df99a1
  modify_ant(g().file, "ANTz", ant);
Packit df99a1
  vprint("set-ant: modified \"%s\"", (const char*)ToNative(g().fileid));
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
print_meta(IFFByteStream &iff, ByteStream &out)
Packit df99a1
{
Packit df99a1
  GUTF8String chkid;  
Packit df99a1
  while (iff.get_chunk(chkid))
Packit df99a1
    {
Packit df99a1
      bool ok = false;
Packit df99a1
      GP<DjVuANT> ant=DjVuANT::create();
Packit df99a1
      if (chkid == "ANTz") {
Packit df99a1
          GP<ByteStream> bsiff=BSByteStream::create(iff.get_bytestream());
Packit df99a1
          ant->decode(*bsiff);
Packit df99a1
          ok = true;
Packit df99a1
      } else if (chkid == "ANTa") {
Packit df99a1
        ant->decode(*iff.get_bytestream());
Packit df99a1
        ok = true;
Packit df99a1
      }
Packit df99a1
      if (ok)
Packit df99a1
        {
Packit df99a1
          { // extra nesting for windows
Packit df99a1
            for (GPosition pos=ant->metadata; pos; ++pos)
Packit df99a1
            { 
Packit df99a1
              GUTF8String tmp;
Packit df99a1
              tmp=ant->metadata.key(pos);
Packit df99a1
              out.writestring(tmp); 
Packit df99a1
              out.write8('\t');
Packit df99a1
              tmp=ant->metadata[pos];
Packit df99a1
              print_c_string((const char*)tmp, tmp.length(), out, utf8);
Packit df99a1
              out.write8('\n');
Packit df99a1
            }
Packit df99a1
          }
Packit df99a1
        }
Packit df99a1
      iff.close_chunk();
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void 
Packit df99a1
command_print_meta(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  if (! g().file )
Packit df99a1
    {
Packit df99a1
      GP<DjVmDir::File> frec = g().doc->get_djvm_dir()->get_shared_anno_file();
Packit df99a1
      if (frec)
Packit df99a1
        {
Packit df99a1
          vprint("print-meta: implicitly selecting shared annotations");
Packit df99a1
          select_clear();
Packit df99a1
          select_add(frec);
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  if ( g().file )
Packit df99a1
    {
Packit df99a1
      GP<ByteStream> out=ByteStream::create("w");
Packit df99a1
      GP<ByteStream> anno = g().file->get_anno();
Packit df99a1
      if (! (anno && anno->size())) return;
Packit df99a1
      GP<IFFByteStream> iff=IFFByteStream::create(anno); 
Packit df99a1
      print_meta(*iff,*out);
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
static bool
Packit df99a1
modify_meta(const GP<DjVuFile> &f,
Packit df99a1
            GMap<GUTF8String,GUTF8String> *newmeta)
Packit df99a1
{
Packit df99a1
  bool changed = false;
Packit df99a1
  GP<ByteStream> newant = ByteStream::create();
Packit df99a1
  if (newmeta && !newmeta->isempty())
Packit df99a1
    {
Packit df99a1
      newant->writestring(GUTF8String("(metadata"));
Packit df99a1
      { // extra nesting for windows
Packit df99a1
        for (GPosition pos=newmeta->firstpos(); pos; ++pos)
Packit df99a1
        {
Packit df99a1
          GUTF8String key = newmeta->key(pos); 
Packit df99a1
          GUTF8String val = (*newmeta)[pos];
Packit df99a1
          newant->write("\n\t(",3);
Packit df99a1
          newant->writestring(key);
Packit df99a1
          newant->write(" ",1);
Packit df99a1
          print_c_string((const char*)val, val.length(),
Packit df99a1
                         *newant, true);
Packit df99a1
          newant->write(")",1);
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
      newant->write(" )\n",3);
Packit df99a1
      changed = true;
Packit df99a1
    }
Packit df99a1
  GP<ByteStream> anno = f->get_anno();
Packit df99a1
  if (anno && anno->size()) 
Packit df99a1
    {
Packit df99a1
      GP<IFFByteStream> iff=IFFByteStream::create(anno);
Packit df99a1
      if (print_ant(iff, newant, DELMETA|CHKCOMPAT|EIGHTBIT))
Packit df99a1
        changed = true;
Packit df99a1
    }
Packit df99a1
  const GP<ByteStream> newantz=ByteStream::create();
Packit df99a1
  if (changed)
Packit df99a1
    {
Packit df99a1
      newant->seek(0);
Packit df99a1
      { 
Packit df99a1
        GP<ByteStream> bzz = BSByteStream::create(newantz,100); 
Packit df99a1
        bzz->copy(*newant); 
Packit df99a1
        bzz = 0;
Packit df99a1
      }
Packit df99a1
      newantz->seek(0);
Packit df99a1
      modify_ant(f, "ANTz", newantz);
Packit df99a1
    }
Packit df99a1
  return changed;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
file_remove_meta(const GP<DjVuFile> &f, const char *id)
Packit df99a1
{
Packit df99a1
  if (modify_meta(f, 0))
Packit df99a1
    vprint("remove_meta: modified \"%s\"", id);
Packit df99a1
}
Packit df99a1
Packit df99a1
void 
Packit df99a1
command_remove_meta(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
   GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
  { // extra nesting for windows
Packit df99a1
    for (GPosition p=lst; p; ++p)
Packit df99a1
    {
Packit df99a1
      GUTF8String id = lst[p]->get_load_name();
Packit df99a1
      const GP<DjVuFile> f(g().doc->get_djvu_file(id));
Packit df99a1
      file_remove_meta(f, id);
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_set_meta(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  // get metadata
Packit df99a1
  GP<ByteStream> metastream = ByteStream::create();
Packit df99a1
  get_data_from_file("set-meta", pbs, *metastream);
Packit df99a1
  metastream->seek(0);
Packit df99a1
  // parse metadata
Packit df99a1
  GMap<GUTF8String,GUTF8String> metadata;
Packit df99a1
  GP<ParsingByteStream> inp = ParsingByteStream::create(metastream);
Packit df99a1
  int c;
Packit df99a1
  while ( (c = inp->get_spaces(true)) != EOF )
Packit df99a1
    {
Packit df99a1
      GUTF8String key, val;
Packit df99a1
      inp->unget(c);
Packit df99a1
      key = inp->get_token();
Packit df99a1
      c = inp->get_spaces(false);
Packit df99a1
      if (c == '\"') {
Packit df99a1
        inp->unget(c);
Packit df99a1
        val = inp->get_token();
Packit df99a1
      } else {
Packit df99a1
        while (c!='\n' && c!='\r' && c!=EOF) {
Packit df99a1
          val += c;
Packit df99a1
          c = inp->get();
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
      if (key.length()>0 && val.length()>0)
Packit df99a1
        metadata[key] = val;
Packit df99a1
    }
Packit df99a1
  // possibly select shared annotations.
Packit df99a1
  if (! g().file)
Packit df99a1
    {
Packit df99a1
      GP<DjVmDir::File> frec = g().doc->get_djvm_dir()->get_shared_anno_file();
Packit df99a1
      if (frec)
Packit df99a1
        {
Packit df99a1
          vprint("set-meta: implicitly selecting shared annotations.");
Packit df99a1
        }
Packit df99a1
      else if (metadata.size() > 0)
Packit df99a1
        {
Packit df99a1
          vprint("set-meta: implicitly creating and selecting shared annotations.");
Packit df99a1
          g().doc->create_shared_anno_file();
Packit df99a1
          frec = g().doc->get_djvm_dir()->get_shared_anno_file();
Packit df99a1
        }
Packit df99a1
      if (frec)
Packit df99a1
        {
Packit df99a1
          select_clear();
Packit df99a1
          select_add(frec);
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  // set metadata
Packit df99a1
  if (g().file && modify_meta(g().file, &metadata))
Packit df99a1
    vprint("set-meta: modified \"%s\"", 
Packit df99a1
           (const char*)ToNative(g().fileid));
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
print_xmp(IFFByteStream &iff, ByteStream &out)
Packit df99a1
{
Packit df99a1
  GUTF8String chkid;  
Packit df99a1
  while (iff.get_chunk(chkid))
Packit df99a1
    {
Packit df99a1
      bool ok = false;
Packit df99a1
      GP<DjVuANT> ant=DjVuANT::create();
Packit df99a1
      if (chkid == "ANTz") {
Packit df99a1
          GP<ByteStream> bsiff=BSByteStream::create(iff.get_bytestream());
Packit df99a1
          ant->decode(*bsiff);
Packit df99a1
          ok = true;
Packit df99a1
      } else if (chkid == "ANTa") {
Packit df99a1
        ant->decode(*iff.get_bytestream());
Packit df99a1
        ok = true;
Packit df99a1
      }
Packit df99a1
      if (ok && ant->xmpmetadata.length()>0)
Packit df99a1
        {
Packit df99a1
          out.writestring(ant->xmpmetadata);
Packit df99a1
          out.write8('\n');
Packit df99a1
        }
Packit df99a1
      iff.close_chunk();
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void 
Packit df99a1
command_print_xmp(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  if (! g().file )
Packit df99a1
    {
Packit df99a1
      GP<DjVmDir::File> frec = g().doc->get_djvm_dir()->get_shared_anno_file();
Packit df99a1
      if (frec)
Packit df99a1
        {
Packit df99a1
          vprint("print-xmp: implicitly selecting shared annotations");
Packit df99a1
          select_clear();
Packit df99a1
          select_add(frec);
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  if ( g().file )
Packit df99a1
    {
Packit df99a1
      GP<ByteStream> out=ByteStream::create("w");
Packit df99a1
      GP<ByteStream> anno = g().file->get_anno();
Packit df99a1
      if (! (anno && anno->size())) return;
Packit df99a1
      GP<IFFByteStream> iff=IFFByteStream::create(anno); 
Packit df99a1
      print_xmp(*iff,*out);
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
static bool
Packit df99a1
modify_xmp(const GP<DjVuFile> &f, GUTF8String *newxmp)
Packit df99a1
{
Packit df99a1
  bool changed = false;
Packit df99a1
  GP<ByteStream> newant = ByteStream::create();
Packit df99a1
  if (newxmp && newxmp->length() > 0)
Packit df99a1
    {
Packit df99a1
      newant->writestring(GUTF8String("(xmp "));
Packit df99a1
      print_c_string((const char*)(*newxmp), newxmp->length(), *newant, true);
Packit df99a1
      newant->write(" )\n",3);
Packit df99a1
      changed = true;
Packit df99a1
    }
Packit df99a1
  GP<ByteStream> anno = f->get_anno();
Packit df99a1
  if (anno && anno->size()) 
Packit df99a1
    {
Packit df99a1
      GP<IFFByteStream> iff=IFFByteStream::create(anno);
Packit df99a1
      if (print_ant(iff, newant, DELXMP|CHKCOMPAT|EIGHTBIT))
Packit df99a1
        changed = true;
Packit df99a1
    }
Packit df99a1
  const GP<ByteStream> newantz=ByteStream::create();
Packit df99a1
  if (changed)
Packit df99a1
    {
Packit df99a1
      newant->seek(0);
Packit df99a1
      { 
Packit df99a1
        GP<ByteStream> bzz = BSByteStream::create(newantz,100); 
Packit df99a1
        bzz->copy(*newant); 
Packit df99a1
        bzz = 0;
Packit df99a1
      }
Packit df99a1
      newantz->seek(0);
Packit df99a1
      modify_ant(f, "ANTz", newantz);
Packit df99a1
    }
Packit df99a1
  return changed;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
file_remove_xmp(const GP<DjVuFile> &f, const char *id)
Packit df99a1
{
Packit df99a1
  if (modify_xmp(f, 0))
Packit df99a1
    vprint("remove_xmp: modified \"%s\"", id);
Packit df99a1
}
Packit df99a1
Packit df99a1
void 
Packit df99a1
command_remove_xmp(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
  { // extra nesting for windows
Packit df99a1
    for (GPosition p=lst; p; ++p)
Packit df99a1
      {
Packit df99a1
        GUTF8String id = lst[p]->get_load_name();
Packit df99a1
        const GP<DjVuFile> f(g().doc->get_djvu_file(id));
Packit df99a1
        file_remove_xmp(f, id);
Packit df99a1
      }
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
void 
Packit df99a1
command_set_xmp(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  // get xmpmetadata
Packit df99a1
  GP<ByteStream> metastream = ByteStream::create();
Packit df99a1
  get_data_from_file("set-meta", pbs, *metastream);
Packit df99a1
  metastream->seek(0);
Packit df99a1
  // read xmpmetadata
Packit df99a1
  int size = metastream->size();
Packit df99a1
  char *buffer = new char[size+1];
Packit df99a1
  metastream->readall(buffer,size);
Packit df99a1
  buffer[size] = 0;
Packit df99a1
  GUTF8String xmpmetadata(buffer);
Packit df99a1
  delete [] buffer;
Packit df99a1
  // possibly select shared annotations.
Packit df99a1
  if (! g().file)
Packit df99a1
    {
Packit df99a1
      GP<DjVmDir::File> frec = g().doc->get_djvm_dir()->get_shared_anno_file();
Packit df99a1
      if (frec)
Packit df99a1
        {
Packit df99a1
          vprint("set-xmp: implicitly selecting shared annotations.");
Packit df99a1
        }
Packit df99a1
      else if (xmpmetadata.length() > 0)
Packit df99a1
        {
Packit df99a1
          vprint("set-xmp: implicitly creating and selecting shared annotations.");
Packit df99a1
          g().doc->create_shared_anno_file();
Packit df99a1
          frec = g().doc->get_djvm_dir()->get_shared_anno_file();
Packit df99a1
        }
Packit df99a1
      if (frec)
Packit df99a1
        {
Packit df99a1
          select_clear();
Packit df99a1
          select_add(frec);
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  // set metadata
Packit df99a1
  if (g().file && modify_xmp(g().file, &xmpmetadata))
Packit df99a1
    vprint("set-xmp: modified \"%s\"", 
Packit df99a1
           (const char*)ToNative(g().fileid));
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
struct  zone_names_struct
Packit df99a1
{ 
Packit df99a1
  const char *name;
Packit df99a1
  DjVuTXT::ZoneType ztype;
Packit df99a1
  char separator;
Packit df99a1
};
Packit df99a1
Packit df99a1
static zone_names_struct* zone_names() {
Packit df99a1
  static zone_names_struct xzone_names[] = 
Packit df99a1
  {
Packit df99a1
    { "page",   DjVuTXT::PAGE,      0 },
Packit df99a1
    { "column", DjVuTXT::COLUMN,    DjVuTXT::end_of_column },
Packit df99a1
    { "region", DjVuTXT::REGION,    DjVuTXT::end_of_region },
Packit df99a1
    { "para",   DjVuTXT::PARAGRAPH, DjVuTXT::end_of_paragraph },
Packit df99a1
    { "line",   DjVuTXT::LINE,      DjVuTXT::end_of_line },
Packit df99a1
    { "word",   DjVuTXT::WORD,      ' ' },
Packit df99a1
    { "char",   DjVuTXT::CHARACTER, 0 },
Packit df99a1
    { 0, (DjVuTXT::ZoneType)0 ,0 }
Packit df99a1
  };
Packit df99a1
  return xzone_names;
Packit df99a1
};
Packit df99a1
Packit df99a1
GP<DjVuTXT>
Packit df99a1
get_text(const GP<DjVuFile> &file)
Packit df99a1
{ 
Packit df99a1
  GUTF8String chkid;
Packit df99a1
  const GP<ByteStream> bs(file->get_text());
Packit df99a1
  if (bs) 
Packit df99a1
    {
Packit df99a1
      long int i=0;
Packit df99a1
      const GP<IFFByteStream> iff(IFFByteStream::create(bs));
Packit df99a1
      while (iff->get_chunk(chkid))
Packit df99a1
        {
Packit df99a1
          i++;
Packit df99a1
          if (chkid == GUTF8String("TXTa")) 
Packit df99a1
            {
Packit df99a1
              GP<DjVuTXT> txt = DjVuTXT::create();
Packit df99a1
              txt->decode(iff->get_bytestream());
Packit df99a1
              return txt;
Packit df99a1
            }
Packit df99a1
          else if (chkid == GUTF8String("TXTz")) 
Packit df99a1
            {
Packit df99a1
              GP<DjVuTXT> txt = DjVuTXT::create();
Packit df99a1
              GP<ByteStream> bsiff=BSByteStream::create(iff->get_bytestream());
Packit df99a1
              txt->decode(bsiff);
Packit df99a1
              return txt;
Packit df99a1
            }
Packit df99a1
          iff->close_chunk();
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
print_txt_sub(const GP<DjVuTXT> &txt, DjVuTXT::Zone &zone, 
Packit df99a1
	      const GP<ByteStream> &out, int indent)
Packit df99a1
{
Packit df99a1
  // Indentation
Packit df99a1
  if (indent)
Packit df99a1
    {
Packit df99a1
      out->write("\n",1);
Packit df99a1
      static const char spaces[] = "                        ";
Packit df99a1
      if (indent > (int)sizeof(spaces))
Packit df99a1
        indent = sizeof(spaces);
Packit df99a1
      out->write(spaces, indent);
Packit df99a1
    }
Packit df99a1
  // Zone header
Packit df99a1
  int zinfo;
Packit df99a1
  for (zinfo=0; zone_names()[zinfo].name; zinfo++)
Packit df99a1
    if (zone.ztype == zone_names()[zinfo].ztype)
Packit df99a1
      break;
Packit df99a1
  GUTF8String message = "(bogus";
Packit df99a1
  if (zone_names()[zinfo].name)
Packit df99a1
    message.format("(%s %d %d %d %d", zone_names()[zinfo].name,
Packit df99a1
                   zone.rect.xmin, zone.rect.ymin, 
Packit df99a1
                   zone.rect.xmax, zone.rect.ymax);
Packit df99a1
  out->write((const char*)message, message.length());
Packit df99a1
  // Zone children
Packit df99a1
  if (zone.children.isempty()) 
Packit df99a1
    {
Packit df99a1
      const char *data = txt->textUTF8.getbuf() + zone.text_start;
Packit df99a1
      int length = zone.text_length;
Packit df99a1
      if (data[length-1] == zone_names()[zinfo].separator)
Packit df99a1
        length -= 1;
Packit df99a1
      out->write(" ",1);
Packit df99a1
      print_c_string(data, length, *out, utf8);
Packit df99a1
    }
Packit df99a1
  else
Packit df99a1
    {
Packit df99a1
      for (GPosition pos=zone.children; pos; ++pos)
Packit df99a1
        print_txt_sub(txt, zone.children[pos], out, indent + 1);
Packit df99a1
    }
Packit df99a1
  // Finish
Packit df99a1
  out->write(")",1);
Packit df99a1
  if (!indent)
Packit df99a1
    out->write("\n", 1);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
print_txt(const GP<DjVuTXT> &txt, const GP<ByteStream> &out)
Packit df99a1
{
Packit df99a1
  if (txt)
Packit df99a1
    print_txt_sub(txt, txt->page_zone, out, 0);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_print_txt(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> out = ByteStream::create("w");
Packit df99a1
  GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
  { // extra nesting for windows
Packit df99a1
    for (GPosition p=lst; p; ++p)
Packit df99a1
      if (lst[p]->is_page())
Packit df99a1
      {
Packit df99a1
        GUTF8String id = lst[p]->get_load_name();
Packit df99a1
        const GP<DjVuFile> f(g().doc->get_djvu_file(id));
Packit df99a1
        const GP<DjVuTXT> txt(get_text(f));
Packit df99a1
        if (txt)
Packit df99a1
          print_txt(txt, out);
Packit df99a1
        else
Packit df99a1
          out->write("(page 0 0 0 0 \"\")\n",18);
Packit df99a1
      }
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_print_pure_txt(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> out = ByteStream::create("w");
Packit df99a1
  GP<DjVuTXT> txt;
Packit df99a1
  GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
  { // extra nesting for windows
Packit df99a1
    for (GPosition p=lst; p; ++p)
Packit df99a1
    {
Packit df99a1
      GUTF8String id = lst[p]->get_load_name();
Packit df99a1
      const GP<DjVuFile> f(g().doc->get_djvu_file(id));
Packit df99a1
      if ((txt = get_text(f)))
Packit df99a1
        {
Packit df99a1
          GUTF8String ntxt = txt->textUTF8;
Packit df99a1
          out->write((const char*)ntxt, ntxt.length());
Packit df99a1
        }
Packit df99a1
      out->write("\f",1);
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
modify_txt(const GP<DjVuFile> &f, 
Packit df99a1
           const char *newchunkid,
Packit df99a1
           const GP<ByteStream> newchunk )
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> text(ByteStream::create());
Packit df99a1
  if (newchunkid && newchunk && newchunk->size())
Packit df99a1
    {
Packit df99a1
      const GP<IFFByteStream> out(IFFByteStream::create(text));
Packit df99a1
      newchunk->seek(0);
Packit df99a1
      out->put_chunk(newchunkid);
Packit df99a1
      out->copy(*newchunk);
Packit df99a1
      out->close_chunk();
Packit df99a1
    }
Packit df99a1
  f->text = text;
Packit df99a1
  if (! text->size())
Packit df99a1
    f->remove_text();
Packit df99a1
  f->set_modified(true);
Packit df99a1
  modified = true;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
file_remove_txt(const GP<DjVuFile> &f, const char *id)
Packit df99a1
{
Packit df99a1
  if (! f) return;
Packit df99a1
  modify_txt(f, 0, 0);
Packit df99a1
  vprint("remove-txt: modified \"%s\"", id);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_remove_txt(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
  { // extra nesting for windows
Packit df99a1
    for (GPosition p=lst; p; ++p)
Packit df99a1
    {
Packit df99a1
      GUTF8String id = lst[p]->get_load_name();
Packit df99a1
      const GP<DjVuFile> f(g().doc->get_djvu_file(id));
Packit df99a1
      file_remove_txt(f, id);
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
construct_djvutxt_sub(ParsingByteStream &pbs, 
Packit df99a1
                      const GP<DjVuTXT> &txt, DjVuTXT::Zone &zone,
Packit df99a1
                      int mintype, bool exact)
Packit df99a1
{
Packit df99a1
  int c;
Packit df99a1
  GUTF8String token;
Packit df99a1
  // Get zone type
Packit df99a1
  c = pbs.get_spaces(true);
Packit df99a1
  if (c != '(')
Packit df99a1
    verror("syntax error in txt data: expecting '(',\n\tnear '%s'", 
Packit df99a1
           pbs.get_error_context(c) );
Packit df99a1
  token = pbs.get_token(true);
Packit df99a1
  int zinfo;
Packit df99a1
  for (zinfo=0; zone_names()[zinfo].name; zinfo++)
Packit df99a1
    if (token == zone_names()[zinfo].name)
Packit df99a1
      break;
Packit df99a1
  if (! zone_names()[zinfo].name)
Packit df99a1
    verror("Syntax error in txt data: undefined token '%s',\n\tnear '%s'",
Packit df99a1
           (const char*)ToNative(token), pbs.get_error_context());
Packit df99a1
  zone.ztype = zone_names()[zinfo].ztype;
Packit df99a1
  if (zone.ztype<mintype || (exact && zone.ztype>mintype))
Packit df99a1
    verror("Syntax error in txt data: illegal zone token '%s',\n\tnear '%s'",
Packit df99a1
           (const char*)ToNative(token), pbs.get_error_context());           
Packit df99a1
  // Get zone rect
Packit df99a1
  GUTF8String str;
Packit df99a1
  str = pbs.get_token(true);
Packit df99a1
  if (!str || !str.is_int()) 
Packit df99a1
    nerror: verror("Syntax error in txt data: number expected,\n\tnear '%s%s'",
Packit df99a1
                   (const char*)ToNative(str), pbs.get_error_context());  
Packit df99a1
  zone.rect.xmin = atoi(str);
Packit df99a1
  str = pbs.get_token(true);
Packit df99a1
  if (!str || !str.is_int()) 
Packit df99a1
    goto nerror;
Packit df99a1
  zone.rect.ymin = atoi(str);
Packit df99a1
  str = pbs.get_token(true);
Packit df99a1
  if (!str || !str.is_int()) 
Packit df99a1
    goto nerror;
Packit df99a1
  zone.rect.xmax = atoi(str);
Packit df99a1
  str = pbs.get_token(true);
Packit df99a1
  if (!str || !str.is_int()) 
Packit df99a1
    goto nerror;
Packit df99a1
  zone.rect.ymax = atoi(str);
Packit df99a1
  if (zone.rect.xmin > zone.rect.xmax) 
Packit df99a1
    {
Packit df99a1
      int tmp = zone.rect.xmin; 
Packit df99a1
      zone.rect.xmin=zone.rect.xmax; 
Packit df99a1
      zone.rect.xmax=tmp; 
Packit df99a1
    }
Packit df99a1
  if (zone.rect.ymin > zone.rect.ymax)
Packit df99a1
    {
Packit df99a1
      int tmp = zone.rect.ymin; 
Packit df99a1
      zone.rect.ymin=zone.rect.ymax; 
Packit df99a1
      zone.rect.ymax=tmp; 
Packit df99a1
    }
Packit df99a1
  // Continue processing
Packit df99a1
  c = pbs.get_spaces(true);
Packit df99a1
  pbs.unget(c);
Packit df99a1
  if (c == '"') 
Packit df99a1
    {
Packit df99a1
      // This is a terminal
Packit df99a1
      str = pbs.get_token(true);
Packit df99a1
      zone.text_start = txt->textUTF8.length();
Packit df99a1
      zone.text_length = str.length();
Packit df99a1
      txt->textUTF8 += str;
Packit df99a1
      
Packit df99a1
    }
Packit df99a1
  else 
Packit df99a1
    {
Packit df99a1
      // This is a non terminal
Packit df99a1
      while (c != ')')
Packit df99a1
        {
Packit df99a1
          if (c != '(')
Packit df99a1
            verror("Syntax error in text data: expecting subzone,\n\tnear '%s'",
Packit df99a1
                   pbs.get_error_context() );
Packit df99a1
          DjVuTXT::Zone *nzone = zone.append_child();
Packit df99a1
          construct_djvutxt_sub(pbs, txt, *nzone, zone.ztype+1, false);
Packit df99a1
          c = pbs.get_spaces(true);
Packit df99a1
          pbs.unget(c);
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  // Skip last parenthesis
Packit df99a1
  c = pbs.get_spaces(true);
Packit df99a1
  if (c != ')')
Packit df99a1
    verror("Syntax error in text data: missing parenthesis,\n\tnear '%s'",
Packit df99a1
           pbs.get_error_context(c) );
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVuTXT>
Packit df99a1
construct_djvutxt(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  GP<DjVuTXT> txt(DjVuTXT::create());
Packit df99a1
  int c = pbs.get_spaces(true);
Packit df99a1
  if (c == EOF)
Packit df99a1
    return 0;
Packit df99a1
  pbs.unget(c);
Packit df99a1
  construct_djvutxt_sub(pbs, txt, txt->page_zone, DjVuTXT::PAGE, true);
Packit df99a1
  if (pbs.get_spaces(true) != EOF)
Packit df99a1
    verror("Syntax error in txt data: garbage after data");
Packit df99a1
  txt->normalize_text();
Packit df99a1
  if (! txt->textUTF8)
Packit df99a1
    return 0;
Packit df99a1
  return txt;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_set_txt(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  if (! g().file)
Packit df99a1
    verror("must select a single page first");
Packit df99a1
  const GP<ByteStream> txtbs(ByteStream::create());
Packit df99a1
  get_data_from_file("set-txt", pbs, *txtbs);
Packit df99a1
  txtbs->seek(0);
Packit df99a1
  GP<ParsingByteStream> txtpbs(ParsingByteStream::create(txtbs));
Packit df99a1
  const GP<DjVuTXT> txt(construct_djvutxt(*txtpbs));
Packit df99a1
  GP<ByteStream> txtobs=ByteStream::create();
Packit df99a1
  if (txt)
Packit df99a1
    {
Packit df99a1
      const GP<ByteStream> bsout(BSByteStream::create(txtobs,1000));
Packit df99a1
      txt->encode(bsout);
Packit df99a1
    }
Packit df99a1
  txtobs->seek(0);
Packit df99a1
  modify_txt(g().file, "TXTz", txtobs);
Packit df99a1
  vprint("set-txt: modified \"%s\"", (const char*)ToNative(g().fileid));
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
output(const GP<DjVuFile> &f, const GP<ByteStream> &out, 
Packit df99a1
       int flag, const char *id=0, int pageno=0)
Packit df99a1
{
Packit df99a1
  if (f)
Packit df99a1
    {
Packit df99a1
      const GP<ByteStream> ant(ByteStream::create());
Packit df99a1
      const GP<ByteStream> txt(ByteStream::create());
Packit df99a1
      char pagenumber[16];
Packit df99a1
      sprintf(pagenumber," # page %d", pageno);
Packit df99a1
      if (flag & 1) 
Packit df99a1
        { 
Packit df99a1
          const GP<ByteStream> anno(f->get_anno());
Packit df99a1
          if (anno && anno->size()) 
Packit df99a1
            {
Packit df99a1
              const GP<IFFByteStream> iff(IFFByteStream::create(anno)); 
Packit df99a1
              print_ant(iff, ant); 
Packit df99a1
              ant->seek(0); 
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
      if (flag & 2)
Packit df99a1
        { 
Packit df99a1
          print_txt(get_text(f),txt); 
Packit df99a1
          txt->seek(0); 
Packit df99a1
        }
Packit df99a1
      if (id && ant->size() + txt->size())
Packit df99a1
        {
Packit df99a1
          static const char msg1[] = "# ------------------------- \nselect \0";
Packit df99a1
          static const char msg2[] = "\n\0";
Packit df99a1
          out->write(msg1, strlen(msg1));
Packit df99a1
          print_c_string(id, strlen(id), *out, utf8);
Packit df99a1
          if (pageno > 0) out->write(pagenumber, strlen(pagenumber));
Packit df99a1
          out->write(msg2, strlen(msg2));
Packit df99a1
        }
Packit df99a1
      if (ant->size()) 
Packit df99a1
        {
Packit df99a1
          out->write("set-ant\n", 8);
Packit df99a1
          out->copy(*ant);
Packit df99a1
          out->write("\n.\n", 3);
Packit df99a1
        }
Packit df99a1
      if (txt->size()) 
Packit df99a1
        {
Packit df99a1
          out->write("set-txt\n", 8);
Packit df99a1
          out->copy(*txt);
Packit df99a1
          out->write("\n.\n", 3);
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_output_ant(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> out = ByteStream::create("w");
Packit df99a1
  if (g().file) 
Packit df99a1
    {
Packit df99a1
      output(g().file, out, 1);
Packit df99a1
    }
Packit df99a1
  else 
Packit df99a1
    {
Packit df99a1
      const char *pre = "select; remove-ant\n";
Packit df99a1
      out->write(pre, strlen(pre));
Packit df99a1
      GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
      { // extra nesting for windows
Packit df99a1
        for (GPosition p=lst; p; ++p)
Packit df99a1
        {
Packit df99a1
          int pageno = lst[p]->get_page_num();
Packit df99a1
          GUTF8String id = lst[p]->get_load_name();
Packit df99a1
          const GP<DjVuFile> f(g().doc->get_djvu_file(id));
Packit df99a1
          output(f, out, 1, id, pageno+1);
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_output_txt(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> out = ByteStream::create("w");
Packit df99a1
  if (g().file) 
Packit df99a1
    {
Packit df99a1
      output(g().file, out, 2);
Packit df99a1
    }
Packit df99a1
  else 
Packit df99a1
    {
Packit df99a1
      const char *pre = "select; remove-txt\n";
Packit df99a1
      out->write(pre, strlen(pre));
Packit df99a1
      GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
      { // extra nesting for windows
Packit df99a1
        for (GPosition p=lst; p; ++p)
Packit df99a1
        {
Packit df99a1
          int pageno = lst[p]->get_page_num();
Packit df99a1
          GUTF8String id = lst[p]->get_load_name();
Packit df99a1
          const GP<DjVuFile> f(g().doc->get_djvu_file(id));
Packit df99a1
          output(f, out, 2, id, pageno+1);
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_output_all(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> out = ByteStream::create("w");
Packit df99a1
  if (g().file) 
Packit df99a1
    {
Packit df99a1
      output(g().file, out, 3);
Packit df99a1
    }
Packit df99a1
  else 
Packit df99a1
    {
Packit df99a1
      const char *pre = "select; remove-ant; remove-txt\n";
Packit df99a1
      out->write(pre, strlen(pre));
Packit df99a1
      GPList<DjVmDir::File> &lst = g().selected;
Packit df99a1
      { // extra nesting for windows
Packit df99a1
        for (GPosition p=lst; p; ++p)
Packit df99a1
        {
Packit df99a1
          int pageno = lst[p]->get_page_num();
Packit df99a1
          GUTF8String id = lst[p]->get_load_name();
Packit df99a1
          const GP<DjVuFile> f(g().doc->get_djvu_file(id));
Packit df99a1
          output(f, out, 3, id, pageno+1);
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
print_outline_sub(const GP<DjVmNav> &nav, int &pos, int count, 
Packit df99a1
                  const GP<ByteStream> &out, int indent)
Packit df99a1
{
Packit df99a1
  GUTF8String str;
Packit df99a1
  GP<DjVmNav::DjVuBookMark> entry;
Packit df99a1
  while (count > 0 && pos < nav->getBookMarkCount())
Packit df99a1
    {
Packit df99a1
      out->write("\n",1);
Packit df99a1
      { // extra nesting for windows
Packit df99a1
        for (int i=0; i
Packit df99a1
          out->write(" ",1);
Packit df99a1
      }
Packit df99a1
      nav->getBookMark(entry, pos++);
Packit df99a1
      out->write("(",1);
Packit df99a1
      str = entry->displayname;
Packit df99a1
      print_c_string(str, str.length(), *out, utf8);
Packit df99a1
      out->write("\n ",2);
Packit df99a1
      { // extra nesting for windows
Packit df99a1
        for (int i=0; i
Packit df99a1
          out->write(" ",1);
Packit df99a1
      }
Packit df99a1
      str = entry->url;
Packit df99a1
      print_c_string(str, str.length(), *out, utf8);
Packit df99a1
      print_outline_sub(nav, pos, entry->count, out, indent+1);
Packit df99a1
      out->write(" )",2);
Packit df99a1
      count--;
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_print_outline(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  GP<DjVmNav> nav = g().doc->get_djvm_nav();
Packit df99a1
  if (nav)
Packit df99a1
    {
Packit df99a1
      int pos = 0;
Packit df99a1
      int count = nav->getBookMarkCount();
Packit df99a1
      if (count > 0)
Packit df99a1
        {
Packit df99a1
          const GP<ByteStream> out = ByteStream::create("w");
Packit df99a1
          out->write("(bookmarks",10);
Packit df99a1
          print_outline_sub(nav, pos, count, out, 1);
Packit df99a1
          out->write(" )\n", 3);
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
construct_outline_sub(ParsingByteStream &pbs, GP<DjVmNav> nav, int &count)
Packit df99a1
{
Packit df99a1
  int c;
Packit df99a1
  GUTF8String name, url;
Packit df99a1
  GP<DjVmNav::DjVuBookMark> mark;
Packit df99a1
  if ((c = pbs.get_spaces(true)) != '\"')
Packit df99a1
    verror("Syntax error in outline: expecting name string,\n\tnear '%s'.",
Packit df99a1
           pbs.get_error_context(c) );    
Packit df99a1
  pbs.unget(c);
Packit df99a1
  name = pbs.get_token();
Packit df99a1
  if ((c = pbs.get_spaces(true)) != '\"')
Packit df99a1
    verror("Syntax error in outline: expecting url string,\n\tnear '%s'.",
Packit df99a1
           pbs.get_error_context(c) );    
Packit df99a1
  pbs.unget(c);
Packit df99a1
  url = pbs.get_token();
Packit df99a1
  mark = DjVmNav::DjVuBookMark::create(0, name, url);
Packit df99a1
  nav->append(mark);
Packit df99a1
  count += 1;
Packit df99a1
  while ((c = pbs.get_spaces(true)) == '(')
Packit df99a1
    construct_outline_sub(pbs, nav, mark->count);
Packit df99a1
  if (c != ')')
Packit df99a1
    verror("Syntax error in outline: expecting ')',\n\tnear '%s'.",
Packit df99a1
           pbs.get_error_context(c) );    
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DjVmNav>
Packit df99a1
construct_outline(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  GP<DjVmNav> nav(DjVmNav::create());
Packit df99a1
  int c = pbs.get_spaces(true);
Packit df99a1
  int count = 0;
Packit df99a1
  if (c == EOF)
Packit df99a1
    return 0;
Packit df99a1
  if (c!='(')
Packit df99a1
    verror("Syntax error in outline data: expecting '(bookmarks'");
Packit df99a1
  if (pbs.get_token()!="bookmarks")
Packit df99a1
    verror("Syntax error in outline data: expecting '(bookmarks'");    
Packit df99a1
  while ((c = pbs.get_spaces(true)) == '(')
Packit df99a1
    construct_outline_sub(pbs, nav, count);
Packit df99a1
  if (c != ')')
Packit df99a1
    verror("Syntax error in outline: expecting parenthesis,\n\tnear '%s'.",
Packit df99a1
           pbs.get_error_context(c) );
Packit df99a1
  if (pbs.get_spaces(true) != EOF)
Packit df99a1
    verror("Syntax error in outline: garbage after last ')',\n\tnear '%s'",
Packit df99a1
           pbs.get_error_context(c) );
Packit df99a1
  if (nav->getBookMarkCount() < 1)
Packit df99a1
    return 0;
Packit df99a1
  if (!nav->isValidBookmark())
Packit df99a1
    verror("Invalid outline data!");
Packit df99a1
  return nav;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_set_outline(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  const GP<ByteStream> outbs(ByteStream::create());
Packit df99a1
  get_data_from_file("set-outline", pbs, *outbs);
Packit df99a1
  outbs->seek(0);
Packit df99a1
  GP<ParsingByteStream> outpbs(ParsingByteStream::create(outbs));
Packit df99a1
  GP<DjVmNav> nav(construct_outline(*outpbs));
Packit df99a1
  if (g().doc->get_djvm_nav() != nav)
Packit df99a1
    {
Packit df99a1
      g().doc->set_djvm_nav(nav);
Packit df99a1
      modified = true;
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_remove_outline(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  if (g().doc->get_djvm_nav())
Packit df99a1
    {
Packit df99a1
      g().doc->set_djvm_nav(0);
Packit df99a1
      modified = true;
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
static bool
Packit df99a1
callback_thumbnails(int page_num, void *)
Packit df99a1
{
Packit df99a1
  vprint("set-thumbnails: processing page %d", page_num+1);
Packit df99a1
  return false;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_set_thumbnails(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  GUTF8String sizestr = pbs.get_token();
Packit df99a1
  if (! sizestr)
Packit df99a1
    sizestr = "128";
Packit df99a1
  if (! sizestr.is_int() )
Packit df99a1
    verror("expecting integer argument");
Packit df99a1
  int size = atoi(sizestr);
Packit df99a1
  if (size<32 || size >256) 
Packit df99a1
    verror("size should be between 32 and 256 (e.g. 128)");
Packit df99a1
  g().doc->generate_thumbnails(size, callback_thumbnails, NULL);
Packit df99a1
  modified = true;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_remove_thumbnails(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  g().doc->remove_thumbnails();
Packit df99a1
  modified = true;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_save_page(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  GUTF8String fname = pbs.get_token();
Packit df99a1
  if (! fname) 
Packit df99a1
    verror("empty filename");
Packit df99a1
  if (! g().file)
Packit df99a1
    verror("must select a single page first");
Packit df99a1
  if (nosave)
Packit df99a1
    vprint("save_page: not saving anything (-n was specified)");
Packit df99a1
  if (nosave)
Packit df99a1
    return;
Packit df99a1
  const GP<ByteStream> bs(g().file->get_djvu_bytestream(false, false));
Packit df99a1
  const GP<ByteStream> out(ByteStream::create(GURL::Filename::UTF8(fname), "wb"));
Packit df99a1
  out->writall("AT&T",4);
Packit df99a1
  out->copy(*bs);
Packit df99a1
  vprint("saved \"%s\" as \"%s\"  (without inserting included files)",
Packit df99a1
         (const char*)ToNative(g().fileid), (const char*)fname);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_save_page_with(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  GUTF8String fname = pbs.get_token();
Packit df99a1
  if (! fname) 
Packit df99a1
    verror("empty filename");
Packit df99a1
  if (! g().file)
Packit df99a1
    verror("must select a single page first");
Packit df99a1
  if (nosave)
Packit df99a1
    vprint("save-page-with: not saving anything (-n was specified)");
Packit df99a1
  if (nosave)
Packit df99a1
    return;
Packit df99a1
  const GP<ByteStream> bs(g().file->get_djvu_bytestream(true, false));
Packit df99a1
  const GP<ByteStream> out(ByteStream::create(GURL::Filename::UTF8(fname), "wb"));
Packit df99a1
  out->writall("AT&T",4);
Packit df99a1
  out->copy(*bs);
Packit df99a1
  vprint("saved \"%s\" as \"%s\"  (inserting included files)",
Packit df99a1
         (const char*)ToNative(g().fileid), (const char*)fname);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_save_bundled(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  GUTF8String fname = pbs.get_token();
Packit df99a1
  if (! fname) 
Packit df99a1
    verror("empty filename");
Packit df99a1
  if (nosave) 
Packit df99a1
    vprint("save-bundled: not saving anything (-n was specified)");
Packit df99a1
  else
Packit df99a1
    g().doc->save_as(GURL::Filename::UTF8(fname), true);
Packit df99a1
  modified = false;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_save_indirect(ParsingByteStream &pbs)
Packit df99a1
{
Packit df99a1
  GUTF8String fname = pbs.get_token();
Packit df99a1
  if (! fname) 
Packit df99a1
    verror("empty filename");
Packit df99a1
  if (nosave) 
Packit df99a1
    vprint("save-indirect: not saving anything (-n was specified)");
Packit df99a1
  else
Packit df99a1
    g().doc->save_as(GURL::Filename::UTF8(fname), false);
Packit df99a1
  modified = false;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_save(void)
Packit df99a1
{
Packit df99a1
  if (!g().doc->can_be_saved())
Packit df99a1
    verror("cannot save old format (use save-bundled or save-indirect)");
Packit df99a1
  if (nosave)
Packit df99a1
    vprint("save: not saving anything (-n was specified)");
Packit df99a1
  else if (!modified)
Packit df99a1
    vprint("save: document was not modified");
Packit df99a1
  else 
Packit df99a1
    g().doc->save();
Packit df99a1
  modified = false;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_save(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  command_save();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_help(void)
Packit df99a1
{
Packit df99a1
  fprintf(stderr,
Packit df99a1
          "\n"
Packit df99a1
          "Commands\n"
Packit df99a1
          "--------\n"
Packit df99a1
          "The following commands can be separated by newlines or semicolons.\n"
Packit df99a1
          "Comment lines start with '#'.  Commands usually operate on pages and files\n"
Packit df99a1
          "specified by the \"select\" command.  All pages and files are initially selected.\n"
Packit df99a1
          "A single page must be selected before executing commands marked with a period.\n"
Packit df99a1
          "Commands marked with an underline do not use the selection\n"
Packit df99a1
          "\n"
Packit df99a1
          "   ls                     -- list all pages/files\n"
Packit df99a1
          "   n                      -- list pages count\n"
Packit df99a1
          "   dump                   -- shows IFF structure\n"
Packit df99a1
          "   size                   -- prints page width and height in html friendly way\n"
Packit df99a1
          "   select                 -- selects the entire document\n"
Packit df99a1
          "   select <id>            -- selects a single page/file by name or page number\n"
Packit df99a1
          "   select-shared-ant      -- selects the shared annotations file\n"
Packit df99a1
          "   create-shared-ant      -- creates and select the shared annotations file\n"
Packit df99a1
          "   showsel                -- displays currently selected pages/files\n"
Packit df99a1
          " . print-ant              -- prints annotations\n"
Packit df99a1
          " . print-merged-ant       -- prints annotations including the shared annotations\n"
Packit df99a1
          " . print-meta             -- prints file metadatas (a subset of the annotations\n"
Packit df99a1
          "   print-txt              -- prints hidden text using a lisp syntax\n"
Packit df99a1
          "   print-pure-txt         -- print hidden text without coordinates\n"
Packit df99a1
          " _ print-outline          -- print outline (bookmarks)\n"
Packit df99a1
          " . print-xmp              -- print xmp annotations\n"
Packit df99a1
          "   output-ant             -- dumps ant as a valid cmdfile\n"
Packit df99a1
          "   output-txt             -- dumps text as a valid cmdfile\n"
Packit df99a1
          "   output-all             -- dumps ant and text as a valid cmdfile\n"
Packit df99a1
          " . set-ant [<antfile>]    -- copies <antfile> into the annotation chunk\n"
Packit df99a1
          " . set-meta [<metafile>]  -- copies <metafile> into the metadata annotation tag\n"
Packit df99a1
          " . set-txt [<txtfile>]    -- copies <txtfile> into the hidden text chunk\n"
Packit df99a1
          " . set-xmp [<xmpfile>]    -- copies <xmpfile> into the xmp metadata annotation tag\n" 
Packit df99a1
          " _ set-outline [<bmfile>] -- sets outline (bootmarks)\n"
Packit df99a1
          " _ set-thumbnails [<sz>]  -- generates all thumbnails with given size\n"
Packit df99a1
          "   set-rotation [+-]<rot> -- sets page rotation\n"
Packit df99a1
          "   set-dpi <dpi>          -- sets page resolution\n"
Packit df99a1
          "   remove-ant             -- removes annotations\n"
Packit df99a1
          "   remove-meta            -- removes metadatas without changing other annotations\n"
Packit df99a1
          "   remove-txt             -- removes hidden text\n"
Packit df99a1
          " _ remove-outline         -- removes outline (bookmarks)\n"
Packit df99a1
          " . remove-xmp             -- removes xmp metadata from annotation chunk\n"
Packit df99a1
          " _ remove-thumbnails      -- removes all thumbnails\n"
Packit df99a1
          " . set-page-title <title> -- sets an alternate page title\n"
Packit df99a1
          " . save-page <name>       -- saves selected page/file as is\n"
Packit df99a1
          " . save-page-with <name>  -- saves selected page/file, inserting all included files\n"
Packit df99a1
          " _ save-bundled <name>    -- saves as bundled document under fname\n"
Packit df99a1
          " _ save-indirect <name>   -- saves as indirect document under fname\n"
Packit df99a1
          " _ save                   -- saves in-place\n"
Packit df99a1
          " _ help                   -- prints this message\n"
Packit df99a1
          "\n"
Packit df99a1
          "Interactive example:\n"
Packit df99a1
          "--------------------\n"
Packit df99a1
          "  Type\n"
Packit df99a1
          "    %% djvused -v file.djvu\n"
Packit df99a1
          "  and play with the commands above\n"
Packit df99a1
          "\n"
Packit df99a1
          "Command line example:\n"
Packit df99a1
          "---------------------\n"
Packit df99a1
          "  Save all text and annotation chunks as a djvused script with\n"
Packit df99a1
          "    %% djvused file.djvu -e output-all > file.dsed\n"
Packit df99a1
          "  Then edit the script with any text editor.\n"
Packit df99a1
          "  Finally restore the modified text and annotation chunks with\n"
Packit df99a1
          "    %% djvused file.djvu -f file.dsed -s\n"
Packit df99a1
          "  You may use option -v to see more messages\n"
Packit df99a1
          "\n" );
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
command_help(ParsingByteStream &)
Packit df99a1
{
Packit df99a1
  command_help();
Packit df99a1
}
Packit df99a1
Packit df99a1
typedef void (*CommandFunc)(ParsingByteStream &pbs);
Packit df99a1
static GMap<GUTF8String,CommandFunc> &command_map() {
Packit df99a1
  static GMap<GUTF8String,CommandFunc> xcommand_map;
Packit df99a1
  static bool first=true;
Packit df99a1
  if(first) {
Packit df99a1
    first=false;
Packit df99a1
    xcommand_map["ls"] = command_ls;
Packit df99a1
    xcommand_map["n"] = command_n;
Packit df99a1
    xcommand_map["dump"] = command_dump;
Packit df99a1
    xcommand_map["size"] = command_size;
Packit df99a1
    xcommand_map["showsel"] = command_showsel;
Packit df99a1
    xcommand_map["select"] = command_select;
Packit df99a1
    xcommand_map["select-shared-ant"] = command_select_shared_ant;
Packit df99a1
    xcommand_map["create-shared-ant"] = command_create_shared_ant;
Packit df99a1
    xcommand_map["print-ant"] = command_print_ant;  
Packit df99a1
    xcommand_map["print-merged-ant"] = command_print_merged_ant;
Packit df99a1
    xcommand_map["print-meta"] = command_print_meta;
Packit df99a1
    xcommand_map["print-txt"] = command_print_txt;
Packit df99a1
    xcommand_map["print-pure-txt"] = command_print_pure_txt;
Packit df99a1
    xcommand_map["print-outline"] = command_print_outline;
Packit df99a1
    xcommand_map["print-xmp"] = command_print_xmp;
Packit df99a1
    xcommand_map["output-ant"] = command_output_ant;
Packit df99a1
    xcommand_map["output-txt"] = command_output_txt;
Packit df99a1
    xcommand_map["output-all"] = command_output_all;
Packit df99a1
    xcommand_map["set-ant"] = command_set_ant;
Packit df99a1
    xcommand_map["set-meta"] = command_set_meta;
Packit df99a1
    xcommand_map["set-txt"] = command_set_txt;
Packit df99a1
    xcommand_map["set-outline"] = command_set_outline;
Packit df99a1
    xcommand_map["set-xmp"] = command_set_xmp;
Packit df99a1
    xcommand_map["set-thumbnails"] = command_set_thumbnails;
Packit df99a1
    xcommand_map["set-rotation"] = command_set_rotation;
Packit df99a1
    xcommand_map["set-dpi"] = command_set_dpi;
Packit df99a1
    xcommand_map["remove-ant"] = command_remove_ant;
Packit df99a1
    xcommand_map["remove-meta"] = command_remove_meta;
Packit df99a1
    xcommand_map["remove-txt"] = command_remove_txt;
Packit df99a1
    xcommand_map["remove-outline"] = command_remove_outline;
Packit df99a1
    xcommand_map["remove-thumbnails"] = command_remove_thumbnails;
Packit df99a1
    xcommand_map["remove-xmp"] = command_remove_xmp;
Packit df99a1
    xcommand_map["set-page-title"] = command_set_page_title;
Packit df99a1
    xcommand_map["save-page"] = command_save_page;
Packit df99a1
    xcommand_map["save-page-with"] = command_save_page_with;
Packit df99a1
    xcommand_map["save-bundled"] = command_save_bundled;
Packit df99a1
    xcommand_map["save-indirect"] = command_save_indirect;
Packit df99a1
    xcommand_map["save"] = command_save;
Packit df99a1
    xcommand_map["help"] = command_help;
Packit df99a1
  }
Packit df99a1
  return xcommand_map;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
usage()
Packit df99a1
{
Packit df99a1
  DjVuPrintErrorUTF8(
Packit df99a1
#ifdef DJVULIBRE_VERSION
Packit df99a1
          "DJVUSED --- DjVuLibre-" DJVULIBRE_VERSION "\n"
Packit df99a1
#endif
Packit df99a1
          "Simple DjVu file manipulation program\n"
Packit df99a1
          "\n"
Packit df99a1
          "Usage: djvused [options] djvufile\n"
Packit df99a1
          "Executes scripting commands on djvufile.\n"
Packit df99a1
          "Script command come either from a script file (option -f),\n"
Packit df99a1
          "from the command line (option -e), or from stdin (default).\n"
Packit df99a1
          "\n"
Packit df99a1
          "Options are\n"
Packit df99a1
          "  -v               -- verbose\n"
Packit df99a1
          "  -f <scriptfile>  -- take commands from a file\n"
Packit df99a1
          "  -e <script>      -- take commands from the command line\n"
Packit df99a1
          "  -s               -- save after execution\n"
Packit df99a1
          "  -u               -- produces utf8 instead of escaping non ascii chars\n"
Packit df99a1
          "  -n               -- do not save anything\n"
Packit df99a1
          "\n"
Packit df99a1
          );
Packit df99a1
  command_help();
Packit df99a1
  exit(10);
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
// --------------------------------------------------
Packit df99a1
// MAIN
Packit df99a1
// --------------------------------------------------
Packit df99a1
Packit df99a1
void 
Packit df99a1
execute()
Packit df99a1
{
Packit df99a1
  if (!g().cmdbs)
Packit df99a1
    g().cmdbs = ByteStream::create("r");
Packit df99a1
  const GP<ParsingByteStream> gcmd(ParsingByteStream::create(g().cmdbs));
Packit df99a1
  ParsingByteStream &cmd=*gcmd;
Packit df99a1
  GUTF8String token;
Packit df99a1
  vprint("type \"help\" to see available commands.");
Packit df99a1
  vprint("ok.");
Packit df99a1
  while (!! (token = cmd.get_token(true)))
Packit df99a1
    {
Packit df99a1
      CommandFunc func = command_map()[token];
Packit df99a1
      G_TRY
Packit df99a1
        {
Packit df99a1
          if (!func) 
Packit df99a1
            verror("unrecognized command");
Packit df99a1
          // Cautious execution
Packit df99a1
          (*func)(cmd);
Packit df99a1
          // Skip extra arguments
Packit df99a1
          int c = cmd.get_spaces();
Packit df99a1
          if (c!=';' && c!='\n' && c!='\r' && c!=EOF)
Packit df99a1
            {
Packit df99a1
              while (c!=';' && c!='\n' && c!='\r' && c!=EOF)
Packit df99a1
                c = cmd.get();
Packit df99a1
              verror("too many arguments");
Packit df99a1
            }
Packit df99a1
          cmd.unget(c);
Packit df99a1
        }
Packit df99a1
      G_CATCH(ex)
Packit df99a1
        {
Packit df99a1
          vprint("Error (%s): %s",
Packit df99a1
                 (const char*)ToNative(token), ex.get_cause());
Packit df99a1
          if (! verbose)
Packit df99a1
            G_RETHROW;
Packit df99a1
        }
Packit df99a1
      G_ENDCATCH;
Packit df99a1
      vprint("ok.");
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
int 
Packit df99a1
main(int argc, char **argv)
Packit df99a1
{
Packit df99a1
  DJVU_LOCALE;
Packit df99a1
  G_TRY
Packit df99a1
     {
Packit df99a1
      { // extra nesting for windows
Packit df99a1
        for (int i=1; i
Packit df99a1
          if (!strcmp(argv[i],"-v"))
Packit df99a1
            verbose = true;
Packit df99a1
          else if (!strcmp(argv[i],"-s"))
Packit df99a1
            save = true; 
Packit df99a1
          else if (!strcmp(argv[i],"-n"))
Packit df99a1
            nosave = true;
Packit df99a1
          else if (!strcmp(argv[i],"-u"))
Packit df99a1
            utf8 = true;
Packit df99a1
          else if (!strcmp(argv[i],"-f") && i+1
Packit df99a1
            g().cmdbs = ByteStream::create(GURL::Filename::UTF8(GNativeString(argv[++i])), "r");
Packit df99a1
          else if (!strcmp(argv[i],"-e") && !g().cmdbs && i+1
Packit df99a1
            g().cmdbs = ByteStream::create_static(argv[i],strlen(argv[i]));
Packit df99a1
          else if (argv[i][0] != '-' && !g().djvufile)
Packit df99a1
            g().djvufile = GNativeString(argv[i]);
Packit df99a1
          else
Packit df99a1
            usage();
Packit df99a1
      }
Packit df99a1
      if (!g().djvufile)
Packit df99a1
        usage();
Packit df99a1
      // BOM
Packit df99a1
#ifdef _WIN32
Packit df99a1
      if (utf8)
Packit df99a1
        fwrite(utf8bom, sizeof(utf8bom), 1, stdout);
Packit df99a1
#endif
Packit df99a1
      // Open file
Packit df99a1
      g().doc = DjVuDocEditor::create_wait(GURL::Filename::UTF8(g().djvufile));
Packit df99a1
      select_all();
Packit df99a1
      // Execute
Packit df99a1
      execute();
Packit df99a1
      if (modified)
Packit df99a1
	{
Packit df99a1
	  if (save)
Packit df99a1
	    command_save();
Packit df99a1
	  else
Packit df99a1
	    fprintf(stderr,"djvused: (warning) file was modified but not saved\n");
Packit df99a1
	}
Packit df99a1
     }
Packit df99a1
  G_CATCH(ex)
Packit df99a1
    {
Packit df99a1
      ex.perror();
Packit df99a1
      return 10;
Packit df99a1
    }
Packit df99a1
  G_ENDCATCH;
Packit df99a1
  return 0;
Packit df99a1
}