From 6112dfe4b45b0305c7a6f61bd8a847f69f892cbb Mon Sep 17 00:00:00 2001 From: Martin Kretzschmar Date: Mon, 31 Mar 2003 16:45:09 +0000 Subject: Initial revision --- diff --git a/pdf/aconf2.h b/pdf/aconf2.h new file mode 100644 index 0000000..379eea2 --- /dev/null +++ b/pdf/aconf2.h @@ -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 index 0000000..716fee1 --- /dev/null +++ b/pdf/xpdf/JBIG2Stream.cc @@ -0,0 +1,3473 @@ +//======================================================================== +// +// JBIG2Stream.cc +// +// Copyright 2002 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include +#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 * 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 * 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 index 0000000..e15c3ac --- /dev/null +++ b/pdf/xpdf/JBIG2Stream.h @@ -0,0 +1,143 @@ +//======================================================================== +// +// JBIG2Stream.h +// +// Copyright 2002 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef JBIG2STREAM_H +#define JBIG2STREAM_H + +#include + +#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 index 0000000..256d38d --- /dev/null +++ b/pdf/xpdf/Outline.cc @@ -0,0 +1,140 @@ +//======================================================================== +// +// Outline.cc +// +// Copyright 2002 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#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 index 0000000..dc79252 --- /dev/null +++ b/pdf/xpdf/Outline.h @@ -0,0 +1,74 @@ +//======================================================================== +// +// Outline.h +// +// Copyright 2002 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef OUTLINE_H +#define OUTLINE_H + +#include + +#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 index 0000000..4f1e201 --- /dev/null +++ b/pdf/xpdf/PDFDocEncoding.cc @@ -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 index 0000000..6fc157f --- /dev/null +++ b/pdf/xpdf/PDFDocEncoding.h @@ -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 index 0000000..e456310 --- /dev/null +++ b/pdf/xpdf/XPDFApp.cc @@ -0,0 +1,386 @@ +//======================================================================== +// +// XPDFApp.cc +// +// Copyright 2002 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#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" + " Ctrla:beginning-of-line()\\n" + " Ctrlb:backward-character()\\n" + " Ctrld:delete-next-character()\\n" + " Ctrle:end-of-line()\\n" + " Ctrlf:forward-character()\\n" + " Ctrlu:beginning-of-line()delete-to-end-of-line()\\n" + " Ctrlk: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 index 0000000..4875456 --- /dev/null +++ b/pdf/xpdf/XPDFApp.h @@ -0,0 +1,104 @@ +//======================================================================== +// +// XPDFApp.h +// +// Copyright 2002 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef XPDFAPP_H +#define XPDFAPP_H + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#define Object XtObject +#include +#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 index 0000000..8376ce9 --- /dev/null +++ b/pdf/xpdf/XPDFCore.cc @@ -0,0 +1,1913 @@ +//======================================================================== +// +// XPDFCore.cc +// +// Copyright 2002 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include +#include +#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 string with one '%s' in it, and an +// 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( + ":DrawingAreaInput()\n" + ":DrawingAreaInput()\n" + ":DrawingAreaInput()\n" + ":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 index 0000000..348486f --- /dev/null +++ b/pdf/xpdf/XPDFCore.h @@ -0,0 +1,296 @@ +//======================================================================== +// +// XPDFCore.h +// +// Copyright 2002 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef XPDFCORE_H +#define XPDFCORE_H + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#define Object XtObject +#include +#undef Object +#include +#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 . + 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 of the current document. + void resizeToPage(int pg); + + // Clear out the current document, if any. + void clear(); + + // Display (or redisplay) the specified page. If is + // set, the window is vertically scrolled to the top; otherwise, no + // scrolling is done. If 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 index 0000000..46e5466 --- /dev/null +++ b/pdf/xpdf/XPDFTree.cc @@ -0,0 +1,929 @@ +//======================================================================== +// +// XPDFTree.cc +// +//======================================================================== + +#include +#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[] = + ": 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 * and * 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 index 0000000..d569f49 --- /dev/null +++ b/pdf/xpdf/XPDFTree.h @@ -0,0 +1,43 @@ +//======================================================================== +// +// XPDFTree.h +// +//======================================================================== + +#ifndef XPDFTREE_H +#define XPDFTREE_H + +#include + +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 index 0000000..16ab137 --- /dev/null +++ b/pdf/xpdf/XPDFTreeP.h @@ -0,0 +1,85 @@ +//======================================================================== +// +// XPDFTreeP.h +// +//======================================================================== + +#ifndef XPDFTREEP_H +#define XPDFTREEP_H + +#include +#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 index 0000000..a8c8f8f --- /dev/null +++ b/pdf/xpdf/XPDFViewer.cc @@ -0,0 +1,2318 @@ +//======================================================================== +// +// XPDFViewer.cc +// +// Copyright 2002 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include +#include +#include +#include +#ifdef HAVE_X11_XPM_H +#include +#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, ""); ++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 index 0000000..77875c4 --- /dev/null +++ b/pdf/xpdf/XPDFViewer.h @@ -0,0 +1,236 @@ +//======================================================================== +// +// XPDFViewer.h +// +// Copyright 2002 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef XPDFVIEWER_H +#define XPDFVIEWER_H + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#define Object XtObject +#include +#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 index 0000000..ecd1498 --- /dev/null +++ b/pdf/xpdf/XPixmapOutputDev.cc @@ -0,0 +1,84 @@ +//======================================================================== +// +// XPixmapOutputDev.cc +// +// Copyright 2002 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#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 index 0000000..1dba8af --- /dev/null +++ b/pdf/xpdf/XPixmapOutputDev.h @@ -0,0 +1,63 @@ +//======================================================================== +// +// XPixmapOutputDev.h +// +// Copyright 2002 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef XPIXMAPOUTPUTDEV_H +#define XPIXMAPOUTPUTDEV_H + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#include +#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 index 0000000..e84c1ef --- /dev/null +++ b/pdf/xpdf/about-text.h @@ -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", + " = = scroll down", + " = = = 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", + " / = top / bottom of page", + " = 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 index 0000000..7529639 --- /dev/null +++ b/pdf/xpdf/backArrowDis.xbm @@ -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 index 0000000..3fb78d8 --- /dev/null +++ b/pdf/xpdf/dblLeftArrowDis.xbm @@ -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 index 0000000..a6c1e37 --- /dev/null +++ b/pdf/xpdf/dblRightArrowDis.xbm @@ -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 index 0000000..cf666f2 --- /dev/null +++ b/pdf/xpdf/findDis.xbm @@ -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 index 0000000..58d0cc0 --- /dev/null +++ b/pdf/xpdf/forwardArrowDis.xbm @@ -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 index 0000000..953092a --- /dev/null +++ b/pdf/xpdf/leftArrowDis.xbm @@ -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 index 0000000..209eb43 --- /dev/null +++ b/pdf/xpdf/print.xbm @@ -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 index 0000000..19e962d --- /dev/null +++ b/pdf/xpdf/printDis.xbm @@ -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 index 0000000..1216c47 --- /dev/null +++ b/pdf/xpdf/rightArrowDis.xbm @@ -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}; -- cgit v0.9.1