]> www.fi.muni.cz Git - evince.git/commitdiff
Initial revision
authorMartin Kretzschmar <mkretzschmar@src.gnome.org>
Mon, 31 Mar 2003 16:45:09 +0000 (16:45 +0000)
committerMartin Kretzschmar <mkretzschmar@src.gnome.org>
Mon, 31 Mar 2003 16:45:09 +0000 (16:45 +0000)
28 files changed:
pdf/aconf2.h [new file with mode: 0644]
pdf/xpdf/JBIG2Stream.cc [new file with mode: 0644]
pdf/xpdf/JBIG2Stream.h [new file with mode: 0644]
pdf/xpdf/Outline.cc [new file with mode: 0644]
pdf/xpdf/Outline.h [new file with mode: 0644]
pdf/xpdf/PDFDocEncoding.cc [new file with mode: 0644]
pdf/xpdf/PDFDocEncoding.h [new file with mode: 0644]
pdf/xpdf/XPDFApp.cc [new file with mode: 0644]
pdf/xpdf/XPDFApp.h [new file with mode: 0644]
pdf/xpdf/XPDFCore.cc [new file with mode: 0644]
pdf/xpdf/XPDFCore.h [new file with mode: 0644]
pdf/xpdf/XPDFTree.cc [new file with mode: 0644]
pdf/xpdf/XPDFTree.h [new file with mode: 0644]
pdf/xpdf/XPDFTreeP.h [new file with mode: 0644]
pdf/xpdf/XPDFViewer.cc [new file with mode: 0644]
pdf/xpdf/XPDFViewer.h [new file with mode: 0644]
pdf/xpdf/XPixmapOutputDev.cc [new file with mode: 0644]
pdf/xpdf/XPixmapOutputDev.h [new file with mode: 0644]
pdf/xpdf/about-text.h [new file with mode: 0644]
pdf/xpdf/backArrowDis.xbm [new file with mode: 0644]
pdf/xpdf/dblLeftArrowDis.xbm [new file with mode: 0644]
pdf/xpdf/dblRightArrowDis.xbm [new file with mode: 0644]
pdf/xpdf/findDis.xbm [new file with mode: 0644]
pdf/xpdf/forwardArrowDis.xbm [new file with mode: 0644]
pdf/xpdf/leftArrowDis.xbm [new file with mode: 0644]
pdf/xpdf/print.xbm [new file with mode: 0644]
pdf/xpdf/printDis.xbm [new file with mode: 0644]
pdf/xpdf/rightArrowDis.xbm [new file with mode: 0644]

diff --git a/pdf/aconf2.h b/pdf/aconf2.h
new file mode 100644 (file)
index 0000000..379eea2
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * aconf2.h
+ *
+ * This gets included by aconf.h, and contains miscellaneous global
+ * settings not directly controlled by autoconf.  This is a separate
+ * file because otherwise the configure script will munge any
+ * #define/#undef constructs.
+ *
+ * Copyright 2002 Glyph & Cog, LLC
+ */
+
+#ifndef ACONF2_H
+#define ACONF2_H
+
+/*
+ * This controls the use of the interface/implementation pragmas.
+ */
+#ifdef __GNUC__
+#define USE_GCC_PRAGMAS
+#endif
+/* There is a bug in the version of gcc which ships with MacOS X 10.2 */
+#ifdef MAC_OS_X_VERSION_MAX_ALLOWED
+#  if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2
+#    undef USE_GCC_PRAGMAS
+#  endif
+#endif
+
+#endif
diff --git a/pdf/xpdf/JBIG2Stream.cc b/pdf/xpdf/JBIG2Stream.cc
new file mode 100644 (file)
index 0000000..716fee1
--- /dev/null
@@ -0,0 +1,3473 @@
+//========================================================================
+//
+// JBIG2Stream.cc
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include "GList.h"
+#include "Error.h"
+#include "JBIG2Stream.h"
+
+//~ share these tables
+#include "Stream-CCITT.h"
+
+//------------------------------------------------------------------------
+
+static int contextSize[4] = { 16, 13, 10, 10 };
+static int refContextSize[2] = { 13, 10 };
+
+//------------------------------------------------------------------------
+// JBIG2ArithmeticDecoderStats
+//------------------------------------------------------------------------
+
+class JBIG2ArithmeticDecoderStats {
+public:
+
+  JBIG2ArithmeticDecoderStats(int contextSizeA);
+  ~JBIG2ArithmeticDecoderStats();
+  JBIG2ArithmeticDecoderStats *copy();
+  void reset();
+  int getContextSize() { return contextSize; }
+  void copyFrom(JBIG2ArithmeticDecoderStats *stats);
+
+private:
+
+  Guchar *cxTab;               // cxTab[cx] = (i[cx] << 1) + mps[cx]
+  int contextSize;
+
+  friend class JBIG2ArithmeticDecoder;
+};
+
+JBIG2ArithmeticDecoderStats::JBIG2ArithmeticDecoderStats(int contextSizeA) {
+  contextSize = contextSizeA;
+  cxTab = (Guchar *)gmalloc((1 << contextSize) * sizeof(Guchar));
+  reset();
+}
+
+JBIG2ArithmeticDecoderStats::~JBIG2ArithmeticDecoderStats() {
+  gfree(cxTab);
+}
+
+JBIG2ArithmeticDecoderStats *JBIG2ArithmeticDecoderStats::copy() {
+  JBIG2ArithmeticDecoderStats *stats;
+
+  stats = new JBIG2ArithmeticDecoderStats(contextSize);
+  memcpy(stats->cxTab, cxTab, 1 << contextSize);
+  return stats;
+}
+
+void JBIG2ArithmeticDecoderStats::reset() {
+  memset(cxTab, 0, 1 << contextSize);
+}
+
+void JBIG2ArithmeticDecoderStats::copyFrom(
+                                     JBIG2ArithmeticDecoderStats *stats) {
+  memcpy(cxTab, stats->cxTab, 1 << contextSize);
+}
+
+//------------------------------------------------------------------------
+// JBIG2ArithmeticDecoder
+//------------------------------------------------------------------------
+
+class JBIG2ArithmeticDecoder {
+public:
+
+  JBIG2ArithmeticDecoder();
+  ~JBIG2ArithmeticDecoder();
+  void setStream(Stream *strA) { str = strA; }
+  void start();
+  int decodeBit(Guint context, JBIG2ArithmeticDecoderStats *stats);
+  int decodeByte(Guint context, JBIG2ArithmeticDecoderStats *stats);
+
+  // Returns false for OOB, otherwise sets *<x> and returns true.
+  GBool decodeInt(int *x, JBIG2ArithmeticDecoderStats *stats);
+
+  Guint decodeIAID(Guint codeLen,
+                  JBIG2ArithmeticDecoderStats *stats);
+
+private:
+
+  int decodeIntBit(JBIG2ArithmeticDecoderStats *stats);
+  void byteIn();
+
+  static Guint qeTab[47];
+  static int nmpsTab[47];
+  static int nlpsTab[47];
+  static int switchTab[47];
+
+  Guint buf0, buf1;
+  Guint c, a;
+  int ct;
+
+  Guint prev;                  // for the integer decoder
+
+  Stream *str;
+};
+
+Guint JBIG2ArithmeticDecoder::qeTab[47] = {
+  0x56010000, 0x34010000, 0x18010000, 0x0AC10000,
+  0x05210000, 0x02210000, 0x56010000, 0x54010000,
+  0x48010000, 0x38010000, 0x30010000, 0x24010000,
+  0x1C010000, 0x16010000, 0x56010000, 0x54010000,
+  0x51010000, 0x48010000, 0x38010000, 0x34010000,
+  0x30010000, 0x28010000, 0x24010000, 0x22010000,
+  0x1C010000, 0x18010000, 0x16010000, 0x14010000,
+  0x12010000, 0x11010000, 0x0AC10000, 0x09C10000,
+  0x08A10000, 0x05210000, 0x04410000, 0x02A10000,
+  0x02210000, 0x01410000, 0x01110000, 0x00850000,
+  0x00490000, 0x00250000, 0x00150000, 0x00090000,
+  0x00050000, 0x00010000, 0x56010000
+};
+
+int JBIG2ArithmeticDecoder::nmpsTab[47] = {
+   1,  2,  3,  4,  5, 38,  7,  8,  9, 10, 11, 12, 13, 29, 15, 16,
+  17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+  33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46
+};
+
+int JBIG2ArithmeticDecoder::nlpsTab[47] = {
+   1,  6,  9, 12, 29, 33,  6, 14, 14, 14, 17, 18, 20, 21, 14, 14,
+  15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+  30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 46
+};
+
+int JBIG2ArithmeticDecoder::switchTab[47] = {
+  1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+JBIG2ArithmeticDecoder::JBIG2ArithmeticDecoder() {
+  str = NULL;
+}
+
+JBIG2ArithmeticDecoder::~JBIG2ArithmeticDecoder() {
+}
+
+void JBIG2ArithmeticDecoder::start() {
+  buf0 = (Guint)str->getChar() & 0xff;
+  buf1 = (Guint)str->getChar() & 0xff;
+
+  // INITDEC
+  c = (buf0 ^ 0xff) << 16;
+  byteIn();
+  c <<= 7;
+  ct -= 7;
+  a = 0x80000000;
+}
+
+int JBIG2ArithmeticDecoder::decodeBit(Guint context,
+                                     JBIG2ArithmeticDecoderStats *stats) {
+  int bit;
+  Guint qe;
+  int iCX, mpsCX;
+
+  iCX = stats->cxTab[context] >> 1;
+  mpsCX = stats->cxTab[context] & 1;
+  qe = qeTab[iCX];
+  a -= qe;
+  if (c < a) {
+    if (a & 0x80000000) {
+      bit = mpsCX;
+    } else {
+      // MPS_EXCHANGE
+      if (a < qe) {
+       bit = 1 - mpsCX;
+       if (switchTab[iCX]) {
+         stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);
+       } else {
+         stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;
+       }
+      } else {
+       bit = mpsCX;
+       stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;
+      }
+      // RENORMD
+      do {
+       if (ct == 0) {
+         byteIn();
+       }
+       a <<= 1;
+       c <<= 1;
+       --ct;
+      } while (!(a & 0x80000000));
+    }
+  } else {
+    c -= a;
+    // LPS_EXCHANGE
+    if (a < qe) {
+      bit = mpsCX;
+      stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;
+    } else {
+      bit = 1 - mpsCX;
+      if (switchTab[iCX]) {
+       stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);
+      } else {
+       stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;
+      }
+    }
+    a = qe;
+    // RENORMD
+    do {
+      if (ct == 0) {
+       byteIn();
+      }
+      a <<= 1;
+      c <<= 1;
+      --ct;
+    } while (!(a & 0x80000000));
+  }
+  return bit;
+}
+
+int JBIG2ArithmeticDecoder::decodeByte(Guint context,
+                                      JBIG2ArithmeticDecoderStats *stats) {
+  int byte;
+  int i;
+
+  byte = 0;
+  for (i = 0; i < 8; ++i) {
+    byte = (byte << 1) | decodeBit(context, stats);
+  }
+  return byte;
+}
+
+GBool JBIG2ArithmeticDecoder::decodeInt(int *x,
+                                       JBIG2ArithmeticDecoderStats *stats) {
+  int s;
+  Guint v;
+  int i;
+
+  prev = 1;
+  s = decodeIntBit(stats);
+  if (decodeIntBit(stats)) {
+    if (decodeIntBit(stats)) {
+      if (decodeIntBit(stats)) {
+       if (decodeIntBit(stats)) {
+         if (decodeIntBit(stats)) {
+           v = 0;
+           for (i = 0; i < 32; ++i) {
+             v = (v << 1) | decodeIntBit(stats);
+           }
+           v += 4436;
+         } else {
+           v = 0;
+           for (i = 0; i < 12; ++i) {
+             v = (v << 1) | decodeIntBit(stats);
+           }
+           v += 340;
+         }
+       } else {
+         v = 0;
+         for (i = 0; i < 8; ++i) {
+           v = (v << 1) | decodeIntBit(stats);
+         }
+         v += 84;
+       }
+      } else {
+       v = 0;
+       for (i = 0; i < 6; ++i) {
+         v = (v << 1) | decodeIntBit(stats);
+       }
+       v += 20;
+      }
+    } else {
+      v = decodeIntBit(stats);
+      v = (v << 1) | decodeIntBit(stats);
+      v = (v << 1) | decodeIntBit(stats);
+      v = (v << 1) | decodeIntBit(stats);
+      v += 4;
+    }
+  } else {
+    v = decodeIntBit(stats);
+    v = (v << 1) | decodeIntBit(stats);
+  }
+
+  if (s) {
+    if (v == 0) {
+      return gFalse;
+    }
+    *x = -(int)v;
+  } else {
+    *x = (int)v;
+  }
+  return gTrue;
+}
+
+int JBIG2ArithmeticDecoder::decodeIntBit(JBIG2ArithmeticDecoderStats *stats) {
+  int bit;
+
+  bit = decodeBit(prev, stats);
+  if (prev < 0x100) {
+    prev = (prev << 1) | bit;
+  } else {
+    prev = (((prev << 1) | bit) & 0x1ff) | 0x100;
+  }
+  return bit;
+}
+
+Guint JBIG2ArithmeticDecoder::decodeIAID(Guint codeLen,
+                                        JBIG2ArithmeticDecoderStats *stats) {
+  Guint i;
+  int bit;
+
+  prev = 1;
+  for (i = 0; i < codeLen; ++i) {
+    bit = decodeBit(prev, stats);
+    prev = (prev << 1) | bit;
+  }
+  return prev - (1 << codeLen);
+}
+
+void JBIG2ArithmeticDecoder::byteIn() {
+  if (buf0 == 0xff) {
+    if (buf1 > 0x8f) {
+      ct = 8;
+    } else {
+      buf0 = buf1;
+      buf1 = (Guint)str->getChar() & 0xff;
+      c = c + 0xfe00 - (buf0 << 9);
+      ct = 7;
+    }
+  } else {
+    buf0 = buf1;
+    buf1 = (Guint)str->getChar() & 0xff;
+    c = c + 0xff00 - (buf0 << 8);
+    ct = 8;
+  }
+}
+
+//------------------------------------------------------------------------
+// JBIG2HuffmanTable
+//------------------------------------------------------------------------
+
+#define jbig2HuffmanLOW 0xfffffffd
+#define jbig2HuffmanOOB 0xfffffffe
+#define jbig2HuffmanEOT 0xffffffff
+
+struct JBIG2HuffmanTable {
+  int val;
+  Guint prefixLen;
+  Guint rangeLen;              // can also be LOW, OOB, or EOT
+  Guint prefix;
+};
+
+JBIG2HuffmanTable huffTableA[] = {
+  {     0, 1,  4,              0x000 },
+  {    16, 2,  8,              0x002 },
+  {   272, 3, 16,              0x006 },
+  { 65808, 3, 32,              0x007 },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableB[] = {
+  {     0, 1,  0,              0x000 },
+  {     1, 2,  0,              0x002 },
+  {     2, 3,  0,              0x006 },
+  {     3, 4,  3,              0x00e },
+  {    11, 5,  6,              0x01e },
+  {    75, 6, 32,              0x03e },
+  {     0, 6, jbig2HuffmanOOB, 0x03f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableC[] = {
+  {     0, 1,  0,              0x000 },
+  {     1, 2,  0,              0x002 },
+  {     2, 3,  0,              0x006 },
+  {     3, 4,  3,              0x00e },
+  {    11, 5,  6,              0x01e },
+  {     0, 6, jbig2HuffmanOOB, 0x03e },
+  {    75, 7, 32,              0x0fe },
+  {  -256, 8,  8,              0x0fe },
+  {  -257, 8, jbig2HuffmanLOW, 0x0ff },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableD[] = {
+  {     1, 1,  0,              0x000 },
+  {     2, 2,  0,              0x002 },
+  {     3, 3,  0,              0x006 },
+  {     4, 4,  3,              0x00e },
+  {    12, 5,  6,              0x01e },
+  {    76, 5, 32,              0x01f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableE[] = {
+  {     1, 1,  0,              0x000 },
+  {     2, 2,  0,              0x002 },
+  {     3, 3,  0,              0x006 },
+  {     4, 4,  3,              0x00e },
+  {    12, 5,  6,              0x01e },
+  {    76, 6, 32,              0x03e },
+  {  -255, 7,  8,              0x07e },
+  {  -256, 7, jbig2HuffmanLOW, 0x07f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableF[] = {
+  {     0, 2,  7,              0x000 },
+  {   128, 3,  7,              0x002 },
+  {   256, 3,  8,              0x003 },
+  { -1024, 4,  9,              0x008 },
+  {  -512, 4,  8,              0x009 },
+  {  -256, 4,  7,              0x00a },
+  {   -32, 4,  5,              0x00b },
+  {   512, 4,  9,              0x00c },
+  {  1024, 4, 10,              0x00d },
+  { -2048, 5, 10,              0x01c },
+  {  -128, 5,  6,              0x01d },
+  {   -64, 5,  5,              0x01e },
+  { -2049, 6, jbig2HuffmanLOW, 0x03e },
+  {  2048, 6, 32,              0x03f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableG[] = {
+  {  -512, 3,  8,              0x000 },
+  {   256, 3,  8,              0x001 },
+  {   512, 3,  9,              0x002 },
+  {  1024, 3, 10,              0x003 },
+  { -1024, 4,  9,              0x008 },
+  {  -256, 4,  7,              0x009 },
+  {   -32, 4,  5,              0x00a },
+  {     0, 4,  5,              0x00b },
+  {   128, 4,  7,              0x00c },
+  {  -128, 5,  6,              0x01a },
+  {   -64, 5,  5,              0x01b },
+  {    32, 5,  5,              0x01c },
+  {    64, 5,  6,              0x01d },
+  { -1025, 5, jbig2HuffmanLOW, 0x01e },
+  {  2048, 5, 32,              0x01f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableH[] = {
+  {     0, 2,  1,              0x000 },
+  {     0, 2, jbig2HuffmanOOB, 0x001 },
+  {     4, 3,  4,              0x004 },
+  {    -1, 4,  0,              0x00a },
+  {    22, 4,  4,              0x00b },
+  {    38, 4,  5,              0x00c },
+  {     2, 5,  0,              0x01a },
+  {    70, 5,  6,              0x01b },
+  {   134, 5,  7,              0x01c },
+  {     3, 6,  0,              0x03a },
+  {    20, 6,  1,              0x03b },
+  {   262, 6,  7,              0x03c },
+  {   646, 6, 10,              0x03d },
+  {    -2, 7,  0,              0x07c },
+  {   390, 7,  8,              0x07d },
+  {   -15, 8,  3,              0x0fc },
+  {    -5, 8,  1,              0x0fd },
+  {    -7, 9,  1,              0x1fc },
+  {    -3, 9,  0,              0x1fd },
+  {   -16, 9, jbig2HuffmanLOW, 0x1fe },
+  {  1670, 9, 32,              0x1ff },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableI[] = {
+  {     0, 2, jbig2HuffmanOOB, 0x000 },
+  {    -1, 3,  1,              0x002 },
+  {     1, 3,  1,              0x003 },
+  {     7, 3,  5,              0x004 },
+  {    -3, 4,  1,              0x00a },
+  {    43, 4,  5,              0x00b },
+  {    75, 4,  6,              0x00c },
+  {     3, 5,  1,              0x01a },
+  {   139, 5,  7,              0x01b },
+  {   267, 5,  8,              0x01c },
+  {     5, 6,  1,              0x03a },
+  {    39, 6,  2,              0x03b },
+  {   523, 6,  8,              0x03c },
+  {  1291, 6, 11,              0x03d },
+  {    -5, 7,  1,              0x07c },
+  {   779, 7,  9,              0x07d },
+  {   -31, 8,  4,              0x0fc },
+  {   -11, 8,  2,              0x0fd },
+  {   -15, 9,  2,              0x1fc },
+  {    -7, 9,  1,              0x1fd },
+  {   -32, 9, jbig2HuffmanLOW, 0x1fe },
+  {  3339, 9, 32,              0x1ff },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableJ[] = {
+  {    -2, 2,  2,              0x000 },
+  {     6, 2,  6,              0x001 },
+  {     0, 2, jbig2HuffmanOOB, 0x002 },
+  {    -3, 5,  0,              0x018 },
+  {     2, 5,  0,              0x019 },
+  {    70, 5,  5,              0x01a },
+  {     3, 6,  0,              0x036 },
+  {   102, 6,  5,              0x037 },
+  {   134, 6,  6,              0x038 },
+  {   198, 6,  7,              0x039 },
+  {   326, 6,  8,              0x03a },
+  {   582, 6,  9,              0x03b },
+  {  1094, 6, 10,              0x03c },
+  {   -21, 7,  4,              0x07a },
+  {    -4, 7,  0,              0x07b },
+  {     4, 7,  0,              0x07c },
+  {  2118, 7, 11,              0x07d },
+  {    -5, 8,  0,              0x0fc },
+  {     5, 8,  0,              0x0fd },
+  {   -22, 8, jbig2HuffmanLOW, 0x0fe },
+  {  4166, 8, 32,              0x0ff },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableK[] = {
+  {     1, 1,  0,              0x000 },
+  {     2, 2,  1,              0x002 },
+  {     4, 4,  0,              0x00c },
+  {     5, 4,  1,              0x00d },
+  {     7, 5,  1,              0x01c },
+  {     9, 5,  2,              0x01d },
+  {    13, 6,  2,              0x03c },
+  {    17, 7,  2,              0x07a },
+  {    21, 7,  3,              0x07b },
+  {    29, 7,  4,              0x07c },
+  {    45, 7,  5,              0x07d },
+  {    77, 7,  6,              0x07e },
+  {   141, 7, 32,              0x07f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableL[] = {
+  {     1, 1,  0,              0x000 },
+  {     2, 2,  0,              0x002 },
+  {     3, 3,  1,              0x006 },
+  {     5, 5,  0,              0x01c },
+  {     6, 5,  1,              0x01d },
+  {     8, 6,  1,              0x03c },
+  {    10, 7,  0,              0x07a },
+  {    11, 7,  1,              0x07b },
+  {    13, 7,  2,              0x07c },
+  {    17, 7,  3,              0x07d },
+  {    25, 7,  4,              0x07e },
+  {    41, 8,  5,              0x0fe },
+  {    73, 8, 32,              0x0ff },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableM[] = {
+  {     1, 1,  0,              0x000 },
+  {     2, 3,  0,              0x004 },
+  {     7, 3,  3,              0x005 },
+  {     3, 4,  0,              0x00c },
+  {     5, 4,  1,              0x00d },
+  {     4, 5,  0,              0x01c },
+  {    15, 6,  1,              0x03a },
+  {    17, 6,  2,              0x03b },
+  {    21, 6,  3,              0x03c },
+  {    29, 6,  4,              0x03d },
+  {    45, 6,  5,              0x03e },
+  {    77, 7,  6,              0x07e },
+  {   141, 7, 32,              0x07f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableN[] = {
+  {     0, 1,  0,              0x000 },
+  {    -2, 3,  0,              0x004 },
+  {    -1, 3,  0,              0x005 },
+  {     1, 3,  0,              0x006 },
+  {     2, 3,  0,              0x007 },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+JBIG2HuffmanTable huffTableO[] = {
+  {     0, 1,  0,              0x000 },
+  {    -1, 3,  0,              0x004 },
+  {     1, 3,  0,              0x005 },
+  {    -2, 4,  0,              0x00c },
+  {     2, 4,  0,              0x00d },
+  {    -4, 5,  1,              0x01c },
+  {     3, 5,  1,              0x01d },
+  {    -8, 6,  2,              0x03c },
+  {     5, 6,  2,              0x03d },
+  {   -24, 7,  4,              0x07c },
+  {     9, 7,  4,              0x07d },
+  {   -25, 7, jbig2HuffmanLOW, 0x07e },
+  {    25, 7, 32,              0x07f },
+  {     0, 0, jbig2HuffmanEOT, 0     }
+};
+
+//------------------------------------------------------------------------
+// JBIG2HuffmanDecoder
+//------------------------------------------------------------------------
+
+class JBIG2HuffmanDecoder {
+public:
+
+  JBIG2HuffmanDecoder();
+  ~JBIG2HuffmanDecoder();
+  void setStream(Stream *strA) { str = strA; }
+
+  void reset();
+
+  // Returns false for OOB, otherwise sets *<x> and returns true.
+  GBool decodeInt(int *x, JBIG2HuffmanTable *table);
+
+  Guint readBits(Guint n);
+  Guint readBit();
+
+  // Sort the table by prefix length and assign prefix values.
+  void buildTable(JBIG2HuffmanTable *table, Guint len);
+
+private:
+
+  Stream *str;
+  Guint buf;
+  Guint bufLen;
+};
+
+JBIG2HuffmanDecoder::JBIG2HuffmanDecoder() {
+  str = NULL;
+  reset();
+}
+
+JBIG2HuffmanDecoder::~JBIG2HuffmanDecoder() {
+}
+
+void JBIG2HuffmanDecoder::reset() {
+  buf = 0;
+  bufLen = 0;
+}
+
+//~ optimize this
+GBool JBIG2HuffmanDecoder::decodeInt(int *x, JBIG2HuffmanTable *table) {
+  Guint i, len, prefix;
+
+  i = 0;
+  len = 0;
+  prefix = 0;
+  while (table[i].rangeLen != jbig2HuffmanEOT) {
+    //~ if buildTable removes the entries with prefixLen=0, this is unneeded
+    if (table[i].prefixLen > 0) {
+      while (len < table[i].prefixLen) {
+       prefix = (prefix << 1) | readBit();
+       ++len;
+      }
+      if (prefix == table[i].prefix) {
+       if (table[i].rangeLen == jbig2HuffmanOOB) {
+         return gFalse;
+       }
+       if (table[i].rangeLen == jbig2HuffmanLOW) {
+         *x = table[i].val - readBits(32);
+       } else if (table[i].rangeLen > 0) {
+         *x = table[i].val + readBits(table[i].rangeLen);
+       } else {
+         *x = table[i].val;
+       }
+       return gTrue;
+      }
+    }
+    ++i;
+  }
+  return gFalse;
+}
+
+Guint JBIG2HuffmanDecoder::readBits(Guint n) {
+  Guint x, mask, nLeft;
+
+  mask = (n == 32) ? 0xffffffff : ((1 << n) - 1);
+  if (bufLen >= n) {
+    x = (buf >> (bufLen - n)) & mask;
+    bufLen -= n;
+  } else {
+    x = buf & ((1 << bufLen) - 1);
+    nLeft = n - bufLen;
+    bufLen = 0;
+    while (nLeft >= 8) {
+      x = (x << 8) | (str->getChar() & 0xff);
+      nLeft -= 8;
+    }
+    if (nLeft > 0) {
+      buf = str->getChar();
+      bufLen = 8 - nLeft;
+      x = (x << nLeft) | ((buf >> bufLen) & ((1 << nLeft) - 1));
+    }
+  }
+  return x;
+}
+
+Guint JBIG2HuffmanDecoder::readBit() {
+  if (bufLen == 0) {
+    buf = str->getChar();
+    bufLen = 8;
+  }
+  --bufLen;
+  return (buf >> bufLen) & 1;
+}
+
+static int cmpHuffmanTabEntries(const void *p1, const void *p2) {
+  return ((JBIG2HuffmanTable *)p1)->prefixLen
+         - ((JBIG2HuffmanTable *)p2)->prefixLen;
+}
+
+//~ should remove entries with prefixLen = 0
+void JBIG2HuffmanDecoder::buildTable(JBIG2HuffmanTable *table, Guint len) {
+  Guint i, prefix;
+
+  qsort(table, len, sizeof(JBIG2HuffmanTable), &cmpHuffmanTabEntries);
+  for (i = 0; i < len && table[i].prefixLen == 0; ++i) {
+    table[i].prefix = 0;
+  }
+  prefix = 0;
+  table[i++].prefix = prefix++;
+  for (; i < len; ++i) {
+    prefix <<= table[i].prefixLen - table[i-1].prefixLen;
+    table[i].prefix = prefix++;
+  }
+}
+
+//------------------------------------------------------------------------
+// JBIG2MMRDecoder
+//------------------------------------------------------------------------
+
+class JBIG2MMRDecoder {
+public:
+
+  JBIG2MMRDecoder();
+  ~JBIG2MMRDecoder();
+  void setStream(Stream *strA) { str = strA; }
+  void reset();
+  int get2DCode();
+  int getBlackCode();
+  int getWhiteCode();
+  Guint get24Bits();
+  void skipTo(Guint length);
+
+private:
+
+  Stream *str;
+  Guint buf;
+  Guint bufLen;
+  Guint nBytesRead;
+};
+
+JBIG2MMRDecoder::JBIG2MMRDecoder() {
+  str = NULL;
+  reset();
+}
+
+JBIG2MMRDecoder::~JBIG2MMRDecoder() {
+}
+
+void JBIG2MMRDecoder::reset() {
+  buf = 0;
+  bufLen = 0;
+  nBytesRead = 0;
+}
+
+int JBIG2MMRDecoder::get2DCode() {
+  CCITTCode *p;
+
+  if (bufLen == 0) {
+    buf = str->getChar() & 0xff;
+    bufLen = 8;
+    ++nBytesRead;
+    p = &twoDimTab1[(buf >> 1) & 0x7f];
+  } else if (bufLen == 8) {
+    p = &twoDimTab1[(buf >> 1) & 0x7f];
+  } else {
+    p = &twoDimTab1[(buf << (7 - bufLen)) & 0x7f];
+    if (p->bits < 0 || p->bits > (int)bufLen) {
+      buf = (buf << 8) | (str->getChar() & 0xff);
+      bufLen += 8;
+      ++nBytesRead;
+      p = &twoDimTab1[(buf >> (bufLen - 7)) & 0x7f];
+    }
+  }
+  if (p->bits < 0) {
+    error(str->getPos(), "Bad two dim code in JBIG2 MMR stream");
+    return 0;
+  }
+  bufLen -= p->bits;
+  return p->n;
+}
+
+int JBIG2MMRDecoder::getWhiteCode() {
+  CCITTCode *p;
+  Guint code;
+
+  if (bufLen == 0) {
+    buf = str->getChar() & 0xff;
+    bufLen = 8;
+    ++nBytesRead;
+  }
+  while (1) {
+    if (bufLen > 7 && ((buf >> (bufLen - 7)) & 0x7f) == 0) {
+      if (bufLen <= 12) {
+       code = buf << (12 - bufLen);
+      } else {
+       code = buf >> (bufLen - 12);
+      }
+      p = &whiteTab1[code & 0x1f];
+    } else {
+      if (bufLen <= 9) {
+       code = buf << (9 - bufLen);
+      } else {
+       code = buf >> (bufLen - 9);
+      }
+      p = &whiteTab2[code & 0x1ff];
+    }
+    if (p->bits > 0 && p->bits < (int)bufLen) {
+      bufLen -= p->bits;
+      return p->n;
+    }
+    if (bufLen >= 12) {
+      break;
+    }
+    buf = (buf << 8) | (str->getChar() & 0xff);
+    bufLen += 8;
+    ++nBytesRead;
+  }
+  error(str->getPos(), "Bad white code in JBIG2 MMR stream");
+  // eat a bit and return a positive number so that the caller doesn't
+  // go into an infinite loop
+  --bufLen;
+  return 1;
+}
+
+int JBIG2MMRDecoder::getBlackCode() {
+  CCITTCode *p;
+  Guint code;
+
+  if (bufLen == 0) {
+    buf = str->getChar() & 0xff;
+    bufLen = 8;
+    ++nBytesRead;
+  }
+  while (1) {
+    if (bufLen > 6 && ((buf >> (bufLen - 6)) & 0x3f) == 0) {
+      if (bufLen <= 13) {
+       code = buf << (13 - bufLen);
+      } else {
+       code = buf >> (bufLen - 13);
+      }
+      p = &blackTab1[code & 0x7f];
+    } else if (bufLen > 4 && ((buf >> (bufLen - 4)) & 0x0f) == 0) {
+      if (bufLen <= 12) {
+       code = buf << (12 - bufLen);
+      } else {
+       code = buf >> (bufLen - 12);
+      }
+      p = &blackTab2[(code & 0xff) - 64];
+    } else {
+      if (bufLen <= 6) {
+       code = buf << (6 - bufLen);
+      } else {
+       code = buf >> (bufLen - 6);
+      }
+      p = &blackTab3[code & 0x3f];
+    }
+    if (p->bits > 0 && p->bits < (int)bufLen) {
+      bufLen -= p->bits;
+      return p->n;
+    }
+    if (bufLen >= 13) {
+      break;
+    }
+    buf = (buf << 8) | (str->getChar() & 0xff);
+    bufLen += 8;
+    ++nBytesRead;
+  }
+  error(str->getPos(), "Bad black code in JBIG2 MMR stream");
+  // eat a bit and return a positive number so that the caller doesn't
+  // go into an infinite loop
+  --bufLen;
+  return 1;
+}
+
+Guint JBIG2MMRDecoder::get24Bits() {
+  while (bufLen < 24) {
+    buf = (buf << 8) | (str->getChar() & 0xff);
+    bufLen += 8;
+    ++nBytesRead;
+  }
+  return (buf >> (bufLen - 24)) & 0xffffff;
+}
+
+void JBIG2MMRDecoder::skipTo(Guint length) {
+  while (nBytesRead < length) {
+    str->getChar();
+    ++nBytesRead;
+  }
+}
+
+//------------------------------------------------------------------------
+// JBIG2Segment
+//------------------------------------------------------------------------
+
+enum JBIG2SegmentType {
+  jbig2SegBitmap,
+  jbig2SegSymbolDict,
+  jbig2SegPatternDict,
+  jbig2SegCodeTable
+};
+
+class JBIG2Segment {
+public:
+
+  JBIG2Segment(Guint segNumA) { segNum = segNumA; }
+  virtual ~JBIG2Segment() {}
+  void setSegNum(Guint segNumA) { segNum = segNumA; }
+  Guint getSegNum() { return segNum; }
+  virtual JBIG2SegmentType getType() = 0;
+
+private:
+
+  Guint segNum;
+};
+
+//------------------------------------------------------------------------
+// JBIG2Bitmap
+//------------------------------------------------------------------------
+
+class JBIG2Bitmap: public JBIG2Segment {
+public:
+
+  JBIG2Bitmap(Guint segNumA, int wA, int hA);
+  virtual ~JBIG2Bitmap();
+  virtual JBIG2SegmentType getType() { return jbig2SegBitmap; }
+  JBIG2Bitmap *copy() { return new JBIG2Bitmap(0, this); }
+  JBIG2Bitmap *getSlice(Guint x, Guint y, Guint wA, Guint hA);
+  void expand(int newH, Guint pixel);
+  void clearToZero();
+  void clearToOne();
+  int getWidth() { return w; }
+  int getHeight() { return h; }
+  int getPixel(int x, int y)
+    { return (x < 0 || x >= w || y < 0 || y >= h) ? 0 :
+             (data[y * line + (x >> 3)] >> (7 - (x & 7))) & 1; }
+  void setPixel(int x, int y)
+    { data[y * line + (x >> 3)] |= 1 << (7 - (x & 7)); }
+  void clearPixel(int x, int y)
+    { data[y * line + (x >> 3)] &= 0x7f7f >> (x & 7); }
+  void duplicateRow(int yDest, int ySrc);
+  void combine(JBIG2Bitmap *bitmap, int x, int y, Guint combOp);
+  Guchar *getDataPtr() { return data; }
+  int getDataSize() { return h * line; }
+
+private:
+
+  JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap);
+
+  int w, h, line;
+  Guchar *data;
+};
+
+JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, int wA, int hA):
+  JBIG2Segment(segNumA)
+{
+  w = wA;
+  h = hA;
+  line = (wA + 7) >> 3;
+  data = (Guchar *)gmalloc(h * line);
+}
+
+JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap):
+  JBIG2Segment(segNumA)
+{
+  w = bitmap->w;
+  h = bitmap->h;
+  line = bitmap->line;
+  data = (Guchar *)gmalloc(h * line);
+  memcpy(data, bitmap->data, h * line);
+}
+
+JBIG2Bitmap::~JBIG2Bitmap() {
+  gfree(data);
+}
+
+//~ optimize this
+JBIG2Bitmap *JBIG2Bitmap::getSlice(Guint x, Guint y, Guint wA, Guint hA) {
+  JBIG2Bitmap *slice;
+  Guint xx, yy;
+
+  slice = new JBIG2Bitmap(0, wA, hA);
+  slice->clearToZero();
+  for (yy = 0; yy < hA; ++yy) {
+    for (xx = 0; xx < wA; ++xx) {
+      if (getPixel(x + xx, y + yy)) {
+       slice->setPixel(xx, yy);
+      }
+    }
+  }
+  return slice;
+}
+
+void JBIG2Bitmap::expand(int newH, Guint pixel) {
+  if (newH <= h) {
+    return;
+  }
+  data = (Guchar *)grealloc(data, newH * line);
+  if (pixel) {
+    memset(data + h * line, 0xff, (newH - h) * line);
+  } else {
+    memset(data + h * line, 0x00, (newH - h) * line);
+  }
+  h = newH;
+}
+
+void JBIG2Bitmap::clearToZero() {
+  memset(data, 0, h * line);
+}
+
+void JBIG2Bitmap::clearToOne() {
+  memset(data, 0xff, h * line);
+}
+
+void JBIG2Bitmap::duplicateRow(int yDest, int ySrc) {
+  memcpy(data + yDest * line, data + ySrc * line, line);
+}
+
+void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y,
+                         Guint combOp) {
+  int x0, x1, y0, y1, xx, yy;
+  Guchar *srcPtr, *destPtr;
+  Guint src0, src1, src, dest, s1, s2, m1, m2, m3;
+  GBool oneByte;
+
+  if (y < 0) {
+    y0 = -y;
+  } else {
+    y0 = 0;
+  }
+  if (y + bitmap->h > h) {
+    y1 = h - y;
+  } else {
+    y1 = bitmap->h;
+  }
+  if (y0 >= y1) {
+    return;
+  }
+
+  if (x >= 0) {
+    x0 = x & ~7;
+  } else {
+    x0 = 0;
+  }
+  x1 = x + bitmap->w;
+  if (x1 > w) {
+    x1 = w;
+  }
+  if (x0 >= x1) {
+    return;
+  }
+
+  s1 = x & 7;
+  s2 = 8 - s1;
+  m1 = 0xff >> (x1 & 7);
+  m2 = 0xff << (((x1 & 7) == 0) ? 0 : 8 - (x1 & 7));
+  m3 = (0xff >> s1) & m2;
+
+  oneByte = x0 == ((x1 - 1) & ~7);
+
+  for (yy = y0; yy < y1; ++yy) {
+
+    // one byte per line -- need to mask both left and right side
+    if (oneByte) {
+      if (x >= 0) {
+       destPtr = data + (y + yy) * line + (x >> 3);
+       srcPtr = bitmap->data + yy * bitmap->line;
+       dest = *destPtr;
+       src1 = *srcPtr;
+       switch (combOp) {
+       case 0: // or
+         dest |= (src1 >> s1) & m2;
+         break;
+       case 1: // and
+         dest &= ((0xff00 | src1) >> s1) | m1;
+         break;
+       case 2: // xor
+         dest ^= (src1 >> s1) & m2;
+         break;
+       case 3: // xnor
+         dest ^= ((src1 ^ 0xff) >> s1) & m2;
+         break;
+       case 4: // replace
+         dest = (dest & ~m3) | ((src1 >> s1) & m3);
+         break;
+       }
+       *destPtr = dest;
+      } else {
+       destPtr = data + (y + yy) * line;
+       srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);
+       dest = *destPtr;
+       src1 = *srcPtr;
+       switch (combOp) {
+       case 0: // or
+         dest |= src1 & m2;
+         break;
+       case 1: // and
+         dest &= src1 | m1;
+         break;
+       case 2: // xor
+         dest ^= src1 & m2;
+         break;
+       case 3: // xnor
+         dest ^= (src1 ^ 0xff) & m2;
+         break;
+       case 4: // replace
+         dest = (src1 & m2) | (dest & m1);
+         break;
+       }
+       *destPtr = dest;
+      }
+
+    // multiple bytes per line -- need to mask left side of left-most
+    // byte and right side of right-most byte
+    } else {
+
+      // left-most byte
+      if (x >= 0) {
+       destPtr = data + (y + yy) * line + (x >> 3);
+       srcPtr = bitmap->data + yy * bitmap->line;
+       src1 = *srcPtr++;
+       dest = *destPtr;
+       switch (combOp) {
+       case 0: // or
+         dest |= src1 >> s1;
+         break;
+       case 1: // and
+         dest &= (0xff00 | src1) >> s1;
+         break;
+       case 2: // xor
+         dest ^= src1 >> s1;
+         break;
+       case 3: // xnor
+         dest ^= (src1 ^ 0xff) >> s1;
+         break;
+       case 4: // replace
+         dest = (dest & (0xff << s2)) | (src1 >> s1);
+         break;
+       }
+       *destPtr++ = dest;
+       xx = x0 + 8;
+      } else {
+       destPtr = data + (y + yy) * line;
+       srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);
+       src1 = *srcPtr++;
+       xx = x0;
+      }
+
+      // middle bytes
+      for (; xx < x1 - 8; xx += 8) {
+       dest = *destPtr;
+       src0 = src1;
+       src1 = *srcPtr++;
+       src = (((src0 << 8) | src1) >> s1) & 0xff;
+       switch (combOp) {
+       case 0: // or
+         dest |= src;
+         break;
+       case 1: // and
+         dest &= src;
+         break;
+       case 2: // xor
+         dest ^= src;
+         break;
+       case 3: // xnor
+         dest ^= src ^ 0xff;
+         break;
+       case 4: // replace
+         dest = src;
+         break;
+       }
+       *destPtr++ = dest;
+      }
+
+      // right-most byte
+      dest = *destPtr;
+      src0 = src1;
+      src1 = *srcPtr++;
+      src = (((src0 << 8) | src1) >> s1) & 0xff;
+      switch (combOp) {
+      case 0: // or
+       dest |= src & m2;
+       break;
+      case 1: // and
+       dest &= src | m1;
+       break;
+      case 2: // xor
+       dest ^= src & m2;
+       break;
+      case 3: // xnor
+       dest ^= (src ^ 0xff) & m2;
+       break;
+      case 4: // replace
+       dest = (src & m2) | (dest & m1);
+       break;
+      }
+      *destPtr = dest;
+    }
+  }
+}
+
+//------------------------------------------------------------------------
+// JBIG2SymbolDict
+//------------------------------------------------------------------------
+
+class JBIG2SymbolDict: public JBIG2Segment {
+public:
+
+  JBIG2SymbolDict(Guint segNumA, Guint sizeA);
+  virtual ~JBIG2SymbolDict();
+  virtual JBIG2SegmentType getType() { return jbig2SegSymbolDict; }
+  Guint getSize() { return size; }
+  void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
+  JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }
+  void setGenericRegionStats(JBIG2ArithmeticDecoderStats *stats)
+    { genericRegionStats = stats; }
+  void setRefinementRegionStats(JBIG2ArithmeticDecoderStats *stats)
+    { refinementRegionStats = stats; }
+  JBIG2ArithmeticDecoderStats *getGenericRegionStats()
+    { return genericRegionStats; }
+  JBIG2ArithmeticDecoderStats *getRefinementRegionStats()
+    { return refinementRegionStats; }
+
+private:
+
+  Guint size;
+  JBIG2Bitmap **bitmaps;
+  JBIG2ArithmeticDecoderStats *genericRegionStats;
+  JBIG2ArithmeticDecoderStats *refinementRegionStats;
+};
+
+JBIG2SymbolDict::JBIG2SymbolDict(Guint segNumA, Guint sizeA):
+  JBIG2Segment(segNumA)
+{
+  size = sizeA;
+  bitmaps = (JBIG2Bitmap **)gmalloc(size * sizeof(JBIG2Bitmap *));
+  genericRegionStats = NULL;
+  refinementRegionStats = NULL;
+}
+
+JBIG2SymbolDict::~JBIG2SymbolDict() {
+  Guint i;
+
+  for (i = 0; i < size; ++i) {
+    delete bitmaps[i];
+  }
+  gfree(bitmaps);
+  if (genericRegionStats) {
+    delete genericRegionStats;
+  }
+  if (refinementRegionStats) {
+    delete refinementRegionStats;
+  }
+}
+
+//------------------------------------------------------------------------
+// JBIG2PatternDict
+//------------------------------------------------------------------------
+
+class JBIG2PatternDict: public JBIG2Segment {
+public:
+
+  JBIG2PatternDict(Guint segNumA, Guint sizeA);
+  virtual ~JBIG2PatternDict();
+  virtual JBIG2SegmentType getType() { return jbig2SegPatternDict; }
+  Guint getSize() { return size; }
+  void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
+  JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }
+
+private:
+
+  Guint size;
+  JBIG2Bitmap **bitmaps;
+};
+
+JBIG2PatternDict::JBIG2PatternDict(Guint segNumA, Guint sizeA):
+  JBIG2Segment(segNumA)
+{
+  size = sizeA;
+  bitmaps = (JBIG2Bitmap **)gmalloc(size * sizeof(JBIG2Bitmap *));
+}
+
+JBIG2PatternDict::~JBIG2PatternDict() {
+  Guint i;
+
+  for (i = 0; i < size; ++i) {
+    delete bitmaps[i];
+  }
+  gfree(bitmaps);
+}
+
+//------------------------------------------------------------------------
+// JBIG2CodeTable
+//------------------------------------------------------------------------
+
+class JBIG2CodeTable: public JBIG2Segment {
+public:
+
+  JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA);
+  virtual ~JBIG2CodeTable();
+  virtual JBIG2SegmentType getType() { return jbig2SegCodeTable; }
+  JBIG2HuffmanTable *getHuffTable() { return table; }
+
+private:
+
+  JBIG2HuffmanTable *table;
+};
+
+JBIG2CodeTable::JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA):
+  JBIG2Segment(segNumA)
+{
+  table = tableA;
+}
+
+JBIG2CodeTable::~JBIG2CodeTable() {
+  gfree(table);
+}
+
+//------------------------------------------------------------------------
+// JBIG2Stream
+//------------------------------------------------------------------------
+
+JBIG2Stream::JBIG2Stream(Stream *strA, Object *globalsStream):
+  FilterStream(strA)
+{
+  pageBitmap = NULL;
+
+  arithDecoder = new JBIG2ArithmeticDecoder();
+  genericRegionStats = new JBIG2ArithmeticDecoderStats(1);
+  refinementRegionStats = new JBIG2ArithmeticDecoderStats(1);
+  iadhStats = new JBIG2ArithmeticDecoderStats(9);
+  iadwStats = new JBIG2ArithmeticDecoderStats(9);
+  iaexStats = new JBIG2ArithmeticDecoderStats(9);
+  iaaiStats = new JBIG2ArithmeticDecoderStats(9);
+  iadtStats = new JBIG2ArithmeticDecoderStats(9);
+  iaitStats = new JBIG2ArithmeticDecoderStats(9);
+  iafsStats = new JBIG2ArithmeticDecoderStats(9);
+  iadsStats = new JBIG2ArithmeticDecoderStats(9);
+  iardxStats = new JBIG2ArithmeticDecoderStats(9);
+  iardyStats = new JBIG2ArithmeticDecoderStats(9);
+  iardwStats = new JBIG2ArithmeticDecoderStats(9);
+  iardhStats = new JBIG2ArithmeticDecoderStats(9);
+  iariStats = new JBIG2ArithmeticDecoderStats(9);
+  iaidStats = new JBIG2ArithmeticDecoderStats(1);
+  huffDecoder = new JBIG2HuffmanDecoder();
+  mmrDecoder = new JBIG2MMRDecoder();
+
+  segments = new GList();
+  if (globalsStream->isStream()) {
+    curStr = globalsStream->getStream();
+    curStr->reset();
+    arithDecoder->setStream(curStr);
+    huffDecoder->setStream(curStr);
+    mmrDecoder->setStream(curStr);
+    readSegments();
+  }
+  globalSegments = segments;
+
+  segments = NULL;
+  curStr = NULL;
+  dataPtr = dataEnd = NULL;
+}
+
+JBIG2Stream::~JBIG2Stream() {
+  delete arithDecoder;
+  delete genericRegionStats;
+  delete refinementRegionStats;
+  delete iadhStats;
+  delete iadwStats;
+  delete iaexStats;
+  delete iaaiStats;
+  delete iadtStats;
+  delete iaitStats;
+  delete iafsStats;
+  delete iadsStats;
+  delete iardxStats;
+  delete iardyStats;
+  delete iardwStats;
+  delete iardhStats;
+  delete iariStats;
+  delete iaidStats;
+  delete huffDecoder;
+  delete mmrDecoder;
+  if (pageBitmap) {
+    delete pageBitmap;
+  }
+  if (segments) {
+    deleteGList(segments, JBIG2Segment);
+  }
+  if (globalSegments) {
+    deleteGList(globalSegments, JBIG2Segment);
+  }
+  delete str;
+}
+
+void JBIG2Stream::reset() {
+  if (pageBitmap) {
+    delete pageBitmap;
+    pageBitmap = NULL;
+  }
+  if (segments) {
+    deleteGList(segments, JBIG2Segment);
+  }
+  segments = new GList();
+
+  curStr = str;
+  curStr->reset();
+  arithDecoder->setStream(curStr);
+  huffDecoder->setStream(curStr);
+  mmrDecoder->setStream(curStr);
+  readSegments();
+
+  if (pageBitmap) {
+    dataPtr = pageBitmap->getDataPtr();
+    dataEnd = dataPtr + pageBitmap->getDataSize();
+  } else {
+    dataPtr = NULL;
+  }
+}
+
+int JBIG2Stream::getChar() {
+  if (dataPtr && dataPtr < dataEnd) {
+    return (*dataPtr++ ^ 0xff) & 0xff;
+  }
+  return EOF;
+}
+
+int JBIG2Stream::lookChar() {
+  if (dataPtr && dataPtr < dataEnd) {
+    return (*dataPtr ^ 0xff) & 0xff;
+  }
+  return EOF;
+}
+
+GString *JBIG2Stream::getPSFilter(char *indent) {
+  return NULL;
+}
+
+GBool JBIG2Stream::isBinary(GBool last) {
+  return str->isBinary(gTrue);
+}
+
+void JBIG2Stream::readSegments() {
+  Guint segNum, segFlags, segType, page, segLength;
+  Guint refFlags, nRefSegs;
+  Guint *refSegs;
+  int c1, c2, c3;
+  Guint i;
+
+  while (readULong(&segNum)) {
+
+    // segment header flags
+    if (!readUByte(&segFlags)) {
+      goto eofError1;
+    }
+    segType = segFlags & 0x3f;
+
+    // referred-to segment count and retention flags
+    if (!readUByte(&refFlags)) {
+      goto eofError1;
+    }
+    nRefSegs = refFlags >> 5;
+    if (nRefSegs == 7) {
+      if ((c1 = curStr->getChar()) == EOF ||
+         (c2 = curStr->getChar()) == EOF ||
+         (c3 = curStr->getChar()) == EOF) {
+       goto eofError1;
+      }
+      refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3;
+      nRefSegs = refFlags & 0x1fffffff;
+      for (i = 0; i < (nRefSegs + 9) >> 3; ++i) {
+       c1 = curStr->getChar();
+      }
+    }
+
+    // referred-to segment numbers
+    refSegs = (Guint *)gmalloc(nRefSegs * sizeof(Guint));
+    if (segNum <= 256) {
+      for (i = 0; i < nRefSegs; ++i) {
+       if (!readUByte(&refSegs[i])) {
+         goto eofError2;
+       }
+      }
+    } else if (segNum <= 65536) {
+      for (i = 0; i < nRefSegs; ++i) {
+       if (!readUWord(&refSegs[i])) {
+         goto eofError2;
+       }
+      }
+    } else {
+      for (i = 0; i < nRefSegs; ++i) {
+       if (!readULong(&refSegs[i])) {
+         goto eofError2;
+       }
+      }
+    }
+
+    // segment page association
+    if (segFlags & 0x40) {
+      if (!readULong(&page)) {
+       goto eofError2;
+      }
+    } else {
+      if (!readUByte(&page)) {
+       goto eofError2;
+      }
+    }
+
+    // segment data length
+    if (!readULong(&segLength)) {
+      goto eofError2;
+    }
+
+    // read the segment data
+    switch (segType) {
+    case 0:
+      readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs);
+      break;
+    case 4:
+      readTextRegionSeg(segNum, gFalse, gFalse, segLength, refSegs, nRefSegs);
+      break;
+    case 6:
+      readTextRegionSeg(segNum, gTrue, gFalse, segLength, refSegs, nRefSegs);
+      break;
+    case 7:
+      readTextRegionSeg(segNum, gTrue, gTrue, segLength, refSegs, nRefSegs);
+      break;
+    case 16:
+      readPatternDictSeg(segNum, segLength);
+      break;
+    case 20:
+      readHalftoneRegionSeg(segNum, gFalse, gFalse, segLength,
+                           refSegs, nRefSegs);
+      break;
+    case 22:
+      readHalftoneRegionSeg(segNum, gTrue, gFalse, segLength,
+                           refSegs, nRefSegs);
+      break;
+    case 23:
+      readHalftoneRegionSeg(segNum, gTrue, gTrue, segLength,
+                           refSegs, nRefSegs);
+      break;
+    case 36:
+      readGenericRegionSeg(segNum, gFalse, gFalse, segLength);
+      break;
+    case 38:
+      readGenericRegionSeg(segNum, gTrue, gFalse, segLength);
+      break;
+    case 39:
+      readGenericRegionSeg(segNum, gTrue, gTrue, segLength);
+      break;
+    case 40:
+      readGenericRefinementRegionSeg(segNum, gFalse, gFalse, segLength,
+                                    refSegs, nRefSegs);
+      break;
+    case 42:
+      readGenericRefinementRegionSeg(segNum, gTrue, gFalse, segLength,
+                                    refSegs, nRefSegs);
+      break;
+    case 43:
+      readGenericRefinementRegionSeg(segNum, gTrue, gTrue, segLength,
+                                    refSegs, nRefSegs);
+      break;
+    case 48:
+      readPageInfoSeg(segLength);
+      break;
+    case 50:
+      readEndOfStripeSeg(segLength);
+      break;
+    case 52:
+      readProfilesSeg(segLength);
+      break;
+    case 53:
+      readCodeTableSeg(segNum, segLength);
+      break;
+    case 62:
+      readExtensionSeg(segLength);
+      break;
+    default:
+      error(getPos(), "Unknown segment type in JBIG2 stream");
+      for (i = 0; i < segLength; ++i) {
+       if ((c1 = curStr->getChar()) == EOF) {
+         goto eofError2;
+       }
+      }
+      break;
+    }
+
+    gfree(refSegs);
+  }
+
+  return;
+
+ eofError2:
+  gfree(refSegs);
+ eofError1:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
+                                   Guint *refSegs, Guint nRefSegs) {
+  JBIG2SymbolDict *symbolDict;
+  JBIG2HuffmanTable *huffDHTable, *huffDWTable;
+  JBIG2HuffmanTable *huffBMSizeTable, *huffAggInstTable;
+  JBIG2Segment *seg;
+  GList *codeTables;
+  JBIG2SymbolDict *inputSymbolDict;
+  Guint flags, sdTemplate, sdrTemplate, huff, refAgg;
+  Guint huffDH, huffDW, huffBMSize, huffAggInst;
+  Guint contextUsed, contextRetained;
+  int sdATX[4], sdATY[4], sdrATX[2], sdrATY[2];
+  Guint numExSyms, numNewSyms, numInputSyms, symCodeLen;
+  JBIG2Bitmap **bitmaps;
+  JBIG2Bitmap *collBitmap, *refBitmap;
+  Guint *symWidths;
+  Guint symHeight, symWidth, totalWidth, x, symID;
+  int dh, dw, refAggNum, refDX, refDY, bmSize;
+  GBool ex;
+  int run, cnt;
+  Guint i, j, k;
+  Guchar *p;
+
+  // symbol dictionary flags
+  if (!readUWord(&flags)) {
+    goto eofError;
+  }
+  sdTemplate = (flags >> 10) & 3;
+  sdrTemplate = (flags >> 12) & 1;
+  huff = flags & 1;
+  refAgg = (flags >> 1) & 1;
+  huffDH = (flags >> 2) & 3;
+  huffDW = (flags >> 4) & 3;
+  huffBMSize = (flags >> 6) & 1;
+  huffAggInst = (flags >> 7) & 1;
+  contextUsed = (flags >> 8) & 1;
+  contextRetained = (flags >> 9) & 1;
+
+  // symbol dictionary AT flags
+  if (!huff) {
+    if (sdTemplate == 0) {
+      if (!readByte(&sdATX[0]) ||
+         !readByte(&sdATY[0]) ||
+         !readByte(&sdATX[1]) ||
+         !readByte(&sdATY[1]) ||
+         !readByte(&sdATX[2]) ||
+         !readByte(&sdATY[2]) ||
+         !readByte(&sdATX[3]) ||
+         !readByte(&sdATY[3])) {
+       goto eofError;
+      }
+    } else {
+      if (!readByte(&sdATX[0]) ||
+         !readByte(&sdATY[0])) {
+       goto eofError;
+      }
+    }
+  }
+
+  // symbol dictionary refinement AT flags
+  if (refAgg && !sdrTemplate) {
+    if (!readByte(&sdrATX[0]) ||
+       !readByte(&sdrATY[0]) ||
+       !readByte(&sdrATX[1]) ||
+       !readByte(&sdrATY[1])) {
+      goto eofError;
+    }
+  }
+
+  // SDNUMEXSYMS and SDNUMNEWSYMS
+  if (!readULong(&numExSyms) || !readULong(&numNewSyms)) {
+    goto eofError;
+  }
+
+  // get referenced segments: input symbol dictionaries and code tables
+  codeTables = new GList();
+  numInputSyms = 0;
+  for (i = 0; i < nRefSegs; ++i) {
+    seg = findSegment(refSegs[i]);
+    if (seg->getType() == jbig2SegSymbolDict) {
+      numInputSyms += ((JBIG2SymbolDict *)seg)->getSize();
+    } else if (seg->getType() == jbig2SegCodeTable) {
+      codeTables->append(seg);
+    }
+  }
+
+  // compute symbol code length
+  symCodeLen = 0;
+  i = 1;
+  while (i < numInputSyms + numNewSyms) {
+    ++symCodeLen;
+    i <<= 1;
+  }
+
+  // get the input symbol bitmaps
+  bitmaps = (JBIG2Bitmap **)gmalloc((numInputSyms + numNewSyms) *
+                                   sizeof(JBIG2Bitmap *));
+  k = 0;
+  inputSymbolDict = NULL;
+  for (i = 0; i < nRefSegs; ++i) {
+    seg = findSegment(refSegs[i]);
+    if (seg->getType() == jbig2SegSymbolDict) {
+      inputSymbolDict = (JBIG2SymbolDict *)seg;
+      for (j = 0; j < inputSymbolDict->getSize(); ++j) {
+       bitmaps[k++] = inputSymbolDict->getBitmap(j);
+      }
+    }
+  }
+
+  // get the Huffman tables
+  huffDHTable = huffDWTable = NULL; // make gcc happy
+  huffBMSizeTable = huffAggInstTable = NULL; // make gcc happy
+  i = 0;
+  if (huff) {
+    if (huffDH == 0) {
+      huffDHTable = huffTableD;
+    } else if (huffDH == 1) {
+      huffDHTable = huffTableE;
+    } else {
+      huffDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffDW == 0) {
+      huffDWTable = huffTableB;
+    } else if (huffDW == 1) {
+      huffDWTable = huffTableC;
+    } else {
+      huffDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffBMSize == 0) {
+      huffBMSizeTable = huffTableA;
+    } else {
+      huffBMSizeTable =
+         ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffAggInst == 0) {
+      huffAggInstTable = huffTableA;
+    } else {
+      huffAggInstTable =
+         ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+  }
+  delete codeTables;
+
+  // set up the Huffman decoder
+  if (huff) {
+    huffDecoder->reset();
+
+  // set up the arithmetic decoder
+  } else {
+    if (contextUsed && inputSymbolDict) {
+      resetGenericStats(sdTemplate, inputSymbolDict->getGenericRegionStats());
+      if (refAgg) {
+       resetRefinementStats(sdrTemplate,
+                            inputSymbolDict->getRefinementRegionStats());
+      }
+    } else {
+      resetGenericStats(sdTemplate, NULL);
+      if (refAgg) {
+       resetRefinementStats(sdrTemplate, NULL);
+      }
+    }
+    resetIntStats(symCodeLen);
+    arithDecoder->start();
+  }
+
+  // allocate symbol widths storage
+  symWidths = NULL;
+  if (huff && !refAgg) {
+    symWidths = (Guint *)gmalloc(numNewSyms * sizeof(Guint));
+  }
+
+  symHeight = 0;
+  i = 0;
+  while (i < numNewSyms) {
+
+    // read the height class delta height
+    if (huff) {
+      huffDecoder->decodeInt(&dh, huffDHTable);
+    } else {
+      arithDecoder->decodeInt(&dh, iadhStats);
+    }
+    symHeight += dh;
+    symWidth = 0;
+    totalWidth = 0;
+    j = i;
+
+    // read the symbols in this height class
+    while (1) {
+
+      // read the delta width
+      if (huff) {
+       if (!huffDecoder->decodeInt(&dw, huffDWTable)) {
+         break;
+       }
+      } else {
+       if (!arithDecoder->decodeInt(&dw, iadwStats)) {
+         break;
+       }
+      }
+      symWidth += dw;
+
+      // using a collective bitmap, so don't read a bitmap here
+      if (huff && !refAgg) {
+       symWidths[i] = symWidth;
+       totalWidth += symWidth;
+
+      // refinement/aggregate coding
+      } else if (refAgg) {
+       if (huff) {
+         if (!huffDecoder->decodeInt(&refAggNum, huffAggInstTable)) {
+           break;
+         }
+       } else {
+         if (!arithDecoder->decodeInt(&refAggNum, iaaiStats)) {
+           break;
+         }
+       }
+       if (refAggNum == 1) {
+         if (huff) {
+           symID = huffDecoder->readBits(symCodeLen);
+           huffDecoder->decodeInt(&refDX, huffTableO);
+           huffDecoder->decodeInt(&refDY, huffTableO);
+           huffDecoder->decodeInt(&bmSize, huffTableA);
+           huffDecoder->reset();
+           arithDecoder->start();
+         } else {
+           symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
+           arithDecoder->decodeInt(&refDX, iardxStats);
+           arithDecoder->decodeInt(&refDY, iardyStats);
+         }
+         refBitmap = bitmaps[symID];
+         bitmaps[numInputSyms + i] =
+             readGenericRefinementRegion(symWidth, symHeight,
+                                         sdrTemplate, gFalse,
+                                         refBitmap, refDX, refDY,
+                                         sdrATX, sdrATY);
+         //~ do we need to use the bmSize value here (in Huffman mode)?
+       } else {
+         bitmaps[numInputSyms + i] =
+             readTextRegion(huff, gTrue, symWidth, symHeight,
+                            refAggNum, 0, numInputSyms + i, NULL,
+                            symCodeLen, bitmaps, 0, 0, 0, 1, 0,
+                            huffTableF, huffTableH, huffTableK, huffTableO,
+                            huffTableO, huffTableO, huffTableO, huffTableA,
+                            sdrTemplate, sdrATX, sdrATY);
+       }
+
+      // non-ref/agg coding
+      } else {
+       bitmaps[numInputSyms + i] =
+           readGenericBitmap(gFalse, symWidth, symHeight,
+                             sdTemplate, gFalse, gFalse, NULL,
+                             sdATX, sdATY, 0);
+      }
+
+      ++i;
+    }
+
+    // read the collective bitmap
+    if (huff && !refAgg) {
+      huffDecoder->decodeInt(&bmSize, huffBMSizeTable);
+      if (huff) {
+       huffDecoder->reset();
+      }
+      if (bmSize == 0) {
+       collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight);
+       bmSize = symHeight * ((totalWidth + 7) >> 3);
+       p = collBitmap->getDataPtr();
+       for (k = 0; k < (Guint)bmSize; ++k) {
+         *p++ = str->getChar();
+       }
+      } else {
+       collBitmap = readGenericBitmap(gTrue, totalWidth, symHeight,
+                                      0, gFalse, gFalse, NULL, NULL, NULL,
+                                      bmSize);
+      }
+      x = 0;
+      for (; j < i; ++j) {
+       bitmaps[numInputSyms + j] =
+           collBitmap->getSlice(x, 0, symWidths[j], symHeight);
+       x += symWidths[j];
+      }
+      delete collBitmap;
+    }
+  }
+
+  // create the symbol dict object
+  symbolDict = new JBIG2SymbolDict(segNum, numExSyms);
+
+  // exported symbol list
+  i = j = 0;
+  ex = gFalse;
+  while (i < numInputSyms + numNewSyms) {
+    if (huff) {
+      huffDecoder->decodeInt(&run, huffTableA);
+    } else {
+      arithDecoder->decodeInt(&run, iaexStats);
+    }
+    if (ex) {
+      for (cnt = 0; cnt < run; ++cnt) {
+       symbolDict->setBitmap(j++, bitmaps[i++]->copy());
+      }
+    } else {
+      i += run;
+    }
+    ex = !ex;
+  }
+  
+  for (i = 0; i < numNewSyms; ++i) {
+    delete bitmaps[numInputSyms + i];
+  }
+  gfree(bitmaps);
+  if (symWidths) {
+    gfree(symWidths);
+  }
+
+  // save the arithmetic decoder stats
+  if (!huff && contextRetained) {
+    symbolDict->setGenericRegionStats(genericRegionStats->copy());
+    if (refAgg) {
+      symbolDict->setRefinementRegionStats(refinementRegionStats->copy());
+    }
+  }
+
+  // store the new symbol dict
+  segments->append(symbolDict);
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm,
+                                   GBool lossless, Guint length,
+                                   Guint *refSegs, Guint nRefSegs) {
+  JBIG2Bitmap *bitmap;
+  JBIG2HuffmanTable runLengthTab[36];
+  JBIG2HuffmanTable *symCodeTab;
+  JBIG2HuffmanTable *huffFSTable, *huffDSTable, *huffDTTable;
+  JBIG2HuffmanTable *huffRDWTable, *huffRDHTable;
+  JBIG2HuffmanTable *huffRDXTable, *huffRDYTable, *huffRSizeTable;
+  JBIG2Segment *seg;
+  GList *codeTables;
+  JBIG2SymbolDict *symbolDict;
+  JBIG2Bitmap **syms;
+  Guint w, h, x, y, segInfoFlags, extCombOp;
+  Guint flags, huff, refine, logStrips, refCorner, transposed;
+  Guint combOp, defPixel, sOffset, templ;
+  Guint huffFlags, huffFS, huffDS, huffDT;
+  Guint huffRDW, huffRDH, huffRDX, huffRDY, huffRSize;
+  Guint numInstances, numSyms, symCodeLen;
+  int atx[2], aty[2];
+  Guint i, k, kk;
+  int j;
+
+  // region segment info field
+  if (!readULong(&w) || !readULong(&h) ||
+      !readULong(&x) || !readULong(&y) ||
+      !readUByte(&segInfoFlags)) {
+    goto eofError;
+  }
+  extCombOp = segInfoFlags & 7;
+
+  // rest of the text region header
+  if (!readUWord(&flags)) {
+    goto eofError;
+  }
+  huff = flags & 1;
+  refine = (flags >> 1) & 1;
+  logStrips = (flags >> 2) & 3;
+  refCorner = (flags >> 4) & 3;
+  transposed = (flags >> 6) & 1;
+  combOp = (flags >> 7) & 3;
+  defPixel = (flags >> 9) & 1;
+  sOffset = (flags >> 10) & 0x1f;
+  templ = (flags >> 15) & 1;
+  huffFS = huffDS = huffDT = 0; // make gcc happy
+  huffRDW = huffRDH = huffRDX = huffRDY = huffRSize = 0; // make gcc happy
+  if (huff) {
+    if (!readUWord(&huffFlags)) {
+      goto eofError;
+    }
+    huffFS = huffFlags & 3;
+    huffDS = (huffFlags >> 2) & 3;
+    huffDT = (huffFlags >> 4) & 3;
+    huffRDW = (huffFlags >> 6) & 3;
+    huffRDH = (huffFlags >> 8) & 3;
+    huffRDX = (huffFlags >> 10) & 3;
+    huffRDY = (huffFlags >> 12) & 3;
+    huffRSize = (huffFlags >> 14) & 1;
+  }
+  if (refine && templ == 0) {
+    if (!readByte(&atx[0]) || !readByte(&aty[0]) ||
+       !readByte(&atx[1]) || !readByte(&aty[1])) {
+      goto eofError;
+    }
+  }
+  if (!readULong(&numInstances)) {
+    goto eofError;
+  }
+
+  // get symbol dictionaries and tables
+  codeTables = new GList();
+  numSyms = 0;
+  for (i = 0; i < nRefSegs; ++i) {
+    seg = findSegment(refSegs[i]);
+    if (seg->getType() == jbig2SegSymbolDict) {
+      numSyms += ((JBIG2SymbolDict *)seg)->getSize();
+    } else if (seg->getType() == jbig2SegCodeTable) {
+      codeTables->append(seg);
+    }
+  }
+  symCodeLen = 0;
+  i = 1;
+  while (i < numSyms) {
+    ++symCodeLen;
+    i <<= 1;
+  }
+
+  // get the symbol bitmaps
+  syms = (JBIG2Bitmap **)gmalloc(numSyms * sizeof(JBIG2Bitmap *));
+  kk = 0;
+  for (i = 0; i < nRefSegs; ++i) {
+    seg = findSegment(refSegs[i]);
+    if (seg->getType() == jbig2SegSymbolDict) {
+      symbolDict = (JBIG2SymbolDict *)seg;
+      for (k = 0; k < symbolDict->getSize(); ++k) {
+       syms[kk++] = symbolDict->getBitmap(k);
+      }
+    }
+  }
+
+  // get the Huffman tables
+  huffFSTable = huffDSTable = huffDTTable = NULL; // make gcc happy
+  huffRDWTable = huffRDHTable = NULL; // make gcc happy
+  huffRDXTable = huffRDYTable = huffRSizeTable = NULL; // make gcc happy
+  i = 0;
+  if (huff) {
+    if (huffFS == 0) {
+      huffFSTable = huffTableF;
+    } else if (huffFS == 1) {
+      huffFSTable = huffTableG;
+    } else {
+      huffFSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffDS == 0) {
+      huffDSTable = huffTableH;
+    } else if (huffDS == 1) {
+      huffDSTable = huffTableI;
+    } else if (huffDS == 2) {
+      huffDSTable = huffTableJ;
+    } else {
+      huffDSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffDT == 0) {
+      huffDTTable = huffTableK;
+    } else if (huffDT == 1) {
+      huffDTTable = huffTableL;
+    } else if (huffDT == 2) {
+      huffDTTable = huffTableM;
+    } else {
+      huffDTTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffRDW == 0) {
+      huffRDWTable = huffTableN;
+    } else if (huffRDW == 1) {
+      huffRDWTable = huffTableO;
+    } else {
+      huffRDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffRDH == 0) {
+      huffRDHTable = huffTableN;
+    } else if (huffRDH == 1) {
+      huffRDHTable = huffTableO;
+    } else {
+      huffRDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffRDX == 0) {
+      huffRDXTable = huffTableN;
+    } else if (huffRDX == 1) {
+      huffRDXTable = huffTableO;
+    } else {
+      huffRDXTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffRDY == 0) {
+      huffRDYTable = huffTableN;
+    } else if (huffRDY == 1) {
+      huffRDYTable = huffTableO;
+    } else {
+      huffRDYTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+    if (huffRSize == 0) {
+      huffRSizeTable = huffTableA;
+    } else {
+      huffRSizeTable =
+         ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+    }
+  }
+  delete codeTables;
+
+  // symbol ID Huffman decoding table
+  if (huff) {
+    huffDecoder->reset();
+    for (i = 0; i < 32; ++i) {
+      runLengthTab[i].val = i;
+      runLengthTab[i].prefixLen = huffDecoder->readBits(4);
+      runLengthTab[i].rangeLen = 0;
+    }
+    runLengthTab[32].val = 0x103;
+    runLengthTab[32].prefixLen = huffDecoder->readBits(4);
+    runLengthTab[32].rangeLen = 2;
+    runLengthTab[33].val = 0x203;
+    runLengthTab[33].prefixLen = huffDecoder->readBits(4);
+    runLengthTab[33].rangeLen = 3;
+    runLengthTab[34].val = 0x20b;
+    runLengthTab[34].prefixLen = huffDecoder->readBits(4);
+    runLengthTab[34].rangeLen = 7;
+    runLengthTab[35].rangeLen = jbig2HuffmanEOT;
+    huffDecoder->buildTable(runLengthTab, 35);
+    symCodeTab = (JBIG2HuffmanTable *)gmalloc((numSyms + 1) *
+                                             sizeof(JBIG2HuffmanTable));
+    for (i = 0; i < numSyms; ++i) {
+      symCodeTab[i].val = i;
+      symCodeTab[i].rangeLen = 0;
+    }
+    i = 0;
+    while (i < numSyms) {
+      huffDecoder->decodeInt(&j, runLengthTab);
+      if (j > 0x200) {
+       for (j -= 0x200; j && i < numSyms; --j) {
+         symCodeTab[i++].prefixLen = 0;
+       }
+      } else if (j > 0x100) {
+       for (j -= 0x100; j && i < numSyms; --j) {
+         symCodeTab[i].prefixLen = symCodeTab[i-1].prefixLen;
+         ++i;
+       }
+      } else {
+       symCodeTab[i++].prefixLen = j;
+      }
+
+    }
+    symCodeTab[numSyms].rangeLen = jbig2HuffmanEOT;
+    huffDecoder->buildTable(symCodeTab, numSyms);
+    huffDecoder->reset();
+
+  // set up the arithmetic decoder
+  } else {
+    symCodeTab = NULL;
+    resetIntStats(symCodeLen);
+    if (refine) {
+      resetRefinementStats(templ, NULL);
+    }
+    arithDecoder->start();
+  }
+
+  bitmap = readTextRegion(huff, refine, w, h, numInstances,
+                         logStrips, numSyms, symCodeTab, symCodeLen, syms,
+                         defPixel, combOp, transposed, refCorner, sOffset,
+                         huffFSTable, huffDSTable, huffDTTable,
+                         huffRDWTable, huffRDHTable,
+                         huffRDXTable, huffRDYTable, huffRSizeTable,
+                         templ, atx, aty);
+
+  gfree(syms);
+
+  // combine the region bitmap into the page bitmap
+  if (imm) {
+    if (pageH == 0xffffffff && y + h > curPageH) {
+      pageBitmap->expand(y + h, pageDefPixel);
+    }
+    pageBitmap->combine(bitmap, x, y, extCombOp);
+    delete bitmap;
+
+  // store the region bitmap
+  } else {
+    bitmap->setSegNum(segNum);
+    segments->append(bitmap);
+  }
+
+  // clean up the Huffman decoder
+  if (huff) {
+    gfree(symCodeTab);
+  }
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+JBIG2Bitmap *JBIG2Stream::readTextRegion(GBool huff, GBool refine,
+                                        int w, int h,
+                                        Guint numInstances,
+                                        Guint logStrips,
+                                        int numSyms,
+                                        JBIG2HuffmanTable *symCodeTab,
+                                        Guint symCodeLen,
+                                        JBIG2Bitmap **syms,
+                                        Guint defPixel, Guint combOp,
+                                        Guint transposed, Guint refCorner,
+                                        Guint sOffset,
+                                        JBIG2HuffmanTable *huffFSTable,
+                                        JBIG2HuffmanTable *huffDSTable,
+                                        JBIG2HuffmanTable *huffDTTable,
+                                        JBIG2HuffmanTable *huffRDWTable,
+                                        JBIG2HuffmanTable *huffRDHTable,
+                                        JBIG2HuffmanTable *huffRDXTable,
+                                        JBIG2HuffmanTable *huffRDYTable,
+                                        JBIG2HuffmanTable *huffRSizeTable,
+                                        Guint templ,
+                                        int *atx, int *aty) {
+  JBIG2Bitmap *bitmap;
+  JBIG2Bitmap *symbolBitmap;
+  Guint strips;
+  int t, dt, tt, s, ds, sFirst, j;
+  int rdw, rdh, rdx, rdy, ri, refDX, refDY, bmSize;
+  Guint symID, inst, bw, bh;
+
+  strips = 1 << logStrips;
+
+  // allocate the bitmap
+  bitmap = new JBIG2Bitmap(0, w, h);
+  if (defPixel) {
+    bitmap->clearToOne();
+  } else {
+    bitmap->clearToZero();
+  }
+
+  // decode initial T value
+  if (huff) {
+    huffDecoder->decodeInt(&t, huffDTTable);
+  } else {
+    arithDecoder->decodeInt(&t, iadtStats);
+  }
+  t *= -strips;
+
+  inst = 0;
+  sFirst = 0;
+  while (inst < numInstances) {
+
+    // decode delta-T
+    if (huff) {
+      huffDecoder->decodeInt(&dt, huffDTTable);
+    } else {
+      arithDecoder->decodeInt(&dt, iadtStats);
+    }
+    t += dt * strips;
+
+    // first S value
+    if (huff) {
+      huffDecoder->decodeInt(&ds, huffFSTable);
+    } else {
+      arithDecoder->decodeInt(&ds, iafsStats);
+    }
+    sFirst += ds;
+    s = sFirst;
+
+    // read the instances
+    while (1) {
+
+      // T value
+      if (strips == 1) {
+       dt = 0;
+      } else if (huff) {
+       dt = huffDecoder->readBits(logStrips);
+      } else {
+       arithDecoder->decodeInt(&dt, iaitStats);
+      }
+      tt = t + dt;
+
+      // symbol ID
+      if (huff) {
+       if (symCodeTab) {
+         huffDecoder->decodeInt(&j, symCodeTab);
+         symID = (Guint)j;
+       } else {
+         symID = huffDecoder->readBits(symCodeLen);
+       }
+      } else {
+       symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
+      }
+
+      // get the symbol bitmap
+      symbolBitmap = NULL;
+      if (refine) {
+       if (huff) {
+         ri = (int)huffDecoder->readBit();
+       } else {
+         arithDecoder->decodeInt(&ri, iariStats);
+       }
+      } else {
+       ri = 0;
+      }
+      if (ri) {
+       if (huff) {
+         huffDecoder->decodeInt(&rdw, huffRDWTable);
+         huffDecoder->decodeInt(&rdh, huffRDHTable);
+         huffDecoder->decodeInt(&rdx, huffRDXTable);
+         huffDecoder->decodeInt(&rdy, huffRDYTable);
+         huffDecoder->decodeInt(&bmSize, huffRSizeTable);
+         huffDecoder->reset();
+         arithDecoder->start();
+       } else {
+         arithDecoder->decodeInt(&rdw, iardwStats);
+         arithDecoder->decodeInt(&rdh, iardhStats);
+         arithDecoder->decodeInt(&rdx, iardxStats);
+         arithDecoder->decodeInt(&rdy, iardyStats);
+       }
+       refDX = ((rdw >= 0) ? rdw : rdw - 1) / 2 + rdx;
+       refDY = ((rdh >= 0) ? rdh : rdh - 1) / 2 + rdy;
+
+       symbolBitmap =
+         readGenericRefinementRegion(rdw + syms[symID]->getWidth(),
+                                     rdh + syms[symID]->getHeight(),
+                                     templ, gFalse, syms[symID],
+                                     refDX, refDY, atx, aty);
+       //~ do we need to use the bmSize value here (in Huffman mode)?
+      } else {
+       symbolBitmap = syms[symID];
+      }
+
+      // combine the symbol bitmap into the region bitmap
+      //~ something is wrong here - refCorner shouldn't degenerate into
+      //~   two cases
+      bw = symbolBitmap->getWidth() - 1;
+      bh = symbolBitmap->getHeight() - 1;
+      if (transposed) {
+       switch (refCorner) {
+       case 0: // bottom left
+         bitmap->combine(symbolBitmap, tt, s, combOp);
+         break;
+       case 1: // top left
+         bitmap->combine(symbolBitmap, tt, s, combOp);
+         break;
+       case 2: // bottom right
+         bitmap->combine(symbolBitmap, tt - bw, s, combOp);
+         break;
+       case 3: // top right
+         bitmap->combine(symbolBitmap, tt - bw, s, combOp);
+         break;
+       }
+       s += bh;
+      } else {
+       switch (refCorner) {
+       case 0: // bottom left
+         bitmap->combine(symbolBitmap, s, tt - bh, combOp);
+         break;
+       case 1: // top left
+         bitmap->combine(symbolBitmap, s, tt, combOp);
+         break;
+       case 2: // bottom right
+         bitmap->combine(symbolBitmap, s, tt - bh, combOp);
+         break;
+       case 3: // top right
+         bitmap->combine(symbolBitmap, s, tt, combOp);
+         break;
+       }
+       s += bw;
+      }
+      if (ri) {
+       delete symbolBitmap;
+      }
+
+      // next instance
+      ++inst;
+
+      // next S value
+      if (huff) {
+       if (!huffDecoder->decodeInt(&ds, huffDSTable)) {
+         break;
+       }
+      } else {
+       if (!arithDecoder->decodeInt(&ds, iadsStats)) {
+         break;
+       }
+      }
+      s += sOffset + ds;
+    }
+  }
+
+  return bitmap;
+}
+
+void JBIG2Stream::readPatternDictSeg(Guint segNum, Guint length) {
+  JBIG2PatternDict *patternDict;
+  JBIG2Bitmap *bitmap;
+  Guint flags, patternW, patternH, grayMax, templ, mmr;
+  int atx[4], aty[4];
+  Guint i, x;
+
+  // halftone dictionary flags, pattern width and height, max gray value
+  if (!readUByte(&flags) ||
+      !readUByte(&patternW) ||
+      !readUByte(&patternH) ||
+      !readULong(&grayMax)) {
+    goto eofError;
+  }
+  templ = (flags >> 1) & 3;
+  mmr = flags & 1;
+
+  // set up the arithmetic decoder
+  if (!mmr) {
+    resetGenericStats(templ, NULL);
+    arithDecoder->start();
+  }
+
+  // read the bitmap
+  atx[0] = -patternW; aty[0] =  0;
+  atx[1] = -3;        aty[1] = -1;
+  atx[2] =  2;        aty[2] = -2;
+  atx[3] = -2;        aty[3] = -2;
+  bitmap = readGenericBitmap(mmr, (grayMax + 1) * patternW, patternH,
+                            templ, gFalse, gFalse, NULL,
+                            atx, aty, length - 7);
+
+  // create the pattern dict object
+  patternDict = new JBIG2PatternDict(segNum, grayMax + 1);
+
+  // split up the bitmap
+  x = 0;
+  for (i = 0; i <= grayMax; ++i) {
+    patternDict->setBitmap(i, bitmap->getSlice(x, 0, patternW, patternH));
+    x += patternW;
+  }
+
+  // free memory
+  delete bitmap;
+
+  // store the new pattern dict
+  segments->append(patternDict);
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readHalftoneRegionSeg(Guint segNum, GBool imm,
+                                       GBool lossless, Guint length,
+                                       Guint *refSegs, Guint nRefSegs) {
+  JBIG2Bitmap *bitmap;
+  JBIG2Segment *seg;
+  JBIG2PatternDict *patternDict;
+  JBIG2Bitmap *skipBitmap;
+  Guint *grayImg;
+  JBIG2Bitmap *grayBitmap;
+  JBIG2Bitmap *patternBitmap;
+  Guint w, h, x, y, segInfoFlags, extCombOp;
+  Guint flags, mmr, templ, enableSkip, combOp;
+  Guint gridW, gridH, stepX, stepY, patW, patH;
+  int atx[4], aty[4];
+  int gridX, gridY, xx, yy, bit, j;
+  Guint bpp, m, n, i;
+
+  // region segment info field
+  if (!readULong(&w) || !readULong(&h) ||
+      !readULong(&x) || !readULong(&y) ||
+      !readUByte(&segInfoFlags)) {
+    goto eofError;
+  }
+  extCombOp = segInfoFlags & 7;
+
+  // rest of the halftone region header
+  if (!readUByte(&flags)) {
+    goto eofError;
+  }
+  mmr = flags & 1;
+  templ = (flags >> 1) & 3;
+  enableSkip = (flags >> 3) & 1;
+  combOp = (flags >> 4) & 7;
+  if (!readULong(&gridW) || !readULong(&gridH) ||
+      !readLong(&gridX) || !readLong(&gridY) ||
+      !readUWord(&stepX) || !readUWord(&stepY)) {
+    goto eofError;
+  }
+
+  // get pattern dictionary
+  if (nRefSegs != 1) {
+    error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
+    return;
+  }
+  seg = findSegment(refSegs[0]);
+  if (seg->getType() != jbig2SegPatternDict) {
+    error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
+    return;
+  }
+  patternDict = (JBIG2PatternDict *)seg;
+  bpp = 0;
+  i = 1;
+  while (i < patternDict->getSize()) {
+    ++bpp;
+    i <<= 1;
+  }
+  patW = patternDict->getBitmap(0)->getWidth();
+  patH = patternDict->getBitmap(0)->getHeight();
+
+  // set up the arithmetic decoder
+  if (!mmr) {
+    resetGenericStats(templ, NULL);
+    arithDecoder->start();
+  }
+
+  // allocate the bitmap
+  bitmap = new JBIG2Bitmap(segNum, w, h);
+  if (flags & 0x80) { // HDEFPIXEL
+    bitmap->clearToOne();
+  } else {
+    bitmap->clearToZero();
+  }
+
+  // compute the skip bitmap
+  skipBitmap = NULL;
+  if (enableSkip) {
+    skipBitmap = new JBIG2Bitmap(0, gridW, gridH);
+    skipBitmap->clearToZero();
+    for (m = 0; m < gridH; ++m) {
+      xx = gridX + m * stepY;
+      yy = gridY + m * stepX;
+      for (n = 0; n < gridW; ++n) {
+       if (((xx + (int)patW) >> 8) <= 0 || (xx >> 8) >= (int)w ||
+           ((yy + (int)patH) >> 8) <= 0 || (yy >> 8) >= (int)h) {
+         skipBitmap->setPixel(n, m);
+       }
+      }
+    }
+  }
+
+  // read the gray-scale image
+  grayImg = (Guint *)gmalloc(gridW * gridH * sizeof(Guint));
+  memset(grayImg, 0, gridW * gridH * sizeof(Guint));
+  atx[0] = templ <= 1 ? 3 : 2;  aty[0] = -1;
+  atx[1] = -3;                  aty[1] = -1;
+  atx[2] =  2;                  aty[2] = -2;
+  atx[3] = -2;                  aty[3] = -2;
+  for (j = bpp - 1; j >= 0; --j) {
+    grayBitmap = readGenericBitmap(mmr, gridW, gridH, templ, gFalse,
+                                  enableSkip, skipBitmap, atx, aty, -1);
+    i = 0;
+    for (m = 0; m < gridH; ++m) {
+      for (n = 0; n < gridW; ++n) {
+       bit = grayBitmap->getPixel(n, m) ^ (grayImg[i] & 1);
+       grayImg[i] = (grayImg[i] << 1) | bit;
+       ++i;
+      }
+    }
+    delete grayBitmap;
+  }
+
+  // decode the image
+  i = 0;
+  for (m = 0; m < gridH; ++m) {
+    xx = gridX + m * stepY;
+    yy = gridY + m * stepX;
+    for (n = 0; n < gridW; ++n) {
+      if (!(enableSkip && skipBitmap->getPixel(n, m))) {
+       patternBitmap = patternDict->getBitmap(grayImg[i]);
+       bitmap->combine(patternBitmap, xx >> 8, yy >> 8, combOp);
+      }
+      xx += stepX;
+      yy -= stepY;
+      ++i;
+    }
+  }
+
+  gfree(grayImg);
+
+  // combine the region bitmap into the page bitmap
+  if (imm) {
+    if (pageH == 0xffffffff && y + h > curPageH) {
+      pageBitmap->expand(y + h, pageDefPixel);
+    }
+    pageBitmap->combine(bitmap, x, y, extCombOp);
+    delete bitmap;
+
+  // store the region bitmap
+  } else {
+    segments->append(bitmap);
+  }
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readGenericRegionSeg(Guint segNum, GBool imm,
+                                      GBool lossless, Guint length) {
+  JBIG2Bitmap *bitmap;
+  Guint w, h, x, y, segInfoFlags, extCombOp;
+  Guint flags, mmr, templ, tpgdOn;
+  int atx[4], aty[4];
+
+  // region segment info field
+  if (!readULong(&w) || !readULong(&h) ||
+      !readULong(&x) || !readULong(&y) ||
+      !readUByte(&segInfoFlags)) {
+    goto eofError;
+  }
+  extCombOp = segInfoFlags & 7;
+
+  // rest of the generic region segment header
+  if (!readUByte(&flags)) {
+    goto eofError;
+  }
+  mmr = flags & 1;
+  templ = (flags >> 1) & 3;
+  tpgdOn = (flags >> 3) & 1;
+
+  // AT flags
+  if (!mmr) {
+    if (templ == 0) {
+      if (!readByte(&atx[0]) ||
+         !readByte(&aty[0]) ||
+         !readByte(&atx[1]) ||
+         !readByte(&aty[1]) ||
+         !readByte(&atx[2]) ||
+         !readByte(&aty[2]) ||
+         !readByte(&atx[3]) ||
+         !readByte(&aty[3])) {
+       goto eofError;
+      }
+    } else {
+      if (!readByte(&atx[0]) ||
+         !readByte(&aty[0])) {
+       goto eofError;
+      }
+    }
+  }
+
+  // set up the arithmetic decoder
+  if (!mmr) {
+    resetGenericStats(templ, NULL);
+    arithDecoder->start();
+  }
+
+  // read the bitmap
+  bitmap = readGenericBitmap(mmr, w, h, templ, tpgdOn, gFalse,
+                            NULL, atx, aty, mmr ? 0 : length - 18);
+
+  // combine the region bitmap into the page bitmap
+  if (imm) {
+    if (pageH == 0xffffffff && y + h > curPageH) {
+      pageBitmap->expand(y + h, pageDefPixel);
+    }
+    pageBitmap->combine(bitmap, x, y, extCombOp);
+    delete bitmap;
+
+  // store the region bitmap
+  } else {
+    bitmap->setSegNum(segNum);
+    segments->append(bitmap);
+  }
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,
+                                           int templ, GBool tpgdOn,
+                                           GBool useSkip, JBIG2Bitmap *skip,
+                                           int *atx, int *aty,
+                                           int mmrDataLength) {
+  JBIG2Bitmap *bitmap;
+  GBool ltp;
+  Guint ltpCX, cx, cx0, cx1, cx2;
+  int *refLine, *codingLine;
+  int code1, code2, code3;
+  int x, y, a0, pix, i, refI, codingI;
+
+  bitmap = new JBIG2Bitmap(0, w, h);
+  bitmap->clearToZero();
+
+  //----- MMR decode
+
+  if (mmr) {
+
+    mmrDecoder->reset();
+    refLine = (int *)gmalloc((w + 2) * sizeof(int));
+    codingLine = (int *)gmalloc((w + 2) * sizeof(int));
+    codingLine[0] = codingLine[1] = w;
+
+    for (y = 0; y < h; ++y) {
+
+      // copy coding line to ref line
+      for (i = 0; codingLine[i] < w; ++i) {
+       refLine[i] = codingLine[i];
+      }
+      refLine[i] = refLine[i + 1] = w;
+
+      // decode a line
+      refI = 0;     // b1 = refLine[refI]
+      codingI = 0;  // a1 = codingLine[codingI]
+      a0 = 0;
+      do {
+       code1 = mmrDecoder->get2DCode();
+       switch (code1) {
+       case twoDimPass:
+         if (refLine[refI] < w) {
+           a0 = refLine[refI + 1];
+           refI += 2;
+         }
+         break;
+       case twoDimHoriz:
+         if (codingI & 1) {
+           code1 = 0;
+           do {
+             code1 += code3 = mmrDecoder->getBlackCode();
+           } while (code3 >= 64);
+           code2 = 0;
+           do {
+             code2 += code3 = mmrDecoder->getWhiteCode();
+           } while (code3 >= 64);
+         } else {
+           code1 = 0;
+           do {
+             code1 += code3 = mmrDecoder->getWhiteCode();
+           } while (code3 >= 64);
+           code2 = 0;
+           do {
+             code2 += code3 = mmrDecoder->getBlackCode();
+           } while (code3 >= 64);
+         }
+         a0 = codingLine[codingI++] = a0 + code1;
+         a0 = codingLine[codingI++] = a0 + code2;
+         while (refLine[refI] <= a0 && refLine[refI] < w) {
+           refI += 2;
+         }
+         break;
+       case twoDimVert0:
+         a0 = codingLine[codingI++] = refLine[refI];
+         if (refLine[refI] < w) {
+           ++refI;
+         }
+         break;
+       case twoDimVertR1:
+         a0 = codingLine[codingI++] = refLine[refI] + 1;
+         if (refLine[refI] < w) {
+           ++refI;
+           while (refLine[refI] <= a0 && refLine[refI] < w) {
+             refI += 2;
+           }
+         }
+         break;
+       case twoDimVertR2:
+         a0 = codingLine[codingI++] = refLine[refI] + 2;
+         if (refLine[refI] < w) {
+           ++refI;
+           while (refLine[refI] <= a0 && refLine[refI] < w) {
+             refI += 2;
+           }
+         }
+         break;
+       case twoDimVertR3:
+         a0 = codingLine[codingI++] = refLine[refI] + 3;
+         if (refLine[refI] < w) {
+           ++refI;
+           while (refLine[refI] <= a0 && refLine[refI] < w) {
+             refI += 2;
+           }
+         }
+         break;
+       case twoDimVertL1:
+         a0 = codingLine[codingI++] = refLine[refI] - 1;
+         if (refI > 0) {
+           --refI;
+         } else {
+           ++refI;
+         }
+         while (refLine[refI] <= a0 && refLine[refI] < w) {
+           refI += 2;
+         }
+         break;
+       case twoDimVertL2:
+         a0 = codingLine[codingI++] = refLine[refI] - 2;
+         if (refI > 0) {
+           --refI;
+         } else {
+           ++refI;
+         }
+         while (refLine[refI] <= a0 && refLine[refI] < w) {
+           refI += 2;
+         }
+         break;
+       case twoDimVertL3:
+         a0 = codingLine[codingI++] = refLine[refI] - 3;
+         if (refI > 0) {
+           --refI;
+         } else {
+           ++refI;
+         }
+         while (refLine[refI] <= a0 && refLine[refI] < w) {
+           refI += 2;
+         }
+         break;
+       default:
+         error(getPos(), "Illegal code in JBIG2 MMR bitmap data");
+         break;
+       }
+      } while (a0 < w);
+      codingLine[codingI++] = w;
+
+      // convert the run lengths to a bitmap line
+      i = 0;
+      while (codingLine[i] < w) {
+       for (x = codingLine[i]; x < codingLine[i+1]; ++x) {
+         bitmap->setPixel(x, y);
+       }
+       i += 2;
+      }
+    }
+
+    if (mmrDataLength >= 0) {
+      mmrDecoder->skipTo(mmrDataLength);
+    } else {
+      if (mmrDecoder->get24Bits() != 0x001001) {
+       error(getPos(), "Missing EOFB in JBIG2 MMR bitmap data");
+      }
+    }
+
+    gfree(refLine);
+    gfree(codingLine);
+
+  //----- arithmetic decode
+
+  } else {
+    // set up the typical row context
+    ltpCX = 0; // make gcc happy
+    if (tpgdOn) {
+      switch (templ) {
+      case 0:
+       ltpCX = 0x3953; // 001 11001 0101 0011
+       break;
+      case 1:
+       ltpCX = 0x079a; // 0011 11001 101 0
+       break;
+      case 2:
+       ltpCX = 0x0e3; // 001 1100 01 1
+       break;
+      case 3:
+       ltpCX = 0x18a; // 01100 0101 1
+       break;
+      }
+    }
+
+    ltp = 0;
+    cx = cx0 = cx1 = cx2 = 0; // make gcc happy
+    for (y = 0; y < h; ++y) {
+
+      // check for a "typical" (duplicate) row
+      if (tpgdOn) {
+       if (arithDecoder->decodeBit(ltpCX, genericRegionStats)) {
+         ltp = !ltp;
+       }
+       if (ltp) {
+         bitmap->duplicateRow(y, y-1);
+         continue;
+       }
+      }
+
+      // set up the context
+      switch (templ) {
+      case 0:
+       cx0 = (bitmap->getPixel(0, y-2) << 1) |
+             bitmap->getPixel(1, y-2);
+       cx1 = (bitmap->getPixel(0, y-1) << 2) |
+             (bitmap->getPixel(1, y-1) << 1) |
+             bitmap->getPixel(2, y-1);
+       cx2 = 0;
+       break;
+      case 1:
+       cx0 = (bitmap->getPixel(0, y-2) << 2) |
+             (bitmap->getPixel(1, y-2) << 1) |
+             bitmap->getPixel(2, y-2);
+       cx1 = (bitmap->getPixel(0, y-1) << 2) |
+             (bitmap->getPixel(1, y-1) << 1) |
+             bitmap->getPixel(2, y-1);
+       cx2 = 0;
+       break;
+      case 2:
+       cx0 = (bitmap->getPixel(0, y-2) << 1) |
+             bitmap->getPixel(1, y-2);
+       cx1 = (bitmap->getPixel(0, y-1) << 1) |
+             bitmap->getPixel(1, y-1);
+       cx2 = 0;
+       break;
+      case 3:
+       cx1 = (bitmap->getPixel(0, y-1) << 1) |
+             bitmap->getPixel(1, y-1);
+       cx2 = 0;
+       break;
+      }
+
+      // decode the row
+      for (x = 0; x < w; ++x) {
+
+       // check for a skipped pixel
+       if (useSkip && skip->getPixel(x, y)) {
+         pix = 0;
+
+       } else {
+
+         // build the context
+         switch (templ) {
+         case 0:
+           cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) |
+                (bitmap->getPixel(x + atx[0], y + aty[0]) << 3) |
+                (bitmap->getPixel(x + atx[1], y + aty[1]) << 2) |
+                (bitmap->getPixel(x + atx[2], y + aty[2]) << 1) |
+                bitmap->getPixel(x + atx[3], y + aty[3]);
+           break;
+         case 1:
+           cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) |
+                bitmap->getPixel(x + atx[0], y + aty[0]);
+           break;
+         case 2:
+           cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) |
+                bitmap->getPixel(x + atx[0], y + aty[0]);
+           break;
+         case 3:
+           cx = (cx1 << 5) | (cx2 << 1) |
+                bitmap->getPixel(x + atx[0], y + aty[0]);
+           break;
+         }
+
+         // decode the pixel
+         if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+           bitmap->setPixel(x, y);
+         }
+       }
+
+       // update the context
+       switch (templ) {
+         case 0:
+           cx0 = ((cx0 << 1) | bitmap->getPixel(x+2, y-2)) & 0x07;
+           cx1 = ((cx1 << 1) | bitmap->getPixel(x+3, y-1)) & 0x1f;
+           cx2 = ((cx2 << 1) | pix) & 0x0f;
+           break;
+         case 1:
+           cx0 = ((cx0 << 1) | bitmap->getPixel(x+3, y-2)) & 0x0f;
+           cx1 = ((cx1 << 1) | bitmap->getPixel(x+3, y-1)) & 0x1f;
+           cx2 = ((cx2 << 1) | pix) & 0x07;
+           break;
+         case 2:
+           cx0 = ((cx0 << 1) | bitmap->getPixel(x+2, y-2)) & 0x07;
+           cx1 = ((cx1 << 1) | bitmap->getPixel(x+2, y-1)) & 0x0f;
+           cx2 = ((cx2 << 1) | pix) & 0x03;
+           break;
+         case 3:
+           cx1 = ((cx1 << 1) | bitmap->getPixel(x+2, y-1)) & 0x1f;
+           cx2 = ((cx2 << 1) | pix) & 0x0f;
+           break;
+       }
+      }
+    }
+  }
+
+  return bitmap;
+}
+
+void JBIG2Stream::readGenericRefinementRegionSeg(Guint segNum, GBool imm,
+                                                GBool lossless, Guint length,
+                                                Guint *refSegs,
+                                                Guint nRefSegs) {
+  JBIG2Bitmap *bitmap, *refBitmap;
+  Guint w, h, x, y, segInfoFlags, extCombOp;
+  Guint flags, templ, tpgrOn;
+  int atx[2], aty[2];
+  JBIG2Segment *seg;
+
+  // region segment info field
+  if (!readULong(&w) || !readULong(&h) ||
+      !readULong(&x) || !readULong(&y) ||
+      !readUByte(&segInfoFlags)) {
+    goto eofError;
+  }
+  extCombOp = segInfoFlags & 7;
+
+  // rest of the generic refinement region segment header
+  if (!readUByte(&flags)) {
+    goto eofError;
+  }
+  templ = flags & 1;
+  tpgrOn = (flags >> 1) & 1;
+
+  // AT flags
+  if (!templ) {
+    if (!readByte(&atx[0]) || !readByte(&aty[0]) ||
+       !readByte(&atx[1]) || !readByte(&aty[1])) {
+      goto eofError;
+    }
+  }
+
+  // resize the page bitmap if needed
+  if (nRefSegs == 0 || imm) {
+    if (pageH == 0xffffffff && y + h > curPageH) {
+      pageBitmap->expand(y + h, pageDefPixel);
+    }
+  }
+
+  // get referenced bitmap
+  if (nRefSegs > 1) {
+    error(getPos(), "Bad reference in JBIG2 generic refinement segment");
+    return;
+  }
+  if (nRefSegs == 1) {
+    seg = findSegment(refSegs[0]);
+    if (seg->getType() != jbig2SegBitmap) {
+      error(getPos(), "Bad bitmap reference in JBIG2 generic refinement segment");
+      return;
+    }
+    refBitmap = (JBIG2Bitmap *)seg;
+  } else {
+    refBitmap = pageBitmap->getSlice(x, y, w, h);
+  }
+
+  // set up the arithmetic decoder
+  resetRefinementStats(templ, NULL);
+  arithDecoder->start();
+
+  // read
+  bitmap = readGenericRefinementRegion(w, h, templ, tpgrOn,
+                                      refBitmap, 0, 0, atx, aty);
+
+  // combine the region bitmap into the page bitmap
+  if (imm) {
+    pageBitmap->combine(bitmap, x, y, extCombOp);
+    delete bitmap;
+
+  // store the region bitmap
+  } else {
+    bitmap->setSegNum(segNum);
+    segments->append(bitmap);
+  }
+
+  // delete the referenced bitmap
+  if (nRefSegs == 1) {
+    discardSegment(refSegs[0]);
+  } else {
+    delete refBitmap;
+  }
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+JBIG2Bitmap *JBIG2Stream::readGenericRefinementRegion(int w, int h,
+                                                     int templ, GBool tpgrOn,
+                                                     JBIG2Bitmap *refBitmap,
+                                                     int refDX, int refDY,
+                                                     int *atx, int *aty) {
+  JBIG2Bitmap *bitmap;
+  GBool ltp;
+  Guint ltpCX, cx, cx0, cx2, cx3, cx4, tpgrCX0, tpgrCX1, tpgrCX2;
+  int x, y, pix;
+
+  bitmap = new JBIG2Bitmap(0, w, h);
+  bitmap->clearToZero();
+
+  // set up the typical row context
+  if (templ) {
+    ltpCX = 0x008;
+  } else {
+    ltpCX = 0x0010;
+  }
+
+  ltp = 0;
+  for (y = 0; y < h; ++y) {
+
+    // set up the context
+    if (templ) {
+      cx0 = bitmap->getPixel(0, y-1);
+      cx2 = 0; // unused
+      cx3 = (refBitmap->getPixel(-1-refDX, y-refDY) << 1) |
+           refBitmap->getPixel(-refDX, y-refDY);
+      cx4 = refBitmap->getPixel(-refDX, y+1-refDY);
+    } else {
+      cx0 = bitmap->getPixel(0, y-1);
+      cx2 = refBitmap->getPixel(-refDX, y-1-refDY);
+      cx3 = (refBitmap->getPixel(-1-refDX, y-refDY) << 1) |
+           refBitmap->getPixel(-refDX, y-refDY);
+      cx4 = (refBitmap->getPixel(-1-refDX, y+1-refDY) << 1) |
+           refBitmap->getPixel(-refDX, y+1-refDY);
+    }
+
+    // set up the typical prediction context
+    tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy
+    if (tpgrOn) {
+      tpgrCX0 = (refBitmap->getPixel(-1-refDX, y-1-refDY) << 2) |
+               (refBitmap->getPixel(-refDX, y-1-refDY) << 1) |
+               refBitmap->getPixel(1-refDX, y-1-refDY);
+      tpgrCX1 = (refBitmap->getPixel(-1-refDX, y-refDY) << 2) |
+               (refBitmap->getPixel(-refDX, y-refDY) << 1) |
+               refBitmap->getPixel(1-refDX, y-refDY);
+      tpgrCX2 = (refBitmap->getPixel(-1-refDX, y+1-refDY) << 2) |
+               (refBitmap->getPixel(-refDX, y+1-refDY) << 1) |
+               refBitmap->getPixel(1-refDX, y+1-refDY);
+    }
+
+    for (x = 0; x < w; ++x) {
+
+      // update the context
+      if (templ) {
+       cx0 = ((cx0 << 1) | bitmap->getPixel(x+1, y-1)) & 7;
+       cx3 = ((cx3 << 1) | refBitmap->getPixel(x+1-refDX, y-refDY)) & 7;
+       cx4 = ((cx4 << 1) | refBitmap->getPixel(x+1-refDX, y+1-refDY)) & 3;
+      } else {
+       cx0 = ((cx0 << 1) | bitmap->getPixel(x+1, y-1)) & 3;
+       cx2 = ((cx2 << 1) | refBitmap->getPixel(x+1-refDX, y-1-refDY)) & 3;
+       cx3 = ((cx3 << 1) | refBitmap->getPixel(x+1-refDX, y-refDY)) & 7;
+       cx4 = ((cx4 << 1) | refBitmap->getPixel(x+1-refDX, y+1-refDY)) & 7;
+      }
+
+      if (tpgrOn) {
+       // update the typical predictor context
+       tpgrCX0 = ((tpgrCX0 << 1) |
+                  refBitmap->getPixel(x+1-refDX, y-1-refDY)) & 7;
+       tpgrCX1 = ((tpgrCX1 << 1) |
+                  refBitmap->getPixel(x+1-refDX, y-refDY)) & 7;
+       tpgrCX2 = ((tpgrCX2 << 1) |
+                  refBitmap->getPixel(x+1-refDX, y+1-refDY)) & 7;
+
+       // check for a "typical" pixel
+       if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) {
+         ltp = !ltp;
+       }
+       if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) {
+         bitmap->clearPixel(x, y);
+         continue;
+       } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) {
+         bitmap->setPixel(x, y);
+         continue;
+       }
+      }
+
+      // build the context
+      if (templ) {
+       cx = (cx0 << 7) | (bitmap->getPixel(x-1, y) << 6) |
+            (refBitmap->getPixel(x-refDX, y-1-refDY) << 5) |
+            (cx3 << 2) | cx4;
+      } else {
+       cx = (cx0 << 11) | (bitmap->getPixel(x-1, y) << 10) |
+            (cx2 << 8) | (cx3 << 5) | (cx4 << 2) |
+            (bitmap->getPixel(x+atx[0], y+aty[0]) << 1) |
+            refBitmap->getPixel(x+atx[1]-refDX, y+aty[1]-refDY);
+      }
+
+      // decode the pixel
+      if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) {
+       bitmap->setPixel(x, y);
+      }
+    }
+  }
+
+  return bitmap;
+}
+
+void JBIG2Stream::readPageInfoSeg(Guint length) {
+  Guint xRes, yRes, flags, striping;
+
+  if (!readULong(&pageW) || !readULong(&pageH) ||
+      !readULong(&xRes) || !readULong(&yRes) ||
+      !readUByte(&flags) || !readUWord(&striping)) {
+    goto eofError;
+  }
+  pageDefPixel = (flags >> 2) & 1;
+  defCombOp = (flags >> 3) & 3;
+
+  // allocate the page bitmap
+  if (pageH == 0xffffffff) {
+    curPageH = striping & 0x7fff;
+  } else {
+    curPageH = pageH;
+  }
+  pageBitmap = new JBIG2Bitmap(0, pageW, curPageH);
+
+  // default pixel value
+  if (pageDefPixel) {
+    pageBitmap->clearToOne();
+  } else {
+    pageBitmap->clearToZero();
+  }
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readEndOfStripeSeg(Guint length) {
+  Guint i;
+
+  // skip the segment
+  for (i = 0; i < length; ++i) {
+    curStr->getChar();
+  }
+}
+
+void JBIG2Stream::readProfilesSeg(Guint length) {
+  Guint i;
+
+  // skip the segment
+  for (i = 0; i < length; ++i) {
+    curStr->getChar();
+  }
+}
+
+void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
+  JBIG2HuffmanTable *huffTab;
+  Guint flags, oob, prefixBits, rangeBits;
+  int lowVal, highVal, val;
+  Guint huffTabSize, i;
+
+  if (!readUByte(&flags) || !readLong(&lowVal) || !readLong(&highVal)) {
+    goto eofError;
+  }
+  oob = flags & 1;
+  prefixBits = (flags >> 1) & 7;
+  rangeBits = (flags >> 4) & 7;
+
+  huffDecoder->reset();
+  huffTabSize = 8;
+  huffTab = (JBIG2HuffmanTable *)
+                gmalloc(huffTabSize * sizeof(JBIG2HuffmanTable));
+  i = 0;
+  val = lowVal;
+  while (val < highVal) {
+    if (i == huffTabSize) {
+      huffTabSize *= 2;
+      huffTab = (JBIG2HuffmanTable *)
+                   grealloc(huffTab, huffTabSize * sizeof(JBIG2HuffmanTable));
+    }
+    huffTab[i].val = val;
+    huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+    huffTab[i].rangeLen = huffDecoder->readBits(rangeBits);
+    val += 1 << huffTab[i].rangeLen;
+    ++i;
+  }
+  if (i + oob + 3 > huffTabSize) {
+    huffTabSize = i + oob + 3;
+    huffTab = (JBIG2HuffmanTable *)
+                  grealloc(huffTab, huffTabSize * sizeof(JBIG2HuffmanTable));
+  }
+  huffTab[i].val = lowVal - 1;
+  huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+  huffTab[i].rangeLen = jbig2HuffmanLOW;
+  ++i;
+  huffTab[i].val = highVal;
+  huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+  huffTab[i].rangeLen = 32;
+  ++i;
+  if (oob) {
+    huffTab[i].val = 0;
+    huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+    huffTab[i].rangeLen = jbig2HuffmanOOB;
+    ++i;
+  }
+  huffTab[i].val = 0;
+  huffTab[i].prefixLen = 0;
+  huffTab[i].rangeLen = jbig2HuffmanEOT;
+  ++i;
+  huffDecoder->buildTable(huffTab, i);
+
+  // create and store the new table segment
+  segments->append(new JBIG2CodeTable(segNum, huffTab));
+
+  return;
+
+ eofError:
+  error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readExtensionSeg(Guint length) {
+  Guint i;
+
+  // skip the segment
+  for (i = 0; i < length; ++i) {
+    curStr->getChar();
+  }
+}
+
+JBIG2Segment *JBIG2Stream::findSegment(Guint segNum) {
+  JBIG2Segment *seg;
+  int i;
+
+  for (i = 0; i < globalSegments->getLength(); ++i) {
+    seg = (JBIG2Segment *)globalSegments->get(i);
+    if (seg->getSegNum() == segNum) {
+      return seg;
+    }
+  }
+  for (i = 0; i < segments->getLength(); ++i) {
+    seg = (JBIG2Segment *)segments->get(i);
+    if (seg->getSegNum() == segNum) {
+      return seg;
+    }
+  }
+  return NULL;
+}
+
+void JBIG2Stream::discardSegment(Guint segNum) {
+  JBIG2Segment *seg;
+  int i;
+
+  for (i = 0; i < globalSegments->getLength(); ++i) {
+    seg = (JBIG2Segment *)globalSegments->get(i);
+    if (seg->getSegNum() == segNum) {
+      globalSegments->del(i);
+      return;
+    }
+  }
+  for (i = 0; i < segments->getLength(); ++i) {
+    seg = (JBIG2Segment *)segments->get(i);
+    if (seg->getSegNum() == segNum) {
+      globalSegments->del(i);
+      return;
+    }
+  }
+}
+
+void JBIG2Stream::resetGenericStats(Guint templ,
+                                   JBIG2ArithmeticDecoderStats *prevStats) {
+  int size;
+
+  size = contextSize[templ];
+  if (prevStats && prevStats->getContextSize() == size) {
+    if (genericRegionStats->getContextSize() == size) {
+      genericRegionStats->copyFrom(prevStats);
+    } else {
+      delete genericRegionStats;
+      genericRegionStats = prevStats->copy();
+    }
+  } else {
+    if (genericRegionStats->getContextSize() == size) {
+      genericRegionStats->reset();
+    } else {
+      delete genericRegionStats;
+      genericRegionStats = new JBIG2ArithmeticDecoderStats(size);
+    }
+  }
+}
+
+void JBIG2Stream::resetRefinementStats(
+                     Guint templ,
+                     JBIG2ArithmeticDecoderStats *prevStats) {
+  int size;
+
+  size = refContextSize[templ];
+  if (prevStats && prevStats->getContextSize() == size) {
+    if (refinementRegionStats->getContextSize() == size) {
+      refinementRegionStats->copyFrom(prevStats);
+    } else {
+      delete refinementRegionStats;
+      refinementRegionStats = prevStats->copy();
+    }
+  } else {
+    if (refinementRegionStats->getContextSize() == size) {
+      refinementRegionStats->reset();
+    } else {
+      delete refinementRegionStats;
+      refinementRegionStats = new JBIG2ArithmeticDecoderStats(size);
+    }
+  }
+}
+
+void JBIG2Stream::resetIntStats(int symCodeLen) {
+  iadhStats->reset();
+  iadwStats->reset();
+  iaexStats->reset();
+  iaaiStats->reset();
+  iadtStats->reset();
+  iaitStats->reset();
+  iafsStats->reset();
+  iadsStats->reset();
+  iardxStats->reset();
+  iardyStats->reset();
+  iardwStats->reset();
+  iardhStats->reset();
+  iariStats->reset();
+  if (iaidStats->getContextSize() == symCodeLen + 1) {
+    iaidStats->reset();
+  } else {
+    delete iaidStats;
+    iaidStats = new JBIG2ArithmeticDecoderStats(symCodeLen + 1);
+  }
+}
+
+GBool JBIG2Stream::readUByte(Guint *x) {
+  int c0;
+
+  if ((c0 = curStr->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = (Guint)c0;
+  return gTrue;
+}
+
+GBool JBIG2Stream::readByte(int *x) {
+ int c0;
+
+  if ((c0 = curStr->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = c0;
+  if (c0 & 0x80) {
+    *x |= -1 - 0xff;
+  }
+  return gTrue;
+}
+
+GBool JBIG2Stream::readUWord(Guint *x) {
+  int c0, c1;
+
+  if ((c0 = curStr->getChar()) == EOF ||
+      (c1 = curStr->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = (Guint)((c0 << 8) | c1);
+  return gTrue;
+}
+
+GBool JBIG2Stream::readULong(Guint *x) {
+  int c0, c1, c2, c3;
+
+  if ((c0 = curStr->getChar()) == EOF ||
+      (c1 = curStr->getChar()) == EOF ||
+      (c2 = curStr->getChar()) == EOF ||
+      (c3 = curStr->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
+  return gTrue;
+}
+
+GBool JBIG2Stream::readLong(int *x) {
+  int c0, c1, c2, c3;
+
+  if ((c0 = curStr->getChar()) == EOF ||
+      (c1 = curStr->getChar()) == EOF ||
+      (c2 = curStr->getChar()) == EOF ||
+      (c3 = curStr->getChar()) == EOF) {
+    return gFalse;
+  }
+  *x = ((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
+  if (c0 & 0x80) {
+    *x |= -1 - 0xffffffff;
+  }
+  return gTrue;
+}
diff --git a/pdf/xpdf/JBIG2Stream.h b/pdf/xpdf/JBIG2Stream.h
new file mode 100644 (file)
index 0000000..e15c3ac
--- /dev/null
@@ -0,0 +1,143 @@
+//========================================================================
+//
+// JBIG2Stream.h
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef JBIG2STREAM_H
+#define JBIG2STREAM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+#include "Stream.h"
+
+class GList;
+class JBIG2Segment;
+class JBIG2Bitmap;
+class JBIG2ArithmeticDecoder;
+class JBIG2ArithmeticDecoderStats;
+class JBIG2HuffmanDecoder;
+struct JBIG2HuffmanTable;
+class JBIG2MMRDecoder;
+
+//------------------------------------------------------------------------
+
+class JBIG2Stream: public FilterStream {
+public:
+
+  JBIG2Stream(Stream *strA, Object *globalsStream);
+  virtual ~JBIG2Stream();
+  virtual StreamKind getKind() { return strJBIG2; }
+  virtual void reset();
+  virtual int getChar();
+  virtual int lookChar();
+  virtual GString *getPSFilter(char *indent);
+  virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+  void readSegments();
+  void readSymbolDictSeg(Guint segNum, Guint length,
+                        Guint *refSegs, Guint nRefSegs);
+  void readTextRegionSeg(Guint segNum, GBool imm,
+                        GBool lossless, Guint length,
+                        Guint *refSegs, Guint nRefSegs);
+  JBIG2Bitmap *readTextRegion(GBool huff, GBool refine,
+                             int w, int h,
+                             Guint numInstances,
+                             Guint logStrips,
+                             int numSyms,
+                             JBIG2HuffmanTable *symCodeTab,
+                             Guint symCodeLen,
+                             JBIG2Bitmap **syms,
+                             Guint defPixel, Guint combOp,
+                             Guint transposed, Guint refCorner,
+                             Guint sOffset,
+                             JBIG2HuffmanTable *huffFSTable,
+                             JBIG2HuffmanTable *huffDSTable,
+                             JBIG2HuffmanTable *huffDTTable,
+                             JBIG2HuffmanTable *huffRDWTable,
+                             JBIG2HuffmanTable *huffRDHTable,
+                             JBIG2HuffmanTable *huffRDXTable,
+                             JBIG2HuffmanTable *huffRDYTable,
+                             JBIG2HuffmanTable *huffRSizeTable,
+                             Guint templ,
+                             int *atx, int *aty);
+  void readPatternDictSeg(Guint segNum, Guint length);
+  void readHalftoneRegionSeg(Guint segNum, GBool imm,
+                            GBool lossless, Guint length,
+                            Guint *refSegs, Guint nRefSegs);
+  void readGenericRegionSeg(Guint segNum, GBool imm,
+                           GBool lossless, Guint length);
+  JBIG2Bitmap *readGenericBitmap(GBool mmr, int w, int h,
+                                int templ, GBool tpgdOn,
+                                GBool useSkip, JBIG2Bitmap *skip,
+                                int *atx, int *aty,
+                                int mmrDataLength);
+  void readGenericRefinementRegionSeg(Guint segNum, GBool imm,
+                                     GBool lossless, Guint length,
+                                     Guint *refSegs,
+                                     Guint nRefSegs);
+  JBIG2Bitmap *readGenericRefinementRegion(int w, int h,
+                                          int templ, GBool tpgrOn,
+                                          JBIG2Bitmap *refBitmap,
+                                          int refDX, int refDY,
+                                          int *atx, int *aty);
+  void readPageInfoSeg(Guint length);
+  void readEndOfStripeSeg(Guint length);
+  void readProfilesSeg(Guint length);
+  void readCodeTableSeg(Guint segNum, Guint length);
+  void readExtensionSeg(Guint length);
+  JBIG2Segment *findSegment(Guint segNum);
+  void discardSegment(Guint segNum);
+  void resetGenericStats(Guint templ,
+                                  JBIG2ArithmeticDecoderStats *prevStats);
+  void resetRefinementStats(Guint templ,
+                                     JBIG2ArithmeticDecoderStats *prevStats);
+  void resetIntStats(int symCodeLen);
+  GBool readUByte(Guint *x);
+  GBool readByte(int *x);
+  GBool readUWord(Guint *x);
+  GBool readULong(Guint *x);
+  GBool readLong(int *x);
+
+  Guint pageW, pageH, curPageH;
+  Guint pageDefPixel;
+  JBIG2Bitmap *pageBitmap;
+  Guint defCombOp;
+  GList *segments;             // [JBIG2Segment]
+  GList *globalSegments;       // [JBIG2Segment]
+  Stream *curStr;
+  Guchar *dataPtr;
+  Guchar *dataEnd;
+
+  JBIG2ArithmeticDecoder *arithDecoder;
+  JBIG2ArithmeticDecoderStats *genericRegionStats;
+  JBIG2ArithmeticDecoderStats *refinementRegionStats;
+  JBIG2ArithmeticDecoderStats *iadhStats;
+  JBIG2ArithmeticDecoderStats *iadwStats;
+  JBIG2ArithmeticDecoderStats *iaexStats;
+  JBIG2ArithmeticDecoderStats *iaaiStats;
+  JBIG2ArithmeticDecoderStats *iadtStats;
+  JBIG2ArithmeticDecoderStats *iaitStats;
+  JBIG2ArithmeticDecoderStats *iafsStats;
+  JBIG2ArithmeticDecoderStats *iadsStats;
+  JBIG2ArithmeticDecoderStats *iardxStats;
+  JBIG2ArithmeticDecoderStats *iardyStats;
+  JBIG2ArithmeticDecoderStats *iardwStats;
+  JBIG2ArithmeticDecoderStats *iardhStats;
+  JBIG2ArithmeticDecoderStats *iariStats;
+  JBIG2ArithmeticDecoderStats *iaidStats;
+  JBIG2HuffmanDecoder *huffDecoder;
+  JBIG2MMRDecoder *mmrDecoder;
+};
+
+#endif
diff --git a/pdf/xpdf/Outline.cc b/pdf/xpdf/Outline.cc
new file mode 100644 (file)
index 0000000..256d38d
--- /dev/null
@@ -0,0 +1,140 @@
+//========================================================================
+//
+// Outline.cc
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
+#include "Link.h"
+#include "PDFDocEncoding.h"
+#include "Outline.h"
+
+//------------------------------------------------------------------------
+
+Outline::Outline(Object *outlineObj, XRef *xref) {
+  Object first;
+
+  items = NULL;
+  if (!outlineObj->isDict()) {
+    return;
+  }
+  items = OutlineItem::readItemList(outlineObj->dictLookupNF("First", &first),
+                                   xref);
+  first.free();
+}
+
+Outline::~Outline() {
+  if (items) {
+    deleteGList(items, OutlineItem);
+  }
+}
+
+//------------------------------------------------------------------------
+
+OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
+  Object obj1;
+  GString *s;
+  int i;
+
+  xref = xrefA;
+  title = NULL;
+  action = NULL;
+  kids = NULL;
+
+  if (dict->lookup("Title", &obj1)->isString()) {
+    s = obj1.getString();
+    if ((s->getChar(0) & 0xff) == 0xfe &&
+       (s->getChar(1) & 0xff) == 0xff) {
+      titleLen = (s->getLength() - 2) / 2;
+      title = (Unicode *)gmalloc(titleLen * sizeof(Unicode));
+      for (i = 0; i < titleLen; ++i) {
+       title[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
+                  (s->getChar(3 + 2*i) & 0xff);
+      }
+    } else {
+      titleLen = s->getLength();
+      title = (Unicode *)gmalloc(titleLen * sizeof(Unicode));
+      for (i = 0; i < titleLen; ++i) {
+       title[i] = pdfDocEncoding[s->getChar(i) & 0xff];
+      }
+    }
+  }
+  obj1.free();
+
+  if (!dict->lookup("Dest", &obj1)->isNull()) {
+    action = LinkAction::parseDest(&obj1);
+  } else {
+    obj1.free();
+    if (dict->lookup("A", &obj1)) {
+      action = LinkAction::parseAction(&obj1);
+    }
+  }
+  obj1.free();
+
+  dict->lookupNF("First", &firstRef);
+  dict->lookupNF("Next", &nextRef);
+
+  startsOpen = gFalse;
+  if (dict->lookup("Count", &obj1)->isInt()) {
+    if (obj1.getInt() > 0) {
+      startsOpen = gTrue;
+    }
+  }
+  obj1.free();
+}
+
+OutlineItem::~OutlineItem() {
+  close();
+  if (title) {
+    delete title;
+  }
+  if (action) {
+    delete action;
+  }
+  firstRef.free();
+  nextRef.free();
+}
+
+GList *OutlineItem::readItemList(Object *itemRef, XRef *xrefA) {
+  GList *items;
+  OutlineItem *item;
+  Object obj;
+  Object *p;
+
+  items = new GList();
+  p = itemRef;
+  while (p->isRef()) {
+    if (!p->fetch(xrefA, &obj)->isDict()) {
+      obj.free();
+      break;
+    }
+    item = new OutlineItem(obj.getDict(), xrefA);
+    obj.free();
+    items->append(item);
+    p = &item->nextRef;
+  }
+  return items;
+}
+
+void OutlineItem::open() {
+  if (!kids) {
+    kids = readItemList(&firstRef, xref);
+  }
+}
+
+void OutlineItem::close() {
+  if (kids) {
+    deleteGList(kids, OutlineItem);
+    kids = NULL;
+  }
+}
diff --git a/pdf/xpdf/Outline.h b/pdf/xpdf/Outline.h
new file mode 100644 (file)
index 0000000..dc79252
--- /dev/null
@@ -0,0 +1,74 @@
+//========================================================================
+//
+// Outline.h
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef OUTLINE_H
+#define OUTLINE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+#include "CharTypes.h"
+
+class GString;
+class GList;
+class XRef;
+class LinkAction;
+
+//------------------------------------------------------------------------
+
+class Outline {
+public:
+
+  Outline(Object *outlineObj, XRef *xref);
+  ~Outline();
+
+  GList *getItems() { return items; }
+
+private:
+
+  GList *items;                        // NULL if document has no outline
+                               //   [OutlineItem]
+};
+
+//------------------------------------------------------------------------
+
+class OutlineItem {
+public:
+
+  OutlineItem(Dict *dict, XRef *xrefA);
+  ~OutlineItem();
+
+  static GList *readItemList(Object *itemRef, XRef *xrefA);
+
+  void open();
+  void close();
+
+  Unicode *getTitle() { return title; }
+  int getTitleLength() { return titleLen; }
+  LinkAction *getAction() { return action; }
+  GBool isOpen() { return startsOpen; }
+  GBool hasKids() { return firstRef.isRef(); }
+  GList *getKids() { return kids; }
+
+private:
+
+  XRef *xref;
+  Unicode *title;
+  int titleLen;
+  LinkAction *action;
+  Object firstRef;
+  Object nextRef;
+  GBool startsOpen;
+  GList *kids;                 // NULL unless this item is open [OutlineItem]
+};
+
+#endif
diff --git a/pdf/xpdf/PDFDocEncoding.cc b/pdf/xpdf/PDFDocEncoding.cc
new file mode 100644 (file)
index 0000000..4f1e201
--- /dev/null
@@ -0,0 +1,44 @@
+//========================================================================
+//
+// PDFDocEncoding.h
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include "PDFDocEncoding.h"
+
+Unicode pdfDocEncoding[256] = {
+  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 00
+  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 10
+  0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc,
+  0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, // 20
+  0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+  0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // 30
+  0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+  0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // 40
+  0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+  0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // 50
+  0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+  0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, // 60
+  0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+  0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, // 70
+  0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000,
+  0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044, // 80
+  0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018,
+  0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160, // 90
+  0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e, 0x0000,
+  0x20ac, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0
+  0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x0000, 0x00ae, 0x00af,
+  0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0
+  0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+  0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
+  0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+  0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
+  0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+  0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
+  0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+  0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
+  0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
+};
diff --git a/pdf/xpdf/PDFDocEncoding.h b/pdf/xpdf/PDFDocEncoding.h
new file mode 100644 (file)
index 0000000..6fc157f
--- /dev/null
@@ -0,0 +1,16 @@
+//========================================================================
+//
+// PDFDocEncoding.h
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PDFDOCENCODING_H
+#define PDFDOCENCODING_H
+
+#include "CharTypes.h"
+
+extern Unicode pdfDocEncoding[256];
+
+#endif
diff --git a/pdf/xpdf/XPDFApp.cc b/pdf/xpdf/XPDFApp.cc
new file mode 100644 (file)
index 0000000..e456310
--- /dev/null
@@ -0,0 +1,386 @@
+//========================================================================
+//
+// XPDFApp.cc
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "XPDFViewer.h"
+#include "XPDFApp.h"
+#include "config.h"
+
+// these macro defns conflict with xpdf's Object class
+#ifdef LESSTIF_VERSION
+#undef XtDisplay
+#undef XtScreen
+#undef XtWindow
+#undef XtParent
+#undef XtIsRealized
+#endif
+
+//------------------------------------------------------------------------
+
+#define remoteCmdSize 512
+
+//------------------------------------------------------------------------
+
+static String fallbackResources[] = {
+  "*XmTextField.fontList: -*-courier-medium-r-normal--12-*-*-*-*-*-iso8859-1",
+  "*.fontList: -*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1",
+  "*XmTextField.translations: #override\\n"
+  "  Ctrl<Key>a:beginning-of-line()\\n"
+  "  Ctrl<Key>b:backward-character()\\n"
+  "  Ctrl<Key>d:delete-next-character()\\n"
+  "  Ctrl<Key>e:end-of-line()\\n"
+  "  Ctrl<Key>f:forward-character()\\n"
+  "  Ctrl<Key>u:beginning-of-line()delete-to-end-of-line()\\n"
+  "  Ctrl<Key>k:delete-to-end-of-line()\\n",
+  NULL
+};
+
+static XrmOptionDescRec xOpts[] = {
+  {"-display",       ".display",         XrmoptionSepArg,  NULL},
+  {"-foreground",    "*Foreground",      XrmoptionSepArg,  NULL},
+  {"-fg",            "*Foreground",      XrmoptionSepArg,  NULL},
+  {"-background",    "*Background",      XrmoptionSepArg,  NULL},
+  {"-bg",            "*Background",      XrmoptionSepArg,  NULL},
+  {"-geometry",      ".geometry",        XrmoptionSepArg,  NULL},
+  {"-g",             ".geometry",        XrmoptionSepArg,  NULL},
+  {"-font",          "*.fontList",       XrmoptionSepArg,  NULL},
+  {"-fn",            "*.fontList",       XrmoptionSepArg,  NULL},
+  {"-title",         ".title",           XrmoptionSepArg,  NULL},
+  {"-cmap",          ".installCmap",     XrmoptionNoArg,   (XPointer)"on"},
+  {"-rgb",           ".rgbCubeSize",     XrmoptionSepArg,  NULL},
+  {"-rv",            ".reverseVideo",    XrmoptionNoArg,   (XPointer)"true"},
+  {"-papercolor",    ".paperColor",      XrmoptionSepArg,  NULL},
+  {"-z",             ".initialZoom",     XrmoptionSepArg,  NULL}
+};
+
+#define nXOpts (sizeof(xOpts) / sizeof(XrmOptionDescRec))
+
+struct XPDFAppResources {
+  String geometry;
+  String title;
+  Bool installCmap;
+  int rgbCubeSize;
+  Bool reverseVideo;
+  String paperColor;
+  String initialZoom;
+  Bool viKeys;
+};
+
+static Bool defInstallCmap = False;
+static int defRGBCubeSize = defaultRGBCube;
+static Bool defReverseVideo = False;
+static Bool defViKeys = False;
+
+static XtResource xResources[] = {
+  { "geometry",     "Geometry",     XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, geometry),     XtRString, (XtPointer)NULL             },
+  { "title",        "Title",        XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, title),        XtRString, (XtPointer)NULL             },
+  { "installCmap",  "InstallCmap",  XtRBool,   sizeof(Bool),   XtOffsetOf(XPDFAppResources, installCmap),  XtRBool,   (XtPointer)&defInstallCmap  },
+  { "rgbCubeSize",  "RgbCubeSize",  XtRInt,    sizeof(int),    XtOffsetOf(XPDFAppResources, rgbCubeSize),  XtRInt,    (XtPointer)&defRGBCubeSize  },
+  { "reverseVideo", "ReverseVideo", XtRBool,   sizeof(Bool),   XtOffsetOf(XPDFAppResources, reverseVideo), XtRBool,   (XtPointer)&defReverseVideo },
+  { "paperColor",   "PaperColor",   XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, paperColor),   XtRString, (XtPointer)NULL             },
+  { "initialZoom",  "InitialZoom",  XtRString, sizeof(String), XtOffsetOf(XPDFAppResources, initialZoom),  XtRString, (XtPointer)NULL             },
+  { "viKeys",       "ViKeys",       XtRBool,   sizeof(Bool),   XtOffsetOf(XPDFAppResources, viKeys),       XtRBool,   (XtPointer)&defViKeys       }
+};
+
+#define nXResources (sizeof(xResources) / sizeof(XtResource))
+
+//------------------------------------------------------------------------
+// XPDFApp
+//------------------------------------------------------------------------
+
+#if 0 //~ for debugging
+static int xErrorHandler(Display *display, XErrorEvent *ev) {
+  printf("X error:\n");
+  printf("  resource ID = %08lx\n", ev->resourceid);
+  printf("  serial = %lu\n", ev->serial);
+  printf("  error_code = %d\n", ev->error_code);
+  printf("  request_code = %d\n", ev->request_code);
+  printf("  minor_code = %d\n", ev->minor_code);
+  fflush(stdout);
+  abort();
+}
+#endif
+
+XPDFApp::XPDFApp(int *argc, char *argv[]) {
+  appShell = XtAppInitialize(&appContext, xpdfAppName, xOpts, nXOpts,
+                            argc, argv, fallbackResources, NULL, 0);
+  display = XtDisplay(appShell);
+  screenNum = XScreenNumberOfScreen(XtScreen(appShell));
+#if XmVERSION > 1
+  XtVaSetValues(XmGetXmDisplay(XtDisplay(appShell)),
+               XmNenableButtonTab, True, NULL);
+#endif
+#if XmVERSION > 1
+  // Drag-and-drop appears to be buggy -- I'm seeing weird crashes
+  // deep in the Motif code when I destroy widgets in the XpdfForms
+  // code.  Xpdf doesn't use it, so just turn it off.
+  XtVaSetValues(XmGetXmDisplay(XtDisplay(appShell)),
+               XmNdragInitiatorProtocolStyle, XmDRAG_NONE,
+               XmNdragReceiverProtocolStyle, XmDRAG_NONE,
+               NULL);
+#endif
+
+#if 0 //~ for debugging
+  XSynchronize(display, True);
+  XSetErrorHandler(&xErrorHandler);
+#endif
+
+  fullScreen = gFalse;
+  remoteAtom = None;
+  remoteViewer = NULL;
+  remoteWin = None;
+
+  getResources();
+
+  viewers = new GList();
+
+}
+
+void XPDFApp::getResources() {
+  XPDFAppResources resources;
+  XColor xcol, xcol2;
+  Colormap colormap;
+  
+  XtGetApplicationResources(appShell, &resources, xResources, nXResources,
+                           NULL, 0);
+  geometry = resources.geometry ? new GString(resources.geometry)
+                                : (GString *)NULL;
+  title = resources.title ? new GString(resources.title) : (GString *)NULL;
+  installCmap = (GBool)resources.installCmap;
+  rgbCubeSize = resources.rgbCubeSize;
+  reverseVideo = (GBool)resources.reverseVideo;
+  paperColor = reverseVideo ? BlackPixel(display, screenNum) :
+                              WhitePixel(display, screenNum);
+  if (resources.paperColor) {
+    XtVaGetValues(appShell, XmNcolormap, &colormap, NULL);
+    if (XAllocNamedColor(display, colormap, resources.paperColor,
+                        &xcol, &xcol2)) {
+      paperColor = xcol.pixel;
+    } else {
+      error(-1, "Couldn't allocate color '%s'", resources.paperColor);
+    }
+  }
+  initialZoom = resources.initialZoom ? new GString(resources.initialZoom)
+                                      : (GString *)NULL;
+  viKeys = (GBool)resources.viKeys;
+}
+
+XPDFApp::~XPDFApp() {
+  deleteGList(viewers, XPDFViewer);
+  if (geometry) {
+    delete geometry;
+  }
+  if (title) {
+    delete title;
+  }
+  if (initialZoom) {
+    delete initialZoom;
+  }
+}
+
+XPDFViewer *XPDFApp::open(GString *fileName, int page,
+                         GString *ownerPassword, GString *userPassword) {
+  XPDFViewer *viewer;
+
+  viewer = new XPDFViewer(this, fileName, page, NULL,
+                         ownerPassword, userPassword);
+  if (!viewer->isOk()) {
+    delete viewer;
+    return NULL;
+  }
+  if (remoteAtom != None) {
+    remoteViewer = viewer;
+    remoteWin = viewer->getWindow();
+    XtAddEventHandler(remoteWin, PropertyChangeMask, False,
+                     &remoteMsgCbk, this);
+    XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin), CurrentTime);
+  }
+  viewers->append(viewer);
+  return viewer;
+}
+
+XPDFViewer *XPDFApp::openAtDest(GString *fileName, GString *dest,
+                               GString *ownerPassword,
+                               GString *userPassword) {
+  XPDFViewer *viewer;
+
+  viewer = new XPDFViewer(this, fileName, 1, dest,
+                         ownerPassword, userPassword);
+  if (!viewer->isOk()) {
+    delete viewer;
+    return NULL;
+  }
+  if (remoteAtom != None) {
+    remoteViewer = viewer;
+    remoteWin = viewer->getWindow();
+    XtAddEventHandler(remoteWin, PropertyChangeMask, False,
+                     &remoteMsgCbk, this);
+    XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin), CurrentTime);
+  }
+  viewers->append(viewer);
+  return viewer;
+}
+
+void XPDFApp::close(XPDFViewer *viewer, GBool closeLast) {
+  int i;
+
+  if (viewers->getLength() == 1) {
+    if (viewer != (XPDFViewer *)viewers->get(0)) {
+      return;
+    }
+    if (closeLast) {
+      quit();
+    } else {
+      viewer->clear();
+    }
+  } else {
+    for (i = 0; i < viewers->getLength(); ++i) {
+      if (((XPDFViewer *)viewers->get(i)) == viewer) {
+       viewers->del(i);
+       if (remoteAtom != None && remoteViewer == viewer) {
+         remoteViewer = (XPDFViewer *)viewers->get(viewers->getLength() - 1);
+         remoteWin = remoteViewer->getWindow();
+         XSetSelectionOwner(display, remoteAtom, XtWindow(remoteWin),
+                            CurrentTime);
+       }
+       delete viewer;
+       return;
+      }
+    }
+  }
+}
+
+void XPDFApp::quit() {
+  if (remoteAtom != None) {
+    XSetSelectionOwner(display, remoteAtom, None, CurrentTime);
+  }
+  while (viewers->getLength() > 0) {
+    delete (XPDFViewer *)viewers->del(0);
+  }
+  XtAppSetExitFlag(appContext);
+}
+
+void XPDFApp::run() {
+  XtAppMainLoop(appContext);
+}
+
+void XPDFApp::setRemoteName(char *remoteName) {
+  remoteAtom = XInternAtom(display, remoteName, False);
+  remoteXWin = XGetSelectionOwner(display, remoteAtom);
+}
+
+GBool XPDFApp::remoteServerRunning() {
+  return remoteXWin != None;
+}
+
+void XPDFApp::remoteOpen(GString *fileName, int page, GBool raise) {
+  char cmd[remoteCmdSize];
+
+  sprintf(cmd, "%c %d %.200s",
+         raise ? 'D' : 'd', page, fileName->getCString());
+  XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
+                 PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1);
+  XFlush(display);
+}
+
+void XPDFApp::remoteOpenAtDest(GString *fileName, GString *dest, GBool raise) {
+  char cmd[remoteCmdSize];
+
+  sprintf(cmd, "%c +%.256s %.200s",
+         raise ? 'D' : 'd', dest->getCString(), fileName->getCString());
+  XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
+                 PropModeReplace, (Guchar *)cmd, strlen(cmd) + 1);
+  XFlush(display);
+}
+
+void XPDFApp::remoteRaise() {
+  XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
+                 PropModeReplace, (Guchar *)"r", 2);
+  XFlush(display);
+}
+
+void XPDFApp::remoteQuit() {
+  XChangeProperty(display, remoteXWin, remoteAtom, remoteAtom, 8,
+                 PropModeReplace, (Guchar *)"q", 2);
+  XFlush(display);
+}
+
+void XPDFApp::remoteMsgCbk(Widget widget, XtPointer ptr,
+                          XEvent *event, Boolean *cont) {
+  XPDFApp *app = (XPDFApp *)ptr;
+  char *cmd;
+  Atom type;
+  int format;
+  Gulong size, remain;
+  char *p, *q;
+  GString *fileName;
+  int page;
+  GString *destName;
+
+  if (event->xproperty.atom != app->remoteAtom) {
+    *cont = True;
+    return;
+  }
+  *cont = False;
+
+  // get command
+  if (XGetWindowProperty(app->display, XtWindow(app->remoteWin),
+                        app->remoteAtom, 0, remoteCmdSize/4,
+                        True, app->remoteAtom,
+                        &type, &format, &size, &remain,
+                        (Guchar **)&cmd) != Success) {
+    return;
+  }
+  if (size == 0) {
+    return;
+  }
+
+  // raise window
+  if (cmd[0] == 'D' || cmd[0] == 'r'){
+    XMapRaised(app->display, XtWindow(app->remoteWin));
+    XFlush(app->display);
+  }
+
+  // display file / page
+  if (cmd[0] == 'd' || cmd[0] == 'D') {
+    p = cmd + 2;
+    q = strchr(p, ' ');
+    if (!q) {
+      return;
+    }
+    *q++ = '\0';
+    page = 1;
+    destName = NULL;
+    if (*p == '+') {
+      destName = new GString(p + 1);
+    } else {
+      page = atoi(p);
+    }
+    if (q) {
+      fileName = new GString(q);
+      app->remoteViewer->open(fileName, page, destName);
+      delete fileName;
+    }
+    XFree((XPointer)cmd);
+    if (destName) {
+      delete destName;
+    }
+
+  // quit
+  } else if (cmd[0] == 'q') {
+    app->quit();
+  }
+}
diff --git a/pdf/xpdf/XPDFApp.h b/pdf/xpdf/XPDFApp.h
new file mode 100644 (file)
index 0000000..4875456
--- /dev/null
@@ -0,0 +1,104 @@
+//========================================================================
+//
+// XPDFApp.h
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef XPDFAPP_H
+#define XPDFAPP_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#define Object XtObject
+#include <Xm/XmAll.h>
+#undef Object
+#include "gtypes.h"
+
+class GString;
+class GList;
+class XPDFViewer;
+
+//------------------------------------------------------------------------
+
+#define xpdfAppName "Xpdf"
+
+//------------------------------------------------------------------------
+// XPDFApp
+//------------------------------------------------------------------------
+
+class XPDFApp {
+public:
+
+  XPDFApp(int *argc, char *argv[]);
+  ~XPDFApp();
+
+  XPDFViewer *open(GString *fileName, int page = 1,
+                  GString *ownerPassword = NULL,
+                  GString *userPassword = NULL);
+  XPDFViewer *openAtDest(GString *fileName, GString *dest,
+                        GString *ownerPassword = NULL,
+                        GString *userPassword = NULL);
+  void close(XPDFViewer *viewer, GBool closeLast);
+  void quit();
+
+  void run();
+
+  //----- remote server
+  void setRemoteName(char *remoteName);
+  GBool remoteServerRunning();
+  void remoteOpen(GString *fileName, int page, GBool raise);
+  void remoteOpenAtDest(GString *fileName, GString *dest, GBool raise);
+  void remoteRaise();
+  void remoteQuit();
+
+  //----- resource/option values
+  GString *getGeometry() { return geometry; }
+  GString *getTitle() { return title; }
+  GBool getInstallCmap() { return installCmap; }
+  int getRGBCubeSize() { return rgbCubeSize; }
+  GBool getReverseVideo() { return reverseVideo; }
+  Gulong getPaperColor() { return paperColor; }
+  GString *getInitialZoom() { return initialZoom; }
+  GBool getViKeys() { return viKeys; }
+  void setFullScreen(GBool fullScreenA) { fullScreen = fullScreenA; }
+  GBool getFullScreen() { return fullScreen; }
+
+  XtAppContext getAppContext() { return appContext; }
+  Widget getAppShell() { return appShell; }
+
+private:
+
+  void getResources();
+  static void remoteMsgCbk(Widget widget, XtPointer ptr,
+                          XEvent *event, Boolean *cont);
+
+  Display *display;
+  int screenNum;
+  XtAppContext appContext;
+  Widget appShell;
+  GList *viewers;              // [XPDFViewer]
+
+  Atom remoteAtom;
+  Window remoteXWin;
+  XPDFViewer *remoteViewer;
+  Widget remoteWin;
+
+  //----- resource/option values
+  GString *geometry;
+  GString *title;
+  GBool installCmap;
+  int rgbCubeSize;
+  GBool reverseVideo;
+  Gulong paperColor;
+  GString *initialZoom;
+  GBool viKeys;
+  GBool fullScreen;
+};
+
+#endif
diff --git a/pdf/xpdf/XPDFCore.cc b/pdf/xpdf/XPDFCore.cc
new file mode 100644 (file)
index 0000000..8376ce9
--- /dev/null
@@ -0,0 +1,1913 @@
+//========================================================================
+//
+// XPDFCore.cc
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <X11/keysym.h>
+#include <X11/cursorfont.h>
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "PDFDoc.h"
+#include "ErrorCodes.h"
+#include "GfxState.h"
+#include "PSOutputDev.h"
+#include "TextOutputDev.h"
+#include "XPixmapOutputDev.h"
+#include "XPDFCore.h"
+
+// these macro defns conflict with xpdf's Object class
+#ifdef LESSTIF_VERSION
+#undef XtDisplay
+#undef XtScreen
+#undef XtWindow
+#undef XtParent
+#undef XtIsRealized
+#endif
+
+// hack around old X includes which are missing these symbols
+#ifndef XK_Page_Up
+#define XK_Page_Up              0xFF55
+#endif
+#ifndef XK_Page_Down
+#define XK_Page_Down            0xFF56
+#endif
+#ifndef XK_KP_Home
+#define XK_KP_Home              0xFF95
+#endif
+#ifndef XK_KP_Left
+#define XK_KP_Left              0xFF96
+#endif
+#ifndef XK_KP_Up
+#define XK_KP_Up                0xFF97
+#endif
+#ifndef XK_KP_Right
+#define XK_KP_Right             0xFF98
+#endif
+#ifndef XK_KP_Down
+#define XK_KP_Down              0xFF99
+#endif
+#ifndef XK_KP_Prior
+#define XK_KP_Prior             0xFF9A
+#endif
+#ifndef XK_KP_Page_Up
+#define XK_KP_Page_Up           0xFF9A
+#endif
+#ifndef XK_KP_Next
+#define XK_KP_Next              0xFF9B
+#endif
+#ifndef XK_KP_Page_Down
+#define XK_KP_Page_Down         0xFF9B
+#endif
+#ifndef XK_KP_End
+#define XK_KP_End               0xFF9C
+#endif
+#ifndef XK_KP_Begin
+#define XK_KP_Begin             0xFF9D
+#endif
+#ifndef XK_KP_Insert
+#define XK_KP_Insert            0xFF9E
+#endif
+#ifndef XK_KP_Delete
+#define XK_KP_Delete            0xFF9F
+#endif
+
+//------------------------------------------------------------------------
+
+#define highlightNone     0
+#define highlightNormal   1
+#define highlightSelected 2
+
+//------------------------------------------------------------------------
+
+static int zoomDPI[maxZoom - minZoom + 1] = {
+  29, 35, 42, 50, 60,
+  72,
+  86, 104, 124, 149, 179
+};
+
+//------------------------------------------------------------------------
+
+GString *XPDFCore::currentSelection = NULL;
+XPDFCore *XPDFCore::currentSelectionOwner = NULL;
+
+//------------------------------------------------------------------------
+// XPDFCore
+//------------------------------------------------------------------------
+
+XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
+                  Gulong paperColorA, GBool fullScreenA, GBool reverseVideo,
+                  GBool installCmap, int rgbCubeSize) {
+  GString *initialZoom;
+  int i;
+
+  shell = shellA;
+  parentWidget = parentWidgetA;
+  display = XtDisplay(parentWidget);
+  screenNum = XScreenNumberOfScreen(XtScreen(parentWidget));
+
+  paperColor = paperColorA;
+  fullScreen = fullScreenA;
+
+  // for some reason, querying XmNvisual doesn't work (even if done
+  // after the window is mapped)
+  visual = DefaultVisual(display, screenNum);
+  XtVaGetValues(shell, XmNcolormap, &colormap, NULL);
+
+  scrolledWin = NULL;
+  hScrollBar = NULL;
+  vScrollBar = NULL;
+  drawAreaFrame = NULL;
+  drawArea = NULL;
+  out = NULL;
+
+  doc = NULL;
+  page = 0;
+  rotate = 0;
+
+  // get the initial zoom value
+  initialZoom = globalParams->getInitialZoom();
+  if (!initialZoom->cmp("page")) {
+    zoom = zoomPage;
+  } else if (!initialZoom->cmp("width")) {
+    zoom = zoomWidth;
+  } else {
+    zoom = atoi(initialZoom->getCString());
+    if (zoom < minZoom) {
+      zoom = minZoom;
+    } else if (zoom > maxZoom) {
+      zoom = maxZoom;
+    }
+  }
+
+  scrollX = 0;
+  scrollY = 0;
+  linkAction = NULL;
+  selectXMin = selectXMax = 0;
+  selectYMin = selectYMax = 0;
+  dragging = gFalse;
+  lastDragLeft = lastDragTop = gTrue;
+
+  panning = gFalse;
+
+
+  updateCbk = NULL;
+  actionCbk = NULL;
+  keyPressCbk = NULL;
+  mouseCbk = NULL;
+  reqPasswordCbk = NULL;
+
+  // no history yet
+  historyCur = xpdfHistorySize - 1;
+  historyBLen = historyFLen = 0;
+  for (i = 0; i < xpdfHistorySize; ++i) {
+    history[i].fileName = NULL;
+  }
+
+  // optional features default to on
+  hyperlinksEnabled = gTrue;
+  selectEnabled = gTrue;
+
+  // do X-specific initialization and create the widgets
+  initWindow();
+
+  // create the OutputDev
+  out = new XPixmapOutputDev(display, screenNum, visual, colormap,
+                            reverseVideo, paperColor,
+                            installCmap, rgbCubeSize, gTrue,
+                            &outputDevRedrawCbk, this);
+  out->startDoc(NULL);
+}
+
+XPDFCore::~XPDFCore() {
+  int i;
+
+  if (out) {
+    delete out;
+  }
+  if (doc) {
+    delete doc;
+  }
+  if (currentSelectionOwner == this && currentSelection) {
+    delete currentSelection;
+    currentSelection = NULL;
+    currentSelectionOwner = NULL;
+  }
+  for (i = 0; i < xpdfHistorySize; ++i) {
+    if (history[i].fileName) {
+      delete history[i].fileName;
+    }
+  }
+  if (selectGC) {
+    XFreeGC(display, selectGC);
+    XFreeGC(display, highlightGC);
+  }
+  if (drawAreaGC) {
+    XFreeGC(display, drawAreaGC);
+  }
+  if (drawArea) {
+    XtDestroyWidget(drawArea);
+  }
+  if (drawAreaFrame) {
+    XtDestroyWidget(drawAreaFrame);
+  }
+  if (vScrollBar) {
+    XtDestroyWidget(vScrollBar);
+  }
+  if (hScrollBar) {
+    XtDestroyWidget(hScrollBar);
+  }
+  if (scrolledWin) {
+    XtDestroyWidget(scrolledWin);
+  }
+  if (busyCursor) {
+    XFreeCursor(display, busyCursor);
+  }
+  if (linkCursor) {
+    XFreeCursor(display, linkCursor);
+  }
+  if (selectCursor) {
+    XFreeCursor(display, selectCursor);
+  }
+}
+
+//------------------------------------------------------------------------
+// loadFile / displayPage / displayDest
+//------------------------------------------------------------------------
+
+int XPDFCore::loadFile(GString *fileName, GString *ownerPassword,
+                      GString *userPassword) {
+  PDFDoc *newDoc;
+  GString *password;
+  GBool again;
+  int err;
+
+  // busy cursor
+  setCursor(busyCursor);
+
+  // open the PDF file
+  newDoc = new PDFDoc(fileName->copy(), ownerPassword, userPassword);
+  if (!newDoc->isOk()) {
+    err = newDoc->getErrorCode();
+    delete newDoc;
+    if (err != errEncrypted || !reqPasswordCbk) {
+      setCursor(None);
+      return err;
+    }
+
+    // try requesting a password
+    again = ownerPassword != NULL || userPassword != NULL;
+    while (1) {
+      if (!(password = (*reqPasswordCbk)(reqPasswordCbkData, again))) {
+       setCursor(None);
+       return errEncrypted;
+      }
+      newDoc = new PDFDoc(fileName->copy(), password, password);
+      if (newDoc->isOk()) {
+       break;
+      }
+      err = newDoc->getErrorCode();
+      delete newDoc;
+      if (err != errEncrypted) {
+       setCursor(None);
+       return err;
+      }
+      again = gTrue;
+    }
+  }
+
+  // replace old document
+  if (doc) {
+    delete doc;
+  }
+  doc = newDoc;
+  if (out) {
+    out->startDoc(doc->getXRef());
+  }
+
+  // nothing displayed yet
+  page = -99;
+
+  // save the modification time
+  modTime = getModTime(doc->getFileName()->getCString());
+
+  // update the parent window
+  if (updateCbk) {
+    (*updateCbk)(updateCbkData, doc->getFileName(), -1,
+                doc->getNumPages(), NULL);
+  }
+
+  // back to regular cursor
+  setCursor(None);
+
+  return errNone;
+}
+
+void XPDFCore::resizeToPage(int pg) {
+  Dimension width, height;
+  double width1, height1;
+  Dimension topW, topH, topBorder, daW, daH;
+  Dimension displayW, displayH;
+
+  displayW = DisplayWidth(display, screenNum);
+  displayH = DisplayHeight(display, screenNum);
+  if (fullScreen) {
+    width = displayW;
+    height = displayH;
+  } else {
+    if (pg < 0 || pg > doc->getNumPages()) {
+      width1 = 612;
+      height1 = 792;
+    } else if (doc->getPageRotate(pg) == 90 ||
+              doc->getPageRotate(pg) == 270) {
+      width1 = doc->getPageHeight(pg);
+      height1 = doc->getPageWidth(pg);
+    } else {
+      width1 = doc->getPageWidth(pg);
+      height1 = doc->getPageHeight(pg);
+    }
+    if (zoom == zoomPage || zoom == zoomWidth) {
+      width = (Dimension)((width1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
+      height = (Dimension)((height1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
+    } else {
+      width = (Dimension)((width1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
+      height = (Dimension)((height1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
+    }
+    if (width > displayW - 100) {
+      width = displayW - 100;
+    }
+    if (height > displayH - 150) {
+      height = displayH - 150;
+    }
+  }
+
+  if (XtIsRealized(shell)) {
+    XtVaGetValues(shell, XmNwidth, &topW, XmNheight, &topH,
+                 XmNborderWidth, &topBorder, NULL);
+    XtVaGetValues(drawArea, XmNwidth, &daW, XmNheight, &daH, NULL);
+    XtVaSetValues(shell, XmNwidth, width + (topW - daW),
+                 XmNheight, height + (topH - daH), NULL);
+  } else {
+    XtVaSetValues(drawArea, XmNwidth, width, XmNheight, height, NULL);
+  }
+}
+
+void XPDFCore::clear() {
+  if (!doc) {
+    return;
+  }
+
+  // no document
+  delete doc;
+  doc = NULL;
+  out->clear();
+
+  // no page displayed
+  page = -99;
+
+  // redraw
+  scrollX = scrollY = 0;
+  updateScrollBars();
+  redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
+}
+
+void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
+                          GBool scrollToTop, GBool addToHist) {
+  double hDPI, vDPI;
+  int rot;
+  XPDFHistory *h;
+  GBool newZoom;
+  XGCValues gcValues;
+  time_t newModTime;
+  int oldScrollX, oldScrollY;
+
+  // update the zoom and rotate values
+  newZoom = zoomA != zoom;
+  zoom = zoomA;
+  rotate = rotateA;
+
+  // check for document and valid page number
+  if (!doc || pageA <= 0 || pageA > doc->getNumPages()) {
+    return;
+  }
+
+  // busy cursor
+  setCursor(busyCursor);
+
+
+  // check for changes to the file
+  newModTime = getModTime(doc->getFileName()->getCString());
+  if (newModTime != modTime) {
+    if (loadFile(doc->getFileName()) == errNone) {
+      if (pageA > doc->getNumPages()) {
+       pageA = doc->getNumPages();
+      }
+    }
+    modTime = newModTime;
+  }
+
+  // free the old GCs
+  if (selectGC) {
+    XFreeGC(display, selectGC);
+    XFreeGC(display, highlightGC);
+  }
+
+  // new page number
+  page = pageA;
+
+  // scroll to top
+  if (scrollToTop) {
+    scrollY = 0;
+  }
+
+  // if zoom level changed, scroll to the top-left corner
+  if (newZoom) {
+    scrollX = scrollY = 0;
+  }
+
+  // initialize mouse-related stuff
+  linkAction = NULL;
+  selectXMin = selectXMax = 0;
+  selectYMin = selectYMax = 0;
+  dragging = gFalse;
+  lastDragLeft = lastDragTop = gTrue;
+
+  // draw the page
+  rot = rotate + doc->getPageRotate(page);
+  if (rot >= 360) {
+    rot -= 360;
+  } else if (rotate < 0) {
+    rot += 360;
+  }
+  if (zoom == zoomPage) {
+    if (rot == 90 || rot == 270) {
+      hDPI = (drawAreaWidth / doc->getPageHeight(page)) * 72;
+      vDPI = (drawAreaHeight / doc->getPageWidth(page)) * 72;
+    } else {
+      hDPI = (drawAreaWidth / doc->getPageWidth(page)) * 72;
+      vDPI = (drawAreaHeight / doc->getPageHeight(page)) * 72;
+    }
+    dpi = (hDPI < vDPI) ? hDPI : vDPI;
+  } else if (zoom == zoomWidth) {
+    if (rot == 90 || rot == 270) {
+      dpi = (drawAreaWidth / doc->getPageHeight(page)) * 72;
+    } else {
+      dpi = (drawAreaWidth / doc->getPageWidth(page)) * 72;
+    }
+  } else {
+    dpi = zoomDPI[zoom - minZoom];
+  }
+  out->setWindow(XtWindow(drawArea));
+  doc->displayPage(out, page, dpi, rotate, gTrue);
+  oldScrollX = scrollX;
+  oldScrollY = scrollY;
+  updateScrollBars();
+  if (scrollX != oldScrollX || scrollY != oldScrollY) {
+    redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
+  }
+
+
+  // add to history
+  if (addToHist) {
+    if (++historyCur == xpdfHistorySize) {
+      historyCur = 0;
+    }
+    h = &history[historyCur];
+    if (h->fileName) {
+      delete h->fileName;
+    }
+    h->fileName = doc->getFileName()->copy();
+    h->page = page;
+    if (historyBLen < xpdfHistorySize) {
+      ++historyBLen;
+    }
+    historyFLen = 0;
+  }
+
+  // update the parent window
+  if (updateCbk) {
+    (*updateCbk)(updateCbkData, NULL, page, -1, "");
+  }
+
+  // allocate new GCs
+  gcValues.foreground = BlackPixel(display, screenNum) ^
+                        WhitePixel(display, screenNum);
+  gcValues.function = GXxor;
+  selectGC = XCreateGC(display, out->getPixmap(),
+                      GCForeground | GCFunction, &gcValues);
+  highlightGC = XCreateGC(display, out->getPixmap(),
+                      GCForeground | GCFunction, &gcValues);
+
+  // back to regular cursor
+  setCursor(None);
+}
+
+void XPDFCore::displayDest(LinkDest *dest, int zoomA, int rotateA,
+                          GBool addToHist) {
+  Ref pageRef;
+  int pg;
+  int dx, dy;
+
+  if (dest->isPageRef()) {
+    pageRef = dest->getPageRef();
+    pg = doc->findPage(pageRef.num, pageRef.gen);
+  } else {
+    pg = dest->getPageNum();
+  }
+  if (pg <= 0 || pg > doc->getNumPages()) {
+    pg = 1;
+  }
+  if (pg != page) {
+    displayPage(pg, zoomA, rotateA, gTrue, addToHist);
+  }
+
+  if (fullScreen) {
+    return;
+  }
+  switch (dest->getKind()) {
+  case destXYZ:
+    out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
+    if (dest->getChangeLeft() || dest->getChangeTop()) {
+      scrollTo(dest->getChangeLeft() ? dx : scrollX,
+              dest->getChangeTop() ? dy : scrollY);
+    }
+    //~ what is the zoom parameter?
+    break;
+  case destFit:
+  case destFitB:
+    //~ do fit
+    scrollTo(0, 0);
+    break;
+  case destFitH:
+  case destFitBH:
+    //~ do fit
+    out->cvtUserToDev(0, dest->getTop(), &dx, &dy);
+    scrollTo(0, dy);
+    break;
+  case destFitV:
+  case destFitBV:
+    //~ do fit
+    out->cvtUserToDev(dest->getLeft(), 0, &dx, &dy);
+    scrollTo(dx, 0);
+    break;
+  case destFitR:
+    //~ do fit
+    out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy);
+    scrollTo(dx, dy);
+    break;
+  }
+}
+
+//------------------------------------------------------------------------
+// page/position changes
+//------------------------------------------------------------------------
+
+void XPDFCore::gotoNextPage(int inc, GBool top) {
+  int pg;
+
+  if (!doc || doc->getNumPages() == 0) {
+    return;
+  }
+  if (page < doc->getNumPages()) {
+    if ((pg = page + inc) > doc->getNumPages()) {
+      pg = doc->getNumPages();
+    }
+    displayPage(pg, zoom, rotate, top, gTrue);
+  } else {
+    XBell(display, 0);
+  }
+}
+
+void XPDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
+  int pg;
+
+  if (!doc || doc->getNumPages() == 0) {
+    return;
+  }
+  if (page > 1) {
+    if (!fullScreen && bottom) {
+      scrollY = out->getPixmapHeight() - drawAreaHeight;
+      if (scrollY < 0) {
+       scrollY = 0;
+      }
+      // displayPage will call updateScrollBars()
+    }
+    if ((pg = page - dec) < 1) {
+      pg = 1;
+    }
+    displayPage(pg, zoom, rotate, top, gTrue);
+  } else {
+    XBell(display, 0);
+  }
+}
+
+void XPDFCore::goForward() {
+  if (historyFLen == 0) {
+    XBell(display, 0);
+    return;
+  }
+  if (++historyCur == xpdfHistorySize) {
+    historyCur = 0;
+  }
+  --historyFLen;
+  ++historyBLen;
+  if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+    if (loadFile(history[historyCur].fileName) != errNone) {
+      XBell(display, 0);
+      return;
+    }
+  }
+  displayPage(history[historyCur].page, zoom, rotate, gFalse, gFalse);
+}
+
+void XPDFCore::goBackward() {
+  if (historyBLen <= 1) {
+    XBell(display, 0);
+    return;
+  }
+  if (--historyCur < 0) {
+    historyCur = xpdfHistorySize - 1;
+  }
+  --historyBLen;
+  ++historyFLen;
+  if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+    if (loadFile(history[historyCur].fileName) != errNone) {
+      XBell(display, 0);
+      return;
+    }
+  }
+  displayPage(history[historyCur].page, zoom, rotate, gFalse, gFalse);
+}
+
+void XPDFCore::scrollLeft(int nCols) {
+  scrollTo(scrollX - nCols * 16, scrollY);
+}
+
+void XPDFCore::scrollRight(int nCols) {
+  scrollTo(scrollX + nCols * 16, scrollY);
+}
+
+void XPDFCore::scrollUp(int nLines) {
+  scrollTo(scrollX, scrollY - nLines * 16);
+}
+
+void XPDFCore::scrollDown(int nLines) {
+  scrollTo(scrollX, scrollY + nLines * 16);
+}
+
+void XPDFCore::scrollPageUp() {
+  if (scrollY == 0) {
+    gotoPrevPage(1, gFalse, gTrue);
+  } else {
+    scrollTo(scrollX, scrollY - drawAreaHeight);
+  }
+}
+
+void XPDFCore::scrollPageDown() {
+  if (scrollY >= out->getPixmapHeight() - drawAreaHeight) {
+    gotoNextPage(1, gTrue);
+  } else {
+    scrollTo(scrollX, scrollY + drawAreaHeight);
+  }
+}
+
+void XPDFCore::scrollTo(int x, int y) {
+  GBool needRedraw;
+  int maxPos, pos;
+
+  needRedraw = gFalse;
+
+  maxPos = out ? out->getPixmapWidth() : 1;
+  if (maxPos < drawAreaWidth) {
+    maxPos = drawAreaWidth;
+  }
+  if (x < 0) {
+    pos = 0;
+  } else if (x > maxPos - drawAreaWidth) {
+    pos = maxPos - drawAreaWidth;
+  } else {
+    pos = x;
+  }
+  if (scrollX != pos) {
+    scrollX = pos;
+    XmScrollBarSetValues(hScrollBar, scrollX, drawAreaWidth, 16,
+                        drawAreaWidth, False);
+    needRedraw = gTrue;
+  }
+
+  maxPos = out ? out->getPixmapHeight() : 1;
+  if (maxPos < drawAreaHeight) {
+    maxPos = drawAreaHeight;
+  }
+  if (y < 0) {
+    pos = 0;
+  } else if (y > maxPos - drawAreaHeight) {
+    pos = maxPos - drawAreaHeight;
+  } else {
+    pos = y;
+  }
+  if (scrollY != pos) {
+    scrollY = pos;
+    XmScrollBarSetValues(vScrollBar, scrollY, drawAreaHeight, 16,
+                        drawAreaHeight, False);
+    needRedraw = gTrue;
+  }
+
+  if (needRedraw) {
+    redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
+  }
+}
+
+//------------------------------------------------------------------------
+// selection
+//------------------------------------------------------------------------
+
+void XPDFCore::setSelection(int newXMin, int newYMin,
+                           int newXMax, int newYMax) {
+  Pixmap pixmap;
+  int x, y;
+  GBool needRedraw, needScroll;
+  GBool moveLeft, moveRight, moveTop, moveBottom;
+
+  pixmap = out->getPixmap();
+
+
+  // erase old selection on off-screen bitmap
+  needRedraw = gFalse;
+  if (selectXMin < selectXMax && selectYMin < selectYMax) {
+    XFillRectangle(display, pixmap,
+                  selectGC, selectXMin, selectYMin,
+                  selectXMax - selectXMin, selectYMax - selectYMin);
+    needRedraw = gTrue;
+  }
+
+  // draw new selection on off-screen bitmap
+  if (newXMin < newXMax && newYMin < newYMax) {
+    XFillRectangle(display, pixmap,
+                  selectGC, newXMin, newYMin,
+                  newXMax - newXMin, newYMax - newYMin);
+    needRedraw = gTrue;
+  }
+
+  // check which edges moved
+  moveLeft = newXMin != selectXMin;
+  moveTop = newYMin != selectYMin;
+  moveRight = newXMax != selectXMax;
+  moveBottom = newYMax != selectYMax;
+
+  // redraw currently visible part of bitmap
+  if (needRedraw) {
+    if (moveLeft) {
+      redrawRectangle((newXMin < selectXMin) ? newXMin : selectXMin,
+                     (newYMin < selectYMin) ? newYMin : selectYMin,
+                     (newXMin > selectXMin) ? newXMin : selectXMin,
+                     (newYMax > selectYMax) ? newYMax : selectYMax);
+    }
+    if (moveRight) {
+      redrawRectangle((newXMax < selectXMax) ? newXMax : selectXMax,
+                     (newYMin < selectYMin) ? newYMin : selectYMin,
+                     (newXMax > selectXMax) ? newXMax : selectXMax,
+                     (newYMax > selectYMax) ? newYMax : selectYMax);
+    }
+    if (moveTop) {
+      redrawRectangle((newXMin < selectXMin) ? newXMin : selectXMin,
+                     (newYMin < selectYMin) ? newYMin : selectYMin,
+                     (newXMax > selectXMax) ? newXMax : selectXMax,
+                     (newYMin > selectYMin) ? newYMin : selectYMin);
+    }
+    if (moveBottom) {
+      redrawRectangle((newXMin < selectXMin) ? newXMin : selectXMin,
+                     (newYMax < selectYMax) ? newYMax : selectYMax,
+                     (newXMax > selectXMax) ? newXMax : selectXMax,
+                     (newYMax > selectYMax) ? newYMax : selectYMax);
+    }
+  }
+
+  // switch to new selection coords
+  selectXMin = newXMin;
+  selectXMax = newXMax;
+  selectYMin = newYMin;
+  selectYMax = newYMax;
+
+  // scroll if necessary
+  if (fullScreen) {
+    return;
+  }
+  needScroll = gFalse;
+  x = scrollX;
+  y = scrollY;
+  if (moveLeft && selectXMin < x) {
+    x = selectXMin;
+    needScroll = gTrue;
+  } else if (moveRight && selectXMax >= x + drawAreaWidth) {
+    x = selectXMax - drawAreaWidth;
+    needScroll = gTrue;
+  } else if (moveLeft && selectXMin >= x + drawAreaWidth) {
+    x = selectXMin - drawAreaWidth;
+    needScroll = gTrue;
+  } else if (moveRight && selectXMax < x) {
+    x = selectXMax;
+    needScroll = gTrue;
+  }
+  if (moveTop && selectYMin < y) {
+    y = selectYMin;
+    needScroll = gTrue;
+  } else if (moveBottom && selectYMax >= y + drawAreaHeight) {
+    y = selectYMax - drawAreaHeight;
+    needScroll = gTrue;
+  } else if (moveTop && selectYMin >= y + drawAreaHeight) {
+    y = selectYMin - drawAreaHeight;
+    needScroll = gTrue;
+  } else if (moveBottom && selectYMax < y) {
+    y = selectYMax;
+    needScroll = gTrue;
+  }
+  if (needScroll) {
+    scrollTo(x, y);
+  }
+}
+
+void XPDFCore::moveSelection(int mx, int my) {
+  int xMin, yMin, xMax, yMax;
+
+  // clip mouse coords
+  if (mx < 0) {
+    mx = 0;
+  } else if (mx >= out->getPixmapWidth()) {
+    mx = out->getPixmapWidth() - 1;
+  }
+  if (my < 0) {
+    my = 0;
+  } else if (my >= out->getPixmapHeight()) {
+    my = out->getPixmapHeight() - 1;
+  }
+
+  // move appropriate edges of selection
+  if (lastDragLeft) {
+    if (mx < selectXMax) {
+      xMin = mx;
+      xMax = selectXMax;
+    } else {
+      xMin = selectXMax;
+      xMax = mx;
+      lastDragLeft = gFalse;
+    }
+  } else {
+    if (mx > selectXMin) {
+      xMin = selectXMin;
+      xMax = mx;
+    } else {
+      xMin = mx;
+      xMax = selectXMin;
+      lastDragLeft = gTrue;
+    }
+  }
+  if (lastDragTop) {
+    if (my < selectYMax) {
+      yMin = my;
+      yMax = selectYMax;
+    } else {
+      yMin = selectYMax;
+      yMax = my;
+      lastDragTop = gFalse;
+    }
+  } else {
+    if (my > selectYMin) {
+      yMin = selectYMin;
+      yMax = my;
+    } else {
+      yMin = my;
+      yMax = selectYMin;
+      lastDragTop = gTrue;
+    }
+  }
+
+  // redraw the selection
+  setSelection(xMin, yMin, xMax, yMax);
+}
+
+// X's copy-and-paste mechanism is brain damaged.  Xt doesn't help
+// any, but doesn't make it too much worse, either.  Motif, on the
+// other hand, adds significant complexity to the mess.  So here we
+// blow off the Motif junk and stick to plain old Xt.  The next two
+// functions (copySelection and convertSelectionCbk) implement the
+// magic needed to deal with Xt's mechanism.  Note that this requires
+// global variables (currentSelection and currentSelectionOwner).
+
+void XPDFCore::copySelection() {
+  if (!doc->okToCopy()) {
+    return;
+  }
+  if (currentSelection) {
+    delete currentSelection;
+  }
+  //~ for multithreading: need a mutex here
+  currentSelection = out->getText(selectXMin, selectYMin,
+                                 selectXMax, selectYMax);
+  currentSelectionOwner = this;
+  XtOwnSelection(drawArea, XA_PRIMARY, XtLastTimestampProcessed(display),
+                &convertSelectionCbk, NULL, NULL);
+}
+
+Boolean XPDFCore::convertSelectionCbk(Widget widget, Atom *selection,
+                                     Atom *target, Atom *type,
+                                     XtPointer *value, unsigned long *length,
+                                     int *format) {
+  if (*target != XA_STRING) {
+    return False;
+  }
+  //~ for multithreading: need a mutex here
+  *value = XtNewString(currentSelection->getCString());
+  *length = currentSelection->getLength();
+  *type = XA_STRING;
+  *format = 8; // 8-bit elements
+  return True;
+}
+
+GBool XPDFCore::getSelection(int *xMin, int *yMin, int *xMax, int *yMax) {
+  if (selectXMin >= selectXMax || selectYMin >= selectYMax) {
+    return gFalse;
+  }
+  *xMin = selectXMin;
+  *yMin = selectYMin;
+  *xMax = selectXMax;
+  *yMax = selectYMax;
+  return gTrue;
+}
+
+GString *XPDFCore::extractText(int xMin, int yMin, int xMax, int yMax) {
+  if (!doc->okToCopy()) {
+    return NULL;
+  }
+  return out->getText(xMin, yMin, xMax, yMax);
+}
+
+GString *XPDFCore::extractText(int pageNum,
+                              int xMin, int yMin, int xMax, int yMax) {
+  TextOutputDev *textOut;
+  GString *s;
+
+  if (!doc->okToCopy()) {
+    return NULL;
+  }
+  textOut = new TextOutputDev(NULL, gFalse, gFalse);
+  if (!textOut->isOk()) {
+    delete textOut;
+    return NULL;
+  }
+  doc->displayPage(textOut, pageNum, dpi, rotate, gFalse);
+  s = textOut->getText(xMin, yMin, xMax, yMax);
+  delete textOut;
+  return s;
+}
+
+//------------------------------------------------------------------------
+// hyperlinks
+//------------------------------------------------------------------------
+
+void XPDFCore::doLink(int mx, int my) {
+  double x, y;
+  LinkAction *action;
+
+  // look for a link
+  out->cvtDevToUser(mx, my, &x, &y);
+  if ((action = doc->findLink(x, y))) {
+    doAction(action);
+  }
+}
+
+void XPDFCore::doAction(LinkAction *action) {
+  LinkActionKind kind;
+  LinkDest *dest;
+  GString *namedDest;
+  char *s;
+  GString *fileName, *fileName2;
+  GString *cmd;
+  GString *actionName;
+  Object movieAnnot, obj1, obj2;
+  GString *msg;
+  int i;
+
+  switch (kind = action->getKind()) {
+
+  // GoTo / GoToR action
+  case actionGoTo:
+  case actionGoToR:
+    if (kind == actionGoTo) {
+      dest = NULL;
+      namedDest = NULL;
+      if ((dest = ((LinkGoTo *)action)->getDest())) {
+       dest = dest->copy();
+      } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
+       namedDest = namedDest->copy();
+      }
+    } else {
+      dest = NULL;
+      namedDest = NULL;
+      if ((dest = ((LinkGoToR *)action)->getDest())) {
+       dest = dest->copy();
+      } else if ((namedDest = ((LinkGoToR *)action)->getNamedDest())) {
+       namedDest = namedDest->copy();
+      }
+      s = ((LinkGoToR *)action)->getFileName()->getCString();
+      //~ translate path name for VMS (deal with '/')
+      if (isAbsolutePath(s)) {
+       fileName = new GString(s);
+      } else {
+       fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
+      }
+      if (loadFile(fileName) != errNone) {
+       if (dest) {
+         delete dest;
+       }
+       if (namedDest) {
+         delete namedDest;
+       }
+       delete fileName;
+       return;
+      }
+      delete fileName;
+    }
+    if (namedDest) {
+      dest = doc->findDest(namedDest);
+      delete namedDest;
+    }
+    if (dest) {
+      displayDest(dest, zoom, rotate, gTrue);
+      delete dest;
+    } else {
+      if (kind == actionGoToR) {
+       displayPage(1, zoom, 0, gFalse, gTrue);
+      }
+    }
+    break;
+
+  // Launch action
+  case actionLaunch:
+    fileName = ((LinkLaunch *)action)->getFileName();
+    s = fileName->getCString();
+    if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
+       !strcmp(s + fileName->getLength() - 4, ".PDF")) {
+      //~ translate path name for VMS (deal with '/')
+      if (isAbsolutePath(s)) {
+       fileName = fileName->copy();
+      } else {
+       fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
+      }
+      if (loadFile(fileName) != errNone) {
+       delete fileName;
+       return;
+      }
+      delete fileName;
+      displayPage(1, zoom, rotate, gFalse, gTrue);
+    } else {
+      fileName = fileName->copy();
+      if (((LinkLaunch *)action)->getParams()) {
+       fileName->append(' ');
+       fileName->append(((LinkLaunch *)action)->getParams());
+      }
+#ifdef VMS
+      fileName->insert(0, "spawn/nowait ");
+#elif defined(__EMX__)
+      fileName->insert(0, "start /min /n ");
+#else
+      fileName->append(" &");
+#endif
+      msg = new GString("About to execute the command:\n");
+      msg->append(fileName);
+      if (doQuestionDialog("Launching external application", msg)) {
+       system(fileName->getCString());
+      }
+      delete fileName;
+      delete msg;
+    }
+    break;
+
+  // URI action
+  case actionURI:
+    if (!(cmd = globalParams->getURLCommand())) {
+      error(-1, "No urlCommand defined in config file");
+      break;
+    }
+    runCommand(cmd, ((LinkURI *)action)->getURI());
+    break;
+
+  // Named action
+  case actionNamed:
+    actionName = ((LinkNamed *)action)->getName();
+    if (!actionName->cmp("NextPage")) {
+      gotoNextPage(1, gTrue);
+    } else if (!actionName->cmp("PrevPage")) {
+      gotoPrevPage(1, gTrue, gFalse);
+    } else if (!actionName->cmp("FirstPage")) {
+      if (page != 1) {
+       displayPage(1, zoom, rotate, gTrue, gTrue);
+      }
+    } else if (!actionName->cmp("LastPage")) {
+      if (page != doc->getNumPages()) {
+       displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
+      }
+    } else if (!actionName->cmp("GoBack")) {
+      goBackward();
+    } else if (!actionName->cmp("GoForward")) {
+      goForward();
+    } else if (!actionName->cmp("Quit")) {
+      if (actionCbk) {
+       (*actionCbk)(actionCbkData, "Quit");
+      }
+    } else {
+      error(-1, "Unknown named action: '%s'", actionName->getCString());
+    }
+    break;
+
+  // Movie action
+  case actionMovie:
+    if (!(cmd = globalParams->getMovieCommand())) {
+      error(-1, "No movieCommand defined in config file");
+      break;
+    }
+    if (((LinkMovie *)action)->hasAnnotRef()) {
+      doc->getXRef()->fetch(((LinkMovie *)action)->getAnnotRef()->num,
+                           ((LinkMovie *)action)->getAnnotRef()->gen,
+                           &movieAnnot);
+    } else {
+      doc->getCatalog()->getPage(page)->getAnnots(&obj1);
+      if (obj1.isArray()) {
+       for (i = 0; i < obj1.arrayGetLength(); ++i) {
+         if (obj1.arrayGet(i, &movieAnnot)->isDict()) {
+           if (movieAnnot.dictLookup("Subtype", &obj2)->isName("Movie")) {
+             obj2.free();
+             break;
+           }
+           obj2.free();
+         }
+         movieAnnot.free();
+       }
+       obj1.free();
+      }
+    }
+    if (movieAnnot.isDict()) {
+      if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) {
+       if (obj1.dictLookup("F", &obj2)) {
+         if ((fileName = LinkAction::getFileSpecName(&obj2))) {
+           if (!isAbsolutePath(fileName->getCString())) {
+             fileName2 = appendToPath(
+                             grabPath(doc->getFileName()->getCString()),
+                             fileName->getCString());
+             delete fileName;
+             fileName = fileName2;
+           }
+           runCommand(cmd, fileName);
+           delete fileName;
+         }
+         obj2.free();
+       }
+       obj1.free();
+      }
+    }
+    movieAnnot.free();
+    break;
+
+  // unknown action type
+  case actionUnknown:
+    error(-1, "Unknown link action type: '%s'",
+         ((LinkUnknown *)action)->getAction()->getCString());
+    break;
+  }
+}
+
+// Run a command, given a <cmdFmt> string with one '%s' in it, and an
+// <arg> string to insert in place of the '%s'.
+void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
+  GString *cmd;
+  char *s;
+  int i;
+
+  if ((s = strstr(cmdFmt->getCString(), "%s"))) {
+    cmd = arg->copy();
+    // filter out any quote marks (' or ") to avoid a potential
+    // security hole
+    i = 0;
+    while (i < cmd->getLength()) {
+      if (cmd->getChar(i) == '"') {
+       cmd->del(i);
+       cmd->insert(i, "%22");
+       i += 3;
+      } else if (cmd->getChar(i) == '\'') {
+       cmd->del(i);
+       cmd->insert(i, "%27");
+       i += 3;
+      } else {
+       ++i;
+      }
+    }
+    cmd->insert(0, cmdFmt->getCString(),
+               s - cmdFmt->getCString());
+    cmd->append(s + 2);
+  } else {
+    cmd = cmdFmt->copy();
+  }
+#ifdef VMS
+  cmd->insert(0, "spawn/nowait ");
+#elif defined(__EMX__)
+  cmd->insert(0, "start /min /n ");
+#else
+  cmd->append(" &");
+#endif
+  system(cmd->getCString());
+  delete cmd;
+}
+
+
+//------------------------------------------------------------------------
+// find
+//------------------------------------------------------------------------
+
+void XPDFCore::find(char *s) {
+  Unicode *u;
+  TextOutputDev *textOut;
+  int xMin, yMin, xMax, yMax;
+  double xMin1, yMin1, xMax1, yMax1;
+  int pg;
+  GBool top;
+  int len, i;
+
+  // check for zero-length string
+  if (!s[0]) {
+    XBell(display, 0);
+    return;
+  }
+
+  // set cursor to watch
+  setCursor(busyCursor);
+
+  // convert to Unicode
+#if 1 //~ should do something more intelligent here
+  len = strlen(s);
+  u = (Unicode *)gmalloc(len * sizeof(Unicode));
+  for (i = 0; i < len; ++i) {
+    u[i] = (Unicode)(s[i] & 0xff);
+  }
+#endif
+
+  // search current page starting at current selection or top of page
+  xMin = yMin = xMax = yMax = 0;
+  if (selectXMin < selectXMax && selectYMin < selectYMax) {
+    xMin = selectXMax;
+    yMin = (selectYMin + selectYMax) / 2;
+    top = gFalse;
+  } else {
+    top = gTrue;
+  }
+  if (out->findText(u, len, top, gTrue, &xMin, &yMin, &xMax, &yMax)) {
+    goto found;
+  }
+
+  // search following pages
+  textOut = new TextOutputDev(NULL, gFalse, gFalse);
+  if (!textOut->isOk()) {
+    delete textOut;
+    goto done;
+  }
+  for (pg = page+1; pg <= doc->getNumPages(); ++pg) {
+    doc->displayPage(textOut, pg, 72, 0, gFalse);
+    if (textOut->findText(u, len, gTrue, gTrue,
+                         &xMin1, &yMin1, &xMax1, &yMax1)) {
+      goto foundPage;
+    }
+  }
+
+  // search previous pages
+  for (pg = 1; pg < page; ++pg) {
+    doc->displayPage(textOut, pg, 72, 0, gFalse);
+    if (textOut->findText(u, len, gTrue, gTrue,
+                         &xMin1, &yMin1, &xMax1, &yMax1)) {
+      goto foundPage;
+    }
+  }
+  delete textOut;
+
+  // search current page ending at current selection
+  if (selectXMin < selectXMax && selectYMin < selectYMax) {
+    xMax = selectXMin;
+    yMax = (selectYMin + selectYMax) / 2;
+    if (out->findText(u, len, gTrue, gFalse, &xMin, &yMin, &xMax, &yMax)) {
+      goto found;
+    }
+  }
+
+  // not found
+  XBell(display, 0);
+  goto done;
+
+  // found on a different page
+ foundPage:
+  delete textOut;
+  displayPage(pg, zoom, rotate, gTrue, gTrue);
+  if (!out->findText(u, len, gTrue, gTrue, &xMin, &yMin, &xMax, &yMax)) {
+    // this can happen if coalescing is bad
+    goto done;
+  }
+
+  // found: change the selection
+ found:
+  setSelection(xMin, yMin, xMax, yMax);
+#ifndef NO_TEXT_SELECT
+  copySelection();
+#endif
+
+ done:
+  gfree(u);
+
+  // reset cursors to normal
+  setCursor(None);
+}
+
+//------------------------------------------------------------------------
+// misc access
+//------------------------------------------------------------------------
+
+void XPDFCore::setBusyCursor(GBool busy) {
+  setCursor(busy ? busyCursor : None);
+}
+
+void XPDFCore::takeFocus() {
+  XmProcessTraversal(drawArea, XmTRAVERSE_CURRENT);
+}
+
+//------------------------------------------------------------------------
+// GUI code
+//------------------------------------------------------------------------
+
+void XPDFCore::initWindow() {
+  Arg args[20];
+  int n;
+
+  // create the cursors
+  busyCursor = XCreateFontCursor(display, XC_watch);
+  linkCursor = XCreateFontCursor(display, XC_hand2);
+  selectCursor = XCreateFontCursor(display, XC_cross);
+  currentCursor = 0;
+
+  // create the scrolled window and scrollbars
+  n = 0;
+  XtSetArg(args[n], XmNscrollingPolicy, XmAPPLICATION_DEFINED); ++n;
+  XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n;
+  scrolledWin = XmCreateScrolledWindow(parentWidget, "scroll", args, n);
+  XtManageChild(scrolledWin);
+  n = 0;
+  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
+  XtSetArg(args[n], XmNminimum, 0); ++n;
+  XtSetArg(args[n], XmNmaximum, 1); ++n;
+  XtSetArg(args[n], XmNsliderSize, 1); ++n;
+  XtSetArg(args[n], XmNvalue, 0); ++n;
+  XtSetArg(args[n], XmNincrement, 1); ++n;
+  XtSetArg(args[n], XmNpageIncrement, 1); ++n;
+  hScrollBar = XmCreateScrollBar(scrolledWin, "hScrollBar", args, n);
+  XtManageChild(hScrollBar);
+  XtAddCallback(hScrollBar, XmNvalueChangedCallback,
+               &hScrollChangeCbk, (XtPointer)this);
+#ifndef DISABLE_SMOOTH_SCROLL
+  XtAddCallback(hScrollBar, XmNdragCallback,
+               &hScrollDragCbk, (XtPointer)this);
+#endif
+  n = 0;
+  XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
+  XtSetArg(args[n], XmNminimum, 0); ++n;
+  XtSetArg(args[n], XmNmaximum, 1); ++n;
+  XtSetArg(args[n], XmNsliderSize, 1); ++n;
+  XtSetArg(args[n], XmNvalue, 0); ++n;
+  XtSetArg(args[n], XmNincrement, 1); ++n;
+  XtSetArg(args[n], XmNpageIncrement, 1); ++n;
+  vScrollBar = XmCreateScrollBar(scrolledWin, "vScrollBar", args, n);
+  XtManageChild(vScrollBar);
+  XtAddCallback(vScrollBar, XmNvalueChangedCallback,
+               &vScrollChangeCbk, (XtPointer)this);
+#ifndef DISABLE_SMOOTH_SCROLL
+  XtAddCallback(vScrollBar, XmNdragCallback,
+               &vScrollDragCbk, (XtPointer)this);
+#endif
+
+  // create the drawing area
+  n = 0;
+  XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 0); ++n;
+  XtSetArg(args[n], XmNmarginHeight, 0); ++n;
+  if (fullScreen) {
+    XtSetArg(args[n], XmNshadowThickness, 0); ++n;
+  }
+  drawAreaFrame = XmCreateFrame(scrolledWin, "drawAreaFrame", args, n);
+  XtManageChild(drawAreaFrame);
+  n = 0;
+  XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n;
+  XtSetArg(args[n], XmNbackground, paperColor); ++n;
+  XtSetArg(args[n], XmNwidth, 700); ++n;
+  XtSetArg(args[n], XmNheight, 500); ++n;
+  drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n);
+  XtManageChild(drawArea);
+  XtAddCallback(drawArea, XmNresizeCallback, &resizeCbk, (XtPointer)this);
+  XtAddCallback(drawArea, XmNexposeCallback, &redrawCbk, (XtPointer)this);
+  XtAddCallback(drawArea, XmNinputCallback, &inputCbk, (XtPointer)this);
+  resizeCbk(drawArea, this, NULL);
+
+  // set up mouse motion translations
+  XtOverrideTranslations(drawArea, XtParseTranslationTable(
+      "<Btn1Down>:DrawingAreaInput()\n"
+      "<Btn1Up>:DrawingAreaInput()\n"
+      "<Btn1Motion>:DrawingAreaInput()\n"
+      "<Motion>:DrawingAreaInput()"));
+
+  // can't create a GC until the window gets mapped
+  drawAreaGC = NULL;
+  selectGC = NULL;
+  highlightGC = NULL;
+}
+
+void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData) {
+  XPDFCore *core = (XPDFCore *)ptr;
+  XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
+
+  core->scrollTo(data->value, core->scrollY);
+}
+
+void XPDFCore::hScrollDragCbk(Widget widget, XtPointer ptr,
+                             XtPointer callData) {
+  XPDFCore *core = (XPDFCore *)ptr;
+  XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
+
+  core->scrollTo(data->value, core->scrollY);
+}
+
+void XPDFCore::vScrollChangeCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData) {
+  XPDFCore *core = (XPDFCore *)ptr;
+  XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
+
+  core->scrollTo(core->scrollX, data->value);
+}
+
+void XPDFCore::vScrollDragCbk(Widget widget, XtPointer ptr,
+                             XtPointer callData) {
+  XPDFCore *core = (XPDFCore *)ptr;
+  XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
+
+  core->scrollTo(core->scrollX, data->value);
+}
+
+void XPDFCore::resizeCbk(Widget widget, XtPointer ptr, XtPointer callData) {
+  XPDFCore *core = (XPDFCore *)ptr;
+  Arg args[2];
+  int n;
+  Dimension w, h;
+
+  n = 0;
+  XtSetArg(args[n], XmNwidth, &w); ++n;
+  XtSetArg(args[n], XmNheight, &h); ++n;
+  XtGetValues(core->drawArea, args, n);
+  core->drawAreaWidth = (int)w;
+  core->drawAreaHeight = (int)h;
+  if (core->page >= 0 &&
+      (core->zoom == zoomPage || core->zoom == zoomWidth)) {
+    core->displayPage(core->page, core->zoom, core->rotate,
+                     gFalse, gFalse);
+  } else {
+    core->updateScrollBars();
+  }
+}
+
+void XPDFCore::redrawCbk(Widget widget, XtPointer ptr, XtPointer callData) {
+  XPDFCore *core = (XPDFCore *)ptr;
+  XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
+  int x, y, w, h;
+
+  if (data->reason == XmCR_EXPOSE) {
+    x = core->scrollX + data->event->xexpose.x;
+    y = core->scrollY + data->event->xexpose.y;
+    w = data->event->xexpose.width;
+    h = data->event->xexpose.height;
+  } else {
+    x = core->scrollX;
+    y = core->scrollY;
+    w = core->drawAreaWidth;
+    h = core->drawAreaHeight;
+  }
+  core->redrawRectangle(x, y, w, h);
+}
+
+void XPDFCore::outputDevRedrawCbk(void *data) {
+  XPDFCore *core = (XPDFCore *)data;
+
+  core->redrawRectangle(core->scrollX, core->scrollY,
+                       core->drawAreaWidth, core->drawAreaHeight);
+}
+
+void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) {
+  XPDFCore *core = (XPDFCore *)ptr;
+  XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
+  LinkAction *action;
+  int mx, my;
+  double x, y;
+  char *s;
+  KeySym key;
+  char buf[20];
+  int n;
+
+  switch (data->event->type) {
+  case ButtonPress:
+    if (data->event->xbutton.button == 1) {
+      core->takeFocus();
+      if (core->doc && core->doc->getNumPages() > 0) {
+       if (core->selectEnabled) {
+         mx = core->scrollX + data->event->xbutton.x;
+         my = core->scrollY + data->event->xbutton.y;
+         core->setSelection(mx, my, mx, my);
+         core->setCursor(core->selectCursor);
+         core->dragging = gTrue;
+       }
+      }
+    } else if (data->event->xbutton.button == 2) {
+      if (!core->fullScreen) {
+       core->panning = gTrue;
+       core->panMX = data->event->xbutton.x;
+       core->panMY = data->event->xbutton.y;
+      }
+    } else if (data->event->xbutton.button == 4) { // mouse wheel up
+      if (core->fullScreen) {
+       core->gotoPrevPage(1, gTrue, gFalse);
+      } else if (core->scrollY == 0) {
+       core->gotoPrevPage(1, gFalse, gTrue);
+      } else {
+       core->scrollUp(1);
+      }
+    } else if (data->event->xbutton.button == 5) { // mouse wheel down
+      if (core->fullScreen ||
+         core->scrollY >=
+           core->out->getPixmapHeight() - core->drawAreaHeight) {
+       core->gotoNextPage(1, gTrue);
+      } else {
+       core->scrollDown(1);
+      }
+    } else if (data->event->xbutton.button == 6) { // second mouse wheel right
+      if (!core->fullScreen) {
+       core->scrollRight(1);
+      }
+    } else if (data->event->xbutton.button == 7) { // second mouse wheel left
+      if (!core->fullScreen) {
+       core->scrollLeft(1);
+      }
+    } else {
+      if (*core->mouseCbk) {
+       (*core->mouseCbk)(core->mouseCbkData, data->event);
+      }
+    }
+    break;
+  case ButtonRelease:
+    if (data->event->xbutton.button == 1) {
+      if (core->doc && core->doc->getNumPages() > 0) {
+       mx = core->scrollX + data->event->xbutton.x;
+       my = core->scrollY + data->event->xbutton.y;
+       if (core->dragging) {
+         core->dragging = gFalse;
+         core->setCursor(None);
+         core->moveSelection(mx, my);
+#ifndef NO_TEXT_SELECT
+         if (core->selectXMin != core->selectXMax &&
+             core->selectYMin != core->selectYMax) {
+           if (core->doc->okToCopy()) {
+             core->copySelection();
+           } else {
+             error(-1, "Copying of text from this document is not allowed.");
+           }
+         }
+#endif
+       }
+       if (core->hyperlinksEnabled) {
+         if (core->selectXMin == core->selectXMax ||
+           core->selectYMin == core->selectYMax) {
+           core->doLink(mx, my);
+         }
+       }
+      }
+    } else if (data->event->xbutton.button == 2) {
+      core->panning = gFalse;
+    } else {
+      if (*core->mouseCbk) {
+       (*core->mouseCbk)(core->mouseCbkData, data->event);
+      }
+    }
+    break;
+  case MotionNotify:
+    if (core->doc && core->doc->getNumPages() > 0) {
+      mx = core->scrollX + data->event->xbutton.x;
+      my = core->scrollY + data->event->xbutton.y;
+      if (core->dragging) {
+       core->moveSelection(mx, my);
+      } else if (core->hyperlinksEnabled) {
+       core->out->cvtDevToUser(mx, my, &x, &y);
+       if ((action = core->doc->findLink(x, y))) {
+         core->setCursor(core->linkCursor);
+         if (action != core->linkAction) {
+           core->linkAction = action;
+           if (core->updateCbk) {
+             s = "";
+             switch (action->getKind()) {
+             case actionGoTo:
+               s = "[internal link]";
+               break;
+             case actionGoToR:
+               s = ((LinkGoToR *)action)->getFileName()->getCString();
+               break;
+             case actionLaunch:
+               s = ((LinkLaunch *)action)->getFileName()->getCString();
+               break;
+             case actionURI:
+               s = ((LinkURI *)action)->getURI()->getCString();
+               break;
+             case actionNamed:
+               s = ((LinkNamed *)action)->getName()->getCString();
+               break;
+             case actionMovie:
+               s = "[movie]";
+               break;
+             case actionUnknown:
+               s = "[unknown link]";
+               break;
+             }
+             (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, s);
+           }
+         }
+       } else {
+         core->setCursor(None);
+         if (core->linkAction) {
+           core->linkAction = NULL;
+           if (core->updateCbk) {
+             (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, "");
+           }
+         }
+       }
+      }
+    }
+    if (core->panning) {
+      core->scrollTo(core->scrollX - (data->event->xbutton.x - core->panMX),
+                    core->scrollY - (data->event->xbutton.y - core->panMY));
+      core->panMX = data->event->xbutton.x;
+      core->panMY = data->event->xbutton.y;
+    }
+    break;
+  case KeyPress:
+    n = XLookupString(&data->event->xkey, buf, sizeof(buf) - 1,
+                     &key, NULL);
+    core->keyPress(buf, key, data->event->xkey.state);
+    break;
+  }
+}
+
+void XPDFCore::keyPress(char *s, KeySym key, Guint modifiers) {
+  switch (key) {
+  case XK_Home:
+  case XK_KP_Home:
+    if (modifiers & ControlMask) {
+      displayPage(1, zoom, rotate, gTrue, gTrue);
+    } else if (!fullScreen) {
+      scrollTo(0, 0);
+    }
+    return;
+  case XK_End:
+  case XK_KP_End:
+    if (modifiers & ControlMask) {
+      displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
+    } else if (!fullScreen) {
+      scrollTo(out->getPixmapWidth() - drawAreaWidth,
+              out->getPixmapHeight() - drawAreaHeight);
+    }
+    return;
+  case XK_Page_Up:
+  case XK_KP_Page_Up:
+    if (fullScreen) {
+      gotoPrevPage(1, gTrue, gFalse);
+    } else {
+      scrollPageUp();
+    }
+    return;
+  case XK_Page_Down:
+  case XK_KP_Page_Down:
+    if (fullScreen) {
+      gotoNextPage(1, gTrue);
+    } else {
+      scrollPageDown();
+    }
+    return;
+  case XK_Left:
+  case XK_KP_Left:
+    if (!fullScreen) {
+      scrollLeft();
+    }
+    return;
+  case XK_Right:
+  case XK_KP_Right:
+    if (!fullScreen) {
+      scrollRight();
+    }
+    return;
+  case XK_Up:
+  case XK_KP_Up:
+    if (!fullScreen) {
+      scrollUp();
+    }
+    return;
+  case XK_Down:
+  case XK_KP_Down:
+    if (!fullScreen) {
+      scrollDown();
+    }
+    return;
+  }
+
+  if (*keyPressCbk) {
+    (*keyPressCbk)(keyPressCbkData, s, key, modifiers);
+  }
+}
+
+void XPDFCore::redrawRectangle(int x, int y, int w, int h) {
+  XGCValues gcValues;
+  Window drawAreaWin;
+
+  // clip to window
+  if (x < scrollX) {
+    w -= scrollX - x;
+    x = scrollX;
+  }
+  if (x + w > scrollX + drawAreaWidth) {
+    w = scrollX + drawAreaWidth - x;
+  }
+  if (y < scrollY) {
+    h -= scrollY - y;
+    y = scrollY;
+  }
+  if (y + h > scrollY + drawAreaHeight) {
+    h = scrollY + drawAreaHeight - y;
+  }
+
+  // create a GC for the drawing area
+  drawAreaWin = XtWindow(drawArea);
+  if (!drawAreaGC) {
+    gcValues.foreground = paperColor;
+    drawAreaGC = XCreateGC(display, drawAreaWin, GCForeground, &gcValues);
+  }
+
+  // draw white background past the edges of the document
+  if (x + w > out->getPixmapWidth()) {
+    XFillRectangle(display, drawAreaWin, drawAreaGC,
+                  out->getPixmapWidth() - scrollX, y - scrollY,
+                  x + w - out->getPixmapWidth(), h);
+    w = out->getPixmapWidth() - x;
+  }
+  if (y + h > out->getPixmapHeight()) {
+    XFillRectangle(display, drawAreaWin, drawAreaGC,
+                  x - scrollX, out->getPixmapHeight() - scrollY,
+                  w, y + h - out->getPixmapHeight());
+    h = out->getPixmapHeight() - y;
+  }
+
+  // redraw (checking to see if pixmap has been allocated yet)
+  if (out->getPixmapWidth() > 0) {
+    XCopyArea(display, out->getPixmap(), drawAreaWin, drawAreaGC,
+             x, y, w, h, x - scrollX, y - scrollY);
+  }
+}
+
+void XPDFCore::updateScrollBars() {
+  Arg args[20];
+  int n;
+  int maxPos;
+
+  maxPos = out ? out->getPixmapWidth() : 1;
+  if (maxPos < drawAreaWidth) {
+    maxPos = drawAreaWidth;
+  }
+  if (scrollX > maxPos - drawAreaWidth) {
+    scrollX = maxPos - drawAreaWidth;
+  }
+  n = 0;
+  XtSetArg(args[n], XmNvalue, scrollX); ++n;
+  XtSetArg(args[n], XmNmaximum, maxPos); ++n;
+  XtSetArg(args[n], XmNsliderSize, drawAreaWidth); ++n;
+  XtSetArg(args[n], XmNincrement, 16); ++n;
+  XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n;
+  XtSetValues(hScrollBar, args, n);
+
+  maxPos = out ? out->getPixmapHeight() : 1;
+  if (maxPos < drawAreaHeight) {
+    maxPos = drawAreaHeight;
+  }
+  if (scrollY > maxPos - drawAreaHeight) {
+    scrollY = maxPos - drawAreaHeight;
+  }
+  n = 0;
+  XtSetArg(args[n], XmNvalue, scrollY); ++n;
+  XtSetArg(args[n], XmNmaximum, maxPos); ++n;
+  XtSetArg(args[n], XmNsliderSize, drawAreaHeight); ++n;
+  XtSetArg(args[n], XmNincrement, 16); ++n;
+  XtSetArg(args[n], XmNpageIncrement, drawAreaHeight); ++n;
+  XtSetValues(vScrollBar, args, n);
+}
+
+void XPDFCore::setCursor(Cursor cursor) {
+  Window topWin;
+
+  if (cursor == currentCursor) {
+    return;
+  }
+  if (!(topWin = XtWindow(shell))) {
+    return;
+  }
+  if (cursor == None) {
+    XUndefineCursor(display, topWin);
+  } else {
+    XDefineCursor(display, topWin, cursor);
+  }
+  XFlush(display);
+  currentCursor = cursor;
+}
+
+GBool XPDFCore::doQuestionDialog(char *title, GString *msg) {
+  return doDialog(XmDIALOG_QUESTION, gTrue, title, msg);
+}
+
+void XPDFCore::doInfoDialog(char *title, GString *msg) {
+  doDialog(XmDIALOG_INFORMATION, gFalse, title, msg);
+}
+
+void XPDFCore::doErrorDialog(char *title, GString *msg) {
+  doDialog(XmDIALOG_ERROR, gFalse, title, msg);
+}
+
+GBool XPDFCore::doDialog(int type, GBool hasCancel,
+                        char *title, GString *msg) {
+  Widget dialog;
+  XtAppContext appContext;
+  Arg args[20];
+  int n;
+  XmString s1, s2;
+  XEvent event;
+
+  n = 0;
+  XtSetArg(args[n], XmNdialogType, type); ++n;
+  XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
+  s1 = XmStringCreateLocalized(title);
+  XtSetArg(args[n], XmNdialogTitle, s1); ++n;
+  s2 = XmStringCreateLocalized(msg->getCString());
+  XtSetArg(args[n], XmNmessageString, s2); ++n;
+  dialog = XmCreateMessageDialog(drawArea, "questionDialog", args, n);
+  XmStringFree(s1);
+  XmStringFree(s2);
+  XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
+  XtAddCallback(dialog, XmNokCallback,
+               &dialogOkCbk, (XtPointer)this);
+  if (hasCancel) {
+    XtAddCallback(dialog, XmNcancelCallback,
+                 &dialogCancelCbk, (XtPointer)this);
+  } else {
+    XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
+  }
+
+  XtManageChild(dialog);
+
+  appContext = XtWidgetToApplicationContext(dialog);
+  dialogDone = 0;
+  do {
+    XtAppNextEvent(appContext, &event);
+    XtDispatchEvent(&event);
+  } while (!dialogDone);
+
+  XtUnmanageChild(dialog);
+  XtDestroyWidget(dialog);
+
+  return dialogDone > 0;
+}
+
+void XPDFCore::dialogOkCbk(Widget widget, XtPointer ptr,
+                          XtPointer callData) {
+  XPDFCore *core = (XPDFCore *)ptr;
+
+  core->dialogDone = 1;
+}
+
+void XPDFCore::dialogCancelCbk(Widget widget, XtPointer ptr,
+                              XtPointer callData) {
+  XPDFCore *core = (XPDFCore *)ptr;
+
+  core->dialogDone = -1;
+}
diff --git a/pdf/xpdf/XPDFCore.h b/pdf/xpdf/XPDFCore.h
new file mode 100644 (file)
index 0000000..348486f
--- /dev/null
@@ -0,0 +1,296 @@
+//========================================================================
+//
+// XPDFCore.h
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef XPDFCORE_H
+#define XPDFCORE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#define Object XtObject
+#include <Xm/XmAll.h>
+#undef Object
+#include <aconf.h>
+#include "gtypes.h"
+#include "gfile.h" // for time_t
+
+class GString;
+class GList;
+class PDFDoc;
+class LinkAction;
+class LinkDest;
+class XPixmapOutputDev;
+
+//------------------------------------------------------------------------
+// zoom factor
+//------------------------------------------------------------------------
+
+#define minZoom    -5
+#define maxZoom     5
+#define zoomPage  100
+#define zoomWidth 101
+#define defZoom     1
+
+//------------------------------------------------------------------------
+// XPDFHistory
+//------------------------------------------------------------------------
+
+struct XPDFHistory {
+  GString *fileName;
+  int page;
+};
+
+#define xpdfHistorySize 50
+
+//------------------------------------------------------------------------
+// XPDFRegion
+//------------------------------------------------------------------------
+
+struct XPDFRegion {
+  int page;
+  double xMin, yMin, xMax, yMax;
+  Gulong color;
+  Gulong selectColor;
+};
+
+//------------------------------------------------------------------------
+// callbacks
+//------------------------------------------------------------------------
+
+typedef void (*XPDFUpdateCbk)(void *data, GString *fileName,
+                               int pageNum, int numPages, char *linkLabel);
+
+typedef void (*XPDFActionCbk)(void *data, char *action);
+
+typedef void (*XPDFKeyPressCbk)(void *data, char *s, KeySym key,
+                               Guint modifiers);
+
+typedef void (*XPDFMouseCbk)(void *data, XEvent *event);
+
+typedef GString *(*XPDFReqPasswordCbk)(void *data, GBool again);
+
+//------------------------------------------------------------------------
+// XPDFCore
+//------------------------------------------------------------------------
+
+class XPDFCore {
+public:
+
+  // Create viewer core inside <parentWidgetA>.
+  XPDFCore(Widget shellA, Widget parentWidgetA,
+          Gulong paperColorA, GBool fullScreenA, GBool reverseVideo,
+          GBool installCmap, int rgbCubeSize);
+
+  ~XPDFCore();
+
+  //----- loadFile / displayPage / displayDest
+
+  // Load a new file.  Returns pdfOk or error code.
+  int loadFile(GString *fileName, GString *ownerPassword = NULL,
+              GString *userPassword = NULL);
+
+  // Resize the window to fit page <pg> of the current document.
+  void resizeToPage(int pg);
+
+  // Clear out the current document, if any.
+  void clear();
+
+  // Display (or redisplay) the specified page.  If <scrollToTop> is
+  // set, the window is vertically scrolled to the top; otherwise, no
+  // scrolling is done.  If <addToHist> is set, this page change is
+  // added to the history list.
+  void displayPage(int pageA, int zoomA, int rotateA,
+                  GBool scrollToTop, GBool addToHist);
+
+  // Display a link destination.
+  void displayDest(LinkDest *dest, int zoomA, int rotateA,
+                  GBool addToHist);
+
+  //----- page/position changes
+
+  void gotoNextPage(int inc, GBool top);
+  void gotoPrevPage(int dec, GBool top, GBool bottom);
+  void goForward();
+  void goBackward();
+  void scrollLeft(int nCols = 1);
+  void scrollRight(int nCols = 1);
+  void scrollUp(int nLines = 1);
+  void scrollDown(int nLines = 1);
+  void scrollPageUp();
+  void scrollPageDown();
+  void scrollTo(int x, int y);
+
+  //----- selection
+
+  void setSelection(int newXMin, int newYMin, int newXMax, int newYMax);
+  void moveSelection(int mx, int my);
+  void copySelection();
+  GBool getSelection(int *xMin, int *yMin, int *xMax, int *yMax);
+  GString *extractText(int xMin, int yMin, int xMax, int yMax);
+  GString *extractText(int pageNum, int xMin, int yMin, int xMax, int yMax);
+
+  //----- hyperlinks
+
+  void doAction(LinkAction *action);
+
+
+  //----- find
+
+  void find(char *s);
+
+  //----- simple modal dialogs
+
+  GBool doQuestionDialog(char *title, GString *msg);
+  void doInfoDialog(char *title, GString *msg);
+  void doErrorDialog(char *title, GString *msg);
+
+  //----- misc access
+
+  Widget getWidget() { return scrolledWin; }
+  Widget getDrawAreaWidget() { return drawArea; }
+  PDFDoc *getDoc() { return doc; }
+  XPixmapOutputDev *getOutputDev() { return out; }
+  int getPageNum() { return page; }
+  int getZoom() { return zoom; }
+  double getZoomDPI() { return dpi; }
+  int getRotate() { return rotate; }
+  GBool canGoBack() { return historyBLen > 1; }
+  GBool canGoForward() { return historyFLen > 0; }
+  int getScrollX() { return scrollX; }
+  int getScrollY() { return scrollY; }
+  int getDrawAreaWidth() { return drawAreaWidth; }
+  int getDrawAreaHeight() { return drawAreaHeight; }
+  void setBusyCursor(GBool busy);
+  void takeFocus();
+  void enableHyperlinks(GBool on) { hyperlinksEnabled = on; }
+  void enableSelect(GBool on) { selectEnabled = on; }
+  void setUpdateCbk(XPDFUpdateCbk cbk, void *data)
+    { updateCbk = cbk; updateCbkData = data; }
+  void setActionCbk(XPDFActionCbk cbk, void *data)
+    { actionCbk = cbk; actionCbkData = data; }
+  void setKeyPressCbk(XPDFKeyPressCbk cbk, void *data)
+    { keyPressCbk = cbk; keyPressCbkData = data; }
+  void setMouseCbk(XPDFMouseCbk cbk, void *data)
+    { mouseCbk = cbk; mouseCbkData = data; }
+  void setReqPasswordCbk(XPDFReqPasswordCbk cbk, void *data)
+    { reqPasswordCbk = cbk; reqPasswordCbkData = data; }
+
+private:
+
+  //----- hyperlinks
+  void doLink(int mx, int my);
+  void runCommand(GString *cmdFmt, GString *arg);
+
+  //----- selection
+  static Boolean convertSelectionCbk(Widget widget, Atom *selection,
+                                    Atom *target, Atom *type,
+                                    XtPointer *value, unsigned long *length,
+                                    int *format);
+
+
+  //----- GUI code
+  void initWindow();
+  static void hScrollChangeCbk(Widget widget, XtPointer ptr,
+                              XtPointer callData);
+  static void hScrollDragCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData);
+  static void vScrollChangeCbk(Widget widget, XtPointer ptr,
+                              XtPointer callData);
+  static void vScrollDragCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData);
+  static void resizeCbk(Widget widget, XtPointer ptr, XtPointer callData);
+  static void redrawCbk(Widget widget, XtPointer ptr, XtPointer callData);
+  static void outputDevRedrawCbk(void *data);
+  static void inputCbk(Widget widget, XtPointer ptr, XtPointer callData);
+  void keyPress(char *s, KeySym key, Guint modifiers);
+  void redrawRectangle(int x, int y, int w, int h);
+  void updateScrollBars();
+  void setCursor(Cursor cursor);
+  GBool doDialog(int type, GBool hasCancel,
+                char *title, GString *msg);
+  static void dialogOkCbk(Widget widget, XtPointer ptr,
+                         XtPointer callData);
+  static void dialogCancelCbk(Widget widget, XtPointer ptr,
+                             XtPointer callData);
+
+  Gulong paperColor;
+  GBool fullScreen;
+
+  Display *display;
+  int screenNum;
+  Visual *visual;
+  Colormap colormap;
+  Widget shell;                        // top-level shell containing the widget
+  Widget parentWidget;         // parent widget (not created by XPDFCore)
+  Widget scrolledWin;
+  Widget hScrollBar;
+  Widget vScrollBar;
+  Widget drawAreaFrame;
+  Widget drawArea;
+  Cursor busyCursor, linkCursor, selectCursor;
+  Cursor currentCursor;
+  GC drawAreaGC;               // GC for blitting into drawArea
+  GC selectGC;
+  GC highlightGC;
+
+  int drawAreaWidth, drawAreaHeight;
+  int scrollX, scrollY;                // current upper-left corner
+
+  int selectXMin, selectYMin,  // coordinates of current selection:
+      selectXMax, selectYMax;  //   (xMin==xMax || yMin==yMax) means there
+                               //   is no selection
+  GBool dragging;              // set while selection is being dragged
+  GBool lastDragLeft;          // last dragged selection edge was left/right
+  GBool lastDragTop;           // last dragged selection edge was top/bottom
+  static GString *currentSelection;  // selected text
+  static XPDFCore *currentSelectionOwner;
+
+  GBool panning;
+  int panMX, panMY;
+
+  XPDFHistory                  // page history queue
+    history[xpdfHistorySize];
+  int historyCur;               // currently displayed page
+  int historyBLen;              // number of valid entries backward from
+                                //   current entry
+  int historyFLen;              // number of valid entries forward from
+                                //   current entry
+
+  PDFDoc *doc;                 // current PDF file
+  int page;                    // current page number
+  int zoom;                    // current zoom level
+  double dpi;                  // current zoom level, in DPI
+  int rotate;                  // current page rotation
+  time_t modTime;              // last modification time of PDF file
+
+  LinkAction *linkAction;      // mouse cursor is over this link
+
+
+  XPDFUpdateCbk updateCbk;
+  void *updateCbkData;
+  XPDFActionCbk actionCbk;
+  void *actionCbkData;
+  XPDFKeyPressCbk keyPressCbk;
+  void *keyPressCbkData;
+  XPDFMouseCbk mouseCbk;
+  void *mouseCbkData;
+  XPDFReqPasswordCbk reqPasswordCbk;
+  void *reqPasswordCbkData;
+
+  GBool hyperlinksEnabled;
+  GBool selectEnabled;
+
+  XPixmapOutputDev *out;
+
+  int dialogDone;
+};
+
+#endif
diff --git a/pdf/xpdf/XPDFTree.cc b/pdf/xpdf/XPDFTree.cc
new file mode 100644 (file)
index 0000000..46e5466
--- /dev/null
@@ -0,0 +1,929 @@
+//========================================================================
+//
+// XPDFTree.cc
+//
+//========================================================================
+
+#include <stdlib.h>
+#include "gmem.h"
+#include "XPDFTreeP.h"
+
+//------------------------------------------------------------------------
+
+#define xpdfTreeIndent 16
+
+//------------------------------------------------------------------------
+
+struct _XPDFTreeEntry {
+  Widget widget;
+  XPDFTreeEntry *children;
+  XPDFTreeEntry *next;
+};
+
+//------------------------------------------------------------------------
+
+static void classPartInitialize(WidgetClass widgetClass);
+static void initialize(Widget requestWidget, Widget newWidget,
+                      ArgList args, Cardinal *numArgs);
+static void destroy(Widget widget);
+static void destroySubtree(XPDFTreeEntry *e);
+static void resize(Widget widget);
+static void redisplay(Widget widget, XEvent *event, Region region);
+static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e,
+                            XEvent *event, Region region);
+static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y);
+static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y);
+static Boolean setValues(Widget oldWidget, Widget requestWidget,
+                        Widget newWidget, ArgList args, Cardinal *numArgs);
+static void setValuesAlmost(Widget oldWidget, Widget newWidget,
+                           XtWidgetGeometry *request,
+                           XtWidgetGeometry *reply);
+static XtGeometryResult queryGeometry(Widget widget,
+                                     XtWidgetGeometry *request,
+                                     XtWidgetGeometry *reply);
+static XtGeometryResult geometryManager(Widget widget,
+                                       XtWidgetGeometry *request,
+                                       XtWidgetGeometry *reply);
+static void changeManaged(Widget widget);
+static void initConstraint(Widget requestWidget, Widget newWidget,
+                          ArgList args, Cardinal *numArgs);
+static void destroyConstraint(Widget widget);
+static void deleteSubtree(Widget widget);
+static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget,
+                                  Widget newWidget,
+                                  ArgList args, Cardinal *numArgs);
+static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead);
+static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead);
+static void createGC(Widget widget);
+static void destroyGC(Widget widget);
+static void layout(Widget widget, Widget instigator);
+static int layoutSubtree(XPDFTreeWidget w, Widget instigator,
+                        XPDFTreeEntry *e, Position x, Position y,
+                        Boolean visible);
+static void calcSize(Widget widget, Widget instigator,
+                    Dimension *totalWidth,
+                    Dimension *totalHeight);
+static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator,
+                           XPDFTreeEntry *e,
+                           Dimension *width, Dimension *height);
+static Boolean needRelayout(Widget oldWidget, Widget newWidget);
+static void click(Widget widget, XEvent *event,
+                 String *params, Cardinal *numParams);
+static Boolean findPosition(XPDFTreeWidget w, int x, int y,
+                           XPDFTreeEntry **e, Boolean *onExpandIcon);
+static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y,
+                                    XPDFTreeEntry **e,
+                                    Boolean *onExpandIcon);
+
+//------------------------------------------------------------------------
+
+static XtResource resources[] = {
+  { XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension,
+    sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginWidth),
+    XmRImmediate, (XtPointer)0 },
+  { XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension,
+    sizeof(Dimension), XtOffsetOf(XPDFTreeRec, tree.marginHeight),
+    XmRImmediate, (XtPointer)0 },
+  { XPDFNselectionCallback, XmCCallback, XmRCallback,
+    sizeof(XtCallbackList), XtOffsetOf(XPDFTreeRec, tree.selectCallback),
+    XmRImmediate, (XtPointer)NULL }
+};
+
+static XmSyntheticResource synResources[] = {
+  { XmNmarginWidth, sizeof(Dimension),
+    XtOffsetOf(XPDFTreeRec, tree.marginWidth),
+#if XmVERSION > 1
+    XmeFromHorizontalPixels, XmeToHorizontalPixels
+#else
+    _XmFromHorizontalPixels, _XmToHorizontalPixels
+#endif
+  },
+  { XmNmarginHeight, sizeof(Dimension),
+    XtOffsetOf(XPDFTreeRec, tree.marginHeight),
+#if XmVERSION > 1
+    XmeFromVerticalPixels, XmeToVerticalPixels
+#else
+    _XmFromVerticalPixels, _XmToVerticalPixels
+#endif
+  }
+};
+
+static XtResource constraints[] = {
+  { XPDFNentryParent, XPDFCentryParent, XmRWidget,
+    sizeof(Widget), XtOffsetOf(XPDFTreeConstraintRec, tree.entryParent),
+    XmRImmediate, (XtPointer)NULL },
+  { XPDFNentryExpanded, XPDFCentryExpanded, XmRBoolean,
+    sizeof(Boolean), XtOffsetOf(XPDFTreeConstraintRec, tree.entryExpanded),
+    XmRImmediate, (XtPointer)False },
+  { XPDFNentryPosition, XPDFCentryPosition, XmRInt,
+    sizeof(int), XtOffsetOf(XPDFTreeConstraintRec, tree.entryPosition),
+    XmRImmediate, (XtPointer)0 }
+};
+
+static char defaultTranslations[] =
+  "<Btn1Down>: XPDFTreeClick()";
+
+static XtActionsRec actions[] = {
+  { "XPDFTreeClick", click }
+};
+
+externaldef(xpdftreeclassrec) XPDFTreeClassRec xpdfTreeClassRec = {
+  { // Core
+    (WidgetClass)&xmManagerClassRec,           // superclass
+    "XPDFTree",                                        // class_name
+    sizeof(XPDFTreeRec),                       // widget_size
+    NULL,                                      // class_initialize
+    &classPartInitialize,                      // class_part_initialize
+    FALSE,                                     // class_inited
+    &initialize,                               // initialize
+    NULL,                                      // initialize_hook
+    XtInheritRealize,                          // realize
+    actions,                                   // actions
+    XtNumber(actions),                         // num_actions
+    resources,                                 // resources
+    XtNumber(resources),                       // num_resources
+    NULLQUARK,                                 // xrm_class
+    TRUE,                                      // compress_motion
+    XtExposeCompressMaximal,                   // compress_exposure
+    TRUE,                                      // compress_enterleave
+    FALSE,                                     // visible_interest
+    &destroy,                                  // destroy
+    &resize,                                   // resize
+    &redisplay,                                        // expose
+    &setValues,                                        // set_values
+    NULL,                                      // set_values_hook
+    &setValuesAlmost,                          // set_values_almost
+    NULL,                                      // get_values_hook
+    NULL,                                      // accept_focus
+    XtVersion,                                 // version
+    NULL,                                      // callback_private
+    defaultTranslations,                       // tm_table
+    &queryGeometry,                            // query_geometry
+    NULL,                                      // display_accelerator
+    NULL                                       // extension
+  },
+  { // Composite
+    &geometryManager,                          // geometry_manager
+    &changeManaged,                            // change_managed
+    XtInheritInsertChild,                      // insert_child
+    XtInheritDeleteChild,                      // delete_child
+    NULL                                       // extension
+  },
+  { // Constraint
+    constraints,                               // constraint_resources
+    XtNumber(constraints),                     // constraint_num_resources
+    sizeof(XPDFTreeConstraintRec),             // constraint_size
+    &initConstraint,                           // constraint_initialize
+    &destroyConstraint,                                // constraint_destroy
+    &constraintSetValues,                      // constraint_set_values
+    NULL                                       // extension
+  },
+  { // XmManager
+    XtInheritTranslations,                     // translations
+#if XmVERSION > 1
+    synResources,                              // syn_resources
+    XtNumber(synResources),                    // num_syn_resources
+#else
+    NULL,                                      // syn_resources
+    0,                                         // num_syn_resources
+#endif
+    NULL,                                      // syn_constraint_resources
+    0,                                         // num_syn_constraint_res's
+    XmInheritParentProcess,                    // parent_process
+    NULL                                       // extension
+  },
+  { // XPDFTree
+    &createGC,                                 // createGC
+    &destroyGC,                                        // destroyGC
+    &layout,                                   // layout
+    &calcSize,                                 // calcSize
+    &needRelayout,                             // needRelayout
+    NULL                                       // extension
+  }
+};
+
+externaldef(xpdftreewidgetclass) WidgetClass xpdfTreeWidgetClass =
+  (WidgetClass)&xpdfTreeClassRec;
+
+//------------------------------------------------------------------------
+
+static void classPartInitialize(WidgetClass widgetCls) {
+  XPDFTreeWidgetClass wc = (XPDFTreeWidgetClass)widgetCls;
+  XPDFTreeWidgetClass sc = (XPDFTreeWidgetClass)wc->coreClass.superclass;
+
+  // method inheritance
+  if (wc->treeClass.createGC == XPDFInheritCreateGC) {
+    wc->treeClass.createGC = sc->treeClass.createGC;
+  }
+  if (wc->treeClass.destroyGC == XPDFInheritDestroyGC) {
+    wc->treeClass.destroyGC = sc->treeClass.destroyGC;
+  }
+  if (wc->treeClass.layout == XPDFInheritLayout) {
+    wc->treeClass.layout = sc->treeClass.layout;
+  }
+  if (wc->treeClass.calcSize == XPDFInheritCalcSize) {
+    wc->treeClass.calcSize = sc->treeClass.calcSize;
+  }
+  if (wc->treeClass.needRelayout == XPDFInheritNeedRelayout) {
+    wc->treeClass.needRelayout = sc->treeClass.needRelayout;
+  }
+}
+
+static void initialize(Widget requestWidget, Widget newWidget,
+                      ArgList args, Cardinal *numArgs) {
+  XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
+  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget);
+
+  nw->tree.root = NULL;
+  nw->tree.redrawY = -1;
+  if (cls->treeClass.createGC) {
+    (*cls->treeClass.createGC)(newWidget);
+  } else {
+    createGC(newWidget);
+  }
+}
+
+static void destroy(Widget widget) {
+  XPDFTreeWidget w = (XPDFTreeWidget)widget;
+  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
+
+  if (w->tree.root) {
+    destroySubtree(w->tree.root);
+    w->tree.root = NULL;
+  }
+  if (cls->treeClass.destroyGC) {
+    (*cls->treeClass.destroyGC)(widget);
+  } else {
+    destroyGC(widget);
+  }
+}
+
+static void destroySubtree(XPDFTreeEntry *e) {
+  if (e->children) {
+    destroySubtree(e->children);
+  }
+  if (e->next) {
+    destroySubtree(e->next);
+  }
+}
+
+static void resize(Widget widget) {
+  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
+
+  if (cls->treeClass.layout) {
+    (*cls->treeClass.layout)(widget, NULL);
+  } else {
+    layout(widget, NULL);
+  }
+}
+
+static void redisplay(Widget widget, XEvent *event, Region region) {
+  XPDFTreeWidget w = (XPDFTreeWidget)widget;
+  XPDFTreeEntry *e;
+
+  if (w->tree.redrawY >= 0) {
+    XClearArea(XtDisplay((Widget)w), XtWindow((Widget)w),
+              0, w->tree.redrawY, w->core.width, w->core.height, False);
+    w->tree.redrawY = -1;
+  }
+  for (e = w->tree.root; e; e = e->next) {
+    redisplaySubtree(w, e, event, region);
+  }
+}
+
+static void redisplaySubtree(XPDFTreeWidget w, XPDFTreeEntry *e,
+                            XEvent *event, Region region) {
+  XPDFTreeConstraint c;
+  Position x, y, y2;
+  XPDFTreeEntry *child;
+
+  (*XtClass(e->widget)->core_class.expose)(e->widget, event, region);
+  c = XPDFTreeCPart(e->widget);
+  x = e->widget->core.x;
+  y = e->widget->core.y + e->widget->core.height / 2;
+  if (e->children) {
+    if (c->entryExpanded) {
+      drawExpandedIcon(w, x - 8, y);
+      y2 = y; // make gcc happy
+      for (child = e->children; child; child = child->next) {
+       y2 = child->widget->core.y + child->widget->core.height / 2;
+       XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.dottedGC,
+                 x - 8, y2, x + 6, y2);
+       redisplaySubtree(w, child, event, region);
+      }
+      XDrawLine(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.dottedGC,
+               x - 8, y + 2, x - 8, y2);
+    } else {
+      drawCollapsedIcon(w, x - 8, y);
+    }
+  }
+}
+
+static void drawExpandedIcon(XPDFTreeWidget w, Position x, Position y) {
+  XPoint pts[4];
+
+  pts[0].x = x - 4;    pts[0].y = y - 2;
+  pts[1].x = x + 4;    pts[1].y = y - 2;
+  pts[2].x = x;        pts[2].y = y + 2;
+  pts[3].x = x - 4;    pts[3].y = y - 2;
+  XDrawLines(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.plainGC,
+            pts, 4, CoordModeOrigin);
+}
+
+static void drawCollapsedIcon(XPDFTreeWidget w, Position x, Position y) {
+  XPoint pts[4];
+
+  pts[0].x = x - 2;    pts[0].y = y - 4;
+  pts[1].x = x - 2;    pts[1].y = y + 4;
+  pts[2].x = x + 2;    pts[2].y = y;
+  pts[3].x = x - 2;    pts[3].y = y - 4;
+  XDrawLines(XtDisplay((Widget)w), XtWindow((Widget)w), w->tree.plainGC,
+            pts, 4, CoordModeOrigin);
+}
+
+static Boolean setValues(Widget oldWidget, Widget requestWidget,
+                        Widget newWidget, ArgList args, Cardinal *numArgs) {
+  XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget;
+  XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
+  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(nw);
+  Boolean relayout, redisp;
+
+  // check to see if layout-affecting resources have changed
+  if (cls->treeClass.needRelayout) {
+    relayout = (*cls->treeClass.needRelayout)((Widget)ow, (Widget)nw);
+  } else {
+    relayout = needRelayout((Widget)ow, (Widget)nw);
+  }
+  redisp = False;
+  if (relayout) {
+
+    // calculate a new ideal size (reset the widget size first so
+    // calcSize will compute a new one)
+    if (nw->core.width == ow->core.width) {
+      nw->core.width = 0;
+    }
+    if (nw->core.height == ow->core.height) {
+      nw->core.height = 0;
+    }
+    if (cls->treeClass.calcSize) {
+      (*cls->treeClass.calcSize)((Widget)nw, NULL,
+                                &nw->core.width, &nw->core.height);
+    } else {
+      calcSize((Widget)nw, NULL, &nw->core.width, &nw->core.height);
+    }
+
+    // if resources have changed but size hasn't, layout manually
+    // (because Xt just looks at the size)
+    if (nw->core.width == ow->core.width &&
+       nw->core.height == ow->core.height) {
+      if (cls->treeClass.layout) {
+       (*cls->treeClass.layout)((Widget)nw, NULL);
+      } else {
+       layout((Widget)nw, NULL);
+      }
+      redisp = True;
+    }
+  }
+
+  return redisp;
+}
+
+static void setValuesAlmost(Widget oldWidget, Widget newWidget,
+                           XtWidgetGeometry *request,
+                           XtWidgetGeometry *reply) {
+  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(newWidget);
+
+  // our parent rejected a geometry request, so accept the compromise
+  // and relayout
+  if (!reply->request_mode) {
+    if (cls->treeClass.layout) {
+      (*cls->treeClass.layout)(newWidget, NULL);
+    } else {
+      layout(newWidget, NULL);
+    }
+  }
+  *request = *reply;
+}
+
+static XtGeometryResult queryGeometry(Widget widget,
+                                     XtWidgetGeometry *request,
+                                     XtWidgetGeometry *reply) {
+  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
+
+  if (!XtIsRealized(widget)) {
+    reply->width = XtWidth(widget);
+    reply->height = XtHeight(widget);
+  } else {
+    reply->width = 0;
+    reply->height = 0;
+  }
+  if (cls->treeClass.calcSize) {
+    (*cls->treeClass.calcSize)(widget, NULL, &reply->width, &reply->height);
+  } else {
+    calcSize(widget, NULL, &reply->width, &reply->height);
+  }
+#if XmVERSION > 1
+  return XmeReplyToQueryGeometry(widget, request, reply);
+#else
+  if ((request->request_mode & CWWidth) &&
+      (request->request_mode & CWHeight) &&
+      request->width == reply->width &&
+      request->height == reply->height) {
+    return XtGeometryYes;
+  }
+  if (reply->width == XtWidth(widget) &&
+      reply->height == XtHeight(widget)) {
+    return XtGeometryNo;
+  }
+  reply->request_mode = CWWidth | CWHeight;
+  return XtGeometryAlmost;
+#endif
+}
+
+static XtGeometryResult geometryManager(Widget widget,
+                                       XtWidgetGeometry *request,
+                                       XtWidgetGeometry *reply) {
+  XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget);
+  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(w);
+  Dimension curWidth, curHeight, curBW;
+  XtWidgetGeometry parentReq;
+  XtGeometryResult result;
+
+  // deny any requests for a new position
+  if ((request->request_mode & CWX) || (request->request_mode & CWY)) {
+    return XtGeometryNo;
+  }
+
+  // save the current geometry
+  curWidth = w->core.width;
+  curHeight = w->core.height;
+  curBW = w->core.border_width;
+
+  // make the requested changes
+  if (request->request_mode & CWWidth) {
+    w->core.width = request->width;
+  }
+  if (request->request_mode & CWHeight) {
+    w->core.height = request->height;
+  }
+  if (request->request_mode & CWBorderWidth) {
+    w->core.border_width = request->border_width;
+  }
+
+  // calculate the new ideal size
+  parentReq.width = 0;
+  parentReq.height = 0;
+  if (cls->treeClass.calcSize) {
+    (*cls->treeClass.calcSize)((Widget)w, widget,
+                              &parentReq.width, &reply->height);
+  } else {
+    calcSize((Widget)w, widget, &parentReq.width, &reply->height);
+  }
+
+  // send geometry request to our parent
+  parentReq.request_mode = CWWidth | CWHeight;
+  if (request->request_mode & XtCWQueryOnly) {
+    parentReq.request_mode |= XtCWQueryOnly;
+  }
+  result = XtMakeGeometryRequest((Widget)w, &parentReq, NULL);
+  if (result == XtGeometryAlmost) {
+    result = XtGeometryNo;
+  }
+
+  if (result == XtGeometryNo || (request->request_mode & XtCWQueryOnly)) {
+    // restore the original geometry
+    w->core.width = curWidth;
+    w->core.height = curHeight;
+    w->core.border_width = curBW;
+  } else {
+    if (cls->treeClass.layout) {
+      (*cls->treeClass.layout)((Widget)w, widget);
+    } else {
+      layout((Widget)w, widget);
+    }
+  }
+
+  return result;
+}
+
+static void changeManaged(Widget widget) {
+  Dimension width, height;
+  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass(widget);
+
+  // compute the ideal size
+  if (!XtIsRealized(widget)) {
+    width = XtWidth(widget);
+    height = XtHeight(widget);
+  } else {
+    width = 0;
+    height = 0;
+  }
+  if (cls->treeClass.calcSize) {
+    (*cls->treeClass.calcSize)(widget, NULL, &width, &height);
+  } else {
+    calcSize(widget, NULL, &width, &height);
+  }
+
+  // make resize request to parent -- keep asking until we get a yes
+  // or no
+  while (XtMakeResizeRequest(widget, width, height, &width, &height)
+        == XtGeometryAlmost) ;
+
+  // relayout
+  if (cls->treeClass.layout) {
+    (*cls->treeClass.layout)(widget, NULL);
+  } else {
+    layout(widget, NULL);
+  }
+
+#if XmVERSION > 1
+  // update keyboard traversal
+  XmeNavigChangeManaged(widget);
+#else
+  _XmNavigChangeManaged(widget);
+#endif
+}
+
+static void initConstraint(Widget requestWidget, Widget newWidget,
+                          ArgList args, Cardinal *numArgs) {
+  XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget);
+  XPDFTreeConstraint c;
+
+  c = XPDFTreeCPart(newWidget);
+  c->e = (XPDFTreeEntry *)gmalloc(sizeof(XPDFTreeEntry));
+  c->e->widget = newWidget;
+  c->e->children = NULL;
+  c->e->next = NULL;
+  if (c->entryParent) {
+    insertChildOnList(c->e, &XPDFTreeCPart(c->entryParent)->e->children);
+  } else {
+    insertChildOnList(c->e, &w->tree.root);
+  }
+}
+
+static void destroyConstraint(Widget widget) {
+  deleteSubtree(widget);
+}
+
+static void deleteSubtree(Widget widget) {
+  XPDFTreeWidget w = (XPDFTreeWidget)XtParent(widget);
+  XPDFTreeConstraint c;
+
+  c = XPDFTreeCPart(widget);
+  if (!c->e) {
+    return;
+  }
+  while (c->e->children) {
+    deleteSubtree(c->e->children->widget);
+  }
+  if (c->entryParent) {
+    deleteChildFromList(c->e, &XPDFTreeCPart(c->entryParent)->e->children);
+  } else {
+    deleteChildFromList(c->e, &w->tree.root);
+  }
+  gfree(c->e);
+  c->e = NULL;
+}
+
+static Boolean constraintSetValues(Widget oldWidget, Widget requestWidget,
+                                  Widget newWidget,
+                                  ArgList args, Cardinal *numArgs) {
+  XPDFTreeWidget w = (XPDFTreeWidget)XtParent(newWidget);
+  XPDFTreeWidgetClass cls = (XPDFTreeWidgetClass)XtClass((Widget)w);
+  XPDFTreeConstraint oc, nc;
+  Boolean relayout;
+  Dimension width, height;
+
+  if (!XtIsManaged(newWidget)) {
+    return False;
+  }
+  oc = XPDFTreeCPart(oldWidget);
+  nc = XPDFTreeCPart(newWidget);
+  relayout = False;
+  if (nc->entryParent != oc->entryParent ||
+      nc->entryPosition != oc->entryPosition) {
+    if (oc->entryParent) {
+      deleteChildFromList(oc->e, &XPDFTreeCPart(oc->entryParent)->e->children);
+    } else {
+      deleteChildFromList(oc->e, &w->tree.root);
+    }
+    if (nc->entryParent) {
+      insertChildOnList(nc->e, &XPDFTreeCPart(nc->entryParent)->e->children);
+    } else {
+      insertChildOnList(nc->e, &w->tree.root);
+    }
+    relayout = True;
+  } else if (nc->entryExpanded != oc->entryExpanded) {
+    relayout = True;
+  }
+
+  if (relayout) {
+
+    // calculate a new ideal size (reset the widget size first so
+    // calcSize will compute a new one)
+    width = 0;
+    height = 0;
+    if (cls->treeClass.calcSize) {
+      (*cls->treeClass.calcSize)((Widget)w, NULL, &width, &height);
+    } else {
+      calcSize((Widget)w, NULL, &width, &height);
+    }
+
+    // make resize request to parent -- keep asking until we get a yes
+    // or no
+    while (XtMakeResizeRequest((Widget)w, width, height, &width, &height)
+          == XtGeometryAlmost) ;
+
+    // relayout the widget
+    if (cls->treeClass.layout) {
+      (*cls->treeClass.layout)((Widget)w, NULL);
+    } else {
+      layout((Widget)w, NULL);
+    }
+  }
+
+  return relayout;
+}
+
+static void insertChildOnList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) {
+  int pos;
+  XPDFTreeEntry *e2;
+
+  pos = XPDFTreeCPart(e->widget)->entryPosition;
+  if (!*listHead || pos < XPDFTreeCPart((*listHead)->widget)->entryPosition) {
+    e->next = *listHead;
+    *listHead = e;
+  } else {
+    for (e2 = *listHead;
+        e2->next && pos >= XPDFTreeCPart(e2->next->widget)->entryPosition;
+        e2 = e2->next) ;
+    e->next = e2->next;
+    e2->next = e;
+  }
+}
+
+static void deleteChildFromList(XPDFTreeEntry *e, XPDFTreeEntry **listHead) {
+  XPDFTreeEntry **p;
+
+  for (p = listHead; *p; p = &(*p)->next) {
+    if (*p == e) {
+      *p = e->next;
+      e->next = NULL;
+      return;
+    }
+  }
+}
+
+static void createGC(Widget widget) {
+  XPDFTreeWidget w = (XPDFTreeWidget)widget;
+  XGCValues gcValues;
+
+  gcValues.foreground = w->manager.foreground;
+  gcValues.line_width = 0;
+  gcValues.line_style = LineSolid;
+  w->tree.plainGC = XtGetGC(widget,
+                           GCForeground | GCLineWidth | GCLineStyle,
+                           &gcValues);
+
+  gcValues.line_style = LineOnOffDash;
+  gcValues.dashes = 1;
+  gcValues.dash_offset = 0;
+  w->tree.dottedGC = XtGetGC(widget,
+                            GCForeground | GCLineWidth | GCLineStyle |
+                                GCDashList | GCDashOffset,
+                            &gcValues);
+}
+
+static void destroyGC(Widget widget) {
+  XPDFTreeWidget w = (XPDFTreeWidget)widget;
+
+  XtReleaseGC(widget, w->tree.plainGC);
+  XtReleaseGC(widget, w->tree.dottedGC);
+}
+
+static void layout(Widget widget, Widget instigator) {
+  XPDFTreeWidget w = (XPDFTreeWidget)widget;
+  XPDFTreeEntry *e;
+  Position x, y;
+
+  x = w->tree.marginWidth + xpdfTreeIndent;
+  y = w->tree.marginHeight;
+  for (e = w->tree.root; e; e = e->next) {
+    y = layoutSubtree(w, instigator, e, x, y, True);
+  }
+}
+
+static int layoutSubtree(XPDFTreeWidget w, Widget instigator,
+                        XPDFTreeEntry *e, Position x, Position y,
+                        Boolean visible) {
+  Widget ew;
+  XPDFTreeEntry *child;
+  XPDFTreeConstraint c;
+
+  ew = e->widget;
+  if (!XtIsManaged(ew)) {
+    return y;
+  }
+  c = XPDFTreeCPart(ew);
+
+  // place this entry
+  if (ew) {
+    if (visible) {
+      if (ew == instigator) {
+       ew->core.x = x;
+       ew->core.y = y;
+      } else {
+#if XmVERSION > 1
+       XmeConfigureObject(ew, x, y, ew->core.width, ew->core.height,
+                          ew->core.border_width);
+#else
+       _XmConfigureObject(ew, x, y, ew->core.width, ew->core.height,
+                          ew->core.border_width);
+#endif
+      }
+      y += ew->core.height + 2 * ew->core.border_width;
+    }
+  }
+
+  // place this entry's children
+  x += xpdfTreeIndent;
+  for (child = e->children; child; child = child->next) {
+    y = layoutSubtree(w, instigator, child, x, y,
+                     visible && (!c || c->entryExpanded));
+  }
+
+  return y;
+}
+
+static void calcSize(Widget widget, Widget instigator,
+                    Dimension *totalWidth,
+                    Dimension *totalHeight) {
+  XPDFTreeWidget w = (XPDFTreeWidget)widget;
+  XPDFTreeEntry *e;
+  Dimension w1, h1, w2, h2;
+
+  w1 = h1 = 0;
+  for (e = w->tree.root; e; e = e->next) {
+    calcSubtreeSize(w, instigator, e, &w2, &h2);
+    if (w2 > w1) {
+      w1 = w2;
+    }
+    h1 += h2;
+  }
+  w1 += xpdfTreeIndent + 2 * w->tree.marginWidth;
+  h1 += 2 * w->tree.marginHeight;
+  if (h1 == 0) {
+    h1 = 1;
+  }
+  if (!*totalWidth) {
+    *totalWidth = w1;
+  }
+  if (!*totalHeight) {
+    *totalHeight = h1;
+  }
+}
+
+static void calcSubtreeSize(XPDFTreeWidget w, Widget instigator,
+                           XPDFTreeEntry *e,
+                           Dimension *width, Dimension *height) {
+  Widget ew;
+  XPDFTreeEntry *child;
+  XPDFTreeConstraint c;
+  XtWidgetGeometry geom;
+  Dimension w1, h1, w2, h2;
+  
+  ew = e->widget;
+  if (!XtIsManaged(ew)) {
+    *width = *height = 0;
+    return;
+  }
+  c = XPDFTreeCPart(ew);
+
+  // get size of this entry
+  if (ew) {
+    if (!XtIsManaged(ew)) {
+      *width = *height = 0;
+      return;
+    }
+    if (ew == instigator) {
+      w1 = ew->core.width;
+      h1 = ew->core.height;
+    } else {
+      XtQueryGeometry(ew, NULL, &geom);
+      w1 = (geom.request_mode & CWWidth) ? geom.width : ew->core.width;
+      h1 = (geom.request_mode & CWHeight) ? geom.height : ew->core.height;
+    }
+    h1 += 2 * ew->core.border_width;
+  } else {
+    // root of tree
+    w1 = 0;
+    h1 = 0;
+  }
+
+  // if this entry is expanded, get size of all of its children
+  if (c->entryExpanded) {
+    for (child = e->children; child; child = child->next) {
+      calcSubtreeSize(w, instigator, child, &w2, &h2);
+      w2 += xpdfTreeIndent;
+      if (w2 > w1) {
+       w1 = w2;
+      }
+      h1 += h2;
+    }
+  }
+
+  *width = w1;
+  *height = h1;
+}
+
+static Boolean needRelayout(Widget oldWidget, Widget newWidget) {
+  XPDFTreeWidget ow = (XPDFTreeWidget)oldWidget;
+  XPDFTreeWidget nw = (XPDFTreeWidget)newWidget;
+
+  if (nw->tree.marginWidth != ow->tree.marginWidth ||
+      nw->tree.marginHeight != ow->tree.marginHeight) {
+    return True;
+  }
+  return False;
+}
+
+static void click(Widget widget, XEvent *event,
+                 String *params, Cardinal *numParams) {
+  XPDFTreeWidget w = (XPDFTreeWidget)widget;
+  XButtonPressedEvent *bpe;
+  XPDFTreeEntry *e;
+  Boolean onExpandIcon;
+  XPDFTreeConstraint c;
+  XPDFTreeSelectCallbackStruct cbs;
+
+  if (event->type != ButtonPress) {
+    return;
+  }
+  bpe = (XButtonPressedEvent *)event;
+  if (findPosition(w, bpe->x, bpe->y, &e, &onExpandIcon)) {
+    if (onExpandIcon) {
+      c = XPDFTreeCPart(e->widget);
+      w->tree.redrawY = e->widget->core.y;
+      XtVaSetValues(e->widget, XPDFNentryExpanded, !c->entryExpanded, NULL);
+    } else {
+      XmProcessTraversal(e->widget, XmTRAVERSE_CURRENT);
+      XtCallActionProc(widget, "ManagerGadgetActivate", event, NULL, 0);
+      cbs.reason = XmCR_ACTIVATE;
+      cbs.event = event;
+      cbs.selectedItem = e->widget;
+      XtCallCallbackList(widget, w->tree.selectCallback, &cbs);
+    }
+  }
+}
+
+static Boolean findPosition(XPDFTreeWidget w, int x, int y,
+                           XPDFTreeEntry **e, Boolean *onExpandIcon) {
+  XPDFTreeEntry *e2;
+
+  for (e2 = w->tree.root; e2; e2 = e2->next) {
+    *e = e2;
+    if (findPositionInSubtree(w, x, y, e, onExpandIcon)) {
+      return True;
+    }
+  }
+  return False;
+}
+
+// If (x,y) falls on either an expand/collapse icon or a label gadget,
+// set *<e> and *<onExpandIcon> and return true.
+static Boolean findPositionInSubtree(XPDFTreeWidget w, int x, int y,
+                                    XPDFTreeEntry **e,
+                                    Boolean *onExpandIcon) {
+  Widget child;
+  XPDFTreeConstraint c;
+  XPDFTreeEntry *e2;
+  int y1;
+
+  child = (*e)->widget;
+  y1 = child->core.y + child->core.height / 2;
+  if (x >= child->core.x && x < child->core.x + child->core.width &&
+      y >= child->core.y && y < child->core.y + child->core.height) {
+    *onExpandIcon = False;
+    return True;
+  } else if (x >= child->core.x - 16 && x < child->core.x - 4 &&
+            y >= y1 - 6 && y < y1 + 6 &&
+            (*e)->children) {
+    *onExpandIcon = True;
+    return True;
+  }
+  c = XPDFTreeCPart(child);
+  if (!c || c->entryExpanded) {
+    for (e2 = (*e)->children; e2; e2 = e2->next) {
+      *e = e2;
+      if (findPositionInSubtree(w, x, y, e, onExpandIcon)) {
+       return True;
+      }
+    }
+  }
+  return False;
+}
+
+Widget XPDFCreateTree(Widget parent, char *name,
+                     ArgList argList, Cardinal numArgs) {
+  return XtCreateWidget(name, xpdfTreeWidgetClass, parent, argList, numArgs);
+}
diff --git a/pdf/xpdf/XPDFTree.h b/pdf/xpdf/XPDFTree.h
new file mode 100644 (file)
index 0000000..d569f49
--- /dev/null
@@ -0,0 +1,43 @@
+//========================================================================
+//
+// XPDFTree.h
+//
+//========================================================================
+
+#ifndef XPDFTREE_H
+#define XPDFTREE_H
+
+#include <Xm/Xm.h>
+
+extern "C" {
+
+externalref WidgetClass xpdfTreeWidgetClass;
+
+typedef struct _XPDFTreeClassRec *XPDFTreeWidgetClass;
+typedef struct _XPDFTreeRec      *XPDFTreeWidget;
+
+#ifndef XPDFIsTree
+#define XPDFIsTree(w) XtIsSubclass(w, xpdfTreeWidgetClass)
+#endif
+
+#define XPDFNentryParent       "entryParent"
+#define XPDFNentryExpanded     "entryExpanded"
+#define XPDFNentryPosition     "entryPosition"
+#define XPDFNselectionCallback "selectionCallback"
+
+#define XPDFCentryParent       "EntryParent"
+#define XPDFCentryExpanded     "EntryExpanded"
+#define XPDFCentryPosition     "EntryPosition"
+
+typedef struct {
+  int reason;
+  XEvent *event;
+  Widget selectedItem;
+} XPDFTreeSelectCallbackStruct;
+
+extern Widget XPDFCreateTree(Widget parent, char *name,
+                            ArgList argList, Cardinal argCount);
+
+} // extern "C"
+
+#endif
diff --git a/pdf/xpdf/XPDFTreeP.h b/pdf/xpdf/XPDFTreeP.h
new file mode 100644 (file)
index 0000000..16ab137
--- /dev/null
@@ -0,0 +1,85 @@
+//========================================================================
+//
+// XPDFTreeP.h
+//
+//========================================================================
+
+#ifndef XPDFTREEP_H
+#define XPDFTREEP_H
+
+#include <Xm/ManagerP.h>
+#include "XPDFTree.h"
+
+extern "C" {
+
+typedef void (*XPDFLayoutProc)(Widget widget, Widget instigator);
+typedef void (*XPDFCalcSizeProc)(Widget widget, Widget instigator,
+                                Dimension *totalWidth,
+                                Dimension *totalHeight);
+typedef Boolean (*XPDFNeedRelayoutProc)(Widget oldWidget, Widget newWidget);
+
+#define XPDFInheritCreateGC ((XtWidgetProc)_XtInherit)
+#define XPDFInheritDestroyGC ((XtWidgetProc)_XtInherit)
+#define XPDFInheritLayout ((XPDFLayoutProc)_XtInherit)
+#define XPDFInheritCalcSize ((XPDFCalcSizeProc)_XtInherit)
+#define XPDFInheritNeedRelayout ((XPDFNeedRelayoutProc)_XtInherit)
+
+typedef struct {
+  XtWidgetProc createGC;
+  XtWidgetProc destroyGC;
+  XPDFLayoutProc layout;
+  XPDFCalcSizeProc calcSize;
+  XPDFNeedRelayoutProc needRelayout;
+  XtPointer extension;
+} XPDFTreeClassPart;
+
+typedef struct _XPDFTreeClassRec {
+  CoreClassPart coreClass;
+  CompositeClassPart compositeClass;
+  ConstraintClassPart constraintClass;
+  XmManagerClassPart managerClass;
+  XPDFTreeClassPart treeClass;
+} XPDFTreeClassRec;
+
+externalref XPDFTreeClassRec xpdfTreeClassRec;
+
+typedef struct _XPDFTreeEntry XPDFTreeEntry;
+
+typedef struct {
+  Dimension marginWidth;
+  Dimension marginHeight;
+  XtCallbackList selectCallback;
+  GC plainGC;
+  GC dottedGC;
+  XPDFTreeEntry *root;
+  int redrawY;
+} XPDFTreePart;
+
+typedef struct _XPDFTreeRec {
+  CorePart core;
+  CompositePart composite;
+  ConstraintPart constraint;
+  XmManagerPart manager;
+  XPDFTreePart tree;
+} XPDFTreeRec;
+
+#define XPDFTreeIndex (XmManagerIndex + 1)
+
+typedef struct _XPDFTreeConstraintPart {
+  Widget entryParent;
+  Boolean entryExpanded;
+  int entryPosition;
+  XPDFTreeEntry *e;
+} XPDFTreeConstraintPart, *XPDFTreeConstraint;
+
+typedef struct _XPDFTreeConstraintRec {
+  XmManagerConstraintPart manager;
+  XPDFTreeConstraintPart tree;
+} XPDFTreeConstraintRec, *XPDFTreeConstraintPtr;
+
+#define XPDFTreeCPart(w) \
+  (&((XPDFTreeConstraintPtr)(w)->core.constraints)->tree)
+
+} // extern "C"
+
+#endif
diff --git a/pdf/xpdf/XPDFViewer.cc b/pdf/xpdf/XPDFViewer.cc
new file mode 100644 (file)
index 0000000..a8c8f8f
--- /dev/null
@@ -0,0 +1,2318 @@
+//========================================================================
+//
+// XPDFViewer.cc
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <X11/cursorfont.h>
+#ifdef HAVE_X11_XPM_H
+#include <X11/xpm.h>
+#endif
+#include "gmem.h"
+#include "gfile.h"
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "PDFDoc.h"
+#include "ErrorCodes.h"
+#include "Outline.h"
+#include "UnicodeMap.h"
+#ifndef DISABLE_OUTLINE
+#define Object XtObject
+#include "XPDFTree.h"
+#undef Object
+#endif
+#include "XPDFApp.h"
+#include "XPDFViewer.h"
+#include "XPixmapOutputDev.h"
+#include "PSOutputDev.h"
+#include "config.h"
+
+// these macro defns conflict with xpdf's Object class
+#ifdef LESSTIF_VERSION
+#undef XtDisplay
+#undef XtScreen
+#undef XtWindow
+#undef XtParent
+#undef XtIsRealized
+#endif
+
+#if XmVERSION <= 1
+#define XmSET   True
+#define XmUNSET False
+#endif
+
+//------------------------------------------------------------------------
+// GUI includes
+//------------------------------------------------------------------------
+
+#include "xpdfIcon.xpm"
+#include "leftArrow.xbm"
+#include "leftArrowDis.xbm"
+#include "dblLeftArrow.xbm"
+#include "dblLeftArrowDis.xbm"
+#include "rightArrow.xbm"
+#include "rightArrowDis.xbm"
+#include "dblRightArrow.xbm"
+#include "dblRightArrowDis.xbm"
+#include "backArrow.xbm"
+#include "backArrowDis.xbm"
+#include "forwardArrow.xbm"
+#include "forwardArrowDis.xbm"
+#include "find.xbm"
+#include "findDis.xbm"
+#include "print.xbm"
+#include "printDis.xbm"
+#include "about.xbm"
+#include "about-text.h"
+
+//------------------------------------------------------------------------
+
+XPDFViewer::XPDFViewer(XPDFApp *appA, GString *fileName,
+                      int pageA, GString *destName,
+                      GString *ownerPassword, GString *userPassword) {
+  LinkDest *dest;
+  int pg, z;
+  GString *dir;
+
+  app = appA;
+  win = NULL;
+  core = NULL;
+  password = NULL;
+  ok = gFalse;
+#ifndef DISABLE_OUTLINE
+  outlineLabels = NULL;
+  outlineLabelsLength = outlineLabelsSize = 0;
+#endif
+
+  // do Motif-specific initialization and create the window;
+  // this also creates the core object
+  initWindow();
+  initAboutDialog();
+  initOpenDialog();
+  initFindDialog();
+  initSaveAsDialog();
+  initPrintDialog();
+  initPasswordDialog();
+
+  dest = NULL; // make gcc happy
+  pg = pageA; // make gcc happy
+
+  if (fileName) {
+    if (loadFile(fileName, ownerPassword, userPassword)) {
+      getPageAndDest(pageA, destName, &pg, &dest);
+      if (pg > 0) {
+       core->resizeToPage(pg);
+      }
+      dir = makePathAbsolute(grabPath(fileName->getCString()));
+      setOpenDialogDir(dir->getCString());
+      setSaveAsDialogDir(dir->getCString());
+      delete dir;
+    } else {
+      return;
+    }
+  }
+
+  // map the window -- we do this after calling resizeToPage to avoid
+  // an annoying on-screen resize
+  mapWindow();
+
+  // display the first page
+  z = app->getFullScreen() ? zoomPage : core->getZoom();
+  if (dest) {
+    displayDest(dest, z, core->getRotate(), gTrue);
+    delete dest;
+  } else {
+    displayPage(pg, z, core->getRotate(), gTrue, gTrue);
+  }
+
+  ok = gTrue;
+}
+
+XPDFViewer::~XPDFViewer() {
+  delete core;
+  XmFontListFree(aboutBigFont);
+  XmFontListFree(aboutVersionFont);
+  XmFontListFree(aboutFixedFont);
+  closeWindow();
+#ifndef DISABLE_OUTLINE
+  if (outlineLabels) {
+    gfree(outlineLabels);
+  }
+#endif
+  if (password) {
+    delete password;
+  }
+}
+
+void XPDFViewer::open(GString *fileName, int pageA, GString *destName) {
+  LinkDest *dest;
+  int pg, z;
+
+  if (!core->getDoc() || fileName->cmp(core->getDoc()->getFileName())) {
+    if (!loadFile(fileName, NULL, NULL)) {
+      return;
+    }
+  }
+  getPageAndDest(pageA, destName, &pg, &dest);
+  z = app->getFullScreen() ? zoomPage : core->getZoom();
+  if (dest) {
+    displayDest(dest, z, core->getRotate(), gTrue);
+    delete dest;
+  } else {
+    displayPage(pg, z, core->getRotate(), gTrue, gTrue);
+  }
+}
+
+void XPDFViewer::clear() {
+  char *title;
+  XmString s;
+
+  core->clear();
+
+  // set up title, number-of-pages display
+  title = app->getTitle() ? app->getTitle()->getCString()
+                          : (char *)xpdfAppName;
+  XtVaSetValues(win, XmNtitle, title, XmNiconName, title, NULL);
+  s = XmStringCreateLocalized("");
+  XtVaSetValues(pageNumText, XmNlabelString, s, NULL);
+  XmStringFree(s);
+  s = XmStringCreateLocalized(" of 0");
+  XtVaSetValues(pageCountLabel, XmNlabelString, s, NULL);
+  XmStringFree(s);
+
+  // disable buttons
+  XtVaSetValues(prevTenPageBtn, XmNsensitive, False, NULL);
+  XtVaSetValues(prevPageBtn, XmNsensitive, False, NULL);
+  XtVaSetValues(nextTenPageBtn, XmNsensitive, False, NULL);
+  XtVaSetValues(nextPageBtn, XmNsensitive, False, NULL);
+}
+
+//------------------------------------------------------------------------
+// load / display
+//------------------------------------------------------------------------
+
+GBool XPDFViewer::loadFile(GString *fileName, GString *ownerPassword,
+                          GString *userPassword) {
+  return core->loadFile(fileName, ownerPassword, userPassword) == errNone;
+}
+
+void XPDFViewer::reloadFile() {
+  int pg;
+
+  if (!core->getDoc()) {
+    return;
+  }
+  pg = core->getPageNum();
+  loadFile(core->getDoc()->getFileName());
+  if (pg > core->getDoc()->getNumPages()) {
+    pg = core->getDoc()->getNumPages();
+  }
+  displayPage(pg, core->getZoom(), core->getRotate(), gFalse, gFalse);
+}
+
+void XPDFViewer::displayPage(int pageA, int zoomA, int rotateA,
+                            GBool scrollToTop, GBool addToHist) {
+  core->displayPage(pageA, zoomA, rotateA, scrollToTop, addToHist);
+}
+
+void XPDFViewer::displayDest(LinkDest *dest, int zoomA, int rotateA,
+                            GBool addToHist) {
+  core->displayDest(dest, zoomA, rotateA, addToHist);
+}
+
+void XPDFViewer::getPageAndDest(int pageA, GString *destName,
+                               int *pageOut, LinkDest **destOut) {
+  Ref pageRef;
+
+  // find the page number for a named destination
+  *pageOut = pageA;
+  *destOut = NULL;
+  if (destName && (*destOut = core->getDoc()->findDest(destName))) {
+    if ((*destOut)->isPageRef()) {
+      pageRef = (*destOut)->getPageRef();
+      *pageOut = core->getDoc()->findPage(pageRef.num, pageRef.gen);
+    } else {
+      *pageOut = (*destOut)->getPageNum();
+    }
+  }
+
+  if (*pageOut <= 0) {
+    *pageOut = 1;
+  }
+  if (*pageOut > core->getDoc()->getNumPages()) {
+    *pageOut = core->getDoc()->getNumPages();
+  }
+}
+
+//------------------------------------------------------------------------
+// password dialog
+//------------------------------------------------------------------------
+
+GString *XPDFViewer::reqPasswordCbk(void *data, GBool again) {
+  XPDFViewer *viewer = (XPDFViewer *)data;
+
+  viewer->getPassword(again);
+  return viewer->password;
+}
+
+//------------------------------------------------------------------------
+// actions
+//------------------------------------------------------------------------
+
+void XPDFViewer::actionCbk(void *data, char *action) {
+  XPDFViewer *viewer = (XPDFViewer *)data;
+
+  if (!strcmp(action, "Quit")) {
+    viewer->app->quit();
+  }
+}
+
+//------------------------------------------------------------------------
+// keyboard/mouse input
+//------------------------------------------------------------------------
+
+void XPDFViewer::keyPressCbk(void *data, char *s, KeySym key,
+                            Guint modifiers) {
+  XPDFViewer *viewer = (XPDFViewer *)data;
+  int z;
+
+  if (s[0]) {
+    switch (s[0]) {
+    case 'O':
+    case 'o':
+      viewer->mapOpenDialog(gFalse);
+      break;
+    case 'R':
+    case 'r':
+      viewer->reloadFile();
+      break;
+    case 'F':
+    case 'f':
+    case '\006':               // ctrl-F
+      if (viewer->core->getDoc()) {
+       XtManageChild(viewer->findDialog);
+      }
+      break;
+    case '\007':               // ctrl-G
+      if (viewer->core->getDoc()) {
+       XPDFViewer::findFindCbk(None, viewer, NULL);
+      }
+      break;
+    case '\020':               // ctrl-P
+      if (viewer->core->getDoc()) {
+       XtManageChild(viewer->printDialog);
+      }
+      break;
+    case 'N':
+    case 'n':
+      viewer->core->gotoNextPage(1, !(modifiers & Mod5Mask));
+      break;
+    case 'P':
+    case 'p':
+      viewer->core->gotoPrevPage(1, !(modifiers & Mod5Mask), gFalse);
+      break;
+    case ' ':
+      if (viewer->app->getFullScreen()) {
+       viewer->core->gotoNextPage(1, gTrue);
+      } else {
+       viewer->core->scrollPageDown();
+      }
+      break;
+    case '\b':                 // bs
+    case '\177':               // del
+      if (viewer->app->getFullScreen()) {
+       viewer->core->gotoPrevPage(1, gTrue, gFalse);
+      } else {
+       viewer->core->scrollPageUp();
+      }
+      break;
+    case 'v':
+      viewer->core->goForward();
+      break;
+    case 'b':
+      viewer->core->goBackward();
+      break;
+    case 'g':
+      if (!viewer->app->getFullScreen()) {
+       XmTextFieldSetSelection(
+            viewer->pageNumText, 0,
+           strlen(XmTextFieldGetString(viewer->pageNumText)),
+           XtLastTimestampProcessed(viewer->display));
+       XmProcessTraversal(viewer->pageNumText, XmTRAVERSE_CURRENT);
+      }
+      break;
+    case 'h':                  // vi-style left
+      if (!viewer->app->getFullScreen() && viewer->app->getViKeys()) {
+       viewer->core->scrollLeft();
+      }
+      break;
+    case 'l':                  // vi-style right
+      if (!viewer->app->getFullScreen() && viewer->app->getViKeys()) {
+       viewer->core->scrollRight();
+      }
+      break;
+    case 'k':                  // vi-style up
+      if (!viewer->app->getFullScreen() && viewer->app->getViKeys()) {
+       viewer->core->scrollUp();
+      }
+      break;
+    case 'j':                  // vi-style down
+      if (!viewer->app->getFullScreen() && viewer->app->getViKeys()) {
+       viewer->core->scrollDown();
+      }
+      break;
+    case '0':
+      if (!viewer->app->getFullScreen() &&
+         viewer->core->getZoom() != defZoom) {
+       XtVaSetValues(viewer->zoomMenu,
+                     XmNmenuHistory, viewer->getZoomMenuBtn(defZoom),
+                     NULL);
+       viewer->displayPage(viewer->core->getPageNum(), defZoom,
+                           viewer->core->getRotate(), gTrue, gFalse);
+      }
+      break;
+    case '+':
+      if (!viewer->app->getFullScreen() &&
+         viewer->core->getZoom() >= minZoom &&
+         viewer->core->getZoom() < maxZoom) {
+       z = viewer->core->getZoom() + 1;
+       XtVaSetValues(viewer->zoomMenu,
+                     XmNmenuHistory, viewer->getZoomMenuBtn(z),
+                     NULL);
+       viewer->displayPage(viewer->core->getPageNum(), z,
+                           viewer->core->getRotate(), gTrue, gFalse);
+      }
+      break;
+    case '-':
+      if (!viewer->app->getFullScreen() &&
+         viewer->core->getZoom() > minZoom &&
+         viewer->core->getZoom() <= maxZoom) {
+       z = viewer->core->getZoom() - 1;
+       XtVaSetValues(viewer->zoomMenu,
+                     XmNmenuHistory, viewer->getZoomMenuBtn(z),
+                     NULL);
+       viewer->displayPage(viewer->core->getPageNum(), z,
+                           viewer->core->getRotate(), gTrue, gFalse);
+      }
+      break;
+    case 'z':
+      if (!viewer->app->getFullScreen() &&
+         viewer->core->getZoom() != zoomPage) {
+       XtVaSetValues(viewer->zoomMenu,
+                     XmNmenuHistory, viewer->getZoomMenuBtn(zoomPage),
+                     NULL);
+       viewer->displayPage(viewer->core->getPageNum(), zoomPage,
+                           viewer->core->getRotate(), gTrue, gFalse);
+      }
+      break;
+    case 'w':
+      if (!viewer->app->getFullScreen() &&
+         viewer->core->getZoom() != zoomWidth) {
+       XtVaSetValues(viewer->zoomMenu,
+                     XmNmenuHistory, viewer->getZoomMenuBtn(zoomWidth),
+                     NULL);
+       viewer->displayPage(viewer->core->getPageNum(), zoomWidth,
+                           viewer->core->getRotate(), gTrue, gFalse);
+      }
+      break;
+    case '\014':               // ^L
+      viewer->displayPage(viewer->core->getPageNum(), viewer->core->getZoom(),
+                         viewer->core->getRotate(), gFalse, gFalse);
+      break;
+    case '\027':               // ^W
+      viewer->app->close(viewer, gFalse);
+      break;
+    case '?':
+      XtManageChild(viewer->aboutDialog);
+      break;
+    case 'Q':
+    case 'q':
+      viewer->app->quit();
+      break;
+    }
+  }
+}
+
+void XPDFViewer::mouseCbk(void *data, XEvent *event) {
+  XPDFViewer *viewer = (XPDFViewer *)data;
+
+  if (event->type == ButtonPress && event->xbutton.button == 3) {
+    XmMenuPosition(viewer->popupMenu, &event->xbutton);
+    XtManageChild(viewer->popupMenu);
+  }
+}
+
+//------------------------------------------------------------------------
+// GUI code: main window
+//------------------------------------------------------------------------
+
+void XPDFViewer::initWindow() {
+  Widget btn, label, menuPane, lastBtn;
+#ifndef DISABLE_OUTLINE
+  Widget clipWin;
+#endif
+  Colormap colormap;
+  XColor xcol;
+  Arg args[20];
+  int n;
+  char *title;
+  XmString s, s2, emptyString;
+  char buf[16];
+  int i;
+
+  display = XtDisplay(app->getAppShell());
+  screenNum = XScreenNumberOfScreen(XtScreen(app->getAppShell()));
+
+  // private colormap
+  if (app->getInstallCmap()) {
+    XtVaGetValues(app->getAppShell(), XmNcolormap, &colormap, NULL);
+    // ensure that BlackPixel and WhitePixel are reserved in the
+    // new colormap
+    xcol.red = xcol.green = xcol.blue = 0;
+    XAllocColor(display, colormap, &xcol);
+    xcol.red = xcol.green = xcol.blue = 65535;
+    XAllocColor(display, colormap, &xcol);
+    colormap = XCopyColormapAndFree(display, colormap);
+  }
+
+  // top-level window
+  n = 0;
+  title = app->getTitle() ? app->getTitle()->getCString()
+                          : (char *)xpdfAppName;
+  XtSetArg(args[n], XmNtitle, title); ++n;
+  XtSetArg(args[n], XmNiconName, title); ++n;
+  XtSetArg(args[n], XmNminWidth, 100); ++n;
+  XtSetArg(args[n], XmNminHeight, 100); ++n;
+  XtSetArg(args[n], XmNbaseWidth, 0); ++n;
+  XtSetArg(args[n], XmNbaseHeight, 0); ++n;
+  XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); ++n;
+  if (app->getFullScreen()) {
+    XtSetArg(args[n], XmNmwmDecorations, 0); ++n;
+    XtSetArg(args[n], XmNgeometry, "+0+0"); ++n;
+  } else if (app->getGeometry()) {
+    XtSetArg(args[n], XmNgeometry, app->getGeometry()->getCString()); ++n;
+  }
+  win = XtCreatePopupShell("win", topLevelShellWidgetClass,
+                          app->getAppShell(), args, n);
+  if (app->getInstallCmap()) {
+    XtVaSetValues(win, XmNcolormap, colormap, NULL);
+  }
+  XmAddWMProtocolCallback(win, XInternAtom(display, "WM_DELETE_WINDOW", False),
+                         &closeMsgCbk, this);
+
+  // form
+  n = 0;
+  form = XmCreateForm(win, "form", args, n);
+  XtManageChild(form);
+
+  // toolbar
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  toolBar = XmCreateForm(form, "toolBar", args, n);
+  XtManageChild(toolBar);
+
+  // create an empty string -- this is used for buttons that will get
+  // pixmaps later
+  emptyString = XmStringCreateLocalized("");
+
+  // page movement buttons
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
+  XtSetArg(args[n], XmNsensitive, False); ++n;
+  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
+  backBtn = XmCreatePushButton(toolBar, "back", args, n);
+  XtManageChild(backBtn);
+  XtAddCallback(backBtn, XmNactivateCallback,
+               &backCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, backBtn); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
+  XtSetArg(args[n], XmNsensitive, False); ++n;
+  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
+  prevTenPageBtn = XmCreatePushButton(toolBar, "prevTenPage", args, n);
+  XtManageChild(prevTenPageBtn);
+  XtAddCallback(prevTenPageBtn, XmNactivateCallback,
+               &prevTenPageCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, prevTenPageBtn); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
+  XtSetArg(args[n], XmNsensitive, False); ++n;
+  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
+  prevPageBtn = XmCreatePushButton(toolBar, "prevPage", args, n);
+  XtManageChild(prevPageBtn);
+  XtAddCallback(prevPageBtn, XmNactivateCallback,
+               &prevPageCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, prevPageBtn); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
+  XtSetArg(args[n], XmNsensitive, False); ++n;
+  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
+  nextPageBtn = XmCreatePushButton(toolBar, "nextPage", args, n);
+  XtManageChild(nextPageBtn);
+  XtAddCallback(nextPageBtn, XmNactivateCallback,
+               &nextPageCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, nextPageBtn); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
+  XtSetArg(args[n], XmNsensitive, False); ++n;
+  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
+  nextTenPageBtn = XmCreatePushButton(toolBar, "nextTenPage", args, n);
+  XtManageChild(nextTenPageBtn);
+  XtAddCallback(nextTenPageBtn, XmNactivateCallback,
+               &nextTenPageCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, nextTenPageBtn); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
+  XtSetArg(args[n], XmNsensitive, False); ++n;
+  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
+  forwardBtn = XmCreatePushButton(toolBar, "forward", args, n);
+  XtManageChild(forwardBtn);
+  XtAddCallback(forwardBtn, XmNactivateCallback,
+               &forwardCbk, (XtPointer)this);
+
+  // page number display
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, forwardBtn); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  s = XmStringCreateLocalized("Page ");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  label = XmCreateLabel(toolBar, "pageLabel", args, n);
+  XmStringFree(s);
+  XtManageChild(label);
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, label); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 3); ++n;
+  XtSetArg(args[n], XmNmarginHeight, 3); ++n;
+  XtSetArg(args[n], XmNcolumns, 5); ++n;
+  pageNumText = XmCreateTextField(toolBar, "pageNum", args, n);
+  XtManageChild(pageNumText);
+  XtAddCallback(pageNumText, XmNactivateCallback,
+               &pageNumCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, pageNumText); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  s = XmStringCreateLocalized(" of 00000");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); ++n;
+  XtSetArg(args[n], XmNrecomputeSize, False); ++n;
+  pageCountLabel = XmCreateLabel(toolBar, "pageCountLabel", args, n);
+  XmStringFree(s);
+  XtManageChild(pageCountLabel);
+  s = XmStringCreateLocalized(" of 0");
+  XtVaSetValues(pageCountLabel, XmNlabelString, s, NULL);
+  XmStringFree(s);
+
+  // zoom menu
+  n = 0;
+  menuPane = XmCreatePulldownMenu(toolBar, "zoomMenuPane", args, n);
+  for (i = minZoom; i <= maxZoom; ++i) {
+    n = 0;
+    sprintf(buf, "%s%d", i > 0 ? "+" : "", i);
+    s = XmStringCreateLocalized(buf);
+    XtSetArg(args[n], XmNlabelString, s); ++n;
+    XtSetArg(args[n], XmNuserData, (XtPointer)i); ++n;
+    sprintf(buf, "zoom%s%d", i < 0 ? "M" : "", i < 0 ? -i : i);
+    btn = XmCreatePushButton(menuPane, buf, args, n);
+    XmStringFree(s);
+    XtManageChild(btn);
+    XtAddCallback(btn, XmNactivateCallback,
+                 &zoomMenuCbk, (XtPointer)this);
+    zoomMenuBtns[i - minZoom] = btn;
+  }
+  n = 0;
+  s = XmStringCreateLocalized("fit page");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  XtSetArg(args[n], XmNuserData, (XtPointer)zoomPage); ++n;
+  btn = XmCreatePushButton(menuPane, "zoomPage", args, n);
+  XmStringFree(s);
+  XtManageChild(btn);
+  XtAddCallback(btn, XmNactivateCallback,
+               &zoomMenuCbk, (XtPointer)this);
+  zoomMenuBtns[maxZoom - minZoom + 1] = btn;
+  n = 0;
+  s = XmStringCreateLocalized("fit width");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  XtSetArg(args[n], XmNuserData, (XtPointer)zoomWidth); ++n;
+  btn = XmCreatePushButton(menuPane, "zoomWidth", args, n);
+  XmStringFree(s);
+  XtManageChild(btn);
+  XtAddCallback(btn, XmNactivateCallback,
+               &zoomMenuCbk, (XtPointer)this);
+  zoomMenuBtns[maxZoom - minZoom + 2] = btn;
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, pageCountLabel); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 0); ++n;
+  XtSetArg(args[n], XmNmarginHeight, 0); ++n;
+  XtSetArg(args[n], XmNsubMenuId, menuPane); ++n;
+  zoomMenu = XmCreateOptionMenu(toolBar, "zoomMenu", args, n);
+  XtManageChild(zoomMenu);
+
+  // find/print/about buttons
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, zoomMenu); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
+  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
+  findBtn = XmCreatePushButton(toolBar, "find", args, n);
+  XtManageChild(findBtn);
+  XtAddCallback(findBtn, XmNactivateCallback,
+               &findCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, findBtn); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
+  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
+  printBtn = XmCreatePushButton(toolBar, "print", args, n);
+  XtManageChild(printBtn);
+  XtAddCallback(printBtn, XmNactivateCallback,
+               &printCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, printBtn); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
+  XtSetArg(args[n], XmNlabelString, emptyString); ++n;
+  aboutBtn = XmCreatePushButton(toolBar, "about", args, n);
+  XtManageChild(aboutBtn);
+  XtAddCallback(aboutBtn, XmNactivateCallback,
+               &aboutCbk, (XtPointer)this);
+  lastBtn = aboutBtn;
+
+  // quit button
+  n = 0;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNmarginWidth, 6); ++n;
+  s = XmStringCreateLocalized("Quit");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  quitBtn = XmCreatePushButton(toolBar, "quit", args, n);
+  XmStringFree(s);
+  XtManageChild(quitBtn);
+  XtAddCallback(quitBtn, XmNactivateCallback,
+               &quitCbk, (XtPointer)this);
+
+  // link label
+  n = 0;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNleftWidget, lastBtn); ++n;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNrightWidget, btn); ++n;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  s = XmStringCreateLocalized("");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  XtSetArg(args[n], XmNrecomputeSize, True); ++n;
+  XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); ++n;
+  linkLabel = XmCreateLabel(toolBar, "linkLabel", args, n);
+  XmStringFree(s);
+  XtManageChild(linkLabel);
+
+#ifndef DISABLE_OUTLINE
+  if (app->getFullScreen()) {
+#endif
+
+    // core
+    core = new XPDFCore(win, form, app->getPaperColor(),
+                       app->getFullScreen(), app->getReverseVideo(),
+                       app->getInstallCmap(), app->getRGBCubeSize());
+    core->setUpdateCbk(&updateCbk, this);
+    core->setActionCbk(&actionCbk, this);
+    core->setKeyPressCbk(&keyPressCbk, this);
+    core->setMouseCbk(&mouseCbk, this);
+    core->setReqPasswordCbk(&reqPasswordCbk, this);
+    n = 0;
+    XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n;
+    XtSetArg(args[n], XmNbottomWidget, toolBar); ++n;
+    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+    XtSetValues(core->getWidget(), args, n);
+
+#ifndef DISABLE_OUTLINE
+  } else {
+
+    // paned window
+    n = 0;
+    XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n;
+    XtSetArg(args[n], XmNbottomWidget, toolBar); ++n;
+    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+    XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
+    panedWin = XmCreatePanedWindow(form, "panedWin", args, n);
+    XtManageChild(panedWin);
+
+    // scrolled window for outline container
+    n = 0;
+    XtSetArg(args[n], XmNpositionIndex, 0); ++n;
+    XtSetArg(args[n], XmNallowResize, True); ++n;
+    XtSetArg(args[n], XmNpaneMinimum, 1); ++n;
+    XtSetArg(args[n], XmNpaneMaximum, 10000); ++n;
+    XtSetArg(args[n], XmNwidth, 1); ++n;
+    XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n;
+    outlineScroll = XmCreateScrolledWindow(panedWin, "outlineScroll", args, n);
+    XtManageChild(outlineScroll);
+    XtVaGetValues(outlineScroll, XmNclipWindow, &clipWin, NULL);
+    XtVaSetValues(clipWin, XmNbackground, app->getPaperColor(), NULL);
+
+    // outline tree
+    n = 0;
+    XtSetArg(args[n], XmNbackground, app->getPaperColor()); ++n;
+    outlineTree = XPDFCreateTree(outlineScroll, "outlineTree", args, n);
+    XtManageChild(outlineTree);
+    XtAddCallback(outlineTree, XPDFNselectionCallback, &outlineSelectCbk,
+                 (XtPointer)this);
+
+    // core
+    core = new XPDFCore(win, panedWin, app->getPaperColor(),
+                       app->getFullScreen(), app->getReverseVideo(),
+                       app->getInstallCmap(), app->getRGBCubeSize());
+    core->setUpdateCbk(&updateCbk, this);
+    core->setActionCbk(&actionCbk, this);
+    core->setKeyPressCbk(&keyPressCbk, this);
+    core->setMouseCbk(&mouseCbk, this);
+    core->setReqPasswordCbk(&reqPasswordCbk, this);
+    n = 0;
+    XtSetArg(args[n], XmNpositionIndex, 1); ++n;
+    XtSetArg(args[n], XmNallowResize, True); ++n;
+    XtSetArg(args[n], XmNpaneMinimum, 1); ++n;
+    XtSetArg(args[n], XmNpaneMaximum, 10000); ++n;
+    XtSetValues(core->getWidget(), args, n);
+  }
+#endif
+
+  // set the zoom menu to match the initial zoom setting
+  XtVaSetValues(zoomMenu, XmNmenuHistory,
+               getZoomMenuBtn(core->getZoom()), NULL);
+
+  // set traversal order
+  XtVaSetValues(core->getDrawAreaWidget(),
+               XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, NULL);
+  XtVaSetValues(backBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
+               NULL);
+  XtVaSetValues(prevTenPageBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
+               NULL);
+  XtVaSetValues(prevPageBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
+               NULL);
+  XtVaSetValues(nextPageBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
+               NULL);
+  XtVaSetValues(nextTenPageBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
+               NULL);
+  XtVaSetValues(forwardBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
+               NULL);
+  XtVaSetValues(pageNumText, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
+               NULL);
+  XtVaSetValues(zoomMenu, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
+               NULL);
+  XtVaSetValues(findBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
+               NULL);
+  XtVaSetValues(printBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
+               NULL);
+  XtVaSetValues(aboutBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
+               NULL);
+  XtVaSetValues(quitBtn, XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
+               NULL);
+
+  // popup menu
+  n = 0;
+  XtSetArg(args[n], XmNmenuPost, "<Btn3Down>"); ++n;
+  popupMenu = XmCreatePopupMenu(core->getDrawAreaWidget(), "popupMenu",
+                               args, n);
+  n = 0;
+  s = XmStringCreateLocalized("Open...");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  s2 = XmStringCreateLocalized("O");
+  XtSetArg(args[n], XmNacceleratorText, s2); ++n;
+  btn = XmCreatePushButton(popupMenu, "open", args, n);
+  XmStringFree(s);
+  XmStringFree(s2);
+  XtManageChild(btn);
+  XtAddCallback(btn, XmNactivateCallback,
+               &openCbk, (XtPointer)this);
+  n = 0;
+  s = XmStringCreateLocalized("Open in new window...");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  btn = XmCreatePushButton(popupMenu, "openInNewWindow", args, n);
+  XmStringFree(s);
+  XtManageChild(btn);
+  XtAddCallback(btn, XmNactivateCallback,
+               &openInNewWindowCbk, (XtPointer)this);
+  n = 0;
+  s = XmStringCreateLocalized("Reload");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  s2 = XmStringCreateLocalized("R");
+  XtSetArg(args[n], XmNacceleratorText, s2); ++n;
+  btn = XmCreatePushButton(popupMenu, "reload", args, n);
+  XmStringFree(s);
+  XmStringFree(s2);
+  XtManageChild(btn);
+  XtAddCallback(btn, XmNactivateCallback,
+               &reloadCbk, (XtPointer)this);
+  n = 0;
+  s = XmStringCreateLocalized("Save as...");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  btn = XmCreatePushButton(popupMenu, "saveAs", args, n);
+  XmStringFree(s);
+  XtManageChild(btn);
+  XtAddCallback(btn, XmNactivateCallback,
+               &saveAsCbk, (XtPointer)this);
+  n = 0;
+  btn = XmCreateSeparator(popupMenu, "sep1", args, n);
+  XtManageChild(btn);
+  n = 0;
+  s = XmStringCreateLocalized("Rotate counterclockwise");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  btn = XmCreatePushButton(popupMenu, "rotateCCW", args, n);
+  XmStringFree(s);
+  XtManageChild(btn);
+  XtAddCallback(btn, XmNactivateCallback,
+               &rotateCCWCbk, (XtPointer)this);
+  n = 0;
+  s = XmStringCreateLocalized("Rotate clockwise");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  btn = XmCreatePushButton(popupMenu, "rotateCW", args, n);
+  XmStringFree(s);
+  XtManageChild(btn);
+  XtAddCallback(btn, XmNactivateCallback,
+               &rotateCWCbk, (XtPointer)this);
+  n = 0;
+  btn = XmCreateSeparator(popupMenu, "sep2", args, n);
+  XtManageChild(btn);
+  n = 0;
+  s = XmStringCreateLocalized("Close");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  s2 = XmStringCreateLocalized("Ctrl+W");
+  XtSetArg(args[n], XmNacceleratorText, s2); ++n;
+  btn = XmCreatePushButton(popupMenu, "close", args, n);
+  XmStringFree(s);
+  XmStringFree(s2);
+  XtManageChild(btn);
+  XtAddCallback(btn, XmNactivateCallback,
+               &closeCbk, (XtPointer)this);
+  n = 0;
+  s = XmStringCreateLocalized("Quit");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  s2 = XmStringCreateLocalized("Q");
+  XtSetArg(args[n], XmNacceleratorText, s2); ++n;
+  btn = XmCreatePushButton(popupMenu, "quit", args, n);
+  XmStringFree(s);
+  XmStringFree(s2);
+  XtManageChild(btn);
+  XtAddCallback(btn, XmNactivateCallback,
+               &quitCbk, (XtPointer)this);
+
+  XmStringFree(emptyString);
+}
+
+void XPDFViewer::mapWindow() {
+#ifdef HAVE_X11_XPM_H
+  Pixmap iconPixmap;
+#endif
+  int depth;
+  Pixel bg, arm;
+
+  // show the window
+  XtPopup(win, XtGrabNone);
+  core->takeFocus();
+
+  // create the icon
+#ifdef HAVE_X11_XPM_H
+  if (XpmCreatePixmapFromData(display, XtWindow(win), xpdfIcon,
+                             &iconPixmap, NULL, NULL) == XpmSuccess) {
+    XtVaSetValues(win, XmNiconPixmap, iconPixmap, NULL);
+  }
+#endif
+
+  // set button bitmaps (must be done after the window is mapped)
+  XtVaGetValues(backBtn, XmNdepth, &depth,
+               XmNbackground, &bg, XmNarmColor, &arm, NULL);
+  XtVaSetValues(backBtn, XmNlabelType, XmPIXMAP,
+               XmNlabelPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)backArrow_bits,
+                                           backArrow_width,
+                                           backArrow_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               XmNarmPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)backArrow_bits,
+                                           backArrow_width,
+                                           backArrow_height,
+                                           BlackPixel(display, screenNum),
+                                           arm, depth),
+               XmNlabelInsensitivePixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)backArrowDis_bits,
+                                           backArrowDis_width,
+                                           backArrowDis_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               NULL);
+  XtVaSetValues(prevTenPageBtn, XmNlabelType, XmPIXMAP,
+               XmNlabelPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)dblLeftArrow_bits,
+                                           dblLeftArrow_width,
+                                           dblLeftArrow_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               XmNarmPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)dblLeftArrow_bits,
+                                           dblLeftArrow_width,
+                                           dblLeftArrow_height,
+                                           BlackPixel(display, screenNum),
+                                           arm, depth),
+               XmNlabelInsensitivePixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)dblLeftArrowDis_bits,
+                                           dblLeftArrowDis_width,
+                                           dblLeftArrowDis_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               NULL);
+  XtVaSetValues(prevPageBtn, XmNlabelType, XmPIXMAP,
+               XmNlabelPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)leftArrow_bits,
+                                           leftArrow_width,
+                                           leftArrow_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               XmNarmPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)leftArrow_bits,
+                                           leftArrow_width,
+                                           leftArrow_height,
+                                           BlackPixel(display, screenNum),
+                                           arm, depth),
+               XmNlabelInsensitivePixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)leftArrowDis_bits,
+                                           leftArrowDis_width,
+                                           leftArrowDis_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               NULL);
+  XtVaSetValues(nextPageBtn, XmNlabelType, XmPIXMAP,
+               XmNlabelPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)rightArrow_bits,
+                                           rightArrow_width,
+                                           rightArrow_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               XmNarmPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)rightArrow_bits,
+                                           rightArrow_width,
+                                           rightArrow_height,
+                                           BlackPixel(display, screenNum),
+                                           arm, depth),
+               XmNlabelInsensitivePixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)rightArrowDis_bits,
+                                           rightArrowDis_width,
+                                           rightArrowDis_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               NULL);
+  XtVaSetValues(nextTenPageBtn, XmNlabelType, XmPIXMAP,
+               XmNlabelPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)dblRightArrow_bits,
+                                           dblRightArrow_width,
+                                           dblRightArrow_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               XmNarmPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)dblRightArrow_bits,
+                                           dblRightArrow_width,
+                                           dblRightArrow_height,
+                                           BlackPixel(display, screenNum),
+                                           arm, depth),
+               XmNlabelInsensitivePixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)dblRightArrowDis_bits,
+                                           dblRightArrowDis_width,
+                                           dblRightArrowDis_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               NULL);
+  XtVaSetValues(forwardBtn, XmNlabelType, XmPIXMAP,
+               XmNlabelPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)forwardArrow_bits,
+                                           forwardArrow_width,
+                                           forwardArrow_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               XmNarmPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)forwardArrow_bits,
+                                           forwardArrow_width,
+                                           forwardArrow_height,
+                                           BlackPixel(display, screenNum),
+                                           arm, depth),
+               XmNlabelInsensitivePixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)forwardArrowDis_bits,
+                                           forwardArrowDis_width,
+                                           forwardArrowDis_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               NULL);
+  XtVaSetValues(findBtn, XmNlabelType, XmPIXMAP,
+               XmNlabelPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)find_bits,
+                                           find_width,
+                                           find_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               XmNarmPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)find_bits,
+                                           find_width,
+                                           find_height,
+                                           BlackPixel(display, screenNum),
+                                           arm, depth),
+               XmNlabelInsensitivePixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)findDis_bits,
+                                           findDis_width,
+                                           findDis_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               NULL);
+  XtVaSetValues(printBtn, XmNlabelType, XmPIXMAP,
+               XmNlabelPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)print_bits,
+                                           print_width,
+                                           print_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               XmNarmPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)print_bits,
+                                           print_width,
+                                           print_height,
+                                           BlackPixel(display, screenNum),
+                                           arm, depth),
+               XmNlabelInsensitivePixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)printDis_bits,
+                                           printDis_width,
+                                           printDis_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               NULL);
+  XtVaSetValues(aboutBtn, XmNlabelType, XmPIXMAP,
+               XmNlabelPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)about_bits,
+                                           about_width,
+                                           about_height,
+                                           BlackPixel(display, screenNum),
+                                           bg, depth),
+               XmNarmPixmap,
+               XCreatePixmapFromBitmapData(display, XtWindow(toolBar),
+                                           (char *)about_bits,
+                                           about_width,
+                                           about_height,
+                                           BlackPixel(display, screenNum),
+                                           arm, depth),
+               NULL);
+}
+
+void XPDFViewer::closeWindow() {
+  XtPopdown(win);
+  XtDestroyWidget(win);
+}
+
+Widget XPDFViewer::getZoomMenuBtn(int z) {
+  if (z >= minZoom && z <= maxZoom) {
+    return zoomMenuBtns[z - minZoom];
+  }
+  if (z == zoomPage) {
+    return zoomMenuBtns[maxZoom - minZoom + 1];
+  }
+  if (z == zoomWidth) {
+    return zoomMenuBtns[maxZoom - minZoom + 2];
+  }
+  return zoomMenuBtns[0];
+}
+
+void XPDFViewer::prevPageCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->core->gotoPrevPage(1, gTrue, gFalse);
+  viewer->core->takeFocus();
+}
+
+void XPDFViewer::prevTenPageCbk(Widget widget, XtPointer ptr,
+                               XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->core->gotoPrevPage(10, gTrue, gFalse);
+  viewer->core->takeFocus();
+}
+
+void XPDFViewer::nextPageCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->core->gotoNextPage(1, gTrue);
+  viewer->core->takeFocus();
+}
+
+void XPDFViewer::nextTenPageCbk(Widget widget, XtPointer ptr,
+                               XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->core->gotoNextPage(10, gTrue);
+  viewer->core->takeFocus();
+}
+
+void XPDFViewer::backCbk(Widget widget, XtPointer ptr,
+                        XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->core->goBackward();
+  viewer->core->takeFocus();
+}
+
+void XPDFViewer::forwardCbk(Widget widget, XtPointer ptr,
+                           XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->core->goForward();
+  viewer->core->takeFocus();
+}
+
+void XPDFViewer::zoomMenuCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+  XtPointer userData;
+
+  XtVaGetValues(widget, XmNuserData, &userData, NULL);
+  if ((int)userData != viewer->core->getZoom()) {
+    viewer->displayPage(viewer->core->getPageNum(), (int)userData,
+                       viewer->core->getRotate(), gTrue, gFalse);
+  }
+  viewer->core->takeFocus();
+}
+
+void XPDFViewer::findCbk(Widget widget, XtPointer ptr,
+                        XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  if (!viewer->core->getDoc()) {
+    return;
+  }
+  XtManageChild(viewer->findDialog);
+}
+
+void XPDFViewer::printCbk(Widget widget, XtPointer ptr,
+                         XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  if (!viewer->core->getDoc()) {
+    return;
+  }
+  XtManageChild(viewer->printDialog);
+}
+
+void XPDFViewer::aboutCbk(Widget widget, XtPointer ptr,
+                         XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  XtManageChild(viewer->aboutDialog);
+}
+
+void XPDFViewer::quitCbk(Widget widget, XtPointer ptr,
+                        XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->app->quit();
+}
+
+void XPDFViewer::openCbk(Widget widget, XtPointer ptr,
+                        XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->mapOpenDialog(gFalse);
+}
+
+void XPDFViewer::openInNewWindowCbk(Widget widget, XtPointer ptr,
+                                   XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->mapOpenDialog(gTrue);
+}
+
+void XPDFViewer::reloadCbk(Widget widget, XtPointer ptr,
+                        XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->reloadFile();
+}
+
+void XPDFViewer::saveAsCbk(Widget widget, XtPointer ptr,
+                          XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  if (!viewer->core->getDoc()) {
+    return;
+  }
+  viewer->mapSaveAsDialog();
+}
+
+void XPDFViewer::rotateCCWCbk(Widget widget, XtPointer ptr,
+                             XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+  int r;
+
+  r = viewer->core->getRotate();
+  r = (r == 0) ? 270 : r - 90;
+  viewer->displayPage(viewer->core->getPageNum(), viewer->core->getZoom(),
+                     r, gTrue, gFalse);
+}
+
+void XPDFViewer::rotateCWCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+  int r;
+
+  r = viewer->core->getRotate();
+  r = (r == 270) ? 0 : r + 90;
+  viewer->displayPage(viewer->core->getPageNum(), viewer->core->getZoom(),
+                     r, gTrue, gFalse);
+}
+
+void XPDFViewer::closeCbk(Widget widget, XtPointer ptr,
+                         XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->app->close(viewer, gFalse);
+}
+
+void XPDFViewer::closeMsgCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->app->close(viewer, gTrue);
+}
+
+void XPDFViewer::pageNumCbk(Widget widget, XtPointer ptr,
+                           XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+  char *s, *p;
+  int pg;
+  char buf[20];
+
+  if (!viewer->core->getDoc()) {
+    goto err;
+  }
+  s = XmTextFieldGetString(viewer->pageNumText);
+  for (p = s; *p; ++p) {
+    if (!isdigit(*p)) {
+      goto err;
+    }
+  }
+  pg = atoi(s);
+  if (pg < 1 || pg > viewer->core->getDoc()->getNumPages()) {
+    goto err;
+  }
+  viewer->displayPage(pg, viewer->core->getZoom(),
+                     viewer->core->getRotate(), gFalse, gTrue);
+  viewer->core->takeFocus();
+  return;
+
+ err:
+  XBell(viewer->display, 0);
+  sprintf(buf, "%d", viewer->core->getPageNum());
+  XmTextFieldSetString(viewer->pageNumText, buf);
+}
+
+void XPDFViewer::updateCbk(void *data, GString *fileName,
+                          int pageNum, int numPages, char *linkLabel) {
+  XPDFViewer *viewer = (XPDFViewer *)data;
+  GString *title;
+  char buf[20];
+  XmString s;
+
+  if (fileName) {
+    if (!(title = viewer->app->getTitle())) {
+      title = (new GString(xpdfAppName))->append(": ")->append(fileName);
+    }
+    XtVaSetValues(viewer->win, XmNtitle, title->getCString(),
+                 XmNiconName, title->getCString(), NULL);
+    if (!viewer->app->getTitle()) {
+      delete title;
+    }
+#ifndef DISABLE_OUTLINE
+    if (!viewer->app->getFullScreen()) {
+      viewer->setupOutline();
+    }
+#endif
+    viewer->setupPrintDialog();
+  }
+
+  if (pageNum >= 0) {
+    s = XmStringCreateLocalized("");
+    XtVaSetValues(viewer->linkLabel, XmNlabelString, s, NULL);
+    XmStringFree(s);
+    sprintf(buf, "%d", pageNum);
+    XmTextFieldSetString(viewer->pageNumText, buf);
+    XtVaSetValues(viewer->prevTenPageBtn, XmNsensitive,
+                 pageNum > 1, NULL);
+    XtVaSetValues(viewer->prevPageBtn, XmNsensitive,
+                 pageNum > 1, NULL);
+    XtVaSetValues(viewer->nextTenPageBtn, XmNsensitive,
+                 pageNum < viewer->core->getDoc()->getNumPages(), NULL);
+    XtVaSetValues(viewer->nextPageBtn, XmNsensitive,
+                 pageNum < viewer->core->getDoc()->getNumPages(), NULL);
+    XtVaSetValues(viewer->backBtn, XmNsensitive,
+                 viewer->core->canGoBack(), NULL);
+    XtVaSetValues(viewer->forwardBtn, XmNsensitive,
+                 viewer->core->canGoForward(), NULL);
+  }
+
+  if (numPages >= 0) {
+    sprintf(buf, " of %d", numPages);
+    s = XmStringCreateLocalized(buf);
+    XtVaSetValues(viewer->pageCountLabel, XmNlabelString, s, NULL);
+    XmStringFree(s);
+  }
+
+  if (linkLabel) {
+    s = XmStringCreateLocalized(linkLabel);
+    XtVaSetValues(viewer->linkLabel, XmNlabelString, s, NULL);
+    XmStringFree(s);
+  }
+}
+
+
+//------------------------------------------------------------------------
+// GUI code: outline
+//------------------------------------------------------------------------
+
+#ifndef DISABLE_OUTLINE
+
+void XPDFViewer::setupOutline() {
+  GList *items;
+  UnicodeMap *uMap;
+  GString *enc;
+  int i;
+
+  // unmanage and destroy the old labels
+  if (outlineLabels) {
+    XtUnmanageChildren(outlineLabels, outlineLabelsLength);
+    for (i = 0; i < outlineLabelsLength; ++i) {
+      XtDestroyWidget(outlineLabels[i]);
+    }
+    gfree(outlineLabels);
+    outlineLabels = NULL;
+    outlineLabelsLength = outlineLabelsSize = 0;
+  }
+
+  // create the new labels
+  items = core->getDoc()->getOutline()->getItems();
+  if (items && items->getLength() > 0) {
+    enc = new GString("Latin1");
+    uMap = globalParams->getUnicodeMap(enc);
+    delete enc;
+    setupOutlineItems(items, NULL, uMap);
+    uMap->decRefCnt();
+  }
+
+  // manage the new labels
+  XtManageChildren(outlineLabels, outlineLabelsLength);
+}
+
+void XPDFViewer::setupOutlineItems(GList *items, Widget parent,
+                                  UnicodeMap *uMap) {
+  OutlineItem *item;
+  GList *kids;
+  Widget label;
+  Arg args[20];
+  GString *title;
+  char buf[8];
+  XmString s;
+  int i, j, n;
+
+  for (i = 0; i < items->getLength(); ++i) {
+    item = (OutlineItem *)items->get(i);
+    title = new GString();
+    for (j = 0; j < item->getTitleLength(); ++j) {
+      n = uMap->mapUnicode(item->getTitle()[j], buf, sizeof(buf));
+      title->append(buf, n);
+    }
+    n = 0;
+    XtSetArg(args[n], XPDFNentryPosition, i); ++n;
+    if (parent) {
+      XtSetArg(args[n], XPDFNentryParent, parent); ++n;
+    }
+    XtSetArg(args[n], XPDFNentryExpanded, item->isOpen()); ++n;
+    s = XmStringCreateLocalized(title->getCString());
+    delete title;
+    XtSetArg(args[n], XmNlabelString, s); ++n;
+    XtSetArg(args[n], XmNuserData, item); ++n;
+    XtSetArg(args[n], XmNmarginWidth, 0); ++n;
+    XtSetArg(args[n], XmNmarginHeight, 2); ++n;
+    XtSetArg(args[n], XmNshadowThickness, 0); ++n;
+    XtSetArg(args[n], XmNforeground,
+            app->getReverseVideo() ? WhitePixel(display, screenNum)
+                                   : BlackPixel(display, screenNum)); ++n;
+    XtSetArg(args[n], XmNbackground, app->getPaperColor()); ++n;
+    label = XmCreateLabelGadget(outlineTree, "label", args, n);
+    XmStringFree(s);
+    if (outlineLabelsLength == outlineLabelsSize) {
+      outlineLabelsSize += 64;
+      outlineLabels = (Widget *)grealloc(outlineLabels,
+                                        outlineLabelsSize * sizeof(Widget *));
+    }
+    outlineLabels[outlineLabelsLength++] = label;
+    item->open();
+    if ((kids = item->getKids())) {
+      setupOutlineItems(kids, label, uMap);
+    }
+  }
+}
+
+void XPDFViewer::outlineSelectCbk(Widget widget, XtPointer ptr,
+                                 XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+  XPDFTreeSelectCallbackStruct *data =
+      (XPDFTreeSelectCallbackStruct *)callData;
+  OutlineItem *item;
+
+  XtVaGetValues(data->selectedItem, XmNuserData, &item, NULL);
+  if (item) {
+    viewer->core->doAction(item->getAction());
+  }
+  viewer->core->takeFocus();
+}
+
+#endif // !DISABLE_OUTLINE
+
+//------------------------------------------------------------------------
+// GUI code: "about" dialog
+//------------------------------------------------------------------------
+
+void XPDFViewer::initAboutDialog() {
+  Widget scrolledWin, col, label, sep, closeBtn;
+  Arg args[20];
+  int n, i;
+  XmString s;
+  char buf[20];
+
+  //----- dialog
+  n = 0;
+  s = XmStringCreateLocalized(xpdfAppName ": About");
+  XtSetArg(args[n], XmNdialogTitle, s); ++n;
+  XtSetArg(args[n], XmNwidth, 450); ++n;
+  XtSetArg(args[n], XmNheight, 300); ++n;
+  aboutDialog = XmCreateFormDialog(win, "aboutDialog", args, n);
+  XmStringFree(s);
+
+  //----- "close" button
+  n = 0;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNrightOffset, 4); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
+  closeBtn = XmCreatePushButton(aboutDialog, "Close", args, n);
+  XtManageChild(closeBtn);
+  n = 0;
+  XtSetArg(args[n], XmNdefaultButton, closeBtn); ++n;
+  XtSetArg(args[n], XmNcancelButton, closeBtn); ++n;
+  XtSetValues(aboutDialog, args, n);
+
+  //----- scrolled window and RowColumn
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNbottomWidget, closeBtn); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n;
+  scrolledWin = XmCreateScrolledWindow(aboutDialog, "scrolledWin", args, n);
+  XtManageChild(scrolledWin);
+  n = 0;
+  XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
+  XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n;
+  col = XmCreateRowColumn(scrolledWin, "col", args, n);
+  XtManageChild(col);
+
+  //----- fonts
+  aboutBigFont = XmFontListAppendEntry(NULL,
+      XmFontListEntryCreate(XmFONTLIST_DEFAULT_TAG, XmFONT_IS_FONT,
+         XLoadQueryFont(display,
+             "-*-times-bold-i-normal--20-*-*-*-*-*-iso8859-1")));
+  aboutVersionFont = XmFontListAppendEntry(NULL,
+      XmFontListEntryCreate(XmFONTLIST_DEFAULT_TAG, XmFONT_IS_FONT,
+         XLoadQueryFont(display,
+             "-*-times-medium-r-normal--16-*-*-*-*-*-iso8859-1")));
+  aboutFixedFont = XmFontListAppendEntry(NULL,
+      XmFontListEntryCreate(XmFONTLIST_DEFAULT_TAG, XmFONT_IS_FONT,
+         XLoadQueryFont(display,
+             "-*-courier-medium-r-normal--12-*-*-*-*-*-iso8859-1")));
+
+  //----- heading
+  n = 0;
+  s = XmStringCreateLocalized("Xpdf");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  XtSetArg(args[n], XmNfontList, aboutBigFont); ++n;
+  label = XmCreateLabel(col, "h0", args, n);
+  XmStringFree(s);
+  XtManageChild(label);
+  n = 0;
+  s = XmStringCreateLocalized("Version " xpdfVersion);
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  XtSetArg(args[n], XmNfontList, aboutVersionFont); ++n;
+  label = XmCreateLabel(col, "h1", args, n);
+  XmStringFree(s);
+  XtManageChild(label);
+  n = 0;
+  s = XmStringCreateLocalized(xpdfCopyright);
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  XtSetArg(args[n], XmNfontList, aboutVersionFont); ++n;
+  label = XmCreateLabel(col, "h2", args, n);
+  XmStringFree(s);
+  XtManageChild(label);
+  n = 0;
+  s = XmStringCreateLocalized(" ");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  XtSetArg(args[n], XmNfontList, aboutVersionFont); ++n;
+  label = XmCreateLabel(col, "h3", args, n);
+  XmStringFree(s);
+  XtManageChild(label);
+  n = 0;
+  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
+  sep = XmCreateSeparator(col, "sep", args, n);
+  XtManageChild(sep);
+  n = 0;
+  s = XmStringCreateLocalized(" ");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  XtSetArg(args[n], XmNfontList, aboutVersionFont); ++n;
+  label = XmCreateLabel(col, "h4", args, n);
+  XmStringFree(s);
+  XtManageChild(label);
+  n = 0;
+
+  //----- text
+  for (i = 0; aboutWinText[i]; ++i) {
+    n = 0;
+    s = XmStringCreateLocalized(aboutWinText[i]);
+    XtSetArg(args[n], XmNlabelString, s); ++n;
+    XtSetArg(args[n], XmNfontList, aboutFixedFont); ++n;
+    sprintf(buf, "t%d", i);
+    label = XmCreateLabel(col, buf, args, n);
+    XtManageChild(label);
+    XmStringFree(s);
+  }
+}
+
+//------------------------------------------------------------------------
+// GUI code: "open" dialog
+//------------------------------------------------------------------------
+
+void XPDFViewer::initOpenDialog() {
+  Arg args[20];
+  int n;
+  XmString s1, s2, s3;
+
+  n = 0;
+  s1 = XmStringCreateLocalized("Open");
+  XtSetArg(args[n], XmNokLabelString, s1); ++n;
+  s2 = XmStringCreateLocalized("*.[Pp][Dd][Ff]");
+  XtSetArg(args[n], XmNpattern, s2); ++n;
+  s3 = XmStringCreateLocalized(xpdfAppName ": Open");
+  XtSetArg(args[n], XmNdialogTitle, s3); ++n;
+  XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
+  XtSetArg(args[n], XmNautoUnmanage, True); ++n;
+  openDialog = XmCreateFileSelectionDialog(win, "openDialog", args, n);
+  XmStringFree(s1);
+  XmStringFree(s2);
+  XmStringFree(s3);
+  XtUnmanageChild(XmFileSelectionBoxGetChild(openDialog,
+                                            XmDIALOG_HELP_BUTTON));
+  XtAddCallback(openDialog, XmNokCallback,
+               &openOkCbk, (XtPointer)this);
+}
+
+void XPDFViewer::setOpenDialogDir(char *dir) {
+  XmString s;
+
+  s = XmStringCreateLocalized(dir);
+  XtVaSetValues(openDialog, XmNdirectory, s, NULL);
+  XmStringFree(s);
+}
+
+void XPDFViewer::mapOpenDialog(GBool openInNewWindowA) {
+  openInNewWindow = openInNewWindowA;
+  XmFileSelectionDoSearch(openDialog, NULL);
+  XtManageChild(openDialog);
+}
+
+void XPDFViewer::openOkCbk(Widget widget, XtPointer ptr,
+                          XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+  XmFileSelectionBoxCallbackStruct *data =
+    (XmFileSelectionBoxCallbackStruct *)callData;
+  char *fileName;
+  XmStringContext context;
+  XmStringCharSet charSet;
+  XmStringDirection dir;
+  Boolean sep;
+  GString *fileNameStr;
+
+  XmStringInitContext(&context, data->value);
+  if (XmStringGetNextSegment(context, &fileName, &charSet, &dir, &sep)) {
+    fileNameStr = new GString(fileName);
+    if (viewer->openInNewWindow) {
+      viewer->app->open(fileNameStr);
+    } else {
+      if (viewer->loadFile(fileNameStr)) {
+       viewer->displayPage(1, viewer->core->getZoom(),
+                           viewer->core->getRotate(), gTrue, gTrue);
+      }
+    }
+    delete fileNameStr;
+    XtFree(charSet);
+    XtFree(fileName);
+  }
+  XmStringFreeContext(context);
+}
+
+//------------------------------------------------------------------------
+// GUI code: "find" dialog
+//------------------------------------------------------------------------
+
+void XPDFViewer::initFindDialog() {
+  Widget row1, label, okBtn, closeBtn;
+  Arg args[20];
+  int n;
+  XmString s;
+
+  //----- dialog
+  n = 0;
+  s = XmStringCreateLocalized(xpdfAppName ": Find");
+  XtSetArg(args[n], XmNdialogTitle, s); ++n;
+  XtSetArg(args[n], XmNnavigationType, XmNONE); ++n;
+  XtSetArg(args[n], XmNautoUnmanage, False); ++n;
+  findDialog = XmCreateFormDialog(win, "findDialog", args, n);
+  XmStringFree(s);
+
+  //----- top row: search string entry
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNtopOffset, 4); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
+  XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n;
+  row1 = XmCreateRowColumn(findDialog, "row1", args, n);
+  XtManageChild(row1);
+  n = 0;
+  s = XmStringCreateLocalized("Find text: ");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  label = XmCreateLabel(row1, "label", args, n);
+  XmStringFree(s);
+  XtManageChild(label);
+  n = 0;
+  XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
+  findText = XmCreateTextField(row1, "text", args, n);
+  XtManageChild(findText);
+
+  //----- "find" and "close" buttons
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, row1); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNleftOffset, 4); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
+  XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
+  okBtn = XmCreatePushButton(findDialog, "Find", args, n);
+  XtManageChild(okBtn);
+  XtAddCallback(okBtn, XmNactivateCallback,
+               &findFindCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, row1); ++n;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNrightOffset, 4); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
+  XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
+  closeBtn = XmCreatePushButton(findDialog, "Close", args, n);
+  XtManageChild(closeBtn);
+  XtAddCallback(closeBtn, XmNactivateCallback,
+               &findCloseCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNdefaultButton, okBtn); ++n;
+  XtSetArg(args[n], XmNcancelButton, closeBtn); ++n;
+  XtSetArg(args[n], XmNinitialFocus, findText); ++n;
+  XtSetValues(findDialog, args, n);
+}
+
+void XPDFViewer::findFindCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->core->find(XmTextFieldGetString(viewer->findText));
+}
+
+void XPDFViewer::findCloseCbk(Widget widget, XtPointer ptr,
+                             XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  XtUnmanageChild(viewer->findDialog);
+}
+
+//------------------------------------------------------------------------
+// GUI code: "save as" dialog
+//------------------------------------------------------------------------
+
+void XPDFViewer::initSaveAsDialog() {
+  Arg args[20];
+  int n;
+  XmString s1, s2, s3;
+
+  n = 0;
+  s1 = XmStringCreateLocalized("Save");
+  XtSetArg(args[n], XmNokLabelString, s1); ++n;
+  s2 = XmStringCreateLocalized("*.[Pp][Dd][Ff]");
+  XtSetArg(args[n], XmNpattern, s2); ++n;
+  s3 = XmStringCreateLocalized(xpdfAppName ": Save as");
+  XtSetArg(args[n], XmNdialogTitle, s3); ++n;
+  XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
+  XtSetArg(args[n], XmNautoUnmanage, True); ++n;
+  saveAsDialog = XmCreateFileSelectionDialog(win, "saveAsDialog", args, n);
+  XmStringFree(s1);
+  XmStringFree(s2);
+  XmStringFree(s3);
+  XtUnmanageChild(XmFileSelectionBoxGetChild(saveAsDialog,
+                                            XmDIALOG_HELP_BUTTON));
+  XtAddCallback(saveAsDialog, XmNokCallback,
+               &saveAsOkCbk, (XtPointer)this);
+}
+
+void XPDFViewer::setSaveAsDialogDir(char *dir) {
+  XmString s;
+
+  s = XmStringCreateLocalized(dir);
+  XtVaSetValues(saveAsDialog, XmNdirectory, s, NULL);
+  XmStringFree(s);
+}
+
+void XPDFViewer::mapSaveAsDialog() {
+  XmFileSelectionDoSearch(saveAsDialog, NULL);
+  XtManageChild(saveAsDialog);
+}
+
+void XPDFViewer::saveAsOkCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+  XmFileSelectionBoxCallbackStruct *data =
+    (XmFileSelectionBoxCallbackStruct *)callData;
+  char *fileName;
+  GString *fileNameStr;
+  XmStringContext context;
+  XmStringCharSet charSet;
+  XmStringDirection dir;
+  Boolean sep;
+
+  XmStringInitContext(&context, data->value);
+  if (XmStringGetNextSegment(context, &fileName, &charSet, &dir, &sep)) {
+    fileNameStr = new GString(fileName);
+    viewer->core->getDoc()->saveAs(fileNameStr);
+    delete fileNameStr;
+    XtFree(charSet);
+    XtFree(fileName);
+  }
+  XmStringFreeContext(context);
+}
+
+//------------------------------------------------------------------------
+// GUI code: "print" dialog
+//------------------------------------------------------------------------
+
+void XPDFViewer::initPrintDialog() {
+  Widget sep1, sep2, row, label1, label2, okBtn, cancelBtn;
+  Arg args[20];
+  int n;
+  XmString s;
+
+  //----- dialog
+  n = 0;
+  s = XmStringCreateLocalized(xpdfAppName ": Print");
+  XtSetArg(args[n], XmNdialogTitle, s); ++n;
+  XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
+  printDialog = XmCreateFormDialog(win, "printDialog", args, n);
+  XmStringFree(s);
+
+  //----- "print with command"
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNtopOffset, 4); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY); ++n;
+  XtSetArg(args[n], XmNset, XmSET); ++n;
+  s = XmStringCreateLocalized("Print with command:");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  printWithCmdBtn = XmCreateToggleButton(printDialog, "printWithCmd", args, n);
+  XmStringFree(s);
+  XtManageChild(printWithCmdBtn);
+  XtAddCallback(printWithCmdBtn, XmNvalueChangedCallback,
+               &printWithCmdBtnCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, printWithCmdBtn); ++n;
+  XtSetArg(args[n], XmNtopOffset, 2); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNleftOffset, 16); ++n;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNrightOffset, 4); ++n;
+  XtSetArg(args[n], XmNcolumns, 40); ++n;
+  printCmdText = XmCreateTextField(printDialog, "printCmd", args, n);
+  XtManageChild(printCmdText);
+
+  //----- "print with command"
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, printCmdText); ++n;
+  XtSetArg(args[n], XmNtopOffset, 4); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY); ++n;
+  XtSetArg(args[n], XmNset, XmUNSET); ++n;
+  s = XmStringCreateLocalized("Print to file:");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  printToFileBtn = XmCreateToggleButton(printDialog, "printToFile", args, n);
+  XmStringFree(s);
+  XtManageChild(printToFileBtn);
+  XtAddCallback(printToFileBtn, XmNvalueChangedCallback,
+               &printToFileBtnCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, printToFileBtn); ++n;
+  XtSetArg(args[n], XmNtopOffset, 2); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNleftOffset, 16); ++n;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNrightOffset, 4); ++n;
+  XtSetArg(args[n], XmNcolumns, 40); ++n;
+  XtSetArg(args[n], XmNsensitive, False); ++n;
+  printFileText = XmCreateTextField(printDialog, "printFile", args, n);
+  XtManageChild(printFileText);
+
+  //----- separator
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, printFileText); ++n;
+  XtSetArg(args[n], XmNtopOffset, 8); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNleftOffset, 8); ++n;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNrightOffset, 8); ++n;
+  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
+  sep1 = XmCreateSeparator(printDialog, "sep1", args, n);
+  XtManageChild(sep1);
+
+  //----- page range
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, sep1); ++n;
+  XtSetArg(args[n], XmNtopOffset, 8); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNleftOffset, 4); ++n;
+  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
+  XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n;
+  row = XmCreateRowColumn(printDialog, "row", args, n);
+  XtManageChild(row);
+  n = 0;
+  s = XmStringCreateLocalized("Pages:");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  label1 = XmCreateLabel(row, "label1", args, n);
+  XmStringFree(s);
+  XtManageChild(label1);
+  n = 0;
+  XtSetArg(args[n], XmNcolumns, 5); ++n;
+  printFirstPage = XmCreateTextField(row, "printFirstPage", args, n);
+  XtManageChild(printFirstPage);
+  n = 0;
+  s = XmStringCreateLocalized("to");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  label2 = XmCreateLabel(row, "label2", args, n);
+  XmStringFree(s);
+  XtManageChild(label2);
+  n = 0;
+  XtSetArg(args[n], XmNcolumns, 5); ++n;
+  printLastPage = XmCreateTextField(row, "printLastPage", args, n);
+  XtManageChild(printLastPage);
+
+  //----- separator
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, row); ++n;
+  XtSetArg(args[n], XmNtopOffset, 8); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNleftOffset, 8); ++n;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNrightOffset, 8); ++n;
+  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
+  sep2 = XmCreateSeparator(printDialog, "sep2", args, n);
+  XtManageChild(sep2);
+
+  //----- "print" and "cancel" buttons
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, sep2); ++n;
+  XtSetArg(args[n], XmNtopOffset, 8); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNleftOffset, 4); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
+  okBtn = XmCreatePushButton(printDialog, "Print", args, n);
+  XtManageChild(okBtn);
+  XtAddCallback(okBtn, XmNactivateCallback,
+               &printPrintCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, sep2); ++n;
+  XtSetArg(args[n], XmNtopOffset, 8); ++n;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNrightOffset, 4); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
+  cancelBtn = XmCreatePushButton(printDialog, "Cancel", args, n);
+  XtManageChild(cancelBtn);
+  n = 0;
+  XtSetArg(args[n], XmNdefaultButton, okBtn); ++n;
+  XtSetArg(args[n], XmNcancelButton, cancelBtn); ++n;
+  XtSetValues(printDialog, args, n);
+}
+
+void XPDFViewer::setupPrintDialog() {
+  PDFDoc *doc;
+  char buf[20];
+  GString *pdfFileName, *psFileName;
+  char *p;
+
+  doc = core->getDoc();
+
+  psFileName = globalParams->getPSFile();
+  if (psFileName && psFileName->getChar(0) != '|') {
+    XmTextFieldSetString(printFileText, psFileName->getCString());
+  } else {
+    pdfFileName = doc->getFileName();
+    p = pdfFileName->getCString() + pdfFileName->getLength() - 4;
+    if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) {
+      psFileName = new GString(pdfFileName->getCString(),
+                              pdfFileName->getLength() - 4);
+    } else {
+      psFileName = pdfFileName->copy();
+    }
+    psFileName->append(".ps");
+    XmTextFieldSetString(printFileText, psFileName->getCString());
+    delete psFileName;
+  }
+
+  psFileName = globalParams->getPSFile();
+  if (psFileName && psFileName->getChar(0) == '|') {
+    XmTextFieldSetString(printCmdText,
+                        psFileName->getCString() + 1);
+  }
+
+  sprintf(buf, "%d", doc->getNumPages());
+  XmTextFieldSetString(printFirstPage, "1");
+  XmTextFieldSetString(printLastPage, buf);
+}
+
+void XPDFViewer::printWithCmdBtnCbk(Widget widget, XtPointer ptr,
+                                   XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+  XmToggleButtonCallbackStruct *data =
+      (XmToggleButtonCallbackStruct *)callData;
+
+  if (data->set != XmSET) {
+    XmToggleButtonSetState(viewer->printWithCmdBtn, True, False);
+  }
+  XmToggleButtonSetState(viewer->printToFileBtn, False, False);
+  XtVaSetValues(viewer->printCmdText, XmNsensitive, True, NULL);
+  XtVaSetValues(viewer->printFileText, XmNsensitive, False, NULL);
+}
+
+void XPDFViewer::printToFileBtnCbk(Widget widget, XtPointer ptr,
+                                  XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+  XmToggleButtonCallbackStruct *data =
+      (XmToggleButtonCallbackStruct *)callData;
+
+  if (data->set != XmSET) {
+    XmToggleButtonSetState(viewer->printToFileBtn, True, False);
+  }
+  XmToggleButtonSetState(viewer->printWithCmdBtn, False, False);
+  XtVaSetValues(viewer->printFileText, XmNsensitive, True, NULL);
+  XtVaSetValues(viewer->printCmdText, XmNsensitive, False, NULL);
+}
+
+void XPDFViewer::printPrintCbk(Widget widget, XtPointer ptr,
+                              XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+  unsigned char withCmd;
+  GString *psFileName;
+  int firstPage, lastPage;
+  PDFDoc *doc;
+  PSOutputDev *psOut;
+
+  doc = viewer->core->getDoc();
+  if (!doc->okToPrint()) {
+    error(-1, "Printing this document is not allowed.");
+    return;
+  }
+
+  viewer->core->setBusyCursor(gTrue);
+
+  XtVaGetValues(viewer->printWithCmdBtn, XmNset, &withCmd, NULL);
+  if (withCmd) {
+    psFileName = new GString(XmTextFieldGetString(viewer->printCmdText));
+    psFileName->insert(0, '|');
+  } else {
+    psFileName = new GString(XmTextFieldGetString(viewer->printFileText));
+  }
+
+  firstPage = atoi(XmTextFieldGetString(viewer->printFirstPage));
+  lastPage = atoi(XmTextFieldGetString(viewer->printLastPage));
+  if (firstPage < 1) {
+    firstPage = 1;
+  } else if (firstPage > doc->getNumPages()) {
+    firstPage = doc->getNumPages();
+  }
+  if (lastPage < firstPage) {
+    lastPage = firstPage;
+  } else if (lastPage > doc->getNumPages()) {
+    lastPage = doc->getNumPages();
+  }
+
+  psOut = new PSOutputDev(psFileName->getCString(), doc->getXRef(),
+                         doc->getCatalog(), firstPage, lastPage,
+                         psModePS);
+  if (psOut->isOk()) {
+    doc->displayPages(psOut, firstPage, lastPage, 72, 0, gFalse);
+  }
+  delete psOut;
+  delete psFileName;
+
+  viewer->core->setBusyCursor(gFalse);
+}
+
+//------------------------------------------------------------------------
+// GUI code: password dialog
+//------------------------------------------------------------------------
+
+void XPDFViewer::initPasswordDialog() {
+  Widget row, label, okBtn, cancelBtn;
+  Arg args[20];
+  int n;
+  XmString s;
+
+  //----- dialog
+  n = 0;
+  s = XmStringCreateLocalized(xpdfAppName ": Password");
+  XtSetArg(args[n], XmNdialogTitle, s); ++n;
+  XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
+  passwordDialog = XmCreateFormDialog(win, "passwordDialog", args, n);
+  XmStringFree(s);
+
+  //----- message
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNtopOffset, 4); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNleftOffset, 4); ++n;
+  s = XmStringCreateLocalized(" ");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  passwordMsg = XmCreateLabel(passwordDialog, "msg", args, n);
+  XmStringFree(s);
+  XtManageChild(passwordMsg);
+
+  //----- label and password entry
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, passwordMsg); ++n;
+  XtSetArg(args[n], XmNtopOffset, 4); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNleftOffset, 4); ++n;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNleftOffset, 4); ++n;
+  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
+  XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n;
+  row = XmCreateRowColumn(passwordDialog, "row", args, n);
+  XtManageChild(row);
+  n = 0;
+  s = XmStringCreateLocalized("Password: ");
+  XtSetArg(args[n], XmNlabelString, s); ++n;
+  label = XmCreateLabel(row, "label", args, n);
+  XmStringFree(s);
+  XtManageChild(label);
+  n = 0;
+  XtSetArg(args[n], XmNcolumns, 16); ++n;
+  passwordText = XmCreateTextField(row, "text", args, n);
+  XtManageChild(passwordText);
+  XtAddCallback(passwordText, XmNmodifyVerifyCallback,
+               &passwordTextVerifyCbk, this);
+
+  //----- "Ok" and "Cancel" buttons
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, row); ++n;
+  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNleftOffset, 4); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
+  XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
+  okBtn = XmCreatePushButton(passwordDialog, "Ok", args, n);
+  XtManageChild(okBtn);
+  XtAddCallback(okBtn, XmNactivateCallback,
+               &passwordOkCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
+  XtSetArg(args[n], XmNtopWidget, row); ++n;
+  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNrightOffset, 4); ++n;
+  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
+  XtSetArg(args[n], XmNbottomOffset, 4); ++n;
+  XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
+  cancelBtn = XmCreatePushButton(passwordDialog, "Cancel", args, n);
+  XtManageChild(cancelBtn);
+  XtAddCallback(cancelBtn, XmNactivateCallback,
+               &passwordCancelCbk, (XtPointer)this);
+  n = 0;
+  XtSetArg(args[n], XmNdefaultButton, okBtn); ++n;
+  XtSetArg(args[n], XmNcancelButton, cancelBtn); ++n;
+  XtSetArg(args[n], XmNinitialFocus, passwordText); ++n;
+  XtSetValues(passwordDialog, args, n);
+}
+
+void XPDFViewer::passwordTextVerifyCbk(Widget widget, XtPointer ptr,
+                                      XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+  XmTextVerifyCallbackStruct *data =
+      (XmTextVerifyCallbackStruct *)callData;
+  int i, n;
+
+  i = (int)data->startPos;
+  n = (int)data->endPos - i;
+  if (i > viewer->password->getLength()) {
+    i = viewer->password->getLength();
+  }
+  if (i + n > viewer->password->getLength()) {
+    n = viewer->password->getLength() - i;
+  }
+  viewer->password->del(i, n);
+  viewer->password->insert(i, data->text->ptr, data->text->length);
+
+  for (i = 0; i < data->text->length; ++i) {
+    data->text->ptr[i] = '*';
+  }
+  data->doit = True;
+}
+
+void XPDFViewer::passwordOkCbk(Widget widget, XtPointer ptr,
+                              XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->passwordDone = 1;
+}
+
+void XPDFViewer::passwordCancelCbk(Widget widget, XtPointer ptr,
+                                  XtPointer callData) {
+  XPDFViewer *viewer = (XPDFViewer *)ptr;
+
+  viewer->passwordDone = -1;
+}
+
+void XPDFViewer::getPassword(GBool again) {
+  XmString s;
+  XEvent event;
+
+  if (password) {
+    delete password;
+  }
+  password = new GString();
+
+  XmTextFieldSetString(passwordText, "");
+  s = XmStringCreateLocalized(
+         again ? (char *)"Incorrect password.  Please try again."
+               : (char *)"This document requires a password.");
+  XtVaSetValues(passwordMsg, XmNlabelString, s, NULL);
+  XmStringFree(s);
+  XtManageChild(passwordDialog);
+  passwordDone = 0;
+  do {
+    XtAppNextEvent(app->getAppContext(), &event);
+    XtDispatchEvent(&event);
+  } while (!passwordDone);
+
+  if (passwordDone < 0) {
+    delete password;
+    password = NULL;
+  }
+}
diff --git a/pdf/xpdf/XPDFViewer.h b/pdf/xpdf/XPDFViewer.h
new file mode 100644 (file)
index 0000000..77875c4
--- /dev/null
@@ -0,0 +1,236 @@
+//========================================================================
+//
+// XPDFViewer.h
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef XPDFVIEWER_H
+#define XPDFVIEWER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#define Object XtObject
+#include <Xm/XmAll.h>
+#undef Object
+#include "gtypes.h"
+#include "XPDFCore.h"
+
+#if XmVERSION <= 1
+#define DISABLE_OUTLINE
+#endif
+
+class GString;
+class GList;
+class UnicodeMap;
+class LinkDest;
+class XPDFApp;
+
+//------------------------------------------------------------------------
+// XPDFViewer
+//------------------------------------------------------------------------
+
+class XPDFViewer {
+public:
+
+  XPDFViewer(XPDFApp *appA, GString *fileName,
+            int pageA, GString *destName,
+            GString *ownerPassword, GString *userPassword);
+  GBool isOk() { return ok; }
+  ~XPDFViewer();
+
+  void open(GString *fileName, int pageA, GString *destName);
+  void clear();
+
+  Widget getWindow() { return win; }
+
+private:
+
+  //----- load / display
+  GBool loadFile(GString *fileName, GString *ownerPassword = NULL,
+                GString *userPassword = NULL);
+  void reloadFile();
+  void displayPage(int pageA, int zoomA, int rotateA,
+                   GBool scrollToTop, GBool addToHist);
+  void displayDest(LinkDest *dest, int zoomA, int rotateA,
+                  GBool addToHist);
+  void getPageAndDest(int pageA, GString *destName,
+                     int *pageOut, LinkDest **destOut);
+
+  //----- password dialog
+  static GString *reqPasswordCbk(void *data, GBool again);
+
+  //----- actions
+  static void actionCbk(void *data, char *action);
+
+  //----- keyboard/mouse input
+  static void keyPressCbk(void *data, char *s, KeySym key,
+                         Guint modifiers);
+  static void mouseCbk(void *data, XEvent *event);
+
+  //----- GUI code: main window
+  void initWindow();
+  void mapWindow();
+  void closeWindow();
+  Widget getZoomMenuBtn(int z);
+  static void prevPageCbk(Widget widget, XtPointer ptr,
+                         XtPointer callData);
+  static void prevTenPageCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData);
+  static void nextPageCbk(Widget widget, XtPointer ptr,
+                         XtPointer callData);
+  static void nextTenPageCbk(Widget widget, XtPointer ptr,
+                            XtPointer callData);
+  static void backCbk(Widget widget, XtPointer ptr,
+                     XtPointer callData);
+  static void forwardCbk(Widget widget, XtPointer ptr,
+                        XtPointer callData);
+  static void zoomMenuCbk(Widget widget, XtPointer ptr,
+                         XtPointer callData);
+  static void findCbk(Widget widget, XtPointer ptr,
+                     XtPointer callData);
+  static void printCbk(Widget widget, XtPointer ptr,
+                      XtPointer callData);
+  static void aboutCbk(Widget widget, XtPointer ptr,
+                      XtPointer callData);
+  static void quitCbk(Widget widget, XtPointer ptr,
+                     XtPointer callData);
+  static void openCbk(Widget widget, XtPointer ptr,
+                     XtPointer callData);
+  static void openInNewWindowCbk(Widget widget, XtPointer ptr,
+                                XtPointer callData);
+  static void reloadCbk(Widget widget, XtPointer ptr,
+                       XtPointer callData);
+  static void saveAsCbk(Widget widget, XtPointer ptr,
+                       XtPointer callData);
+  static void rotateCCWCbk(Widget widget, XtPointer ptr,
+                          XtPointer callData);
+  static void rotateCWCbk(Widget widget, XtPointer ptr,
+                         XtPointer callData);
+  static void closeCbk(Widget widget, XtPointer ptr,
+                      XtPointer callData);
+  static void closeMsgCbk(Widget widget, XtPointer ptr,
+                         XtPointer callData);
+  static void pageNumCbk(Widget widget, XtPointer ptr,
+                        XtPointer callData);
+  static void updateCbk(void *data, GString *fileName,
+                       int pageNum, int numPages, char *linkLabel);
+
+  //----- GUI code: outline
+#ifndef DISABLE_OUTLINE
+  void setupOutline();
+  void setupOutlineItems(GList *items, Widget parent, UnicodeMap *uMap);
+  static void outlineSelectCbk(Widget widget, XtPointer ptr,
+                              XtPointer callData);
+#endif
+
+  //----- GUI code: "about" dialog
+  void initAboutDialog();
+
+  //----- GUI code: "open" dialog
+  void initOpenDialog();
+  void setOpenDialogDir(char *dir);
+  void mapOpenDialog(GBool openInNewWindowA);
+  static void openOkCbk(Widget widget, XtPointer ptr,
+                       XtPointer callData);
+
+  //----- GUI code: "find" dialog
+  void initFindDialog();
+  static void findFindCbk(Widget widget, XtPointer ptr,
+                         XtPointer callData);
+  static void findCloseCbk(Widget widget, XtPointer ptr,
+                          XtPointer callData);
+
+  //----- GUI code: "save as" dialog
+  void initSaveAsDialog();
+  void setSaveAsDialogDir(char *dir);
+  void mapSaveAsDialog();
+  static void saveAsOkCbk(Widget widget, XtPointer ptr,
+                         XtPointer callData);
+
+  //----- GUI code: "print" dialog
+  void initPrintDialog();
+  void setupPrintDialog();
+  static void printWithCmdBtnCbk(Widget widget, XtPointer ptr,
+                                XtPointer callData);
+  static void printToFileBtnCbk(Widget widget, XtPointer ptr,
+                               XtPointer callData);
+  static void printPrintCbk(Widget widget, XtPointer ptr,
+                           XtPointer callData);
+
+  //----- GUI code: password dialog
+  void initPasswordDialog();
+  static void passwordTextVerifyCbk(Widget widget, XtPointer ptr,
+                                   XtPointer callData);
+  static void passwordOkCbk(Widget widget, XtPointer ptr,
+                           XtPointer callData);
+  static void passwordCancelCbk(Widget widget, XtPointer ptr,
+                               XtPointer callData);
+  void getPassword(GBool again);
+
+  XPDFApp *app;
+  GBool ok;
+
+  Display *display;
+  int screenNum;
+  Widget win;                  // top-level window
+  Widget form;
+  Widget panedWin;
+#ifndef DISABLE_OUTLINE
+  Widget outlineScroll;
+  Widget outlineTree;
+  Widget *outlineLabels;
+  int outlineLabelsLength;
+  int outlineLabelsSize;
+#endif
+  XPDFCore *core;
+  Widget toolBar;
+  Widget backBtn;
+  Widget prevTenPageBtn;
+  Widget prevPageBtn;
+  Widget nextPageBtn;
+  Widget nextTenPageBtn;
+  Widget forwardBtn;
+  Widget pageNumText;
+  Widget pageCountLabel;
+  Widget zoomMenu;
+  Widget zoomMenuBtns[maxZoom - minZoom + 1 + 2];
+  Widget findBtn;
+  Widget printBtn;
+  Widget aboutBtn;
+  Widget linkLabel;
+  Widget quitBtn;
+  Widget popupMenu;
+
+  Widget aboutDialog;
+  XmFontList aboutBigFont, aboutVersionFont, aboutFixedFont;
+
+  Widget openDialog;
+  GBool openInNewWindow;
+
+  Widget findDialog;
+  Widget findText;
+
+  Widget saveAsDialog;
+
+  Widget printDialog;
+  Widget printWithCmdBtn;
+  Widget printToFileBtn;
+  Widget printCmdText;
+  Widget printFileText;
+  Widget printFirstPage;
+  Widget printLastPage;
+
+  Widget passwordDialog;
+  Widget passwordMsg;
+  Widget passwordText;
+  int passwordDone;
+  GString *password;
+};
+
+#endif
diff --git a/pdf/xpdf/XPixmapOutputDev.cc b/pdf/xpdf/XPixmapOutputDev.cc
new file mode 100644 (file)
index 0000000..ecd1498
--- /dev/null
@@ -0,0 +1,84 @@
+//========================================================================
+//
+// XPixmapOutputDev.cc
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "Object.h"
+#include "GfxState.h"
+#include "XPixmapOutputDev.h"
+
+//------------------------------------------------------------------------
+
+#define xoutRound(x) ((int)(x + 0.5))
+
+//------------------------------------------------------------------------
+
+XPixmapOutputDev::XPixmapOutputDev(Display *displayA, int screenNumA,
+                                  Visual *visualA, Colormap colormapA,
+                                  GBool reverseVideoA, Gulong paperColorA,
+                                  GBool installCmapA, int rgbCubeSizeA,
+                                  GBool incrementalUpdateA,
+                                  void (*redrawCbkA)(void *data),
+                                  void *redrawCbkDataA):
+  XOutputDev(displayA, screenNumA, visualA, colormapA,
+            reverseVideoA, paperColorA, installCmapA, rgbCubeSizeA)
+{
+  incrementalUpdate = incrementalUpdateA;
+  redrawCbk = redrawCbkA;
+  redrawCbkData = redrawCbkDataA;
+}
+
+XPixmapOutputDev::~XPixmapOutputDev() {
+  if (getPixmapWidth() > 0) {
+    XFreePixmap(getDisplay(), getPixmap());
+  }
+}
+
+void XPixmapOutputDev::clear() {
+  startDoc(NULL);
+  startPage(0, NULL);
+}
+
+void XPixmapOutputDev::startPage(int pageNum, GfxState *state) {
+  int oldPixmapW, oldPixmapH, newPixmapW, newPixmapH;
+
+  // resize the off-screen pixmap (if needed)
+  oldPixmapW = getPixmapWidth();
+  oldPixmapH = getPixmapHeight();
+  newPixmapW = xoutRound(state ? state->getPageWidth() : 1);
+  newPixmapH = xoutRound(state ? state->getPageHeight() : 1);
+  if (oldPixmapW == 0 ||
+      newPixmapW != oldPixmapW || newPixmapH != oldPixmapH) {
+    if (oldPixmapW > 0) {
+      XFreePixmap(getDisplay(), getPixmap());
+    }
+    setPixmap(XCreatePixmap(getDisplay(), win, newPixmapW, newPixmapH,
+                           getDepth()),
+             newPixmapW, newPixmapH);
+  }
+
+  XOutputDev::startPage(pageNum, state);
+}
+
+void XPixmapOutputDev::endPage() {
+  if (!incrementalUpdate) {
+    (*redrawCbk)(redrawCbkData);
+  }
+  XOutputDev::endPage();
+}
+
+void XPixmapOutputDev::dump() {
+  if (incrementalUpdate) {
+    (*redrawCbk)(redrawCbkData);
+  }
+  XOutputDev::dump();
+}
diff --git a/pdf/xpdf/XPixmapOutputDev.h b/pdf/xpdf/XPixmapOutputDev.h
new file mode 100644 (file)
index 0000000..1dba8af
--- /dev/null
@@ -0,0 +1,63 @@
+//========================================================================
+//
+// XPixmapOutputDev.h
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef XPIXMAPOUTPUTDEV_H
+#define XPIXMAPOUTPUTDEV_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <X11/Xlib.h>
+#include "XOutputDev.h"
+
+//------------------------------------------------------------------------
+
+class XPixmapOutputDev: public XOutputDev {
+public:
+
+  XPixmapOutputDev(Display *displayA, int screenNumA,
+                  Visual *visualA, Colormap colormapA,
+                  GBool reverseVideoA, Gulong paperColorA,
+                  GBool installCmapA, int rgbCubeSizeA,
+                  GBool incrementalUpdateA,
+                  void (*redrawCbkA)(void *data),
+                  void *redrawCbkDataA);
+
+  ~XPixmapOutputDev();
+
+  //----- initialization and control
+
+  // Start a page.
+  virtual void startPage(int pageNum, GfxState *state);
+
+  // End a page.
+  virtual void endPage();
+
+  // Dump page contents to display.
+  virtual void dump();
+
+  //----- special access
+
+  // Set the window - this is used only to create a compatible pixmap.
+  void setWindow(Window winA) { win = winA; }
+
+  // Clear out the document (used when displaying an empty window).
+  void clear();
+
+private:
+
+  GBool incrementalUpdate;     // incrementally update the display?
+  void (*redrawCbk)(void *data);
+  void *redrawCbkData;
+  Window win;
+};
+
+#endif
diff --git a/pdf/xpdf/about-text.h b/pdf/xpdf/about-text.h
new file mode 100644 (file)
index 0000000..e84c1ef
--- /dev/null
@@ -0,0 +1,47 @@
+//========================================================================
+//
+// about-text.h
+//
+// Copyright 2002 Glyph & Cog, LLC
+//
+//========================================================================
+
+static char *aboutWinText[] = {
+  "http://www.foolabs.com/xpdf/",
+  "derekn@foolabs.com",
+  " ",
+  "Licensed under the GNU General Public License (GPL).",
+  "See the 'COPYING' file for details.",
+  " ",
+  "Supports PDF version " supportedPDFVersionStr ".",
+  " ",
+  "The PDF data structures, operators, and specification",
+  "are copyright 1985-2001 Adobe Systems Inc.",
+  " ",
+  "Mouse bindings:",
+  "  button 1: select text / follow link",
+  "  button 2: pan window",
+  "  button 3: menu",
+  " ",
+  "Key bindings:",
+  "  o              = open file",
+  "  r              = reload",
+  "  f / ctrl-F     = find text",
+  "  ctrl-G         = find next"
+  "  ctrl-P         = print",
+  "  n              = next page",
+  "  p              = previous page",
+  "  <PgDn> = <space>                = scroll down",
+  "  <PgUp> = <backspace> = <delete> = scroll up",
+  "  v              = forward (history path)",
+  "  b              = backward (history path)",
+  "  0 / + / -      = zoom zero / in / out",
+  "  z / w          = zoom page / page width",
+  "  ctrl-L         = redraw",
+  "  q              = quit",
+  "  <home> / <end> = top / bottom of page",
+  "  <arrows>       = scroll",
+  " ",
+  "For more information, please read the xpdf(1) man page.",
+  NULL
+};
diff --git a/pdf/xpdf/backArrowDis.xbm b/pdf/xpdf/backArrowDis.xbm
new file mode 100644 (file)
index 0000000..7529639
--- /dev/null
@@ -0,0 +1,6 @@
+#define backArrowDis_width 16
+#define backArrowDis_height 15
+static unsigned char backArrowDis_bits[] = {
+   0x80, 0x00, 0x40, 0x00, 0xa0, 0x00, 0x50, 0x00, 0xa8, 0x00, 0x54, 0x44,
+   0xaa, 0x88, 0x55, 0x44, 0xaa, 0x88, 0x54, 0x44, 0xa8, 0x88, 0x50, 0x00,
+   0xa0, 0x00, 0x40, 0x00, 0x80, 0x00};
diff --git a/pdf/xpdf/dblLeftArrowDis.xbm b/pdf/xpdf/dblLeftArrowDis.xbm
new file mode 100644 (file)
index 0000000..3fb78d8
--- /dev/null
@@ -0,0 +1,6 @@
+#define dblLeftArrowDis_width 16
+#define dblLeftArrowDis_height 15
+static unsigned char dblLeftArrowDis_bits[] = {
+   0x80, 0x80, 0x40, 0x40, 0xa0, 0xa0, 0x50, 0x50, 0xa8, 0xa8, 0x54, 0x54,
+   0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x54, 0x54, 0xa8, 0xa8, 0x50, 0x50,
+   0xa0, 0xa0, 0x40, 0x40, 0x80, 0x80};
diff --git a/pdf/xpdf/dblRightArrowDis.xbm b/pdf/xpdf/dblRightArrowDis.xbm
new file mode 100644 (file)
index 0000000..a6c1e37
--- /dev/null
@@ -0,0 +1,6 @@
+#define dblRightArrowDis_width 16
+#define dblRightArrowDis_height 15
+static unsigned char dblRightArrowDis_bits[] = {
+   0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x0a, 0x0a, 0x15, 0x15, 0x2a, 0x2a,
+   0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0x2a, 0x2a, 0x15, 0x15, 0x0a, 0x0a,
+   0x05, 0x05, 0x02, 0x02, 0x01, 0x01};
diff --git a/pdf/xpdf/findDis.xbm b/pdf/xpdf/findDis.xbm
new file mode 100644 (file)
index 0000000..cf666f2
--- /dev/null
@@ -0,0 +1,6 @@
+#define findDis_width 15
+#define findDis_height 15
+static unsigned char findDis_bits[] = {
+   0x10, 0x04, 0x28, 0x0a, 0x04, 0x10, 0xaa, 0x2a, 0x55, 0x55, 0x20, 0x02,
+   0x41, 0x41, 0x20, 0x02, 0x41, 0x41, 0xa0, 0x02, 0x01, 0x40, 0x20, 0x02,
+   0x01, 0x40, 0x20, 0x02, 0x15, 0x54};
diff --git a/pdf/xpdf/forwardArrowDis.xbm b/pdf/xpdf/forwardArrowDis.xbm
new file mode 100644 (file)
index 0000000..58d0cc0
--- /dev/null
@@ -0,0 +1,6 @@
+#define forwardArrowDis_width 16
+#define forwardArrowDis_height 15
+static unsigned char forwardArrowDis_bits[] = {
+   0x00, 0x01, 0x00, 0x02, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x15, 0x22, 0x2a,
+   0x11, 0x55, 0x22, 0xaa, 0x11, 0x55, 0x22, 0x2a, 0x00, 0x15, 0x00, 0x0a,
+   0x00, 0x05, 0x00, 0x02, 0x00, 0x01};
diff --git a/pdf/xpdf/leftArrowDis.xbm b/pdf/xpdf/leftArrowDis.xbm
new file mode 100644 (file)
index 0000000..953092a
--- /dev/null
@@ -0,0 +1,5 @@
+#define leftArrowDis_width 8
+#define leftArrowDis_height 15
+static unsigned char leftArrowDis_bits[] = {
+   0x80, 0x40, 0xa0, 0x50, 0xa8, 0x54, 0xaa, 0x55, 0xaa, 0x54, 0xa8, 0x50,
+   0xa0, 0x40, 0x80};
diff --git a/pdf/xpdf/print.xbm b/pdf/xpdf/print.xbm
new file mode 100644 (file)
index 0000000..209eb43
--- /dev/null
@@ -0,0 +1,6 @@
+#define print_width 15
+#define print_height 15
+static unsigned char print_bits[] = {
+   0xf0, 0x7f, 0x10, 0x40, 0x10, 0x40, 0xc8, 0x23, 0x08, 0x20, 0x68, 0x23,
+   0x04, 0x10, 0x34, 0x10, 0x04, 0x10, 0xff, 0x7f, 0x55, 0x55, 0xab, 0x6a,
+   0x55, 0x55, 0xab, 0x6a, 0xfe, 0x3f};
diff --git a/pdf/xpdf/printDis.xbm b/pdf/xpdf/printDis.xbm
new file mode 100644 (file)
index 0000000..19e962d
--- /dev/null
@@ -0,0 +1,6 @@
+#define printDis_width 15
+#define printDis_height 15
+static unsigned char printDis_bits[] = {
+   0xa0, 0x2a, 0x10, 0x40, 0x00, 0x00, 0x40, 0x01, 0x08, 0x20, 0x40, 0x01,
+   0x00, 0x00, 0x14, 0x10, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x01, 0x40,
+   0x00, 0x00, 0x01, 0x40, 0xaa, 0x2a};
diff --git a/pdf/xpdf/rightArrowDis.xbm b/pdf/xpdf/rightArrowDis.xbm
new file mode 100644 (file)
index 0000000..1216c47
--- /dev/null
@@ -0,0 +1,5 @@
+#define rightArrowDis_width 8
+#define rightArrowDis_height 15
+static unsigned char rightArrowDis_bits[] = {
+   0x01, 0x02, 0x05, 0x0a, 0x15, 0x2a, 0x55, 0xaa, 0x55, 0x2a, 0x15, 0x0a,
+   0x05, 0x02, 0x01};