summaryrefslogtreecommitdiff
path: root/Build/source/libs/xpdf/xpdf-src/xpdf
diff options
context:
space:
mode:
authorAkira Kakuto <kakuto@fuk.kindai.ac.jp>2021-02-01 06:11:18 +0000
committerAkira Kakuto <kakuto@fuk.kindai.ac.jp>2021-02-01 06:11:18 +0000
commit4f9de1dfebdc66d367d730252d6f224d37e96ee6 (patch)
tree3750421fabf3aa8a8e1f4f6d5558d489e9d4fe02 /Build/source/libs/xpdf/xpdf-src/xpdf
parent9d694205709f79666458dc2ee685767b28bcd9ac (diff)
xpdf 4.03
git-svn-id: svn://tug.org/texlive/trunk@57588 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/libs/xpdf/xpdf-src/xpdf')
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc1825
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h66
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc2
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt8
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc10
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h6
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc59
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h1
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Form.cc99
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Form.h77
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc288
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h11
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc37
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h2
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc104
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h8
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc596
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h120
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc5
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc85
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h9
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc57
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h1
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc615
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h16
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc15
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc9
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc47
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc4
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h25
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc164
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h21
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc52
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h1
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc84
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h25
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc11
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Page.h1
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc35
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h15
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc1327
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h106
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc1040
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h27
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc132
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h4
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc1224
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h68
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc10
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h3
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc37
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc3245
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h181
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc675
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h166
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc365
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h10
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc8
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h2
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/config.h24
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc52
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc6
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc2
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc27
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc19
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc14
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc7
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc4
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc24
69 files changed, 7513 insertions, 5912 deletions
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc
index c7f30405483..b98ca2f0189 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc
@@ -27,6 +27,9 @@
#include "OptionalContent.h"
#include "Annot.h"
#include "Lexer.h"
+#include "XFAScanner.h"
+#include "UTF8.h"
+#include "PDF417Barcode.h"
#include "AcroForm.h"
//------------------------------------------------------------------------
@@ -55,6 +58,11 @@
#define acroFormQuadCenter 1
#define acroFormQuadRight 2
+#define acroFormVAlignTop 0
+#define acroFormVAlignMiddle 1
+#define acroFormVAlignMiddleNoDescender 2
+#define acroFormVAlignBottom 3
+
#define annotFlagHidden 0x0002
#define annotFlagPrint 0x0004
#define annotFlagNoView 0x0020
@@ -63,6 +71,256 @@
// = (4 * (sqrt(2) - 1) / 3) * r
#define bezierCircle 0.55228475
+// limit recursive field-parent lookups to avoid infinite loops
+#define maxFieldObjectDepth 50
+
+//------------------------------------------------------------------------
+
+// 5 bars + 5 spaces -- each can be wide (1) or narrow (0)
+// (there are always exactly 3 wide elements;
+// the last space is always narrow)
+static Guchar code3Of9Data[128][10] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x00
+ { 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, 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, 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, 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, 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 }, // 0x10
+ { 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, 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, 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, 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, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 1, 0, 0, 0, 1, 0, 0, 0 }, // ' ' = 0x20
+ { 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, 1, 0, 1, 0, 1, 0, 0, 0, 0 }, // '$' = 0x24
+ { 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, // '%' = 0x25
+ { 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, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 1, 0, 1, 0, 0, 0 }, // '*' = 0x2a
+ { 0, 1, 0, 0, 0, 1, 0, 1, 0, 0 }, // '+' = 0x2b
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0 }, // '-' = 0x2d
+ { 1, 1, 0, 0, 0, 0, 1, 0, 0, 0 }, // '.' = 0x2e
+ { 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }, // '/' = 0x2f
+ { 0, 0, 0, 1, 1, 0, 1, 0, 0, 0 }, // '0' = 0x30
+ { 1, 0, 0, 1, 0, 0, 0, 0, 1, 0 }, // '1'
+ { 0, 0, 1, 1, 0, 0, 0, 0, 1, 0 }, // '2'
+ { 1, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, // '3'
+ { 0, 0, 0, 1, 1, 0, 0, 0, 1, 0 }, // '4'
+ { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 }, // '5'
+ { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 }, // '6'
+ { 0, 0, 0, 1, 0, 0, 1, 0, 1, 0 }, // '7'
+ { 1, 0, 0, 1, 0, 0, 1, 0, 0, 0 }, // '8'
+ { 0, 0, 1, 1, 0, 0, 1, 0, 0, 0 }, // '9'
+ { 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, 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, 0, 0, 0, 0, 0, 0 }, // 0x40
+ { 1, 0, 0, 0, 0, 1, 0, 0, 1, 0 }, // 'A' = 0x41
+ { 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 }, // 'B'
+ { 1, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, // 'C'
+ { 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 }, // 'D'
+ { 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // 'E'
+ { 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // 'F'
+ { 0, 0, 0, 0, 0, 1, 1, 0, 1, 0 }, // 'G'
+ { 1, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, // 'H'
+ { 0, 0, 1, 0, 0, 1, 1, 0, 0, 0 }, // 'I'
+ { 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, // 'J'
+ { 1, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, // 'K'
+ { 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }, // 'L'
+ { 1, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, // 'M'
+ { 0, 0, 0, 0, 1, 0, 0, 1, 1, 0 }, // 'N'
+ { 1, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, // 'O'
+ { 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 }, // 'P' = 0x50
+ { 0, 0, 0, 0, 0, 0, 1, 1, 1, 0 }, // 'Q'
+ { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, // 'R'
+ { 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 }, // 'S'
+ { 0, 0, 0, 0, 1, 0, 1, 1, 0, 0 }, // 'T'
+ { 1, 1, 0, 0, 0, 0, 0, 0, 1, 0 }, // 'U'
+ { 0, 1, 1, 0, 0, 0, 0, 0, 1, 0 }, // 'V'
+ { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, // 'W'
+ { 0, 1, 0, 0, 1, 0, 0, 0, 1, 0 }, // 'X'
+ { 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, // 'Y'
+ { 0, 1, 1, 0, 1, 0, 0, 0, 0, 0 }, // 'Z'
+ { 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, 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 }, // 0x60
+ { 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, 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, 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, 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, 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 }, // 0x70
+ { 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, 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, 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, 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, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+// 3 bars + 3 spaces -- each can be 1, 2, 3, or 4 units wide
+static Guchar code128Data[107][6] = {
+ { 2, 1, 2, 2, 2, 2 },
+ { 2, 2, 2, 1, 2, 2 },
+ { 2, 2, 2, 2, 2, 1 },
+ { 1, 2, 1, 2, 2, 3 },
+ { 1, 2, 1, 3, 2, 2 },
+ { 1, 3, 1, 2, 2, 2 },
+ { 1, 2, 2, 2, 1, 3 },
+ { 1, 2, 2, 3, 1, 2 },
+ { 1, 3, 2, 2, 1, 2 },
+ { 2, 2, 1, 2, 1, 3 },
+ { 2, 2, 1, 3, 1, 2 },
+ { 2, 3, 1, 2, 1, 2 },
+ { 1, 1, 2, 2, 3, 2 },
+ { 1, 2, 2, 1, 3, 2 },
+ { 1, 2, 2, 2, 3, 1 },
+ { 1, 1, 3, 2, 2, 2 },
+ { 1, 2, 3, 1, 2, 2 },
+ { 1, 2, 3, 2, 2, 1 },
+ { 2, 2, 3, 2, 1, 1 },
+ { 2, 2, 1, 1, 3, 2 },
+ { 2, 2, 1, 2, 3, 1 },
+ { 2, 1, 3, 2, 1, 2 },
+ { 2, 2, 3, 1, 1, 2 },
+ { 3, 1, 2, 1, 3, 1 },
+ { 3, 1, 1, 2, 2, 2 },
+ { 3, 2, 1, 1, 2, 2 },
+ { 3, 2, 1, 2, 2, 1 },
+ { 3, 1, 2, 2, 1, 2 },
+ { 3, 2, 2, 1, 1, 2 },
+ { 3, 2, 2, 2, 1, 1 },
+ { 2, 1, 2, 1, 2, 3 },
+ { 2, 1, 2, 3, 2, 1 },
+ { 2, 3, 2, 1, 2, 1 },
+ { 1, 1, 1, 3, 2, 3 },
+ { 1, 3, 1, 1, 2, 3 },
+ { 1, 3, 1, 3, 2, 1 },
+ { 1, 1, 2, 3, 1, 3 },
+ { 1, 3, 2, 1, 1, 3 },
+ { 1, 3, 2, 3, 1, 1 },
+ { 2, 1, 1, 3, 1, 3 },
+ { 2, 3, 1, 1, 1, 3 },
+ { 2, 3, 1, 3, 1, 1 },
+ { 1, 1, 2, 1, 3, 3 },
+ { 1, 1, 2, 3, 3, 1 },
+ { 1, 3, 2, 1, 3, 1 },
+ { 1, 1, 3, 1, 2, 3 },
+ { 1, 1, 3, 3, 2, 1 },
+ { 1, 3, 3, 1, 2, 1 },
+ { 3, 1, 3, 1, 2, 1 },
+ { 2, 1, 1, 3, 3, 1 },
+ { 2, 3, 1, 1, 3, 1 },
+ { 2, 1, 3, 1, 1, 3 },
+ { 2, 1, 3, 3, 1, 1 },
+ { 2, 1, 3, 1, 3, 1 },
+ { 3, 1, 1, 1, 2, 3 },
+ { 3, 1, 1, 3, 2, 1 },
+ { 3, 3, 1, 1, 2, 1 },
+ { 3, 1, 2, 1, 1, 3 },
+ { 3, 1, 2, 3, 1, 1 },
+ { 3, 3, 2, 1, 1, 1 },
+ { 3, 1, 4, 1, 1, 1 },
+ { 2, 2, 1, 4, 1, 1 },
+ { 4, 3, 1, 1, 1, 1 },
+ { 1, 1, 1, 2, 2, 4 },
+ { 1, 1, 1, 4, 2, 2 },
+ { 1, 2, 1, 1, 2, 4 },
+ { 1, 2, 1, 4, 2, 1 },
+ { 1, 4, 1, 1, 2, 2 },
+ { 1, 4, 1, 2, 2, 1 },
+ { 1, 1, 2, 2, 1, 4 },
+ { 1, 1, 2, 4, 1, 2 },
+ { 1, 2, 2, 1, 1, 4 },
+ { 1, 2, 2, 4, 1, 1 },
+ { 1, 4, 2, 1, 1, 2 },
+ { 1, 4, 2, 2, 1, 1 },
+ { 2, 4, 1, 2, 1, 1 },
+ { 2, 2, 1, 1, 1, 4 },
+ { 4, 1, 3, 1, 1, 1 },
+ { 2, 4, 1, 1, 1, 2 },
+ { 1, 3, 4, 1, 1, 1 },
+ { 1, 1, 1, 2, 4, 2 },
+ { 1, 2, 1, 1, 4, 2 },
+ { 1, 2, 1, 2, 4, 1 },
+ { 1, 1, 4, 2, 1, 2 },
+ { 1, 2, 4, 1, 1, 2 },
+ { 1, 2, 4, 2, 1, 1 },
+ { 4, 1, 1, 2, 1, 2 },
+ { 4, 2, 1, 1, 1, 2 },
+ { 4, 2, 1, 2, 1, 1 },
+ { 2, 1, 2, 1, 4, 1 },
+ { 2, 1, 4, 1, 2, 1 },
+ { 4, 1, 2, 1, 2, 1 },
+ { 1, 1, 1, 1, 4, 3 },
+ { 1, 1, 1, 3, 4, 1 },
+ { 1, 3, 1, 1, 4, 1 },
+ { 1, 1, 4, 1, 1, 3 },
+ { 1, 1, 4, 3, 1, 1 },
+ { 4, 1, 1, 1, 1, 3 },
+ { 4, 1, 1, 3, 1, 1 },
+ { 1, 1, 3, 1, 4, 1 },
+ { 1, 1, 4, 1, 3, 1 },
+ { 3, 1, 1, 1, 4, 1 },
+ { 4, 1, 1, 1, 3, 1 },
+ { 2, 1, 1, 4, 1, 2 }, // start code A
+ { 2, 1, 1, 2, 1, 4 }, // start code B
+ { 2, 1, 1, 2, 3, 2 }, // start code C
+ { 2, 3, 3, 1, 1, 1 } // stop code (without final bar)
+};
+
//------------------------------------------------------------------------
// map an annotation ref to a page number
@@ -83,13 +341,21 @@ AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) {
Object acroFormObj2;
AcroForm *acroForm;
AcroFormField *field;
- Object fieldsObj, annotsObj, annotRef, annotObj, obj1, obj2;
+ Object xfaObj, fieldsObj, annotsObj, annotRef, annotObj, obj1, obj2;
int pageNum, i, j;
// this is the normal case: acroFormObj is a dictionary, as expected
if (acroFormObjA->isDict()) {
acroForm = new AcroForm(docA, acroFormObjA);
+ if (!acroFormObjA->dictLookup("XFA", &xfaObj)->isNull()) {
+ acroForm->xfaScanner = XFAScanner::load(&xfaObj);
+ if (!catalog->getNeedsRendering()) {
+ acroForm->isStaticXFA = gTrue;
+ }
+ }
+ xfaObj.free();
+
if (acroFormObjA->dictLookup("NeedAppearances", &obj1)->isBool()) {
acroForm->needAppearances = obj1.getBool();
}
@@ -183,17 +449,25 @@ AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) {
return acroForm;
}
-AcroForm::AcroForm(PDFDoc *docA, Object *acroFormObjA): Form(docA) {
+AcroForm::AcroForm(PDFDoc *docA, Object *acroFormObjA) {
+ doc = docA;
acroFormObjA->copy(&acroFormObj);
needAppearances = gFalse;
annotPages = new GList();
fields = new GList();
+ xfaScanner = NULL;
+ isStaticXFA = gFalse;
}
AcroForm::~AcroForm() {
acroFormObj.free();
deleteGList(annotPages, AcroFormAnnotPage);
deleteGList(fields, AcroFormField);
+ delete xfaScanner;
+}
+
+const char *AcroForm::getType() {
+ return isStaticXFA ? "static XFA" : "AcroForm";
}
void AcroForm::buildAnnotPageList(Catalog *catalog) {
@@ -296,10 +570,44 @@ int AcroForm::getNumFields() {
return fields->getLength();
}
-FormField *AcroForm::getField(int idx) {
+AcroFormField *AcroForm::getField(int idx) {
return (AcroFormField *)fields->get(idx);
}
+AcroFormField *AcroForm::findField(int pg, double x, double y) {
+ AcroFormField *field;
+ double llx, lly, urx, ury;
+ int i;
+
+ for (i = 0; i < fields->getLength(); ++i) {
+ field = (AcroFormField *)fields->get(i);
+ if (field->getPageNum() == pg) {
+ field->getBBox(&llx, &lly, &urx, &ury);
+ if (llx <= x && x <= urx && lly <= y && y <= ury) {
+ return field;
+ }
+ }
+ }
+ return NULL;
+}
+
+int AcroForm::findFieldIdx(int pg, double x, double y) {
+ AcroFormField *field;
+ double llx, lly, urx, ury;
+ int i;
+
+ for (i = 0; i < fields->getLength(); ++i) {
+ field = (AcroFormField *)fields->get(i);
+ if (field->getPageNum() == pg) {
+ field->getBBox(&llx, &lly, &urx, &ury);
+ if (llx <= x && x <= urx && lly <= y && y <= ury) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
//------------------------------------------------------------------------
// AcroFormField
//------------------------------------------------------------------------
@@ -307,11 +615,14 @@ FormField *AcroForm::getField(int idx) {
AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
GString *typeStr;
TextString *nameA;
+ GString *xfaName;
Guint flagsA;
GBool haveFlags, typeFromParentA;
Object fieldObjA, parentObj, parentObj2, obj1, obj2;
AcroFormFieldType typeA;
+ XFAField *xfaFieldA;
AcroFormField *field;
+ int depth, i0, i1;
fieldRefA->fetch(acroFormA->doc->getXRef(), &fieldObjA);
@@ -345,7 +656,8 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
//----- get info from parent non-terminal fields
fieldObjA.dictLookup("Parent", &parentObj);
- while (parentObj.isDict()) {
+ depth = 0;
+ while (parentObj.isDict() && depth < maxFieldObjectDepth) {
if (parentObj.dictLookup("T", &obj1)->isString()) {
if (nameA->getLength()) {
@@ -373,6 +685,8 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
parentObj.dictLookup("Parent", &parentObj2);
parentObj.free();
parentObj = parentObj2;
+
+ ++depth;
}
parentObj.free();
@@ -381,6 +695,33 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
goto err1;
}
+ //----- get static XFA info
+
+ xfaFieldA = NULL;
+ if (acroFormA->xfaScanner) {
+ // convert field name to UTF-8, and remove segments that start
+ // with '#' -- to match the XFA field name
+ xfaName = nameA->toUTF8();
+ i0 = 0;
+ while (i0 < xfaName->getLength()) {
+ i1 = i0;
+ while (i1 < xfaName->getLength()) {
+ if (xfaName->getChar(i1) == '.') {
+ ++i1;
+ break;
+ }
+ ++i1;
+ }
+ if (xfaName->getChar(i0) == '#') {
+ xfaName->del(i0, i1 - i0);
+ } else {
+ i0 = i1;
+ }
+ }
+ xfaFieldA = acroFormA->xfaScanner->findField(xfaName);
+ delete xfaName;
+ }
+
//----- check for a radio button
// this is a kludge: if we see a Btn-type field with kids, and the
@@ -400,7 +741,9 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
typeA = acroFormFieldCheckbox;
}
} else if (!typeStr->cmp("Tx")) {
- if (flagsA & acroFormFlagFileSelect) {
+ if (xfaFieldA && xfaFieldA->getBarcodeInfo()) {
+ typeA = acroFormFieldBarcode;
+ } else if (flagsA & acroFormFlagFileSelect) {
typeA = acroFormFieldFileSelect;
} else if (flagsA & acroFormFlagMultiline) {
typeA = acroFormFieldMultilineText;
@@ -422,7 +765,7 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
delete typeStr;
field = new AcroFormField(acroFormA, fieldRefA, &fieldObjA,
- typeA, nameA, flagsA, typeFromParentA);
+ typeA, nameA, flagsA, typeFromParentA, xfaFieldA);
fieldObjA.free();
return field;
@@ -436,7 +779,8 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
AcroFormField::AcroFormField(AcroForm *acroFormA,
Object *fieldRefA, Object *fieldObjA,
AcroFormFieldType typeA, TextString *nameA,
- Guint flagsA, GBool typeFromParentA) {
+ Guint flagsA, GBool typeFromParentA,
+ XFAField *xfaFieldA) {
acroForm = acroFormA;
fieldRefA->copy(&fieldRef);
fieldObjA->copy(&fieldObj);
@@ -444,6 +788,7 @@ AcroFormField::AcroFormField(AcroForm *acroFormA,
name = nameA;
flags = flagsA;
typeFromParent = typeFromParentA;
+ xfaField = xfaFieldA;
}
AcroFormField::~AcroFormField() {
@@ -478,6 +823,7 @@ const char *AcroFormField::getType() {
case acroFormFieldFileSelect: return "FileSelect";
case acroFormFieldMultilineText: return "MultilineText";
case acroFormFieldText: return "Text";
+ case acroFormFieldBarcode: return "Barcode";
case acroFormFieldComboBox: return "ComboBox";
case acroFormFieldListBox: return "ListBox";
case acroFormFieldSignature: return "Signature";
@@ -508,36 +854,47 @@ Unicode *AcroFormField::getValue(int *length) {
u = NULL;
*length = 0;
- fieldLookup("V", &obj1);
- if (obj1.isName()) {
- s = obj1.getName();
- n = (int)strlen(s);
- u = (Unicode *)gmallocn(n, sizeof(Unicode));
- for (i = 0; i < n; ++i) {
- u[i] = s[i] & 0xff;
- }
- *length = n;
- } else if (obj1.isString()) {
- ts = new TextString(obj1.getString());
- n = ts->getLength();
- u = (Unicode *)gmallocn(n, sizeof(Unicode));
- memcpy(u, ts->getUnicode(), n * sizeof(Unicode));
- *length = n;
- delete ts;
- } else if (obj1.isDict()) {
- obj1.dictLookup("Contents", &obj2);
- if (obj2.isString()) {
- gs = obj2.getString();
- n = gs->getLength();
+ // if this field has a counterpart in the XFA form, take the value
+ // from the XFA field (NB: an XFA field with no value overrides the
+ // AcroForm value)
+ if (xfaField) {
+ if (xfaField->getValue()) {
+ u = utf8ToUnicode(xfaField->getValue(), length);
+ }
+
+ // no XFA form - take the AcroForm value
+ } else {
+ fieldLookup("V", &obj1);
+ if (obj1.isName()) {
+ s = obj1.getName();
+ n = (int)strlen(s);
u = (Unicode *)gmallocn(n, sizeof(Unicode));
for (i = 0; i < n; ++i) {
- u[i] = gs->getChar(i) & 0xff;
+ u[i] = s[i] & 0xff;
}
*length = n;
+ } else if (obj1.isString()) {
+ ts = new TextString(obj1.getString());
+ n = ts->getLength();
+ u = (Unicode *)gmallocn(n, sizeof(Unicode));
+ memcpy(u, ts->getUnicode(), n * sizeof(Unicode));
+ *length = n;
+ delete ts;
+ } else if (obj1.isDict()) {
+ obj1.dictLookup("Contents", &obj2);
+ if (obj2.isString()) {
+ gs = obj2.getString();
+ n = gs->getLength();
+ u = (Unicode *)gmallocn(n, sizeof(Unicode));
+ for (i = 0; i < n; ++i) {
+ u[i] = gs->getChar(i) & 0xff;
+ }
+ *length = n;
+ }
+ obj2.free();
}
- obj2.free();
+ obj1.free();
}
- obj1.free();
return u;
}
@@ -818,6 +1175,8 @@ void AcroFormField::drawAnnot(int pageNum, Gfx *gfx, GBool printing,
render = gFalse;
if (acroForm->needAppearances) {
render = gTrue;
+ } else if (xfaField && xfaField->getValue()) {
+ render = gTrue;
} else {
if (!annotObj->dictLookup("AP", &obj1)->isDict()) {
render = gTrue;
@@ -889,9 +1248,17 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
double borderWidth;
double *borderDash;
GString *appearanceState;
- int borderDashLength, rot, quadding, comb, nOptions, topIdx, i, j;
+ int borderDashLength, rot, quadding, vAlign, comb, nOptions, topIdx, i;
appearBuf = new GString();
+#if 0 //~debug
+ appearBuf->appendf("1 1 0 rg 0 0 {0:.4f} {1:.4f} re f\n",
+ xMax - xMin, yMax - yMin);
+#endif
+#if 0 //~debug
+ appearBuf->appendf("1 1 0 RG 0 0 {0:.4f} {1:.4f} re s\n",
+ xMax - xMin, yMax - yMin);
+#endif
// get the appearance characteristics (MK) dictionary
if (annot->lookup("MK", &mkObj)->isDict()) {
@@ -1141,6 +1508,9 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
asObj.free();
apObj.free();
+ int valueLength;
+ Unicode *value = getValue(&valueLength);
+
// draw the field contents
if (ftObj.isName("Btn")) {
caption = NULL;
@@ -1153,12 +1523,13 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
// radio button
if (flags & acroFormFlagRadio) {
//~ Acrobat doesn't draw a caption if there is no AP dict (?)
- if (fieldLookup("V", &obj1)
- ->isName(appearanceState->getCString())) {
+ if (value && unicodeStringEqual(value, valueLength,
+ appearanceState->getCString())) {
if (caption) {
- drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter,
- gFalse, gTrue, rot, xMin, yMin, xMax, yMax, borderWidth,
- appearBuf);
+ drawText(caption, da, fontDict, gFalse, 0,
+ acroFormQuadCenter, acroFormVAlignMiddleNoDescender,
+ gFalse, gTrue, rot, 0, 0, xMax - xMin, yMax - yMin,
+ borderWidth, gFalse, appearBuf);
} else {
if (mkDict) {
if (mkDict->lookup("BC", &obj2)->isArray() &&
@@ -1173,55 +1544,112 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
}
}
}
- obj1.free();
// pushbutton
} else if (flags & acroFormFlagPushbutton) {
if (caption) {
- drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter,
- gFalse, gFalse, rot, xMin, yMin, xMax, yMax, borderWidth,
- appearBuf);
+ drawText(caption, da, fontDict, gFalse, 0,
+ acroFormQuadCenter, acroFormVAlignMiddle,
+ gFalse, gFalse, rot, 0, 0, xMax - xMin, yMax - yMin,
+ borderWidth, gFalse, appearBuf);
}
// checkbox
} else {
- fieldLookup("V", &obj1);
- if (obj1.isName() && !(obj1.isName("Off") ||
- obj1.isName("No") ||
- obj1.isName(""))) {
+ if (value && !(unicodeStringEqual(value, valueLength, "Off") ||
+ unicodeStringEqual(value, valueLength, "No") ||
+ unicodeStringEqual(value, valueLength, "0") ||
+ valueLength == 0)) {
if (!caption) {
caption = new GString("3"); // ZapfDingbats checkmark
}
- drawText(caption, da, fontDict, gFalse, 0, acroFormQuadCenter,
- gFalse, gTrue, rot, xMin, yMin, xMax, yMax, borderWidth,
- appearBuf);
+ drawText(caption, da, fontDict, gFalse, 0,
+ acroFormQuadCenter, acroFormVAlignMiddleNoDescender,
+ gFalse, gTrue, rot, 0, 0, xMax - xMin, yMax - yMin,
+ borderWidth, gFalse, appearBuf);
}
- obj1.free();
}
if (caption) {
delete caption;
}
} else if (ftObj.isName("Tx")) {
- //~ value strings can be Unicode
- fieldLookup("V", &obj1);
- if (obj1.isString()) {
- if (fieldLookup("Q", &obj2)->isInt()) {
- quadding = obj2.getInt();
+ XFAFieldBarcodeInfo *barcodeInfo = xfaField ? xfaField->getBarcodeInfo()
+ : (XFAFieldBarcodeInfo *)NULL;
+ if (value) {
+ //~ value strings can be Unicode
+ GString *valueLatin1 = unicodeToLatin1(value, valueLength);
+ if (barcodeInfo) {
+ drawBarcode(valueLatin1, da, fontDict, rot, xMin, yMin, xMax, yMax,
+ barcodeInfo, appearBuf);
} else {
- quadding = acroFormQuadLeft;
- }
- obj2.free();
- comb = 0;
- if (flags & acroFormFlagComb) {
- if (fieldLookup("MaxLen", &obj2)->isInt()) {
- comb = obj2.getInt();
+ if (fieldLookup("Q", &obj2)->isInt()) {
+ quadding = obj2.getInt();
+ } else {
+ quadding = acroFormQuadLeft;
}
obj2.free();
+ vAlign = (flags & acroFormFlagMultiline) ? acroFormVAlignTop
+ : acroFormVAlignMiddle;
+ XFAFieldLayoutInfo *layoutInfo = xfaField ? xfaField->getLayoutInfo()
+ : (XFAFieldLayoutInfo *)NULL;
+ if (layoutInfo) {
+ switch (layoutInfo->hAlign) {
+ case xfaFieldLayoutHAlignLeft:
+ default:
+ quadding = acroFormQuadLeft;
+ break;
+ case xfaFieldLayoutHAlignCenter:
+ quadding = acroFormQuadCenter;
+ break;
+ case xfaFieldLayoutHAlignRight:
+ quadding = acroFormQuadRight;
+ break;
+ }
+ switch (layoutInfo->vAlign) {
+ case xfaFieldLayoutVAlignTop:
+ default:
+ vAlign = acroFormVAlignTop;
+ break;
+ case xfaFieldLayoutVAlignMiddle:
+ vAlign = acroFormVAlignMiddle;
+ break;
+ case xfaFieldLayoutVAlignBottom:
+ vAlign = acroFormVAlignBottom;
+ break;
+ }
+ }
+ comb = 0;
+ if (flags & acroFormFlagComb) {
+ if (fieldLookup("MaxLen", &obj2)->isInt()) {
+ comb = obj2.getInt();
+ }
+ obj2.free();
+ }
+ XFAFieldPictureInfo *pictureInfo =
+ xfaField ? xfaField->getPictureInfo()
+ : (XFAFieldPictureInfo *)NULL;
+ GString *value2 = valueLatin1;
+ if (pictureInfo) {
+ switch (pictureInfo->subtype) {
+ case xfaFieldPictureDateTime:
+ value2 = pictureFormatDateTime(valueLatin1, pictureInfo->format);
+ break;
+ case xfaFieldPictureNumeric:
+ value2 = pictureFormatNumber(valueLatin1, pictureInfo->format);
+ break;
+ case xfaFieldPictureText:
+ value2 = pictureFormatText(valueLatin1, pictureInfo->format);
+ break;
+ }
+ }
+ drawText(value2, da, fontDict,
+ flags & acroFormFlagMultiline, comb, quadding, vAlign,
+ gTrue, gFalse, rot, 0, 0, xMax - xMin, yMax - yMin,
+ borderWidth, gFalse, appearBuf);
+ if (value2 != valueLatin1) {
+ delete value2;
+ }
}
- drawText(obj1.getString(), da, fontDict,
- flags & acroFormFlagMultiline, comb, quadding,
- gTrue, gFalse, rot, xMin, yMin, xMax, yMax, borderWidth,
- appearBuf);
+ delete valueLatin1;
}
- obj1.free();
} else if (ftObj.isName("Ch")) {
//~ value/option strings can be Unicode
if (fieldLookup("Q", &obj1)->isInt()) {
@@ -1230,10 +1658,39 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
quadding = acroFormQuadLeft;
}
obj1.free();
+ vAlign = acroFormVAlignMiddle;
+ XFAFieldLayoutInfo *layoutInfo = xfaField ? xfaField->getLayoutInfo()
+ : (XFAFieldLayoutInfo *)NULL;
+ if (layoutInfo) {
+ switch (layoutInfo->hAlign) {
+ case xfaFieldLayoutHAlignLeft:
+ default:
+ quadding = acroFormQuadLeft;
+ break;
+ case xfaFieldLayoutHAlignCenter:
+ quadding = acroFormQuadCenter;
+ break;
+ case xfaFieldLayoutHAlignRight:
+ quadding = acroFormQuadRight;
+ break;
+ }
+ switch (layoutInfo->vAlign) {
+ case xfaFieldLayoutVAlignTop:
+ default:
+ vAlign = acroFormVAlignTop;
+ break;
+ case xfaFieldLayoutVAlignMiddle:
+ vAlign = acroFormVAlignMiddle;
+ break;
+ case xfaFieldLayoutVAlignBottom:
+ vAlign = acroFormVAlignBottom;
+ break;
+ }
+ }
// combo box
if (flags & acroFormFlagCombo) {
- if (fieldLookup("V", &obj1)->isString()) {
- val = obj1.getString()->copy();
+ if (value) {
+ val = unicodeToLatin1(value, valueLength);
if (fieldObj.dictLookup("Opt", &obj2)->isArray()) {
for (i = 0, done = false; i < obj2.arrayGetLength() && !done; ++i) {
obj2.arrayGet(i, &obj3);
@@ -1254,12 +1711,12 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
}
obj2.free();
drawText(val, da, fontDict,
- gFalse, 0, quadding, gTrue, gFalse, rot,
- xMin, yMin, xMax, yMax, borderWidth, appearBuf);
+ gFalse, 0, quadding, vAlign, gTrue, gFalse, rot,
+ 0, 0, xMax - xMin, yMax - yMin, borderWidth,
+ gFalse, appearBuf);
delete val;
//~ Acrobat draws a popup icon on the right side
}
- obj1.free();
// list box
} else {
if (fieldObj.dictLookup("Opt", &obj1)->isArray()) {
@@ -1285,27 +1742,15 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
// get the selected option(s)
selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
//~ need to use the I field in addition to the V field
- fieldLookup("V", &obj2);
for (i = 0; i < nOptions; ++i) {
- selection[i] = gFalse;
- if (obj2.isString()) {
- if (!obj2.getString()->cmp(text[i])) {
- selection[i] = gTrue;
- }
- } else if (obj2.isArray()) {
- for (j = 0; j < obj2.arrayGetLength(); ++j) {
- if (obj2.arrayGet(j, &obj3)->isString() &&
- !obj3.getString()->cmp(text[i])) {
- selection[i] = gTrue;
- }
- obj3.free();
- }
- }
+ selection[i] = unicodeStringEqual(value, valueLength, text[i]);
}
- obj2.free();
// get the top index
if (fieldObj.dictLookup("TI", &obj2)->isInt()) {
topIdx = obj2.getInt();
+ if (topIdx < 0 || topIdx >= nOptions) {
+ topIdx = 0;
+ }
} else {
topIdx = 0;
}
@@ -1334,14 +1779,16 @@ void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
delete da;
}
da = new GString("/Helv 10 Tf 1 0 0 rg");
- drawText(caption, da, fontDict,
- gFalse, 0, acroFormQuadLeft, gFalse, gFalse, rot,
- xMin, yMin, xMax, yMax, borderWidth, appearBuf);
+ drawText(caption, da, fontDict, gFalse, 0,
+ acroFormQuadLeft, acroFormVAlignMiddle, gFalse, gFalse, rot,
+ 0, 0, xMax - xMin, yMax - yMin, borderWidth, gFalse, appearBuf);
delete caption;
} else {
error(errSyntaxError, -1, "Unknown field type");
}
+ gfree(value);
+
delete appearanceState;
if (da) {
delete da;
@@ -1457,17 +1904,20 @@ void AcroFormField::setColor(Array *a, GBool fill, int adjust,
// Draw the variable text or caption for a field.
void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
- GBool multiline, int comb, int quadding,
+ GBool multiline, int comb,
+ int quadding, int vAlign,
GBool txField, GBool forceZapfDingbats, int rot,
- double xMin, double yMin, double xMax, double yMax,
- double border, GString *appearBuf) {
+ double x, double y, double width, double height,
+ double border, GBool whiteBackground,
+ GString *appearBuf) {
GString *text2;
GList *daToks;
GString *tok;
GfxFont *font;
double dx, dy;
- double fontSize, fontSize2, x, xPrev, y, w, w2, wMax;
- int tfPos, tmPos, i, j, k, c;
+ double fontSize, fontSize2, topBorder, xx, xPrev, yy, w, wMax;
+ double offset, offset2, charWidth, ascent, descent;
+ int tfPos, tmPos, nLines, i, j, k, c;
//~ if there is no MK entry, this should use the existing content stream,
//~ and only replace the marked content portion of it
@@ -1501,7 +1951,7 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
if (da) {
daToks = tokenize(da);
for (i = 2; i < daToks->getLength(); ++i) {
- if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
+ if (!((GString *)daToks->get(i))->cmp("Tf")) {
tfPos = i - 2;
} else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
tmPos = i - 6;
@@ -1558,23 +2008,21 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
}
appearBuf->append("q\n");
if (rot == 90) {
- appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", xMax - xMin);
- dx = yMax - yMin;
- dy = xMax - xMin;
+ appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", width);
+ dx = height;
+ dy = width;
} else if (rot == 180) {
- appearBuf->appendf("-1 0 0 -1 {0:.4f} {1:.4f} cm\n",
- xMax - xMin, yMax - yMin);
- dx = xMax - yMax;
- dy = yMax - yMin;
+ appearBuf->appendf("-1 0 0 -1 {0:.4f} {1:.4f} cm\n", width, height);
+ dx = width;
+ dy = height;
} else if (rot == 270) {
- appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin);
- dx = yMax - yMin;
- dy = xMax - xMin;
+ appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", height);
+ dx = height;
+ dy = width;
} else { // assume rot == 0
- dx = xMax - xMin;
- dy = yMax - yMin;
+ dx = width;
+ dy = height;
}
- appearBuf->append("BT\n");
// multi-line text
if (multiline) {
@@ -1582,22 +2030,29 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
wMax = dx - 2 * border - 4;
+#if 1 //~tmp
+ // this is a kludge that appears to match Adobe's behavior
+ if (height > 15) {
+ topBorder = 5;
+ } else {
+ topBorder = 2;
+ }
+#else
+ topBorder = 5;
+#endif
+
// compute font autosize
if (fontSize == 0) {
for (fontSize = 10; fontSize > 1; --fontSize) {
- y = dy - 3;
- w2 = 0;
+ yy = dy - topBorder;
i = 0;
while (i < text2->getLength()) {
getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
- if (w > w2) {
- w2 = w;
- }
i = k;
- y -= fontSize;
+ yy -= fontSize;
}
// approximate the descender for the last line
- if (y >= 0.33 * fontSize && w <= wMax) {
+ if (yy >= 0.25 * fontSize && w <= wMax) {
break;
}
}
@@ -1609,22 +2064,54 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
}
// starting y coordinate
- // (note: each line of text starts with a Td operator that moves
- // down a line)
- if (dy > fontSize + 6) {
- y = dy - 3;
+ nLines = 0;
+ i = 0;
+ while (i < text2->getLength()) {
+ getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
+ i = k;
+ ++nLines;
+ }
+ if (font) {
+ ascent = font->getDeclaredAscent() * fontSize;
+ descent = font->getDescent() * fontSize;
} else {
- y = 0.5 * dy - 0.4 * fontSize + fontSize;
+ ascent = 0.75 * fontSize;
+ descent = -0.25 * fontSize;
}
+ switch (vAlign) {
+ case acroFormVAlignTop:
+ default:
+ yy = dy - ascent - topBorder;
+ break;
+ case acroFormVAlignMiddle:
+ yy = 0.5 * (dy - nLines * fontSize) + (nLines - 1) * fontSize - descent;
+ break;
+ case acroFormVAlignMiddleNoDescender:
+ yy = 0.5 * (dy - nLines * fontSize) + (nLines - 1) * fontSize;
+ break;
+ case acroFormVAlignBottom:
+ yy = (nLines - 1) * fontSize - descent;
+ break;
+ }
+ // if the field is shorter than a line of text, Acrobat positions
+ // the text relative to the bottom edge
+ if (dy < fontSize + topBorder) {
+ yy = 2 - descent;
+ }
+ // each line of text starts with a Td operator that moves down a
+ // line -- so move up a line here
+ yy += fontSize;
+
+ appearBuf->append("BT\n");
// set the font matrix
if (tmPos >= 0) {
tok = (GString *)daToks->get(tmPos + 4);
tok->clear();
- tok->append('0');
+ tok->appendf("{0:.4f}", x);
tok = (GString *)daToks->get(tmPos + 5);
tok->clear();
- tok->appendf("{0:.4f}", y);
+ tok->appendf("{0:.4f}", y + yy);
}
// write the DA string
@@ -1636,7 +2123,7 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
// write the font matrix (if not part of the DA string)
if (tmPos < 0) {
- appearBuf->appendf("1 0 0 1 0 {0:.4f} Tm\n", y);
+ appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y + yy);
}
// write a series of lines of text
@@ -1650,18 +2137,18 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
switch (quadding) {
case acroFormQuadLeft:
default:
- x = border + 2;
+ xx = border + 2;
break;
case acroFormQuadCenter:
- x = (dx - w) / 2;
+ xx = (dx - w) / 2;
break;
case acroFormQuadRight:
- x = dx - border - 2 - w;
+ xx = dx - border - 2 - w;
break;
}
// draw the line
- appearBuf->appendf("{0:.4f} {1:.4f} Td\n", x - xPrev, -fontSize);
+ appearBuf->appendf("{0:.4f} {1:.4f} Td\n", xx - xPrev, -fontSize);
appearBuf->append('(');
for (; i < j; ++i) {
c = text2->getChar(i) & 0xff;
@@ -1678,9 +2165,11 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
// next line
i = k;
- xPrev = x;
+ xPrev = xx;
}
+ appearBuf->append("ET\n");
+
// single-line text
} else {
//~ replace newlines with spaces? - what does Acrobat do?
@@ -1689,7 +2178,7 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
if (comb > 0) {
// compute comb spacing
- w = (dx - 2 * border) / comb;
+ w = dx / comb;
// compute font autosize
if (fontSize == 0) {
@@ -1712,25 +2201,48 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
switch (quadding) {
case acroFormQuadLeft:
default:
- x = border + 2;
+ xx = 0;
break;
case acroFormQuadCenter:
- x = border + 2 + 0.5 * (comb - text2->getLength()) * w;
+ xx = ((comb - text2->getLength()) / 2) * w;
break;
case acroFormQuadRight:
- x = border + 2 + (comb - text2->getLength()) * w;
+ xx = (comb - text2->getLength()) * w;
+ break;
+ }
+ if (font) {
+ ascent = font->getDeclaredAscent() * fontSize;
+ descent = font->getDescent() * fontSize;
+ } else {
+ ascent = 0.75 * fontSize;
+ descent = -0.25 * fontSize;
+ }
+ switch (vAlign) {
+ case acroFormVAlignTop:
+ default:
+ yy = dy - ascent;
+ break;
+ case acroFormVAlignMiddle:
+ yy = 0.5 * (dy - ascent - descent);
+ break;
+ case acroFormVAlignMiddleNoDescender:
+ yy = 0.5 * (dy - ascent);
+ break;
+ case acroFormVAlignBottom:
+ yy = -descent;
break;
}
- y = 0.5 * dy - 0.4 * fontSize;
+
+ appearBuf->append("BT\n");
// set the font matrix
if (tmPos >= 0) {
tok = (GString *)daToks->get(tmPos + 4);
tok->clear();
- tok->appendf("{0:.4f}", x);
+ tok->appendf("{0:.4f}", x + xx);
tok = (GString *)daToks->get(tmPos + 5);
tok->clear();
- tok->appendf("{0:.4f}", y);
+ tok->appendf("{0:.4f}", y + yy);
}
// write the DA string
@@ -1742,29 +2254,35 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
// write the font matrix (if not part of the DA string)
if (tmPos < 0) {
- appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y);
+ appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x + xx, y + yy);
}
// write the text string
- //~ this should center (instead of left-justify) each character within
- //~ its comb cell
+ offset = 0;
for (i = 0; i < text2->getLength(); ++i) {
- if (i > 0) {
- appearBuf->appendf("{0:.4f} 0 Td\n", w);
- }
- appearBuf->append('(');
c = text2->getChar(i) & 0xff;
- if (c == '(' || c == ')' || c == '\\') {
- appearBuf->append('\\');
- appearBuf->append((char)c);
- } else if (c < 0x20 || c >= 0x80) {
- appearBuf->appendf("{0:.4f} 0 Td\n", w);
+ if (c >= 0x20 && c < 0x80) {
+ if (font && !font->isCIDFont()) {
+ charWidth = ((Gfx8BitFont *)font)->getWidth((Guchar)c) * fontSize;
+ } else {
+ // otherwise, make a crude estimate
+ charWidth = 0.5 * fontSize;
+ }
+ offset2 = 0.5 * (w - charWidth);
+ appearBuf->appendf("{0:.4f} 0 Td\n", offset + offset2);
+ if (c == '(' || c == ')' || c == '\\') {
+ appearBuf->appendf("(\\{0:c}) Tj\n", c);
+ } else {
+ appearBuf->appendf("({0:c}) Tj\n", c);
+ }
+ offset = w - offset2;
} else {
- appearBuf->append((char)c);
+ offset += w;
}
- appearBuf->append(") Tj\n");
}
+ appearBuf->append("ET\n");
+
// regular (non-comb) formatting
} else {
@@ -1802,25 +2320,54 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
switch (quadding) {
case acroFormQuadLeft:
default:
- x = border + 2;
+ xx = border + 2;
break;
case acroFormQuadCenter:
- x = (dx - w) / 2;
+ xx = (dx - w) / 2;
break;
case acroFormQuadRight:
- x = dx - border - 2 - w;
+ xx = dx - border - 2 - w;
break;
}
- y = 0.5 * dy - 0.4 * fontSize;
+ if (font) {
+ ascent = font->getDeclaredAscent() * fontSize;
+ descent = font->getDescent() * fontSize;
+ } else {
+ ascent = 0.75 * fontSize;
+ descent = -0.25 * fontSize;
+ }
+ switch (vAlign) {
+ case acroFormVAlignTop:
+ default:
+ yy = dy - ascent;
+ break;
+ case acroFormVAlignMiddle:
+ yy = 0.5 * (dy - ascent - descent);
+ break;
+ case acroFormVAlignMiddleNoDescender:
+ yy = 0.5 * (dy - ascent);
+ break;
+ case acroFormVAlignBottom:
+ yy = -descent;
+ break;
+ }
+
+ if (whiteBackground) {
+ appearBuf->appendf("q 1 g {0:.4f} {1:.4f} {2:.4f} {3:.4f} re f Q\n",
+ xx - 0.25 * fontSize, yy - 0.35 * fontSize,
+ w + 0.5 * fontSize, 1.2 * fontSize);
+ }
+
+ appearBuf->append("BT\n");
// set the font matrix
if (tmPos >= 0) {
tok = (GString *)daToks->get(tmPos + 4);
tok->clear();
- tok->appendf("{0:.4f}", x);
+ tok->appendf("{0:.4f}", x + xx);
tok = (GString *)daToks->get(tmPos + 5);
tok->clear();
- tok->appendf("{0:.4f}", y);
+ tok->appendf("{0:.4f}", y + yy);
}
// write the DA string
@@ -1832,7 +2379,7 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
// write the font matrix (if not part of the DA string)
if (tmPos < 0) {
- appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x, y);
+ appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", x + xx, y + yy);
}
// write the text string
@@ -1850,10 +2397,11 @@ void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
}
appearBuf->append(") Tj\n");
}
+
+ appearBuf->append("ET\n");
}
// cleanup
- appearBuf->append("ET\n");
appearBuf->append("Q\n");
if (txField) {
appearBuf->append("EMC\n");
@@ -2197,6 +2745,203 @@ void AcroFormField::drawCircleBottomRight(double cx, double cy, double r,
appearBuf->append("S\n");
}
+void AcroFormField::drawBarcode(GString *value, GString *da,
+ GfxFontDict *fontDict, int rot,
+ double xMin, double yMin,
+ double xMax, double yMax,
+ XFAFieldBarcodeInfo *barcodeInfo,
+ GString *appearBuf) {
+ //--- handle rotation
+ double w, h;
+ appearBuf->append("q\n");
+ switch (rot) {
+ case 0:
+ default:
+ w = xMax - xMin;
+ h = yMax - yMin;
+ break;
+ case 90:
+ appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", xMax - xMin);
+ w = yMax - yMin;
+ h = xMax - xMin;
+ break;
+ case 180:
+ appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin);
+ w = yMax - yMin;
+ h = xMax - xMin;
+ break;
+ case 270:
+ appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin);
+ w = yMax - yMin;
+ h = xMax - xMin;
+ break;
+ }
+
+ //--- get the font size
+ double fontSize = 0.2 * h;
+ if (da) {
+ GList *daToks = tokenize(da);
+ for (int i = 2; i < daToks->getLength(); ++i) {
+ if (!((GString *)daToks->get(i))->cmp("Tf")) {
+ fontSize = atof(((GString *)daToks->get(i - 1))->getCString());
+ break;
+ }
+ }
+ deleteGList(daToks, GString);
+ }
+
+ //--- compute the embedded text type position
+ GBool doText = gTrue;
+ double yText = 0;
+ int vAlign = acroFormVAlignTop;
+ double yBarcode = 0;
+ double hBarcode = 0;
+ GBool whiteBackground = gFalse;
+ //~ this uses an estimate of the font baseline position
+ if (barcodeInfo->textLocation &&
+ !barcodeInfo->textLocation->cmp("above")) {
+ yText = h;
+ vAlign = acroFormVAlignTop;
+ yBarcode = 0;
+ hBarcode = h - fontSize;
+ } else if (barcodeInfo->textLocation &&
+ !barcodeInfo->textLocation->cmp("belowEmbedded")) {
+ yText = 0;
+ vAlign = acroFormVAlignBottom;
+ yBarcode = 0;
+ hBarcode = h;
+ whiteBackground = gTrue;
+ } else if (barcodeInfo->textLocation &&
+ !barcodeInfo->textLocation->cmp("aboveEmbedded")) {
+ yText = h;
+ vAlign = acroFormVAlignTop;
+ yBarcode = 0;
+ hBarcode = h;
+ whiteBackground = gTrue;
+ } else if (barcodeInfo->textLocation &&
+ !barcodeInfo->textLocation->cmp("none")) {
+ doText = gFalse;
+ } else { // default is "below"
+ yText = 0;
+ vAlign = acroFormVAlignBottom;
+ yBarcode = fontSize;
+ hBarcode = h - fontSize;
+ }
+ double wText = w;
+
+ //--- remove extraneous start/stop chars
+ GString *value2 = value->copy();
+ if (!barcodeInfo->barcodeType->cmp("code3Of9")) {
+ if (value2->getLength() >= 1 && value2->getChar(0) == '*') {
+ value2->del(0);
+ }
+ if (value2->getLength() >= 1 &&
+ value2->getChar(value2->getLength() - 1) == '*') {
+ value2->del(value2->getLength() - 1);
+ }
+ }
+
+ //--- draw the bar code
+ if (!barcodeInfo->barcodeType->cmp("code3Of9")) {
+ if (!barcodeInfo->dataLength) {
+ error(errSyntaxError, -1,
+ "Missing 'dataLength' attribute in barcode field");
+ goto err;
+ }
+ appearBuf->append("0 g\n");
+ double wNarrow = w / ((7 + 3 * barcodeInfo->wideNarrowRatio)
+ * (barcodeInfo->dataLength + 2));
+ double xx = 0;
+ for (int i = -1; i <= value2->getLength(); ++i) {
+ int c;
+ if (i < 0 || i >= value2->getLength()) {
+ c = '*';
+ } else {
+ c = value2->getChar(i) & 0x7f;
+ }
+ for (int j = 0; j < 10; j += 2) {
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
+ xx, yBarcode,
+ (code3Of9Data[c][j] ? barcodeInfo->wideNarrowRatio
+ : 1) * wNarrow,
+ hBarcode);
+ xx += ((code3Of9Data[c][j] ? barcodeInfo->wideNarrowRatio : 1) +
+ (code3Of9Data[c][j+1] ? barcodeInfo->wideNarrowRatio : 1))
+ * wNarrow;
+ }
+ }
+ // center the text on the drawn barcode (not the max length barcode)
+ wText = (value2->getLength() + 2) * (7 + 3 * barcodeInfo->wideNarrowRatio)
+ * wNarrow;
+ } else if (!barcodeInfo->barcodeType->cmp("code128B")) {
+ if (!barcodeInfo->dataLength) {
+ error(errSyntaxError, -1,
+ "Missing 'dataLength' attribute in barcode field");
+ goto err;
+ }
+ appearBuf->append("0 g\n");
+ double wNarrow = w / (11 * (barcodeInfo->dataLength + 3) + 2);
+ double xx = 0;
+ int checksum = 0;
+ for (int i = -1; i <= value2->getLength() + 1; ++i) {
+ int c;
+ if (i == -1) {
+ // start code B
+ c = 104;
+ checksum += c;
+ } else if (i == value2->getLength()) {
+ // checksum
+ c = checksum % 103;
+ } else if (i == value2->getLength() + 1) {
+ // stop code
+ c = 106;
+ } else {
+ c = value2->getChar(i) & 0xff;
+ if (c >= 32 && c <= 127) {
+ c -= 32;
+ } else {
+ c = 0;
+ }
+ checksum += (i + 1) * c;
+ }
+ for (int j = 0; j < 6; j += 2) {
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
+ xx, yBarcode,
+ code128Data[c][j] * wNarrow, hBarcode);
+ xx += (code128Data[c][j] + code128Data[c][j+1]) * wNarrow;
+ }
+ }
+ // final bar of the stop code
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
+ xx, yBarcode, 2 * wNarrow, hBarcode);
+ // center the text on the drawn barcode (not the max length barcode)
+ wText = (11 * (value2->getLength() + 3) + 2) * wNarrow;
+ } else if (!barcodeInfo->barcodeType->cmp("pdf417")) {
+ drawPDF417Barcode(w, h, barcodeInfo->moduleWidth,
+ barcodeInfo->moduleHeight,
+ barcodeInfo->errorCorrectionLevel,
+ value2, appearBuf);
+ doText = gFalse;
+ } else {
+ error(errSyntaxError, -1,
+ "Unimplemented barcode type '{0:t}' in barcode field",
+ barcodeInfo->barcodeType);
+ }
+ //~ add other barcode types here
+
+ //--- draw the embedded text
+ if (doText) {
+ drawText(value2, da, fontDict, gFalse, 0, acroFormQuadCenter,
+ vAlign, gFalse, gFalse, 0, 0, yText, wText, yText + fontSize,
+ 0, whiteBackground, appearBuf);
+ }
+
+ appearBuf->append("Q\n");
+
+ err:
+ delete value2;
+}
+
GList *AcroFormField::tokenize(GString *s) {
GList *toks;
int i, j;
@@ -2373,19 +3118,769 @@ Object *AcroFormField::fieldLookup(const char *key, Object *obj) {
}
Object *AcroFormField::fieldLookup(Dict *dict, const char *key, Object *obj) {
- Object parent;
+ Object parent, parent2;
+ int depth;
if (!dict->lookup(key, obj)->isNull()) {
return obj;
}
obj->free();
- if (dict->lookup("Parent", &parent)->isDict()) {
- fieldLookup(parent.getDict(), key, obj);
- } else {
- // some fields don't specify a parent, so we check the AcroForm
- // dictionary just in case
- acroForm->acroFormObj.dictLookup(key, obj);
+
+ dict->lookup("Parent", &parent)->isDict();
+ depth = 0;
+ while (parent.isDict() && depth < maxFieldObjectDepth) {
+ if (!parent.dictLookup(key, obj)->isNull()) {
+ parent.free();
+ return obj;
+ }
+ obj->free();
+ parent.dictLookup("Parent", &parent2);
+ parent.free();
+ parent = parent2;
+ ++depth;
}
parent.free();
+
+ // some fields don't specify a parent, so we check the AcroForm
+ // dictionary just in case
+ acroForm->acroFormObj.dictLookup(key, obj);
return obj;
}
+
+Unicode *AcroFormField::utf8ToUnicode(GString *s, int *unicodeLength) {
+ int n = 0;
+ int i = 0;
+ Unicode u;
+ while (getUTF8(s, &i, &u)) {
+ ++n;
+ }
+ Unicode *uVec = (Unicode *)gmallocn(n, sizeof(Unicode));
+ n = 0;
+ i = 0;
+ while (getUTF8(s, &i, &uVec[n])) {
+ ++n;
+ }
+ *unicodeLength = n;
+ return uVec;
+}
+
+GString *AcroFormField::unicodeToLatin1(Unicode *u, int unicodeLength) {
+ GString *s = new GString();
+ for (int i = 0; i < unicodeLength; ++i) {
+ if (u[i] <= 0xff) {
+ s->append((char)u[i]);
+ }
+ }
+ return s;
+}
+
+GBool AcroFormField::unicodeStringEqual(Unicode *u, int unicodeLength,
+ GString *s) {
+ if (s->getLength() != unicodeLength) {
+ return gFalse;
+ }
+ for (int i = 0; i < unicodeLength; ++i) {
+ if ((s->getChar(i) & 0xff) != u[i]) {
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
+GBool AcroFormField::unicodeStringEqual(Unicode *u, int unicodeLength,
+ const char *s) {
+ for (int i = 0; i < unicodeLength; ++i) {
+ if (!s[i] || (s[i] & 0xff) != u[i]) {
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// 'picture' formatting
+//------------------------------------------------------------------------
+
+class PictureNode {
+public:
+ virtual ~PictureNode() {}
+ virtual GBool isLiteral() { return gFalse; }
+ virtual GBool isSign() { return gFalse; }
+ virtual GBool isDigit() { return gFalse; }
+ virtual GBool isDecPt() { return gFalse; }
+ virtual GBool isSeparator() { return gFalse; }
+ virtual GBool isYear() { return gFalse; }
+ virtual GBool isMonth() { return gFalse; }
+ virtual GBool isDay() { return gFalse; }
+ virtual GBool isHour() { return gFalse; }
+ virtual GBool isMinute() { return gFalse; }
+ virtual GBool isSecond() { return gFalse; }
+ virtual GBool isChar() { return gFalse; }
+};
+
+class PictureLiteral: public PictureNode {
+public:
+ PictureLiteral(GString *sA) { s = sA; }
+ virtual ~PictureLiteral() { delete s; }
+ virtual GBool isLiteral() { return gTrue; }
+ GString *s;
+};
+
+class PictureSign: public PictureNode {
+public:
+ PictureSign(char cA) { c = cA; }
+ virtual GBool isSign() { return gTrue; }
+ char c;
+};
+
+class PictureDigit: public PictureNode {
+public:
+ PictureDigit(char cA) { c = cA; pos = 0; }
+ virtual GBool isDigit() { return gTrue; }
+ char c;
+ int pos;
+};
+
+class PictureDecPt: public PictureNode {
+public:
+ PictureDecPt() { }
+ virtual GBool isDecPt() { return gTrue; }
+};
+
+class PictureSeparator: public PictureNode {
+public:
+ PictureSeparator() { }
+ virtual GBool isSeparator() { return gTrue; }
+};
+
+class PictureYear: public PictureNode {
+public:
+ PictureYear(int nDigitsA) { nDigits = nDigitsA; }
+ virtual GBool isYear() { return gTrue; }
+ int nDigits;
+};
+
+class PictureMonth: public PictureNode {
+public:
+ PictureMonth(int nDigitsA) { nDigits = nDigitsA; }
+ virtual GBool isMonth() { return gTrue; }
+ int nDigits;
+};
+
+class PictureDay: public PictureNode {
+public:
+ PictureDay(int nDigitsA) { nDigits = nDigitsA; }
+ virtual GBool isDay() { return gTrue; }
+ int nDigits;
+};
+
+class PictureHour: public PictureNode {
+public:
+ PictureHour(GBool is24HourA, int nDigitsA)
+ { is24Hour = is24HourA; nDigits = nDigitsA; }
+ virtual GBool isHour() { return gTrue; }
+ GBool is24Hour;
+ int nDigits;
+};
+
+class PictureMinute: public PictureNode {
+public:
+ PictureMinute(int nDigitsA) { nDigits = nDigitsA; }
+ virtual GBool isMinute() { return gTrue; }
+ int nDigits;
+};
+
+class PictureSecond: public PictureNode {
+public:
+ PictureSecond(int nDigitsA) { nDigits = nDigitsA; }
+ virtual GBool isSecond() { return gTrue; }
+ int nDigits;
+};
+
+class PictureChar: public PictureNode {
+public:
+ PictureChar() {}
+ virtual GBool isChar() { return gTrue; }
+};
+
+GString *AcroFormField::pictureFormatDateTime(GString *value,
+ GString *picture) {
+ GList *pic;
+ PictureNode *node;
+ GString *ret, *s;
+ char c;
+ int year, month, day, hour, min, sec;
+ int len, picStart, picEnd, u, n, i, j;
+
+ len = value->getLength();
+ if (len == 0) {
+ return value->copy();
+ }
+
+ //--- parse the value
+
+ // expected format is yyyy(-mm(-dd)?)?Thh(:mm(:ss)?)?
+ // where:
+ // - the '-'s and ':'s are optional
+ // - the 'T' is literal
+ // - we're ignoring optional time zone info at the end
+ // (if the value is not in this canonical format, we just punt and
+ // return the value string)
+ //~ another option would be to parse the value following the
+ //~ <ui><picture> element
+ year = month = day = hour = min = sec = 0;
+ i = 0;
+ if (!(i + 4 <= len && isValidInt(value, i, 4))) {
+ return value->copy();
+ }
+ year = convertInt(value, i, 4);
+ i += 4;
+ if (i < len && value->getChar(i) == '-') {
+ ++i;
+ }
+ if (i + 2 <= len && isValidInt(value, i, 2)) {
+ month = convertInt(value, i, 2);
+ i += 2;
+ if (i < len && value->getChar(i) == '-') {
+ ++i;
+ }
+ if (i + 2 <= len && isValidInt(value, i, 2)) {
+ day = convertInt(value, i, 2);
+ i += 2;
+ }
+ }
+ if (i < len) {
+ if (value->getChar(i) != 'T') {
+ return value->copy();
+ }
+ ++i;
+ if (!(i + 2 <= len && isValidInt(value, i, 2))) {
+ return value->copy();
+ }
+ hour = convertInt(value, i, 2);
+ i += 2;
+ if (i < len && value->getChar(i) == ':') {
+ ++i;
+ }
+ if (i + 2 <= len && isValidInt(value, i, 2)) {
+ min = convertInt(value, i, 2);
+ i += 2;
+ if (i < len && value->getChar(i) == ':') {
+ ++i;
+ }
+ if (i + 2 <= len && isValidInt(value, i, 2)) {
+ sec = convertInt(value, i, 2);
+ i += 2;
+ }
+ }
+ }
+ if (i < len) {
+ return value->copy();
+ }
+
+ //--- skip the category and locale in the picture
+
+ picStart = 0;
+ picEnd = picture->getLength();
+ for (i = 0; i < picture->getLength(); ++i) {
+ c = picture->getChar(i);
+ if (c == '{') {
+ picStart = i + 1;
+ for (picEnd = picStart;
+ picEnd < picture->getLength() && picture->getChar(picEnd) != '}';
+ ++picEnd) ;
+ break;
+ } else if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ c == '(' ||
+ c == ')')) {
+ break;
+ }
+ }
+
+ //--- parse the picture
+
+ pic = new GList();
+ i = picStart;
+ while (i < picEnd) {
+ c = picture->getChar(i);
+ ++i;
+ if (c == '\'') {
+ s = new GString();
+ while (i < picEnd) {
+ c = picture->getChar(i);
+ if (c == '\'') {
+ ++i;
+ if (i < picEnd && picture->getChar(i) == '\'') {
+ s->append('\'');
+ ++i;
+ } else {
+ break;
+ }
+ } else if (c == '\\') {
+ ++i;
+ if (i == picEnd) {
+ break;
+ }
+ c = picture->getChar(i);
+ ++i;
+ if (c == 'u' && i+4 <= picEnd) {
+ u = 0;
+ for (j = 0; j < 4; ++j, ++i) {
+ c = picture->getChar(i);
+ u <<= 4;
+ if (c >= '0' && c <= '9') {
+ u += c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ u += c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ u += c - 'A' + 10;
+ }
+ }
+ //~ this should convert to UTF-8 (?)
+ if (u <= 0xff) {
+ s->append((char)u);
+ }
+ } else {
+ s->append(c);
+ }
+ } else {
+ s->append(c);
+ }
+ }
+ pic->append(new PictureLiteral(s));
+ } else if (c == ',' || c == '-' || c == ':' ||
+ c == '/' || c == '.' || c == ' ') {
+ s = new GString();
+ s->append(c);
+ pic->append(new PictureLiteral(s));
+ } else if (c == 'Y') {
+ for (n = 1; n < 4 && i < picEnd && picture->getChar(i) == 'Y'; ++n, ++i) ;
+ pic->append(new PictureYear(n));
+ } else if (c == 'M') {
+ for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'M'; ++n, ++i) ;
+ pic->append(new PictureMonth(n));
+ } else if (c == 'D') {
+ for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'D'; ++n, ++i) ;
+ pic->append(new PictureDay(n));
+ } else if (c == 'h') {
+ for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'h'; ++n, ++i) ;
+ pic->append(new PictureHour(gFalse, n));
+ } else if (c == 'H') {
+ for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'H'; ++n, ++i) ;
+ pic->append(new PictureHour(gTrue, n));
+ } else if (c == 'M') {
+ for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'M'; ++n, ++i) ;
+ pic->append(new PictureMinute(n));
+ } else if (c == 'S') {
+ for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'S'; ++n, ++i) ;
+ pic->append(new PictureSecond(n));
+ }
+ }
+
+ //--- generate formatted text
+
+ ret = new GString();
+ for (i = 0; i < pic->getLength(); ++i) {
+ node = (PictureNode *)pic->get(i);
+ if (node->isLiteral()) {
+ ret->append(((PictureLiteral *)node)->s);
+ } else if (node->isYear()) {
+ if (((PictureYear *)node)->nDigits == 2) {
+ if (year >= 1930 && year < 2030) {
+ ret->appendf("{0:02d}", year % 100);
+ } else {
+ ret->append("??");
+ }
+ } else {
+ ret->appendf("{0:04d}", year);
+ }
+ } else if (node->isMonth()) {
+ if (((PictureMonth *)node)->nDigits == 1) {
+ ret->appendf("{0:d}", month);
+ } else {
+ ret->appendf("{0:02d}", month);
+ }
+ } else if (node->isDay()) {
+ if (((PictureDay *)node)->nDigits == 1) {
+ ret->appendf("{0:d}", day);
+ } else {
+ ret->appendf("{0:02d}", day);
+ }
+ } else if (node->isHour()) {
+ if (((PictureHour *)node)->is24Hour) {
+ n = hour;
+ } else {
+ n = hour % 12;
+ if (n == 0) {
+ n = 12;
+ }
+ }
+ if (((PictureHour *)node)->nDigits == 1) {
+ ret->appendf("{0:d}", n);
+ } else {
+ ret->appendf("{0:02d}", n);
+ }
+ } else if (node->isMinute()) {
+ if (((PictureMinute *)node)->nDigits == 1) {
+ ret->appendf("{0:d}", min);
+ } else {
+ ret->appendf("{0:02d}", min);
+ }
+ } else if (node->isSecond()) {
+ if (((PictureSecond *)node)->nDigits == 1) {
+ ret->appendf("{0:d}", sec);
+ } else {
+ ret->appendf("{0:02d}", sec);
+ }
+ }
+ }
+ deleteGList(pic, PictureNode);
+
+ return ret;
+}
+
+GString *AcroFormField::pictureFormatNumber(GString *value, GString *picture) {
+ GList *pic;
+ PictureNode *node;
+ GString *ret, *s;
+ GBool neg, haveDigits;
+ char c;
+ int start, decPt, trailingZero, len;
+ int picStart, picEnd, u, pos, i, j;
+
+ len = value->getLength();
+ if (len == 0) {
+ return value->copy();
+ }
+
+ //--- parse the value
+
+ // -nnnn.nnnn0000
+ // ^ ^ ^ ^
+ // | | | +-- len
+ // | | +------ trailingZero
+ // | +----------- decPt
+ // +--------------- start
+ start = 0;
+ neg = gFalse;
+ if (value->getChar(start) == '-') {
+ neg = gTrue;
+ ++start;
+ } else if (value->getChar(start) == '+') {
+ ++start;
+ }
+ for (decPt = start; decPt < len && value->getChar(decPt) != '.'; ++decPt) ;
+ for (trailingZero = len;
+ trailingZero > decPt && value->getChar(trailingZero - 1) == '0';
+ --trailingZero) ;
+
+ //--- skip the category and locale in the picture
+
+ picStart = 0;
+ picEnd = picture->getLength();
+ for (i = 0; i < picture->getLength(); ++i) {
+ c = picture->getChar(i);
+ if (c == '{') {
+ picStart = i + 1;
+ for (picEnd = picStart;
+ picEnd < picture->getLength() && picture->getChar(picEnd) != '}';
+ ++picEnd) ;
+ break;
+ } else if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ c == '(' ||
+ c == ')')) {
+ break;
+ }
+ }
+
+ //--- parse the picture
+
+ pic = new GList();
+ i = picStart;
+ while (i < picEnd) {
+ c = picture->getChar(i);
+ ++i;
+ if (c == '\'') {
+ s = new GString();
+ while (i < picEnd) {
+ c = picture->getChar(i);
+ if (c == '\'') {
+ ++i;
+ if (i < picEnd && picture->getChar(i) == '\'') {
+ s->append('\'');
+ ++i;
+ } else {
+ break;
+ }
+ } else if (c == '\\') {
+ ++i;
+ if (i == picEnd) {
+ break;
+ }
+ c = picture->getChar(i);
+ ++i;
+ if (c == 'u' && i+4 <= picEnd) {
+ u = 0;
+ for (j = 0; j < 4; ++j, ++i) {
+ c = picture->getChar(i);
+ u <<= 4;
+ if (c >= '0' && c <= '9') {
+ u += c - '0';
+ } else if (c >= 'a' && c <= 'F') {
+ u += c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ u += c - 'A' + 10;
+ }
+ }
+ //~ this should convert to UTF-8 (?)
+ if (u <= 0xff) {
+ s->append((char)u);
+ }
+ } else {
+ s->append(c);
+ }
+ } else {
+ s->append(c);
+ ++i;
+ }
+ }
+ pic->append(new PictureLiteral(s));
+ } else if (c == '-' || c == ':' || c == '/' || c == ' ') {
+ s = new GString();
+ s->append(c);
+ pic->append(new PictureLiteral(s));
+ } else if (c == 's' || c == 'S') {
+ pic->append(new PictureSign(c));
+ } else if (c == 'Z' || c == 'z' || c == '8' || c == '9') {
+ pic->append(new PictureDigit(c));
+ } else if (c == '.') {
+ pic->append(new PictureDecPt());
+ } else if (c == ',') {
+ pic->append(new PictureSeparator());
+ }
+ }
+ for (i = 0; i < pic->getLength(); ++i) {
+ node = (PictureNode *)pic->get(i);
+ if (node->isDecPt()) {
+ break;
+ }
+ }
+ pos = 0;
+ for (j = i - 1; j >= 0; --j) {
+ node = (PictureNode *)pic->get(j);
+ if (node->isDigit()) {
+ ((PictureDigit *)node)->pos = pos;
+ ++pos;
+ }
+ }
+ pos = -1;
+ for (j = i + 1; j < pic->getLength(); ++j) {
+ node = (PictureNode *)pic->get(j);
+ if (node->isDigit()) {
+ ((PictureDigit *)node)->pos = pos;
+ --pos;
+ }
+ }
+
+ //--- generate formatted text
+
+ ret = new GString();
+ haveDigits = gFalse;
+ for (i = 0; i < pic->getLength(); ++i) {
+ node = (PictureNode *)pic->get(i);
+ if (node->isLiteral()) {
+ ret->append(((PictureLiteral *)node)->s);
+ } else if (node->isSign()) {
+ if (((PictureSign *)node)->c == 'S') {
+ ret->append(neg ? '-' : ' ');
+ } else {
+ if (neg) {
+ ret->append('-');
+ }
+ }
+ } else if (node->isDigit()) {
+ pos = ((PictureDigit *)node)->pos;
+ c = ((PictureDigit *)node)->c;
+ if (pos >= 0 && pos < decPt - start) {
+ ret->append(value->getChar(decPt - 1 - pos));
+ haveDigits = gTrue;
+ } else if (pos < 0 && -pos <= trailingZero - decPt - 1) {
+ ret->append(value->getChar(decPt - pos));
+ haveDigits = gTrue;
+ } else if (c == '8' &&
+ pos < 0 &&
+ -pos <= len - decPt - 1) {
+ ret->append('0');
+ haveDigits = gTrue;
+ } else if (c == '9') {
+ ret->append('0');
+ haveDigits = gTrue;
+ } else if (c == 'Z' && pos >= 0) {
+ ret->append(' ');
+ }
+ } else if (node->isDecPt()) {
+ if (!(i+1 < pic->getLength() &&
+ ((PictureNode *)pic->get(i+1))->isDigit() &&
+ ((PictureDigit *)pic->get(i+1))->c == 'z') ||
+ trailingZero > decPt + 1) {
+ ret->append('.');
+ }
+ } else if (node->isSeparator()) {
+ if (haveDigits) {
+ ret->append(',');
+ }
+ }
+ }
+ deleteGList(pic, PictureNode);
+
+ return ret;
+}
+
+GString *AcroFormField::pictureFormatText(GString *value, GString *picture) {
+ GList *pic;
+ PictureNode *node;
+ GString *ret, *s;
+ char c;
+ int len, picStart, picEnd, u, i, j;
+
+ len = value->getLength();
+ if (len == 0) {
+ return value->copy();
+ }
+
+ //--- skip the category and locale in the picture
+
+ picStart = 0;
+ picEnd = picture->getLength();
+ for (i = 0; i < picture->getLength(); ++i) {
+ c = picture->getChar(i);
+ if (c == '{') {
+ picStart = i + 1;
+ for (picEnd = picStart;
+ picEnd < picture->getLength() && picture->getChar(picEnd) != '}';
+ ++picEnd) ;
+ break;
+ } else if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ c == '(' ||
+ c == ')')) {
+ break;
+ }
+ }
+
+ //--- parse the picture
+
+ pic = new GList();
+ i = picStart;
+ while (i < picEnd) {
+ c = picture->getChar(i);
+ ++i;
+ if (c == '\'') {
+ s = new GString();
+ while (i < picEnd) {
+ c = picture->getChar(i);
+ if (c == '\'') {
+ ++i;
+ if (i < picEnd && picture->getChar(i) == '\'') {
+ s->append('\'');
+ ++i;
+ } else {
+ break;
+ }
+ } else if (c == '\\') {
+ ++i;
+ if (i == picEnd) {
+ break;
+ }
+ c = picture->getChar(i);
+ ++i;
+ if (c == 'u' && i+4 <= picEnd) {
+ u = 0;
+ for (j = 0; j < 4; ++j, ++i) {
+ c = picture->getChar(i);
+ u <<= 4;
+ if (c >= '0' && c <= '9') {
+ u += c - '0';
+ } else if (c >= 'a' && c <= 'F') {
+ u += c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ u += c - 'A' + 10;
+ }
+ }
+ //~ this should convert to UTF-8 (?)
+ if (u <= 0xff) {
+ s->append((char)u);
+ }
+ } else {
+ s->append(c);
+ }
+ } else {
+ s->append(c);
+ ++i;
+ }
+ }
+ pic->append(new PictureLiteral(s));
+ } else if (c == ',' || c == '-' || c == ':' ||
+ c == '/' || c == '.' || c == ' ') {
+ s = new GString();
+ s->append(c);
+ pic->append(new PictureLiteral(s));
+ } else if (c == 'A' || c == 'X' || c == 'O' || c == '0' || c == '9') {
+ pic->append(new PictureChar());
+ }
+ }
+
+ //--- generate formatted text
+
+ ret = new GString();
+ j = 0;
+ for (i = 0; i < pic->getLength(); ++i) {
+ node = (PictureNode *)pic->get(i);
+ if (node->isLiteral()) {
+ ret->append(((PictureLiteral *)node)->s);
+ } else if (node->isChar()) {
+ // if there are more chars in the picture than in the value,
+ // Adobe renders the value as-is, without picture formatting
+ if (j >= value->getLength()) {
+ delete ret;
+ ret = value->copy();
+ break;
+ }
+ ret->append(value->getChar(j));
+ ++j;
+ }
+ }
+ deleteGList(pic, PictureNode);
+
+ return ret;
+}
+
+GBool AcroFormField::isValidInt(GString *s, int start, int len) {
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ if (!(start + i < s->getLength() &&
+ s->getChar(start + i) >= '0' &&
+ s->getChar(start + i) <= '9')) {
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
+int AcroFormField::convertInt(GString *s, int start, int len) {
+ char c;
+ int x, i;
+
+ x = 0;
+ for (i = 0; i < len && start + i < s->getLength(); ++i) {
+ c = s->getChar(start + i);
+ if (c < '0' || c > '9') {
+ break;
+ }
+ x = x * 10 + (c - '0');
+ }
+ return x;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h
index a92979a3e2f..7678d46dbc8 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h
@@ -15,27 +15,32 @@
#pragma interface
#endif
-#include "Form.h"
-
class TextString;
+class Gfx;
class GfxFont;
class GfxFontDict;
+class AcroFormField;
+class XFAScanner;
+class XFAField;
+class XFAFieldBarcodeInfo;
//------------------------------------------------------------------------
-class AcroForm: public Form {
+class AcroForm {
public:
static AcroForm *load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA);
- virtual ~AcroForm();
+ ~AcroForm();
- virtual const char *getType() { return "AcroForm"; }
+ const char *getType();
- virtual void draw(int pageNum, Gfx *gfx, GBool printing);
+ void draw(int pageNum, Gfx *gfx, GBool printing);
- virtual int getNumFields();
- virtual FormField *getField(int idx);
+ int getNumFields();
+ AcroFormField *getField(int idx);
+ AcroFormField *findField(int pg, double x, double y);
+ int findFieldIdx(int pg, double x, double y);
private:
@@ -44,10 +49,13 @@ private:
int lookupAnnotPage(Object *annotRef);
void scanField(Object *fieldRef);
+ PDFDoc *doc;
Object acroFormObj;
GBool needAppearances;
GList *annotPages; // [AcroFormAnnotPage]
GList *fields; // [AcroFormField]
+ XFAScanner *xfaScanner;
+ GBool isStaticXFA;
friend class AcroFormField;
};
@@ -61,28 +69,29 @@ enum AcroFormFieldType {
acroFormFieldFileSelect,
acroFormFieldMultilineText,
acroFormFieldText,
+ acroFormFieldBarcode,
acroFormFieldComboBox,
acroFormFieldListBox,
acroFormFieldSignature
};
-class AcroFormField: public FormField {
+class AcroFormField {
public:
static AcroFormField *load(AcroForm *acroFormA, Object *fieldRefA);
- virtual ~AcroFormField();
+ ~AcroFormField();
- virtual int getPageNum();
- virtual const char *getType();
- virtual Unicode *getName(int *length);
- virtual Unicode *getValue(int *length);
- virtual void getBBox(double *llx, double *lly, double *urx, double *ury);
- virtual void getFont(Ref *fontID, double *fontSize);
- virtual void getColor(double *red, double *green, double *blue);
+ int getPageNum();
+ const char *getType();
+ Unicode *getName(int *length);
+ Unicode *getValue(int *length);
+ void getBBox(double *llx, double *lly, double *urx, double *ury);
+ void getFont(Ref *fontID, double *fontSize);
+ void getColor(double *red, double *green, double *blue);
int getMaxLen();
- virtual Object *getResources(Object *res);
+ Object *getResources(Object *res);
AcroFormFieldType getAcroFormFieldType() { return type; }
Object *getFieldRef(Object *ref);
@@ -94,7 +103,7 @@ private:
AcroFormField(AcroForm *acroFormA, Object *fieldRefA, Object *fieldObjA,
AcroFormFieldType typeA, TextString *nameA,
- Guint flagsA, GBool typeFromParentA);
+ Guint flagsA, GBool typeFromParentA, XFAField *xfaFieldA);
Ref findFontName(char *fontTag);
void draw(int pageNum, Gfx *gfx, GBool printing);
void drawAnnot(int pageNum, Gfx *gfx, GBool printing,
@@ -107,10 +116,10 @@ private:
double xMax, double yMax);
void setColor(Array *a, GBool fill, int adjust, GString *appearBuf);
void drawText(GString *text, GString *da, GfxFontDict *fontDict,
- GBool multiline, int comb, int quadding,
+ GBool multiline, int comb, int quadding, int vAlign,
GBool txField, GBool forceZapfDingbats, int rot,
- double xMin, double yMin, double xMax, double yMax,
- double border, GString *appearBuf);
+ double x, double y, double width, double height,
+ double border, GBool whiteBackground, GString *appearBuf);
void drawListBox(GString **text, GBool *selection,
int nOptions, int topIdx,
GString *da, GfxFontDict *fontDict,
@@ -126,12 +135,24 @@ private:
GString *appearBuf);
void drawCircleBottomRight(double cx, double cy, double r,
GString *appearBuf);
+ void drawBarcode(GString *value, GString *da, GfxFontDict *fontDict, int rot,
+ double xMin, double yMin, double xMax, double yMax,
+ XFAFieldBarcodeInfo *barcodeInfo, GString *appearBuf);
GList *tokenize(GString *s);
Object *getAnnotObj(Object *annotObj);
Object *getAnnotResources(Dict *annot, Object *res);
void buildDefaultResourceDict(Object *dr);
Object *fieldLookup(const char *key, Object *obj);
Object *fieldLookup(Dict *dict, const char *key, Object *obj);
+ Unicode *utf8ToUnicode(GString *s, int *unicodeLength);
+ GString *unicodeToLatin1(Unicode *u, int unicodeLength);
+ GBool unicodeStringEqual(Unicode *u, int unicodeLength, GString *s);
+ GBool unicodeStringEqual(Unicode *u, int unicodeLength, const char *s);
+ GString *pictureFormatDateTime(GString *value, GString *picture);
+ GString *pictureFormatNumber(GString *value, GString *picture);
+ GString *pictureFormatText(GString *value, GString *picture);
+ GBool isValidInt(GString *s, int start, int len);
+ int convertInt(GString *s, int start, int len);
AcroForm *acroForm;
Object fieldRef;
@@ -140,6 +161,7 @@ private:
TextString *name;
Guint flags;
GBool typeFromParent;
+ XFAField *xfaField;
friend class AcroForm;
};
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc
index 61a1d434abf..d7229a2fe86 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc
@@ -25,7 +25,7 @@
#include "Lexer.h"
#include "PDFDoc.h"
#include "OptionalContent.h"
-#include "Form.h"
+#include "AcroForm.h"
#include "BuiltinFontTables.h"
#include "FontEncodingTables.h"
#include "Annot.h"
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt b/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt
index 4c88d1d351f..7b515d1d036 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt
@@ -38,7 +38,6 @@ add_library(xpdf_objs OBJECT
Dict.cc
Error.cc
FontEncodingTables.cc
- Form.cc
Function.cc
Gfx.cc
GfxFont.cc
@@ -67,7 +66,7 @@ add_library(xpdf_objs OBJECT
UnicodeRemapping.cc
UnicodeTypeTable.cc
UTF8.cc
- XFAForm.cc
+ XFAScanner.cc
XRef.cc
Zoox.cc
)
@@ -88,6 +87,7 @@ if ((QT4_FOUND OR Qt5Widgets_FOUND)
PDFCore.cc
PreScanOutputDev.cc
PSOutputDev.cc
+ ShadingImage.cc
SplashOutputDev.cc
TextOutputDev.cc
TileCache.cc
@@ -104,6 +104,7 @@ if (HAVE_SPLASH)
$<TARGET_OBJECTS:xpdf_objs>
PreScanOutputDev.cc
PSOutputDev.cc
+ ShadingImage.cc
SplashOutputDev.cc
pdftops.cc
)
@@ -151,6 +152,7 @@ if (HAVE_SPLASH AND PNG_FOUND)
add_executable(pdftohtml
$<TARGET_OBJECTS:xpdf_objs>
HTMLGen.cc
+ ShadingImage.cc
SplashOutputDev.cc
TextOutputDev.cc
WebFont.cc
@@ -215,6 +217,7 @@ install(FILES ${PROJECT_SOURCE_DIR}/doc/pdfdetach.1 DESTINATION ${CMAKE_INSTALL_
if (HAVE_SPLASH)
add_executable(pdftoppm
$<TARGET_OBJECTS:xpdf_objs>
+ ShadingImage.cc
SplashOutputDev.cc
pdftoppm.cc
)
@@ -234,6 +237,7 @@ endif ()
if (HAVE_SPLASH AND PNG_FOUND)
add_executable(pdftopng
$<TARGET_OBJECTS:xpdf_objs>
+ ShadingImage.cc
SplashOutputDev.cc
pdftopng.cc
)
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc
index bec30a8dcfd..6ea8fa89ff4 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc
@@ -28,7 +28,7 @@
#include "Page.h"
#include "Error.h"
#include "Link.h"
-#include "Form.h"
+#include "AcroForm.h"
#include "TextString.h"
#include "Catalog.h"
@@ -159,6 +159,7 @@ Catalog::Catalog(PDFDoc *docA) {
baseURI = NULL;
form = NULL;
embeddedFiles = NULL;
+ pageLabels = NULL;
#if MULTITHREADED
gInitMutex(&pageMutex);
#endif
@@ -222,7 +223,7 @@ Catalog::Catalog(PDFDoc *docA) {
catDict.dictLookup("AcroForm", &acroForm);
// get the NeedsRendering flag
- // NB: Form::load() uses this value
+ // NB: AcroForm::load() uses this value
needsRendering = catDict.dictLookup("NeedsRendering", &obj)->isBool() &&
obj.getBool();
obj.free();
@@ -230,7 +231,7 @@ Catalog::Catalog(PDFDoc *docA) {
// create the Form
// (if acroForm is a null object, this will still create an AcroForm
// if there are unattached Widget-type annots)
- form = Form::load(doc, this, &acroForm);
+ form = AcroForm::load(doc, this, &acroForm);
// get the OCProperties dictionary
catDict.dictLookup("OCProperties", &ocProperties);
@@ -241,7 +242,6 @@ Catalog::Catalog(PDFDoc *docA) {
// get the ViewerPreferences object
catDict.dictLookupNF("ViewerPreferences", &viewerPrefs);
- pageLabels = NULL;
if (catDict.dictLookup("PageLabels", &obj)->isDict()) {
readPageLabelTree(&obj);
}
@@ -1190,7 +1190,7 @@ GBool Catalog::convertPageLabelToInt(TextString *pageLabel, int prefixLength,
return gFalse;
}
}
- *n = (len - prefixLength - 1) * 26 + (u[i] - (Unicode)style) + 1;
+ *n = (len - prefixLength - 1) * 26 + (u[prefixLength] - (Unicode)style) + 1;
return gTrue;
}
return gFalse;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h
index b775ae9b9b8..0acdd2d1574 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h
@@ -30,7 +30,7 @@ struct Ref;
class LinkDest;
class PageTreeNode;
class PageLabelNode;
-class Form;
+class AcroForm;
class TextString;
//------------------------------------------------------------------------
@@ -88,7 +88,7 @@ public:
Object *getAcroForm() { return &acroForm; }
- Form *getForm() { return form; }
+ AcroForm *getForm() { return form; }
GBool getNeedsRendering() { return needsRendering; }
@@ -137,7 +137,7 @@ private:
Object outline; // outline dictionary
Object acroForm; // AcroForm dictionary
GBool needsRendering; // NeedsRendering flag
- Form *form; // parsed form
+ AcroForm *form; // parsed form
Object ocProperties; // OCProperties dictionary
GList *embeddedFiles; // embedded file list [EmbeddedFile]
GList *pageLabels; // page labels [PageLabelNode]
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc
index 0d064cff379..5eec7a9fc6c 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc
@@ -470,14 +470,24 @@ GBool CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n,
int offset) {
CharCode oldLen, i;
+#if 1 //~utf16
+ Unicode u[maxUnicodeString];
+ int uLen, j;
+#else
Unicode u;
int j;
+#endif
if (code > 0xffffff) {
// This is an arbitrary limit to avoid integer overflow issues.
// (I've seen CMaps with mappings for <ffffffff>.)
return;
}
+#if 1 //~utf16
+ if ((uLen = parseUTF16String(uStr, n, u)) == 0) {
+ return;
+ }
+#endif
if (code >= mapLen) {
oldLen = mapLen;
mapLen = mapLen ? 2 * mapLen : 256;
@@ -489,6 +499,25 @@ void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n,
map[i] = 0;
}
}
+#if 1 //~utf16
+ if (uLen == 1) {
+ map[code] = u[0] + offset;
+ } else {
+ if (sMapLen >= sMapSize) {
+ sMapSize = sMapSize + 16;
+ sMap = (CharCodeToUnicodeString *)
+ greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
+ }
+ map[code] = 0;
+ sMap[sMapLen].c = code;
+ for (j = 0; j < uLen; ++j) {
+ sMap[sMapLen].u[j] = u[j];
+ }
+ sMap[sMapLen].u[uLen - 1] += offset;
+ sMap[sMapLen].len = uLen;
+ ++sMapLen;
+ }
+#else //~utf16
if (n <= 4) {
if (!parseHex(uStr, n, &u)) {
error(errSyntaxWarning, -1, "Illegal entry in ToUnicode CMap");
@@ -515,6 +544,36 @@ void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n,
sMap[sMapLen].u[sMap[sMapLen].len - 1] += offset;
++sMapLen;
}
+#endif //~utf16
+}
+
+// Convert a UTF-16BE hex string into a sequence of up to
+// maxUnicodeString Unicode chars.
+int CharCodeToUnicode::parseUTF16String(char *uStr, int n, Unicode *uOut) {
+ int i = 0;
+ int uLen = 0;
+ while (i < n) {
+ Unicode u;
+ int j = n;
+ if (j - i > 4) {
+ j = i + 4;
+ }
+ if (!parseHex(uStr + i, j - i, &u)) {
+ error(errSyntaxWarning, -1, "Illegal entry in ToUnicode CMap");
+ return 0;
+ }
+ // look for a UTF-16 pair
+ if (uLen > 0 && uOut[uLen-1] >= 0xd800 && uOut[uLen-1] <= 0xdbff &&
+ u >= 0xdc00 && u <= 0xdfff) {
+ uOut[uLen-1] = 0x10000 + ((uOut[uLen-1] & 0x03ff) << 10) + (u & 0x03ff);
+ } else {
+ if (uLen < maxUnicodeString) {
+ uOut[uLen++] = u;
+ }
+ }
+ i = j;
+ }
+ return uLen;
}
void CharCodeToUnicode::addMappingInt(CharCode code, Unicode u) {
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h b/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h
index 5d7bea5b057..c390a8c5c50 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h
@@ -80,6 +80,7 @@ private:
GBool parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);
void addMapping(CharCode code, char *uStr, int n, int offset);
+ int parseUTF16String(char *uStr, int n, Unicode *uOut);
void addMappingInt(CharCode code, Unicode u);
CharCodeToUnicode();
CharCodeToUnicode(GString *tagA);
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Form.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Form.cc
deleted file mode 100644
index 681401ad5de..00000000000
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Form.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-//========================================================================
-//
-// Form.cc
-//
-// Copyright 2012 Glyph & Cog, LLC
-//
-//========================================================================
-
-#include <aconf.h>
-
-#ifdef USE_GCC_PRAGMAS
-#pragma implementation
-#endif
-
-#include "gmempp.h"
-#include "GlobalParams.h"
-#include "Error.h"
-#include "Object.h"
-#include "PDFDoc.h"
-#include "AcroForm.h"
-#include "XFAForm.h"
-#include "Form.h"
-
-//------------------------------------------------------------------------
-// Form
-//------------------------------------------------------------------------
-
-Form *Form::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObj) {
- Form *form;
- Object xfaObj;
-
- if (acroFormObj->isDict()) {
- //~ temporary: create an XFAForm only for XFAF, not for dynamic XFA
- acroFormObj->dictLookup("XFA", &xfaObj);
- if (globalParams->getEnableXFA() &&
- !xfaObj.isNull() &&
- !catalog->getNeedsRendering()) {
- form = XFAForm::load(docA, catalog, acroFormObj, &xfaObj);
- xfaObj.free();
- return form;
- }
- xfaObj.free();
- }
-
- // if acroFormObj is a null object, this will still create an
- // AcroForm if there are unattached Widget-type annots
- return AcroForm::load(docA, catalog, acroFormObj);
-}
-
-Form::Form(PDFDoc *docA) {
- doc = docA;
-}
-
-Form::~Form() {
-}
-
-FormField *Form::findField(int pg, double x, double y) {
- FormField *field;
- double llx, lly, urx, ury;
- int i;
-
- for (i = 0; i < getNumFields(); ++i) {
- field = getField(i);
- if (field->getPageNum() == pg) {
- field->getBBox(&llx, &lly, &urx, &ury);
- if (llx <= x && x <= urx && lly <= y && y <= ury) {
- return field;
- }
- }
- }
- return NULL;
-}
-
-int Form::findFieldIdx(int pg, double x, double y) {
- FormField *field;
- double llx, lly, urx, ury;
- int i;
-
- for (i = 0; i < getNumFields(); ++i) {
- field = getField(i);
- if (field->getPageNum() == pg) {
- field->getBBox(&llx, &lly, &urx, &ury);
- if (llx <= x && x <= urx && lly <= y && y <= ury) {
- return i;
- }
- }
- }
- return -1;
-}
-
-//------------------------------------------------------------------------
-// FormField
-//------------------------------------------------------------------------
-
-FormField::FormField() {
-}
-
-FormField::~FormField() {
-}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Form.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Form.h
deleted file mode 100644
index 0e851fe16e0..00000000000
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Form.h
+++ /dev/null
@@ -1,77 +0,0 @@
-//========================================================================
-//
-// Form.h
-//
-// Copyright 2012 Glyph & Cog, LLC
-//
-//========================================================================
-
-#ifndef FORM_H
-#define FORM_H
-
-#include <aconf.h>
-
-#ifdef USE_GCC_PRAGMAS
-#pragma interface
-#endif
-
-#include "gtypes.h"
-
-class Gfx;
-class FormField;
-
-//------------------------------------------------------------------------
-
-class Form {
-public:
-
- static Form *load(PDFDoc *docA, Catalog *catalog, Object *acroFormObj);
-
- virtual ~Form();
-
- virtual const char *getType() = 0;
-
- virtual void draw(int pageNum, Gfx *gfx, GBool printing) = 0;
-
- virtual int getNumFields() = 0;
- virtual FormField *getField(int idx) = 0;
-
- FormField *findField(int pg, double x, double y);
- int findFieldIdx(int pg, double x, double y);
-
-protected:
-
- Form(PDFDoc *docA);
-
- PDFDoc *doc;
-};
-
-//------------------------------------------------------------------------
-
-class FormField {
-public:
-
- FormField();
- virtual ~FormField();
-
- virtual int getPageNum() = 0;
- virtual const char *getType() = 0;
-
- // Return the field name. This never returns NULL.
- virtual Unicode *getName(int *length) = 0;
-
- // Return the field value. This returns NULL if the field does not
- // have a value.
- virtual Unicode *getValue(int *length) = 0;
-
- virtual void getBBox(double *llx, double *lly, double *urx, double *ury) = 0;
- virtual void getFont(Ref *fontID, double *fontSize) = 0;
- virtual void getColor(double *red, double *green, double *blue) = 0;
-
- // Return the resource dictionaries used to draw this field. The
- // returned object must be either a dictionary or an array of
- // dictonaries.
- virtual Object *getResources(Object *res) = 0;
-};
-
-#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc
index 7a0c9c6a9a5..ed98d9d165f 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc
@@ -21,6 +21,7 @@
#include "gmempp.h"
#include "GString.h"
#include "GList.h"
+#include "Trace.h"
#include "GlobalParams.h"
#include "CharTypes.h"
#include "Object.h"
@@ -146,7 +147,15 @@ Operator Gfx::opTab[] = {
&Gfx::opSetStrokeRGBColor},
{"S", 0, {tchkNone},
&Gfx::opStroke},
- {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum},
&Gfx::opSetStrokeColor},
{"SCN", -33, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
@@ -240,7 +249,15 @@ Operator Gfx::opTab[] = {
&Gfx::opSetRenderingIntent},
{"s", 0, {tchkNone},
&Gfx::opCloseStroke},
- {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum},
&Gfx::opSetFillColor},
{"scn", -33, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
tchkSCN, tchkSCN, tchkSCN, tchkSCN,
@@ -277,6 +294,7 @@ GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
Ref r;
if (resDict) {
+ valid = gTrue;
// build font dictionary
fonts = NULL;
@@ -312,6 +330,7 @@ GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
resDict->lookup("Properties", &propsDict);
} else {
+ valid = gFalse;
fonts = NULL;
xObjDict.initNull();
colorSpaceDict.initNull();
@@ -394,7 +413,8 @@ GBool GfxResources::lookupXObjectNF(const char *name, Object *obj) {
return gFalse;
}
-void GfxResources::lookupColorSpace(const char *name, Object *obj) {
+void GfxResources::lookupColorSpace(const char *name, Object *obj,
+ GBool inherit) {
GfxResources *resPtr;
//~ should also test for G, RGB, and CMYK - but only in inline images (?)
@@ -411,6 +431,9 @@ void GfxResources::lookupColorSpace(const char *name, Object *obj) {
}
obj->free();
}
+ if (!inherit && valid) {
+ break;
+ }
}
obj->initNull();
}
@@ -692,7 +715,7 @@ void Gfx::go(GBool topLevel) {
aborted = gFalse;
errCount = 0;
numArgs = 0;
- parser->getObj(&obj);
+ getContentObj(&obj);
while (!obj.isEOF()) {
// check for an abort
@@ -748,7 +771,7 @@ void Gfx::go(GBool topLevel) {
}
// grab the next object
- parser->getObj(&obj);
+ getContentObj(&obj);
}
obj.free();
@@ -772,6 +795,15 @@ void Gfx::go(GBool topLevel) {
}
}
+void Gfx::getContentObj(Object *obj) {
+ parser->getObj(obj);
+ if (obj->isRef()) {
+ error(errSyntaxError, getPos(), "Indirect reference in content stream");
+ obj->free();
+ obj->initError();
+ }
+}
+
// Returns true if successful, false on error.
GBool Gfx::execOp(Object *cmd, Object args[], int numArgs) {
Operator *op;
@@ -807,10 +839,9 @@ GBool Gfx::execOp(Object *cmd, Object args[], int numArgs) {
}
} else {
if (numArgs > -op->numArgs) {
- error(errSyntaxError, getPos(),
+ error(errSyntaxWarning, getPos(),
"Too many ({0:d}) args to '{1:s}' operator",
numArgs, name);
- return gFalse;
}
}
for (i = 0; i < numArgs; ++i) {
@@ -951,14 +982,11 @@ void Gfx::opSetLineWidth(Object args[], int numArgs) {
}
void Gfx::opSetExtGState(Object args[], int numArgs) {
- Object obj1, obj2, obj3, objRef3, obj4, obj5;
+ Object obj1, obj2, obj3, objRef3, obj4, obj5, backdropColorObj;
Object args2[2];
GfxBlendMode mode;
GBool haveFillOP;
Function *funcs[4];
- GfxColor backdropColor;
- GBool haveBackdropColor;
- GfxColorSpace *blendingColorSpace;
GBool alpha, knockout;
double opac;
int i;
@@ -1167,48 +1195,20 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
}
}
obj3.free();
- if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {
- for (i = 0; i < gfxColorMaxComps; ++i) {
- backdropColor.c[i] = 0;
- }
- for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {
- obj3.arrayGet(i, &obj4);
- if (obj4.isNum()) {
- backdropColor.c[i] = dblToCol(obj4.getNum());
- }
- obj4.free();
- }
- }
- obj3.free();
+ obj2.dictLookup("BC", &backdropColorObj);
if (obj2.dictLookup("G", &obj3)->isStream()) {
if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
- blendingColorSpace = NULL;
knockout = gFalse;
- if (!obj4.dictLookup("CS", &obj5)->isNull()) {
- blendingColorSpace = GfxColorSpace::parse(&obj5
- );
- }
- obj5.free();
if (obj4.dictLookup("K", &obj5)->isBool()) {
knockout = obj5.getBool();
}
obj5.free();
- if (!haveBackdropColor) {
- if (blendingColorSpace) {
- blendingColorSpace->getDefaultColor(&backdropColor);
- } else {
- //~ need to get the parent or default color space (?)
- for (i = 0; i < gfxColorMaxComps; ++i) {
- backdropColor.c[i] = 0;
- }
- }
- }
obj2.dictLookupNF("G", &objRef3);
// it doesn't make sense for softmasks to be non-isolated,
// because they're blended with a backdrop color, rather
// than the original backdrop
- doSoftMask(&obj3, &objRef3, alpha, blendingColorSpace,
- gTrue, knockout, funcs[0], &backdropColor);
+ doSoftMask(&obj3, &objRef3, alpha, gTrue, knockout, funcs[0],
+ &backdropColorObj);
objRef3.free();
if (funcs[0]) {
delete funcs[0];
@@ -1223,6 +1223,7 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
"Invalid soft mask in ExtGState - missing group");
}
obj3.free();
+ backdropColorObj.free();
} else if (!obj2.isNull()) {
error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState");
}
@@ -1233,9 +1234,8 @@ void Gfx::opSetExtGState(Object args[], int numArgs) {
}
void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha,
- GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
- Function *transferFunc, GfxColor *backdropColor) {
+ Function *transferFunc, Object *backdropColorObj) {
Dict *dict, *resDict;
double m[6], bbox[4];
Object obj1, obj2;
@@ -1291,14 +1291,10 @@ void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha,
// draw it
++formDepth;
- drawForm(strRef, resDict, m, bbox, gTrue, gTrue,
- blendingColorSpace, isolated, knockout,
- alpha, transferFunc, backdropColor);
+ drawForm(strRef, resDict, m, bbox, gTrue, gTrue, isolated, knockout,
+ alpha, transferFunc, backdropColorObj);
--formDepth;
- if (blendingColorSpace) {
- delete blendingColorSpace;
- }
obj1.free();
}
@@ -1502,6 +1498,10 @@ void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
}
}
+// Technically, per the PDF spec, the 'sc' operator can only be used
+// with device, CIE, and Indexed color spaces. Some buggy PDF
+// generators use it for DeviceN, so we also allow that (same as
+// 'scn', minus the optional pattern name argument).
void Gfx::opSetFillColor(Object args[], int numArgs) {
GfxColor color;
int i;
@@ -1524,6 +1524,10 @@ void Gfx::opSetFillColor(Object args[], int numArgs) {
out->updateFillColor(state);
}
+// Technically, per the PDF spec, the 'SC' operator can only be used
+// with device, CIE, and Indexed color spaces. Some buggy PDF
+// generators use it for DeviceN, so we also allow that (same as
+// 'SCN', minus the optional pattern name argument).
void Gfx::opSetStrokeColor(Object args[], int numArgs) {
GfxColor color;
int i;
@@ -1998,6 +2002,7 @@ void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height,
state->lineTo(0, 1);
state->closePath();
doPatternFill(gTrue);
+ state->clearPath();
restoreState();
}
@@ -2318,34 +2323,8 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
state->clearPath();
}
-#if 1 //~tmp: turn off anti-aliasing temporarily
- out->setInShading(gTrue);
-#endif
-
- // do shading type-specific operations
- switch (shading->getType()) {
- case 1:
- doFunctionShFill((GfxFunctionShading *)shading);
- break;
- case 2:
- doAxialShFill((GfxAxialShading *)shading);
- break;
- case 3:
- doRadialShFill((GfxRadialShading *)shading);
- break;
- case 4:
- case 5:
- doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
- break;
- case 6:
- case 7:
- doPatchMeshShFill((GfxPatchMeshShading *)shading);
- break;
- }
-
-#if 1 //~tmp: turn off anti-aliasing temporarily
- out->setInShading(gFalse);
-#endif
+ // perform the fill
+ doShFill(shading);
// restore graphics state
restoreStateStack(savedState);
@@ -2395,9 +2374,19 @@ void Gfx::opShFill(Object args[], int numArgs) {
state->setFillColorSpace(shading->getColorSpace()->copy());
out->updateFillColorSpace(state);
-#if 1 //~tmp: turn off anti-aliasing temporarily
- out->setInShading(gTrue);
-#endif
+ // perform the fill
+ doShFill(shading);
+
+ // restore graphics state
+ restoreStateStack(savedState);
+
+ delete shading;
+}
+
+void Gfx::doShFill(GfxShading *shading) {
+ if (out->shadedFill(state, shading)) {
+ return;
+ }
// do shading type-specific operations
switch (shading->getType()) {
@@ -2419,26 +2408,12 @@ void Gfx::opShFill(Object args[], int numArgs) {
doPatchMeshShFill((GfxPatchMeshShading *)shading);
break;
}
-
-#if 1 //~tmp: turn off anti-aliasing temporarily
- out->setInShading(gFalse);
-#endif
-
- // restore graphics state
- restoreStateStack(savedState);
-
- delete shading;
}
void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
double x0, y0, x1, y1;
GfxColor colors[4];
- if (out->useShadedFills() &&
- out->functionShadedFill(state, shading)) {
- return;
- }
-
shading->getDomain(&x0, &y0, &x1, &y1);
shading->getColor(x0, y0, &colors[0]);
shading->getColor(x0, y1, &colors[1]);
@@ -2567,11 +2542,6 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) {
GfxColor colors[axialSplits];
int abortCheckCounter, nComps, i, j, k;
- if (out->useShadedFills() &&
- out->axialShadedFill(state, shading)) {
- return;
- }
-
// get the clip region bbox
state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
@@ -2872,11 +2842,6 @@ void Gfx::doRadialShFill(GfxRadialShading *shading) {
double theta, alpha, angle, t;
int abortCheckCounter, ia, ib, k, n;
- if (out->useShadedFills() &&
- out->radialShadedFill(state, shading)) {
- return;
- }
-
// get the shading info
shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
t0 = shading->getDomain0();
@@ -3515,8 +3480,8 @@ void Gfx::fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth) {
}
for (i = 0; i < shading->getNComps(); ++i) {
patch00.color[0][0][i] = patch->color[0][0][i];
- patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] +
- patch->color[0][1][i]);
+ patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] +
+ patch->color[0][1][i]);
patch01.color[0][0][i] = patch00.color[0][1][i];
patch01.color[0][1][i] = patch->color[0][1][i];
patch01.color[1][1][i] = 0.5 * (patch->color[0][1][i] +
@@ -3905,6 +3870,19 @@ void Gfx::doShowText(GString *s) {
//~ the CTM concat values here are wrong (but never used)
out->updateCTM(state, 1, 0, 0, 1, 0, 0);
state->transformDelta(dx, dy, &ddx, &ddy);
+#if 0
+ // The PDF spec says: "the graphics state shall be inherited
+ // from the environment of the text-showing operator that caused
+ // the [Type 3] glyph description to be invoked". However,
+ // Acrobat apparently copies the fill color to the stroke color.
+ // It looks like Ghostscript does the same. (This is only
+ // relevant for uncached Type 3 glyphs.) Uncomment this block
+ // to make Xpdf mimic Adobe (in violation of the PDF spec).
+ state->setStrokeColorSpace(state->getFillColorSpace()->copy());
+ out->updateStrokeColorSpace(state);
+ state->setStrokeColor(state->getFillColor());
+ out->updateStrokeColor(state);
+#endif
if (!out->beginType3Char(state, curX + riseX, curY + riseY, ddx, ddy,
code, u, uLen)) {
((Gfx8BitFont *)font)->getCharProcNF(code, &charProcRef);
@@ -4149,7 +4127,7 @@ GBool Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
GBool mask, invert;
GfxColorSpace *colorSpace, *maskColorSpace;
GfxImageColorMap *colorMap, *maskColorMap;
- Object maskObj, smaskObj;
+ Object maskObj, smaskObj, maskRef;
GBool haveColorKeyMask, haveExplicitMask, haveSoftMask, haveMatte;
int maskColors[2*gfxColorMaxComps];
int maskWidth, maskHeight;
@@ -4591,15 +4569,20 @@ GBool Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
// draw it
} else {
if (haveSoftMask) {
+ dict->lookupNF("Mask", &maskRef);
out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
- maskStr, maskWidth, maskHeight, maskColorMap,
+ &maskRef, maskStr, maskWidth, maskHeight,
+ maskColorMap,
haveMatte ? matte : (double *)NULL,
interpolate);
+ maskRef.free();
delete maskColorMap;
} else if (haveExplicitMask) {
+ dict->lookupNF("Mask", &maskRef);
out->drawMaskedImage(state, ref, str, width, height, colorMap,
- maskStr, maskWidth, maskHeight, maskInvert,
- interpolate);
+ &maskRef, maskStr, maskWidth, maskHeight,
+ maskInvert, interpolate);
+ maskRef.free();
} else {
out->drawImage(state, ref, str, width, height, colorMap,
haveColorKeyMask ? maskColors : (int *)NULL, inlineImg,
@@ -4642,7 +4625,6 @@ GBool Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
void Gfx::doForm(Object *strRef, Object *str) {
Dict *dict;
GBool transpGroup, isolated, knockout;
- GfxColorSpace *blendingColorSpace;
Object matrixObj, bboxObj;
double m[6], bbox[4];
Object resObj;
@@ -4705,15 +4687,9 @@ void Gfx::doForm(Object *strRef, Object *str) {
// check for a transparency group
transpGroup = isolated = knockout = gFalse;
- blendingColorSpace = NULL;
if (dict->lookup("Group", &obj1)->isDict()) {
if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
transpGroup = gTrue;
- if (!obj1.dictLookup("CS", &obj3)->isNull()) {
- blendingColorSpace = GfxColorSpace::parse(&obj3
- );
- }
- obj3.free();
if (obj1.dictLookup("I", &obj3)->isBool()) {
isolated = obj3.getBool();
}
@@ -4729,25 +4705,23 @@ void Gfx::doForm(Object *strRef, Object *str) {
// draw it
++formDepth;
- drawForm(strRef, resDict, m, bbox,
- transpGroup, gFalse, blendingColorSpace, isolated, knockout);
+ drawForm(strRef, resDict, m, bbox, transpGroup, gFalse, isolated, knockout);
--formDepth;
- if (blendingColorSpace) {
- delete blendingColorSpace;
- }
resObj.free();
}
void Gfx::drawForm(Object *strRef, Dict *resDict,
double *matrix, double *bbox,
GBool transpGroup, GBool softMask,
- GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
GBool alpha, Function *transferFunc,
- GfxColor *backdropColor) {
+ Object *backdropColorObj) {
Parser *oldParser;
GfxState *savedState;
+ GfxColorSpace *blendingColorSpace;
+ GfxColor backdropColor;
+ Object strObj, groupAttrsObj, csObj, obj1;
double oldBaseMatrix[6];
int i;
@@ -4779,7 +4753,23 @@ void Gfx::drawForm(Object *strRef, Dict *resDict,
out->clip(state);
state->clearPath();
+ blendingColorSpace = NULL;
if (softMask || transpGroup) {
+ // get the blending color space
+ // NB: this must be done AFTER pushing the resource dictionary,
+ // so that any Default*** color spaces are available
+ strRef->fetch(xref, &strObj);
+ if (strObj.streamGetDict()->lookup("Group", &groupAttrsObj)->isDict()) {
+ if (!groupAttrsObj.dictLookup("CS", &csObj)->isNull()) {
+ blendingColorSpace = GfxColorSpace::parse(&csObj
+ );
+ }
+ csObj.free();
+ }
+ groupAttrsObj.free();
+ strObj.free();
+
+ traceBegin(oldBaseMatrix, softMask ? "begin soft mask" : "begin t-group");
if (state->getBlendMode() != gfxBlendNormal) {
state->setBlendMode(gfxBlendNormal);
out->updateBlendMode(state);
@@ -4831,9 +4821,32 @@ void Gfx::drawForm(Object *strRef, Dict *resDict,
popResources();
if (softMask) {
- out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ backdropColor.c[i] = 0;
+ }
+ if (backdropColorObj->isArray()) {
+ for (i = 0;
+ i < backdropColorObj->arrayGetLength() && i < gfxColorMaxComps;
+ ++i) {
+ backdropColorObj->arrayGet(i, &obj1);
+ if (obj1.isNum()) {
+ backdropColor.c[i] = dblToCol(obj1.getNum());
+ }
+ obj1.free();
+ }
+ } else if (blendingColorSpace) {
+ blendingColorSpace->getDefaultColor(&backdropColor);
+ }
+ //~ else: need to get the parent or default color space (?)
+ out->setSoftMask(state, bbox, alpha, transferFunc, &backdropColor);
+ traceEnd(oldBaseMatrix, "end soft mask");
} else if (transpGroup) {
out->paintTransparencyGroup(state, bbox);
+ traceEnd(oldBaseMatrix, "end t-group");
+ }
+
+ if (blendingColorSpace) {
+ delete blendingColorSpace;
}
return;
@@ -4911,7 +4924,7 @@ Stream *Gfx::buildImageStream(GBool *haveLength) {
// build dictionary
dict.initDict(xref);
- parser->getObj(&obj);
+ getContentObj(&obj);
while (!obj.isCmd("ID") && !obj.isEOF()) {
if (!obj.isName()) {
error(errSyntaxError, getPos(),
@@ -4920,14 +4933,19 @@ Stream *Gfx::buildImageStream(GBool *haveLength) {
} else {
key = copyString(obj.getName());
obj.free();
- parser->getObj(&obj);
- if (obj.isEOF() || obj.isError()) {
+ getContentObj(&obj);
+ if (obj.isEOF()) {
gfree(key);
break;
}
- dict.dictAdd(key, &obj);
+ if (obj.isError()) {
+ gfree(key);
+ obj.free();
+ } else {
+ dict.dictAdd(key, &obj);
+ }
}
- parser->getObj(&obj);
+ getContentObj(&obj);
}
if (obj.isEOF()) {
error(errSyntaxError, getPos(), "End of file in inline image");
@@ -5108,7 +5126,7 @@ void Gfx::drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle,
// get the form bounding box
dict->lookup("BBox", &bboxObj);
- if (!bboxObj.isArray()) {
+ if (!bboxObj.isArray() || bboxObj.arrayGetLength() != 4) {
error(errSyntaxError, getPos(), "Bad form bounding box");
bboxObj.free();
str.free();
@@ -5116,7 +5134,11 @@ void Gfx::drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle,
}
for (i = 0; i < 4; ++i) {
bboxObj.arrayGet(i, &obj1);
- bbox[i] = obj1.getNum();
+ if (obj1.isNum()) {
+ bbox[i] = obj1.getNum();
+ } else {
+ bbox[i] = 0;
+ }
obj1.free();
}
bboxObj.free();
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h
index 2159eaaf2e9..4625ac289d0 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h
@@ -76,7 +76,7 @@ public:
GfxFont *lookupFontByRef(Ref ref);
GBool lookupXObject(const char *name, Object *obj);
GBool lookupXObjectNF(const char *name, Object *obj);
- void lookupColorSpace(const char *name, Object *obj);
+ void lookupColorSpace(const char *name, Object *obj, GBool inherit = gTrue);
GfxPattern *lookupPattern(const char *name
);
GfxShading *lookupShading(const char *name
@@ -88,6 +88,7 @@ public:
private:
+ GBool valid;
GfxFontDict *fonts;
Object xObjDict;
Object colorSpaceDict;
@@ -164,10 +165,9 @@ public:
void drawForm(Object *strRef, Dict *resDict, double *matrix, double *bbox,
GBool transpGroup = gFalse, GBool softMask = gFalse,
- GfxColorSpace *blendingColorSpace = NULL,
GBool isolated = gFalse, GBool knockout = gFalse,
GBool alpha = gFalse, Function *transferFunc = NULL,
- GfxColor *backdropColor = NULL);
+ Object *backdropColorObj = NULL);
// Take all of the content stream stack entries from <oldGfx>. This
// is useful when creating a new Gfx object to handle a pattern,
@@ -214,6 +214,7 @@ private:
GBool checkForContentStreamLoop(Object *ref);
void go(GBool topLevel);
+ void getContentObj(Object *obj);
GBool execOp(Object *cmd, Object args[], int numArgs);
Operator *findOp(char *name);
GBool checkArg(Object *arg, TchkType type);
@@ -231,9 +232,8 @@ private:
void opSetLineWidth(Object args[], int numArgs);
void opSetExtGState(Object args[], int numArgs);
void doSoftMask(Object *str, Object *strRef, GBool alpha,
- GfxColorSpace *blendingColorSpace,
GBool isolated, GBool knockout,
- Function *transferFunc, GfxColor *backdropColor);
+ Function *transferFunc, Object *backdropColorObj);
void opSetRenderingIntent(Object args[], int numArgs);
GfxRenderingIntent parseRenderingIntent(const char *name);
@@ -280,6 +280,7 @@ private:
void doShadingPatternFill(GfxShadingPattern *sPat,
GBool stroke, GBool eoFill, GBool text);
void opShFill(Object args[], int numArgs);
+ void doShFill(GfxShading *shading);
void doFunctionShFill(GfxFunctionShading *shading);
void doFunctionShFill1(GfxFunctionShading *shading,
double x0, double y0,
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc
index b10c81779ff..f224ff75b5f 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc
@@ -467,6 +467,9 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
} else {
t2 = 0;
}
+ if (t != 0 && t < 1.9) {
+ declaredAscent = t;
+ }
// if both Ascent and CapHeight are set, use the smaller one
// (because the most common problem is that Ascent is too large)
if (t2 != 0 && (t == 0 || t2 < t)) {
@@ -851,19 +854,22 @@ char *GfxFont::readEmbFontFile(XRef *xref, int *len) {
}
str = obj2.getStream();
- size = 0;
- buf = NULL;
+ size = 4096;
+ buf = (char *)gmalloc(size);
+ *len = 0;
str->reset();
do {
- if (size > INT_MAX - 4096) {
- error(errSyntaxError, -1, "Embedded font file is too large");
- break;
+ if (*len > size - 4096) {
+ if (size > INT_MAX / 2) {
+ error(errSyntaxError, -1, "Embedded font file is too large");
+ break;
+ }
+ size *= 2;
+ buf = (char *)grealloc(buf, size);
}
- buf = (char *)grealloc(buf, size + 4096);
- n = str->getBlock(buf + size, 4096);
- size += n;
+ n = str->getBlock(buf + *len, 4096);
+ *len += n;
} while (n == 4096);
- *len = size;
str->close();
obj2.free();
@@ -947,6 +953,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, GString *nameA,
missingWidth = builtinFont->missingWidth;
ascent = 0.001 * builtinFont->ascent;
descent = 0.001 * builtinFont->descent;
+ declaredAscent = ascent;
fontBBox[0] = 0.001 * builtinFont->bbox[0];
fontBBox[1] = 0.001 * builtinFont->bbox[1];
fontBBox[2] = 0.001 * builtinFont->bbox[2];
@@ -955,6 +962,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, GString *nameA,
missingWidth = 0;
ascent = 0.75;
descent = -0.25;
+ declaredAscent = ascent;
fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
}
@@ -966,6 +974,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, GString *nameA,
if (builtinFont) {
ascent = 0.001 * builtinFont->ascent;
descent = 0.001 * builtinFont->descent;
+ declaredAscent = ascent;
fontBBox[0] = 0.001 * builtinFont->bbox[0];
fontBBox[1] = 0.001 * builtinFont->bbox[1];
fontBBox[2] = 0.001 * builtinFont->bbox[2];
@@ -1459,13 +1468,20 @@ int *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) {
}
// reverse map the char names through MacRomanEncoding, then map the
- // char codes through the cmap
+ // char codes through the cmap; fall back on Unicode if that doesn't
+ // work
if (useMacRoman) {
for (i = 0; i < 256; ++i) {
if ((charName = enc[i])) {
if ((code = globalParams->getMacRomanCharCode(charName))) {
map[i] = ff->mapCodeToGID(cmap, code);
+ } else if (unicodeCmap >= 0 &&
+ (u = globalParams->mapNameToUnicode(charName))) {
+ map[i] = ff->mapCodeToGID(unicodeCmap, u);
}
+ } else if (unicodeCmap >= 0 &&
+ (n = ctu->mapToUnicode((CharCode)i, &u, 1))) {
+ map[i] = ff->mapCodeToGID(cmap, u);
} else {
map[i] = -1;
}
@@ -1631,6 +1647,7 @@ GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, GString *nameA,
missingWidth = 0;
ascent = 0.95;
descent = -0.35;
+ declaredAscent = ascent;
fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
collection = NULL;
cMap = NULL;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h
index 3b63fa69fe0..ded34bc1ee7 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h
@@ -185,6 +185,7 @@ public:
// Return the ascent and descent values.
double getAscent() { return ascent; }
double getDescent() { return descent; }
+ double getDeclaredAscent() { return declaredAscent; }
// Return the writing mode (0=horizontal, 1=vertical).
virtual int getWMode() { return 0; }
@@ -234,6 +235,7 @@ protected:
double missingWidth; // "default" width
double ascent; // max height above baseline
double descent; // max depth below baseline
+ double declaredAscent; // ascent value, before munging
GBool hasToUnicode; // true if the font has a ToUnicode map
GBool ok;
};
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc
index 60b5f577054..5e9991e1b97 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc
@@ -101,6 +101,7 @@ static const char *gfxColorSpaceModeNames[] = {
GfxColorSpace::GfxColorSpace() {
overprintMask = 0x0f;
+ defaultColorSpace = gFalse;
}
GfxColorSpace::~GfxColorSpace() {
@@ -2683,6 +2684,11 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(
"Missing or invalid BitsPerCoordinate in shading dictionary");
goto err2;
}
+ if (coordBits <= 0 || coordBits > 32) {
+ error(errSyntaxError, -1,
+ "Invalid BitsPerCoordinate in shading dictionary");
+ goto err2;
+ }
obj1.free();
if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
compBits = obj1.getInt();
@@ -2691,6 +2697,11 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(
"Missing or invalid BitsPerComponent in shading dictionary");
goto err2;
}
+ if (compBits <= 0 || compBits > 16) {
+ error(errSyntaxError, -1,
+ "Invalid BitsPerComponent in shading dictionary");
+ goto err2;
+ }
obj1.free();
flagBits = vertsPerRow = 0; // make gcc happy
if (typeA == 4) {
@@ -2701,6 +2712,10 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(
"Missing or invalid BitsPerFlag in shading dictionary");
goto err2;
}
+ if (flagBits < 2 || flagBits > 8) {
+ error(errSyntaxError, -1, "Invalid BitsPerFlag in shading dictionary");
+ goto err2;
+ }
obj1.free();
} else {
if (dict->lookup("VerticesPerRow", &obj1)->isInt()) {
@@ -2711,6 +2726,11 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(
goto err2;
}
obj1.free();
+ if (vertsPerRow < 2) {
+ error(errSyntaxError, -1,
+ "Invalid VerticesPerRow in shading dictionary");
+ goto err2;
+ }
}
if (dict->lookup("Decode", &obj1)->isArray() &&
obj1.arrayGetLength() >= 6) {
@@ -2911,6 +2931,34 @@ void GfxGouraudTriangleShading::getTriangle(
}
}
+void GfxGouraudTriangleShading::getBBox(double *xMin, double *yMin,
+ double *xMax, double *yMax) {
+ double xxMin = 0;
+ double yyMin = 0;
+ double xxMax = 0;
+ double yyMax = 0;
+ if (nVertices > 0) {
+ xxMin = xxMax = vertices[0].x;
+ yyMin = yyMax = vertices[0].y;
+ }
+ for (int i = 1; i < nVertices; ++i) {
+ if (vertices[i].x < xxMin) {
+ xxMin = vertices[i].x;
+ } else if (vertices[i].x > xxMax) {
+ xxMax = vertices[i].x;
+ }
+ if (vertices[i].y < yyMin) {
+ yyMin = vertices[i].y;
+ } else if (vertices[i].y > yyMax) {
+ yyMax = vertices[i].y;
+ }
+ }
+ *xMin = xxMin;
+ *yMin = yyMin;
+ *xMax = xxMax;
+ *yMax = yyMax;
+}
+
void GfxGouraudTriangleShading::getColor(double *in, GfxColor *out) {
double c[gfxColorMaxComps];
int i;
@@ -2996,6 +3044,10 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
Object obj1, obj2;
int i, j;
+ nPatchesA = 0;
+ patchesA = NULL;
+ patchesSize = 0;
+
if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
coordBits = obj1.getInt();
} else {
@@ -3003,6 +3055,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
"Missing or invalid BitsPerCoordinate in shading dictionary");
goto err2;
}
+ if (coordBits <= 0 || coordBits > 32) {
+ error(errSyntaxError, -1,
+ "Invalid BitsPerCoordinate in shading dictionary");
+ goto err2;
+ }
obj1.free();
if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
compBits = obj1.getInt();
@@ -3011,6 +3068,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
"Missing or invalid BitsPerComponent in shading dictionary");
goto err2;
}
+ if (compBits <= 0 || compBits > 16) {
+ error(errSyntaxError, -1,
+ "Invalid BitsPerComponent in shading dictionary");
+ goto err2;
+ }
obj1.free();
if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
flagBits = obj1.getInt();
@@ -3019,6 +3081,10 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
"Missing or invalid BitsPerFlag in shading dictionary");
goto err2;
}
+ if (flagBits < 2 || flagBits > 8) {
+ error(errSyntaxError, -1, "Invalid BitsPerFlag in shading dictionary");
+ goto err2;
+ }
obj1.free();
if (dict->lookup("Decode", &obj1)->isArray() &&
obj1.arrayGetLength() >= 6) {
@@ -3076,9 +3142,6 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
}
obj1.free();
- nPatchesA = 0;
- patchesA = NULL;
- patchesSize = 0;
bitBuf = new GfxShadingBitBuf(str);
while (1) {
if (!bitBuf->getBits(flagBits, &flag)) {
@@ -3506,6 +3569,9 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
err2:
obj1.free();
err1:
+ if (patchesA) {
+ gfree(patchesA);
+ }
return NULL;
}
@@ -3513,6 +3579,38 @@ GfxShading *GfxPatchMeshShading::copy() {
return new GfxPatchMeshShading(this);
}
+void GfxPatchMeshShading::getBBox(double *xMin, double *yMin,
+ double *xMax, double *yMax) {
+ double xxMin = 0;
+ double yyMin = 0;
+ double xxMax = 0;
+ double yyMax = 0;
+ if (nPatches > 0) {
+ xxMin = patches[0].x[0][0];
+ yyMin = patches[0].y[0][0];
+ }
+ for (int i = 0; i < nPatches; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ for (int k = 0; k < 4; ++k) {
+ if (patches[i].x[j][k] < xxMin) {
+ xxMin = patches[i].x[j][k];
+ } else if (patches[i].x[j][k] > xxMax) {
+ xxMax = patches[i].x[j][k];
+ }
+ if (patches[i].y[j][k] < yyMin) {
+ yyMin = patches[i].y[j][k];
+ } else if (patches[i].y[j][k] > yyMax) {
+ yyMax = patches[i].y[j][k];
+ }
+ }
+ }
+ }
+ *xMin = xxMin;
+ *yMin = yyMin;
+ *xMax = xxMax;
+ *yMax = yyMax;
+}
+
void GfxPatchMeshShading::getColor(double *in, GfxColor *out) {
double c[gfxColorMaxComps];
int i;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h
index 50507040fed..f9c0cfcad6e 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h
@@ -207,6 +207,11 @@ public:
// Return the color space's overprint mask.
Guint getOverprintMask() { return overprintMask; }
+ // Return true if this color space object is the result of
+ // substituting a DefaultGray/RGB/CMYK color space for
+ // DeviceGray/RGB/CMYK.
+ GBool isDefaultColorSpace() { return defaultColorSpace; }
+
// Return the number of color space modes
static int getNumColorSpaceModes();
@@ -216,6 +221,7 @@ public:
protected:
Guint overprintMask;
+ GBool defaultColorSpace;
};
//------------------------------------------------------------------------
@@ -877,6 +883,7 @@ public:
void getTriangle(int i, double *x0, double *y0, double *color0,
double *x1, double *y1, double *color1,
double *x2, double *y2, double *color2);
+ void getBBox(double *xMin, double *yMin, double *xMax, double *yMax);
void getColor(double *in, GfxColor *out);
private:
@@ -916,6 +923,7 @@ public:
int getNComps() { return nComps; }
int getNPatches() { return nPatches; }
GfxPatch *getPatch(int i) { return &patches[i]; }
+ void getBBox(double *xMin, double *yMin, double *xMax, double *yMax);
void getColor(double *in, GfxColor *out);
private:
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc
index 56bfba8e3cc..5ff932479d1 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc
@@ -178,24 +178,21 @@ class SysFontInfo {
public:
GString *name;
- GBool bold;
- GBool italic;
GString *path;
SysFontType type;
int fontNum; // for TrueType collections
- SysFontInfo(GString *nameA, GBool boldA, GBool italicA,
- GString *pathA, SysFontType typeA, int fontNumA);
+ SysFontInfo(GString *nameA, GString *pathA, SysFontType typeA, int fontNumA);
~SysFontInfo();
- GBool match(SysFontInfo *fi);
- GBool match(GString *nameA, GBool boldA, GBool italicA);
+ GString *mungeName1(GString *in);
+ GString *mungeName2(GString *in);
+ void mungeName3(GString *name, GBool *bold, GBool *italic);
+ int match(GString *nameA);
};
-SysFontInfo::SysFontInfo(GString *nameA, GBool boldA, GBool italicA,
- GString *pathA, SysFontType typeA, int fontNumA) {
+SysFontInfo::SysFontInfo(GString *nameA, GString *pathA,
+ SysFontType typeA, int fontNumA) {
name = nameA;
- bold = boldA;
- italic = italicA;
path = pathA;
type = typeA;
fontNum = fontNumA;
@@ -206,14 +203,150 @@ SysFontInfo::~SysFontInfo() {
delete path;
}
-GBool SysFontInfo::match(SysFontInfo *fi) {
- return !strcasecmp(name->getCString(), fi->name->getCString()) &&
- bold == fi->bold && italic == fi->italic;
+// Remove space/comma/dash/underscore chars.
+// Uppercase the name.
+GString *SysFontInfo::mungeName1(GString *in) {
+ GString *out = new GString();
+ for (char *p = in->getCString(); *p; ++p) {
+ if (*p == ' ' || *p == ',' || *p == '-' || *p == '_') {
+ // skip
+ } else if (*p >= 'a' && *p <= 'z') {
+ out->append((char)(*p & 0xdf));
+ } else {
+ out->append(*p);
+ }
+ }
+ return out;
+}
+
+// Remove trailing "Identity-H"/"Identity-V" from the name.
+// Split the name into tokens at space/comma/dash/underscore.
+// Remove trailing "MT" or "BT" from tokens.
+// Remove trailing "PS" and "WGL4" from tokens.
+// Uppercase each token.
+// Concatenate tokens (dropping the space/comma/dash chars).
+GString *SysFontInfo::mungeName2(GString *in) {
+ GString *out = new GString();
+ char *p0 = in->getCString();
+ while (*p0) {
+ if (!strcmp(p0, "Identity-H") || !strcmp(p0, "Identity-V")) {
+ break;
+ }
+ char *p1;
+ for (p1 = p0 + 1;
+ *p1 && *p1 != ' ' && *p1 != ',' && *p1 != '-' && *p1 != '_';
+ ++p1) ;
+ char *p2 = p1;
+ if (p2 - p0 >= 2 && (p2[-2] == 'B' || p2[-2] == 'M') && p2[-1] == 'T') {
+ p2 -= 2;
+ }
+ if (p2 - p0 >= 2 && p2[-2] == 'P' && p2[-1] == 'S') {
+ p2 -= 2;
+ }
+ if (p2 - p0 >= 4 &&
+ p2[-4] == 'W' && p2[-3] == 'G' && p2[-2] == 'L' && p2[-1] == '4') {
+ p2 -= 4;
+ }
+ for (; p0 < p2; ++p0) {
+ if (*p0 >= 'a' && *p0 <= 'z') {
+ out->append((char)(*p0 & 0xdf));
+ } else {
+ out->append(*p0);
+ }
+ }
+ for (p0 = p1; *p0 == ' ' || *p0 == ',' || *p0 == '-' || *p0 == '_'; ++p0) ;
+ }
+ return out;
}
-GBool SysFontInfo::match(GString *nameA, GBool boldA, GBool italicA) {
- return !strcasecmp(name->getCString(), nameA->getCString()) &&
- bold == boldA && italic == italicA;
+// Remove trailing bold/italic/regular/roman tags from the name.
+// (Note: the names have already been uppercased by mungeName1/2.)
+void SysFontInfo::mungeName3(GString *name, GBool *bold, GBool *italic) {
+ *bold = gFalse;
+ *italic = gFalse;
+ int n = name->getLength();
+ while (1) {
+ if (n >= 4 && !strcmp(name->getCString() + n - 4, "BOLD")) {
+ name->del(n - 4, 4);
+ n -= 4;
+ *bold = gTrue;
+ } else if (n >= 6 && !strcmp(name->getCString() + n - 6, "ITALIC")) {
+ name->del(n - 6, 6);
+ n -= 6;
+ *italic = gTrue;
+ } else if (n >= 7 && !strcmp(name->getCString() + n - 7, "REGULAR")) {
+ name->del(n - 7, 7);
+ n -= 7;
+ } else if (n >= 5 && !strcmp(name->getCString() + n - 5, "ROMAN")) {
+ name->del(n - 5, 5);
+ n -= 5;
+ } else {
+ break;
+ }
+ }
+}
+
+// Returns a score indicating how well this font matches [nameA]. A
+// higher score is better. Zero indicates a non-match.
+int SysFontInfo::match(GString *nameA) {
+ // fast fail: check if the first two letters match
+ if (strncasecmp(name->getCString(), nameA->getCString(), 2)) {
+ return 0;
+ }
+
+ GString *pdfName1 = mungeName1(nameA);
+ GString *sysName1 = mungeName1(name);
+ if (!pdfName1->cmp(sysName1)) {
+ delete pdfName1;
+ delete sysName1;
+ return 8;
+ }
+
+ GString *pdfName2 = mungeName2(nameA);
+ GString *sysName2 = mungeName2(name);
+ if (!pdfName2->cmp(sysName2)) {
+ delete pdfName1;
+ delete sysName1;
+ delete pdfName2;
+ delete sysName2;
+ return 7;
+ }
+
+ GBool pdfBold1, pdfItalic1, sysBold1, sysItalic1;
+ mungeName3(pdfName1, &pdfBold1, &pdfItalic1);
+ mungeName3(sysName1, &sysBold1, &sysItalic1);
+ int eq1 = !pdfName1->cmp(sysName1);
+
+ GBool pdfBold2, pdfItalic2, sysBold2, sysItalic2;
+ mungeName3(pdfName2, &pdfBold2, &pdfItalic2);
+ mungeName3(sysName2, &sysBold2, &sysItalic2);
+ int eq2 =!pdfName2->cmp(sysName2);
+
+ delete pdfName1;
+ delete sysName1;
+ delete pdfName2;
+ delete sysName2;
+
+ if (eq1 && pdfBold1 == sysBold1 && pdfItalic1 == sysItalic1) {
+ return 6;
+ }
+ if (eq2 && pdfBold2 == sysBold2 && pdfItalic2 == sysItalic2) {
+ return 5;
+ }
+ if (eq1 && pdfItalic1 == sysItalic1) {
+ return 4;
+ }
+ if (eq2 && pdfItalic2 == sysItalic2) {
+ return 3;
+ }
+ if (eq1) {
+ return 2;
+ }
+ if (eq2) {
+ return 1;
+ }
+
+ return 0;
}
//------------------------------------------------------------------------
@@ -254,102 +387,17 @@ SysFontList::~SysFontList() {
}
SysFontInfo *SysFontList::find(GString *name) {
- GString *name2;
- GBool bold, italic;
- SysFontInfo *fi;
- char c;
- int n, i;
-
- name2 = name->copy();
-
- // remove space, comma, dash chars
- i = 0;
- while (i < name2->getLength()) {
- c = name2->getChar(i);
- if (c == ' ' || c == ',' || c == '-') {
- name2->del(i);
- } else {
- ++i;
+ SysFontInfo *match = NULL;
+ int score = 0;
+ for (int i = 0; i < fonts->getLength(); ++i) {
+ SysFontInfo *fi = (SysFontInfo *)fonts->get(i);
+ int s = fi->match(name);
+ if (s > score) {
+ match = fi;
+ score = s;
}
}
- n = name2->getLength();
-
- // font names like "Arial-BoldMT,Bold" are occasionally used,
- // so run this loop twice
- bold = italic = gFalse;
- for (i = 0; i < 2; ++i) {
-
- // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.)
- if (n > 2 && !strcmp(name2->getCString() + n - 2, "MT")) {
- name2->del(n - 2, 2);
- n -= 2;
- }
-
- // look for "Regular"
- if (n > 7 && !strcmp(name2->getCString() + n - 7, "Regular")) {
- name2->del(n - 7, 7);
- n -= 7;
- }
-
- // look for "Italic"
- if (n > 6 && !strcmp(name2->getCString() + n - 6, "Italic")) {
- name2->del(n - 6, 6);
- italic = gTrue;
- n -= 6;
- }
-
- // look for "Bold"
- if (n > 4 && !strcmp(name2->getCString() + n - 4, "Bold")) {
- name2->del(n - 4, 4);
- bold = gTrue;
- n -= 4;
- }
- }
-
- // remove trailing "PS"
- if (n > 2 && !strcmp(name2->getCString() + n - 2, "PS")) {
- name2->del(n - 2, 2);
- n -= 2;
- }
-
- // remove trailing "IdentityH"
- if (n > 9 && !strcmp(name2->getCString() + n - 9, "IdentityH")) {
- name2->del(n - 9, 9);
- n -= 9;
- }
-
- // search for the font
- fi = NULL;
- for (i = 0; i < fonts->getLength(); ++i) {
- fi = (SysFontInfo *)fonts->get(i);
- if (fi->match(name2, bold, italic)) {
- break;
- }
- fi = NULL;
- }
- if (!fi && bold) {
- // try ignoring the bold flag
- for (i = 0; i < fonts->getLength(); ++i) {
- fi = (SysFontInfo *)fonts->get(i);
- if (fi->match(name2, gFalse, italic)) {
- break;
- }
- fi = NULL;
- }
- }
- if (!fi && (bold || italic)) {
- // try ignoring the bold and italic flags
- for (i = 0; i < fonts->getLength(); ++i) {
- fi = (SysFontInfo *)fonts->get(i);
- if (fi->match(name2, gFalse, gFalse)) {
- break;
- }
- fi = NULL;
- }
- }
-
- delete name2;
- return fi;
+ return match;
}
#ifdef _WIN32
@@ -421,50 +469,15 @@ void SysFontList::scanWindowsFonts(char *winFontDir) {
SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum,
char *path) {
- int n;
- GBool bold, italic;
- GString *s;
- char c;
- int i;
- SysFontType type;
-
- n = (int)strlen(name);
- bold = italic = gFalse;
+ int n = (int)strlen(name);
- // remove trailing ' (TrueType)'
- if (n > 11 && !strncmp(name + n - 11, " (TrueType)", 11)) {
+ // remove trailing ' (TrueType)' or ' (OpenType)'
+ if (n > 11 && (!strncmp(name + n - 11, " (TrueType)", 11) ||
+ !strncmp(name + n - 11, " (OpenType)", 11))) {
n -= 11;
}
- // remove trailing ' Italic'
- if (n > 7 && !strncmp(name + n - 7, " Italic", 7)) {
- n -= 7;
- italic = gTrue;
- }
-
- // remove trailing ' Bold'
- if (n > 5 && !strncmp(name + n - 5, " Bold", 5)) {
- n -= 5;
- bold = gTrue;
- }
-
- // remove trailing ' Regular'
- if (n > 8 && !strncmp(name + n - 8, " Regular", 8)) {
- n -= 8;
- }
-
- //----- normalize the font name
- s = new GString(name, n);
- i = 0;
- while (i < s->getLength()) {
- c = s->getChar(i);
- if (c == ' ' || c == ',' || c == '-') {
- s->del(i);
- } else {
- ++i;
- }
- }
-
+ SysFontType type;
if (!strcasecmp(path + strlen(path) - 4, ".ttc")) {
type = sysFontTTC;
} else if (!strcasecmp(path + strlen(path) - 4, ".otf")) {
@@ -472,31 +485,32 @@ SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum,
} else {
type = sysFontTTF;
}
- return new SysFontInfo(s, bold, italic, new GString(path), type, fontNum);
+
+ return new SysFontInfo(new GString(name, n), new GString(path),
+ type, fontNum);
}
-#endif
+#endif // _WIN32
#if HAVE_FONTCONFIG
void SysFontList::scanFontconfigFonts() {
+ FcConfig *cfg;
FcPattern *pattern;
FcObjectSet *objSet;
FcFontSet *fontSet;
- char *family, *file, *styleLang, *style;
- GString *family2;
+ char *name, *file;
SysFontType type;
- GBool bold, italic;
- char c;
- int fontNum, i, j, n;
+ int fontNum, i, n;
- FcInit();
+ if (!(cfg = FcInitLoadConfigAndFonts())) {
+ return;
+ }
pattern = FcPatternBuild(NULL,
FC_OUTLINE, FcTypeBool, FcTrue,
FC_SCALABLE, FcTypeBool, FcTrue,
NULL);
- objSet = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_STYLELANG,
- FC_FILE, FC_INDEX, NULL);
- fontSet = FcFontList(NULL, pattern, objSet);
+ objSet = FcObjectSetBuild(FC_FULLNAME, FC_FILE, FC_INDEX, NULL);
+ fontSet = FcFontList(cfg, pattern, objSet);
FcPatternDestroy(pattern);
FcObjectSetDestroy(objSet);
@@ -528,54 +542,21 @@ void SysFontList::scanFontconfigFonts() {
fontNum = 0;
}
- //--- font family
- if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0,
- (FcChar8 **)&family)
+ //--- font name
+ if (FcPatternGetString(fontSet->fonts[i], FC_FULLNAME, 0,
+ (FcChar8 **)&name)
!= FcResultMatch) {
continue;
}
- //----- normalize the font name
- family2 = new GString(family);
- j = 0;
- while (j < family2->getLength()) {
- c = family2->getChar(j);
- if (c == ' ' || c == ',' || c == '-') {
- family2->del(j);
- } else {
- ++j;
- }
- }
-
- //--- font style
- style = NULL;
- for (j = 0;
- FcPatternGetString(fontSet->fonts[i], FC_STYLELANG, j,
- (FcChar8 **)&styleLang)
- == FcResultMatch;
- ++j) {
- if (!strcmp(styleLang, "en")) {
- if (FcPatternGetString(fontSet->fonts[i], FC_STYLE, j,
- (FcChar8 **)&style)
- != FcResultMatch) {
- style = NULL;
- }
- break;
- }
- ++j;
- }
- bold = style && strstr(style, "Bold") != NULL;
- italic = style && (strstr(style, "Italic") != NULL ||
- strstr(style, "Oblique") != NULL);
-
- fonts->append(new SysFontInfo(family2, bold, italic,
- new GString(file), type, fontNum));
+ fonts->append(new SysFontInfo(new GString(name), new GString(file),
+ type, fontNum));
}
FcFontSetDestroy(fontSet);
}
- FcFini();
+ FcConfigDestroy(cfg);
}
#endif // HAVE_FONTCONFIG
@@ -663,6 +644,8 @@ GlobalParams::GlobalParams(const char *cfgFileName) {
#else
baseDir = appendToPath(getHomeDir(), ".xpdf");
#endif
+ configFileVars = new GHash(gTrue);
+ setDataDirVar();
nameToUnicode = new NameToCharCode();
cidToUnicodes = new GHash(gTrue);
unicodeToUnicodes = new GHash(gTrue);
@@ -736,6 +719,7 @@ GlobalParams::GlobalParams(const char *cfgFileName) {
initialDisplayMode = new GString("continuous");
initialToolbarState = gTrue;
initialSidebarState = gTrue;
+ initialSidebarWidth = 0;
initialSelectMode = new GString("linear");
maxTileWidth = 1500;
maxTileHeight = 1500;
@@ -761,6 +745,7 @@ GlobalParams::GlobalParams(const char *cfgFileName) {
paperColor = new GString("#ffffff");
matteColor = new GString("#808080");
fullScreenMatteColor = new GString("#000000");
+ selectionColor = new GString("#8080ff");
reverseVideoInvertImages = gFalse;
launchCommand = NULL;
movieCommand = NULL;
@@ -769,7 +754,6 @@ GlobalParams::GlobalParams(const char *cfgFileName) {
mapUnknownCharNames = gFalse;
mapExtTrueTypeFontsViaUnicode = gTrue;
droppedFonts = new GHash(gTrue);
- enableXFA = gTrue;
createDefaultKeyBindings();
popupMenuCmds = new GList();
tabStateFile = appendToPath(getHomeDir(), ".xpdf.tab-state");
@@ -847,14 +831,51 @@ GlobalParams::GlobalParams(const char *cfgFileName) {
}
}
+void GlobalParams::setDataDirVar() {
+ GString *dir;
+
+#if defined(XPDFRC_DATADIR)
+ dir = new GString(XPDFRC_DATADIR);
+#elif defined(_WIN32)
+ wchar_t buf[512];
+ DWORD n = GetModuleFileNameW(NULL, buf, sizeof(buf) / sizeof(wchar_t));
+ if (n <= 0 || n >= sizeof(buf)) {
+ // error or path too long for buffer - just use the current dir
+ buf[0] = L'\0';
+ }
+ GString *path = fileNameToUTF8(buf);
+ dir = grabPath(path->getCString());
+ delete path;
+ appendToPath(dir, "data");
+#else
+ //~ may be useful to allow the options of using the install dir
+ //~ and/or the user's home dir (?)
+ dir = new GString("./data");
+#endif
+
+ configFileVars->add(new GString("DATADIR"), dir);
+}
+
void GlobalParams::createDefaultKeyBindings() {
keyBindings = new GList();
//----- mouse buttons
keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress1, xpdfKeyModNone,
xpdfKeyContextAny, "startSelection"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress1, xpdfKeyModShift,
+ xpdfKeyContextAny,
+ "startExtendedSelection"));
keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease1, xpdfKeyModNone,
xpdfKeyContextAny, "endSelection"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease1, xpdfKeyModShift,
+ xpdfKeyContextAny,
+ "endSelection"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMouseDoubleClick1,
+ xpdfKeyModNone, xpdfKeyContextAny,
+ "selectWord"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMouseTripleClick1,
+ xpdfKeyModNone, xpdfKeyContextAny,
+ "selectLine"));
keyBindings->append(new KeyBinding(xpdfKeyCodeMouseClick1, xpdfKeyModNone,
xpdfKeyContextAny, "followLinkNoSel"));
keyBindings->append(new KeyBinding(xpdfKeyCodeMouseClick2, xpdfKeyModNone,
@@ -876,6 +897,10 @@ void GlobalParams::createDefaultKeyBindings() {
xpdfKeyContextAny, "scrollLeft(16)"));
keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress7, xpdfKeyModNone,
xpdfKeyContextAny, "scrollRight(16)"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress4, xpdfKeyModCtrl,
+ xpdfKeyContextAny, "zoomIn"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress5, xpdfKeyModCtrl,
+ xpdfKeyContextAny, "zoomOut"));
//----- control keys
keyBindings->append(new KeyBinding('o', xpdfKeyModCtrl,
@@ -1002,26 +1027,10 @@ void GlobalParams::parseFile(GString *fileName, FILE *f) {
void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
GList *tokens;
GString *cmd, *incFile;
- char *p1, *p2;
FILE *f2;
// break the line into tokens
- tokens = new GList();
- p1 = buf;
- while (*p1) {
- for (; *p1 && isspace(*p1); ++p1) ;
- if (!*p1) {
- break;
- }
- if (*p1 == '"' || *p1 == '\'') {
- for (p2 = p1 + 1; *p2 && *p2 != *p1; ++p2) ;
- ++p1;
- } else {
- for (p2 = p1 + 1; *p2 && !isspace(*p2); ++p2) ;
- }
- tokens->append(new GString(p1, (int)(p2 - p1)));
- p1 = *p2 ? p2 + 1 : p2;
- }
+ tokens = parseLineTokens(buf, fileName, line);
// parse the line
if (tokens->getLength() > 0 &&
@@ -1153,6 +1162,9 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
} else if (!cmd->cmp("initialSidebarState")) {
parseYesNo("initialSidebarState", &initialSidebarState,
tokens, fileName, line);
+ } else if (!cmd->cmp("initialSidebarWidth")) {
+ parseInteger("initialSidebarWidth", &initialSidebarWidth,
+ tokens, fileName, line);
} else if (!cmd->cmp("initialSelectMode")) {
parseString("initialSelectMode", &initialSelectMode,
tokens, fileName, line);
@@ -1217,6 +1229,8 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
} else if (!cmd->cmp("fullScreenMatteColor")) {
parseString("fullScreenMatteColor", &fullScreenMatteColor,
tokens, fileName, line);
+ } else if (!cmd->cmp("selectionColor")) {
+ parseString("selectionColor", &selectionColor, tokens, fileName, line);
} else if (!cmd->cmp("reverseVideoInvertImages")) {
parseYesNo("reverseVideoInvertImages", &reverseVideoInvertImages,
tokens, fileName, line);
@@ -1238,8 +1252,6 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
tokens, fileName, line);
} else if (!cmd->cmp("dropFont")) {
parseDropFont(tokens, fileName, line);
- } else if (!cmd->cmp("enableXFA")) {
- parseYesNo("enableXFA", &enableXFA, tokens, fileName, line);
} else if (!cmd->cmp("bind")) {
parseBind(tokens, fileName, line);
} else if (!cmd->cmp("unbind")) {
@@ -1269,6 +1281,8 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
} else if (!cmd->cmp("fontpath") || !cmd->cmp("fontmap")) {
error(errConfig, -1,
"The config file format has changed since Xpdf 0.9x");
+ } else if (!cmd->cmp("enableXFA")) {
+ error(errConfig, -1, "The enableXFA option is no longer used");
}
}
}
@@ -1276,6 +1290,71 @@ void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
deleteGList(tokens, GString);
}
+// Split a line into a sequence of tokens. Tokens are separated by
+// whitespace. Each token is one of:
+// - unquoted string, which can contain any char other than
+// whitespace, and which cannot start with a single quote, double
+// quote, or at-double-quote (xxxx)
+// - single-quoted string, which can contain any char other than the
+// single quote ('xxxx')
+// - double-quoted string, which can contain any char other than the
+// double quote ("xxxx")
+// - at-double-quoted string, which can contain variables and escape
+// chars (@"xxxx")
+// - variables look like ${name}
+// - special chars (${}") can be escaped with %, e.g.,
+// @"foo%"bar", @"foo%$bar", @"foo%%bar"
+GList *GlobalParams::parseLineTokens(char *buf, GString *fileName, int line) {
+ GList *tokens = new GList();
+ char *p1 = buf;
+ while (*p1) {
+ for (; *p1 && isspace(*p1); ++p1) ;
+ if (!*p1) {
+ break;
+ }
+ if (*p1 == '"' || *p1 == '\'') {
+ char *p2;
+ for (p2 = p1 + 1; *p2 && *p2 != *p1; ++p2) ;
+ ++p1;
+ tokens->append(new GString(p1, (int)(p2 - p1)));
+ p1 = *p2 ? p2 + 1 : p2;
+ } else if (*p1 == '@' && p1[1] == '"') {
+ GString *token = new GString();
+ char *p2 = p1 + 2;
+ while (*p2 && *p2 != '"') {
+ if (*p2 == '%' && p2[1]) {
+ token->append(p2[1]);
+ p2 += 2;
+ } else if (*p2 == '$' && p2[1] == '{') {
+ p2 += 2;
+ char *p3;
+ for (p3 = p2; *p3 && *p3 != '}'; ++p3) ;
+ GString *varName = new GString(p2, (int)(p3 - p2));
+ GString *varValue = (GString *)configFileVars->lookup(varName);
+ if (varValue) {
+ token->append(varValue);
+ } else {
+ error(errConfig, -1, "Unknown config file variable '%t'", varName);
+ }
+ delete varName;
+ p2 = *p3 ? p3 + 1 : p3;
+ } else {
+ token->append(*p2);
+ ++p2;
+ }
+ }
+ tokens->append(token);
+ p1 = *p2 ? p2 + 1 : p2;
+ } else {
+ char *p2;
+ for (p2 = p1 + 1; *p2 && !isspace(*p2); ++p2) ;
+ tokens->append(new GString(p1, (int)(p2 - p1)));
+ p1 = p2;
+ }
+ }
+ return tokens;
+}
+
void GlobalParams::parseNameToUnicode(GList *tokens, GString *fileName,
int line) {
GString *name;
@@ -1738,6 +1817,8 @@ GBool GlobalParams::parseKey(GString *modKeyStr, GString *contextStr,
*code = xpdfKeyCodeEnter;
} else if (!strcmp(p0, "backspace")) {
*code = xpdfKeyCodeBackspace;
+ } else if (!strcmp(p0, "esc")) {
+ *code = xpdfKeyCodeEsc;
} else if (!strcmp(p0, "insert")) {
*code = xpdfKeyCodeInsert;
} else if (!strcmp(p0, "delete")) {
@@ -1758,8 +1839,6 @@ GBool GlobalParams::parseKey(GString *modKeyStr, GString *contextStr,
*code = xpdfKeyCodeUp;
} else if (!strcmp(p0, "down")) {
*code = xpdfKeyCodeDown;
- } else if (!strcmp(p0, "esc")) {
- *code = xpdfKeyCodeEsc;
} else if (p0[0] == 'f' && p0[1] >= '1' && p0[1] <= '9' && !p0[2]) {
*code = xpdfKeyCodeF1 + (p0[1] - '1');
} else if (p0[0] == 'f' &&
@@ -1782,6 +1861,16 @@ GBool GlobalParams::parseKey(GString *modKeyStr, GString *contextStr,
(!p0[11] || (p0[11] >= '0' && p0[11] <= '9' && !p0[12])) &&
(btn = atoi(p0 + 10)) >= 1 && btn <= 32) {
*code = xpdfKeyCodeMouseClick1 + btn - 1;
+ } else if (!strncmp(p0, "mouseDoubleClick", 16) &&
+ p0[16] >= '0' && p0[16] <= '9' &&
+ (!p0[17] || (p0[17] >= '0' && p0[17] <= '9' && !p0[18])) &&
+ (btn = atoi(p0 + 16)) >= 1 && btn <= 32) {
+ *code = xpdfKeyCodeMouseDoubleClick1 + btn - 1;
+ } else if (!strncmp(p0, "mouseTripleClick", 16) &&
+ p0[16] >= '0' && p0[16] <= '9' &&
+ (!p0[17] || (p0[17] >= '0' && p0[17] <= '9' && !p0[18])) &&
+ (btn = atoi(p0 + 16)) >= 1 && btn <= 32) {
+ *code = xpdfKeyCodeMouseTripleClick1 + btn - 1;
} else if (*p0 >= 0x20 && *p0 <= 0x7e && !p0[1]) {
*code = (int)*p0;
} else {
@@ -1981,6 +2070,7 @@ GlobalParams::~GlobalParams() {
delete macRomanReverseMap;
delete baseDir;
+ deleteGHash(configFileVars, GString);
delete nameToUnicode;
deleteGHash(cidToUnicodes, GString);
deleteGHash(unicodeToUnicodes, GString);
@@ -2009,6 +2099,9 @@ GlobalParams::~GlobalParams() {
if (fullScreenMatteColor) {
delete fullScreenMatteColor;
}
+ if (selectionColor) {
+ delete selectionColor;
+ }
if (launchCommand) {
delete launchCommand;
}
@@ -2784,6 +2877,26 @@ GString *GlobalParams::getTextEncodingName() {
return s;
}
+GList *GlobalParams::getAvailableTextEncodings() {
+ GList *list; // [GString]
+ GHashIter *iter;
+ GString *key;
+ void *val;
+
+ list = new GList();
+ lockGlobalParams;
+ residentUnicodeMaps->startIter(&iter);
+ while (residentUnicodeMaps->getNext(&iter, &key, &val)) {
+ list->append(key->copy());
+ }
+ unicodeMaps->startIter(&iter);
+ while (unicodeMaps->getNext(&iter, &key, &val)) {
+ list->append(key->copy());
+ }
+ unlockGlobalParams;
+ return list;
+}
+
EndOfLineKind GlobalParams::getTextEOL() {
EndOfLineKind eol;
@@ -2856,6 +2969,15 @@ GBool GlobalParams::getInitialSidebarState() {
return state;
}
+int GlobalParams::getInitialSidebarWidth() {
+ int w;
+
+ lockGlobalParams;
+ w = initialSidebarWidth;
+ unlockGlobalParams;
+ return w;
+}
+
GString *GlobalParams::getInitialSelectMode() {
GString *s;
@@ -3075,6 +3197,15 @@ GString *GlobalParams::getFullScreenMatteColor() {
return s;
}
+GString *GlobalParams::getSelectionColor() {
+ GString *s;
+
+ lockGlobalParams;
+ s = selectionColor->copy();
+ unlockGlobalParams;
+ return s;
+}
+
GBool GlobalParams::getReverseVideoInvertImages() {
GBool invert;
@@ -3129,15 +3260,6 @@ GBool GlobalParams::isDroppedFont(const char *fontName) {
return isDropped;
}
-GBool GlobalParams::getEnableXFA() {
- GBool enable;
-
- lockGlobalParams;
- enable = enableXFA;
- unlockGlobalParams;
- return enable;
-}
-
GList *GlobalParams::getKeyBinding(int code, int mods, int context) {
KeyBinding *binding;
GList *cmds;
@@ -3164,6 +3286,10 @@ GList *GlobalParams::getKeyBinding(int code, int mods, int context) {
return cmds;
}
+GList *GlobalParams::getAllKeyBindings() {
+ return keyBindings;
+}
+
int GlobalParams::getNumPopupMenuCmds() {
int n;
@@ -3214,7 +3340,7 @@ GString *GlobalParams::getDebugLogFile() {
return debugLogFile;
}
-void GlobalParams::debugLogPrintf(char *fmt, ...) {
+void GlobalParams::debugLogPrintf(const char *fmt, ...) {
GString *path;
FILE *f;
GBool needClose;
@@ -3620,12 +3746,6 @@ void GlobalParams::setMapExtTrueTypeFontsViaUnicode(GBool map) {
unlockGlobalParams;
}
-void GlobalParams::setEnableXFA(GBool enable) {
- lockGlobalParams;
- enableXFA = enable;
- unlockGlobalParams;
-}
-
void GlobalParams::setTabStateFile(char *tabStateFileA) {
lockGlobalParams;
delete tabStateFile;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h
index ed1fed3152d..2823ea59421 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h
@@ -134,50 +134,68 @@ public:
~KeyBinding();
};
-#define xpdfKeyCodeTab 0x1000
-#define xpdfKeyCodeReturn 0x1001
-#define xpdfKeyCodeEnter 0x1002
-#define xpdfKeyCodeBackspace 0x1003
-#define xpdfKeyCodeEsc 0x1004
-#define xpdfKeyCodeInsert 0x1005
-#define xpdfKeyCodeDelete 0x1006
-#define xpdfKeyCodeHome 0x1007
-#define xpdfKeyCodeEnd 0x1008
-#define xpdfKeyCodePgUp 0x1009
-#define xpdfKeyCodePgDn 0x100a
-#define xpdfKeyCodeLeft 0x100b
-#define xpdfKeyCodeRight 0x100c
-#define xpdfKeyCodeUp 0x100d
-#define xpdfKeyCodeDown 0x100e
-#define xpdfKeyCodeF1 0x1100
-#define xpdfKeyCodeF35 0x1122
-#define xpdfKeyCodeMousePress1 0x2001
-#define xpdfKeyCodeMousePress2 0x2002
-#define xpdfKeyCodeMousePress3 0x2003
-#define xpdfKeyCodeMousePress4 0x2004
-#define xpdfKeyCodeMousePress5 0x2005
-#define xpdfKeyCodeMousePress6 0x2006
-#define xpdfKeyCodeMousePress7 0x2007
+#define xpdfKeyCodeTab 0x1000
+#define xpdfKeyCodeReturn 0x1001
+#define xpdfKeyCodeEnter 0x1002
+#define xpdfKeyCodeBackspace 0x1003
+#define xpdfKeyCodeEsc 0x1004
+#define xpdfKeyCodeInsert 0x1005
+#define xpdfKeyCodeDelete 0x1006
+#define xpdfKeyCodeHome 0x1007
+#define xpdfKeyCodeEnd 0x1008
+#define xpdfKeyCodePgUp 0x1009
+#define xpdfKeyCodePgDn 0x100a
+#define xpdfKeyCodeLeft 0x100b
+#define xpdfKeyCodeRight 0x100c
+#define xpdfKeyCodeUp 0x100d
+#define xpdfKeyCodeDown 0x100e
+#define xpdfKeyCodeF1 0x1100
+#define xpdfKeyCodeF35 0x1122
+#define xpdfKeyCodeMousePress1 0x2001
+#define xpdfKeyCodeMousePress2 0x2002
+#define xpdfKeyCodeMousePress3 0x2003
+#define xpdfKeyCodeMousePress4 0x2004
+#define xpdfKeyCodeMousePress5 0x2005
+#define xpdfKeyCodeMousePress6 0x2006
+#define xpdfKeyCodeMousePress7 0x2007
// ...
-#define xpdfKeyCodeMousePress32 0x2020
-#define xpdfKeyCodeMouseRelease1 0x2101
-#define xpdfKeyCodeMouseRelease2 0x2102
-#define xpdfKeyCodeMouseRelease3 0x2103
-#define xpdfKeyCodeMouseRelease4 0x2104
-#define xpdfKeyCodeMouseRelease5 0x2105
-#define xpdfKeyCodeMouseRelease6 0x2106
-#define xpdfKeyCodeMouseRelease7 0x2107
+#define xpdfKeyCodeMousePress32 0x2020
+#define xpdfKeyCodeMouseRelease1 0x2101
+#define xpdfKeyCodeMouseRelease2 0x2102
+#define xpdfKeyCodeMouseRelease3 0x2103
+#define xpdfKeyCodeMouseRelease4 0x2104
+#define xpdfKeyCodeMouseRelease5 0x2105
+#define xpdfKeyCodeMouseRelease6 0x2106
+#define xpdfKeyCodeMouseRelease7 0x2107
// ...
-#define xpdfKeyCodeMouseRelease32 0x2120
-#define xpdfKeyCodeMouseClick1 0x2201
-#define xpdfKeyCodeMouseClick2 0x2202
-#define xpdfKeyCodeMouseClick3 0x2203
-#define xpdfKeyCodeMouseClick4 0x2204
-#define xpdfKeyCodeMouseClick5 0x2205
-#define xpdfKeyCodeMouseClick6 0x2206
-#define xpdfKeyCodeMouseClick7 0x2207
+#define xpdfKeyCodeMouseRelease32 0x2120
+#define xpdfKeyCodeMouseClick1 0x2201
+#define xpdfKeyCodeMouseClick2 0x2202
+#define xpdfKeyCodeMouseClick3 0x2203
+#define xpdfKeyCodeMouseClick4 0x2204
+#define xpdfKeyCodeMouseClick5 0x2205
+#define xpdfKeyCodeMouseClick6 0x2206
+#define xpdfKeyCodeMouseClick7 0x2207
// ...
-#define xpdfKeyCodeMouseClick32 0x2220
+#define xpdfKeyCodeMouseClick32 0x2220
+#define xpdfKeyCodeMouseDoubleClick1 0x2301
+#define xpdfKeyCodeMouseDoubleClick2 0x2302
+#define xpdfKeyCodeMouseDoubleClick3 0x2303
+#define xpdfKeyCodeMouseDoubleClick4 0x2304
+#define xpdfKeyCodeMouseDoubleClick5 0x2305
+#define xpdfKeyCodeMouseDoubleClick6 0x2306
+#define xpdfKeyCodeMouseDoubleClick7 0x2307
+// ...
+#define xpdfKeyCodeMouseDoubleClick32 0x2320
+#define xpdfKeyCodeMouseTripleClick1 0x2401
+#define xpdfKeyCodeMouseTripleClick2 0x2402
+#define xpdfKeyCodeMouseTripleClick3 0x2403
+#define xpdfKeyCodeMouseTripleClick4 0x2404
+#define xpdfKeyCodeMouseTripleClick5 0x2405
+#define xpdfKeyCodeMouseTripleClick6 0x2406
+#define xpdfKeyCodeMouseTripleClick7 0x2407
+// ...
+#define xpdfKeyCodeMouseTripleClick32 0x2420
#define xpdfKeyModNone 0
#define xpdfKeyModShift (1 << 0)
#define xpdfKeyModCtrl (1 << 1)
@@ -279,6 +297,7 @@ public:
GBool getPSAlwaysRasterize();
GBool getPSNeverRasterize();
GString *getTextEncodingName();
+ GList *getAvailableTextEncodings();
EndOfLineKind getTextEOL();
GBool getTextPageBreaks();
GBool getTextKeepTinyChars();
@@ -287,6 +306,7 @@ public:
GString *getInitialDisplayMode();
GBool getInitialToolbarState();
GBool getInitialSidebarState();
+ int getInitialSidebarWidth();
GString *getInitialSelectMode();
int getMaxTileWidth();
int getMaxTileHeight();
@@ -312,6 +332,7 @@ public:
GString *getPaperColor();
GString *getMatteColor();
GString *getFullScreenMatteColor();
+ GString *getSelectionColor();
GBool getReverseVideoInvertImages();
GString *getLaunchCommand() { return launchCommand; }
GString *getMovieCommand() { return movieCommand; }
@@ -320,15 +341,15 @@ public:
GBool getMapUnknownCharNames();
GBool getMapExtTrueTypeFontsViaUnicode();
GBool isDroppedFont(const char *fontName);
- GBool getEnableXFA();
GList *getKeyBinding(int code, int mods, int context);
+ GList *getAllKeyBindings();
int getNumPopupMenuCmds();
PopupMenuCmd *getPopupMenuCmd(int idx);
GString *getTabStateFile();
GBool getPrintCommands();
GBool getErrQuiet();
GString *getDebugLogFile();
- void debugLogPrintf(char *fmt, ...);
+ void debugLogPrintf(const char *fmt, ...);
CharCodeToUnicode *getCIDToUnicode(GString *collection);
CharCodeToUnicode *getUnicodeToUnicode(GString *fontName);
@@ -378,7 +399,6 @@ public:
void setMapNumericCharNames(GBool map);
void setMapUnknownCharNames(GBool map);
void setMapExtTrueTypeFontsViaUnicode(GBool map);
- void setEnableXFA(GBool enable);
void setTabStateFile(char *tabStateFileA);
void setPrintCommands(GBool printCommandsA);
void setErrQuiet(GBool errQuietA);
@@ -392,8 +412,10 @@ public:
private:
+ void setDataDirVar();
void createDefaultKeyBindings();
void parseFile(GString *fileName, FILE *f);
+ GList *parseLineTokens(char *buf, GString *fileName, int line);
void parseNameToUnicode(GList *tokens, GString *fileName, int line);
void parseCIDToUnicode(GList *tokens, GString *fileName, int line);
void parseUnicodeToUnicode(GList *tokens, GString *fileName, int line);
@@ -438,9 +460,14 @@ private:
NameToCharCode * // mapping from char name to
macRomanReverseMap; // MacRomanEncoding index
- //----- user-modifiable settings
+ //----- meta settings
GString *baseDir; // base directory - for plugins, etc.
+ GHash *configFileVars; // variables for use in the config file
+ // [GString]
+
+ //----- user-modifiable settings
+
NameToCharCode * // mapping from char name to Unicode
nameToUnicode;
GHash *cidToUnicodes; // files for mappings from char collections
@@ -522,6 +549,7 @@ private:
// or closed (false)
GBool initialSidebarState; // initial sidebar state - open (true)
// or closed (false)
+ int initialSidebarWidth; // initial sidebar width
GString *initialSelectMode; // initial selection mode (block or linear)
int maxTileWidth; // maximum rasterization tile width
int maxTileHeight; // maximum rasterization tile height
@@ -548,6 +576,7 @@ private:
GString *paperColor; // paper (page background) color
GString *matteColor; // matte (background outside of page) color
GString *fullScreenMatteColor; // matte color in full-screen mode
+ GString *selectionColor; // selection color
GBool reverseVideoInvertImages; // invert images in reverse video mode
GString *launchCommand; // command executed for 'launch' links
GString *movieCommand; // command executed for movie annotations
@@ -558,7 +587,6 @@ private:
GBool mapExtTrueTypeFontsViaUnicode; // map char codes to GID via Unicode
// for external TrueType fonts?
GHash *droppedFonts; // dropped fonts [int]
- GBool enableXFA; // enable XFA form rendering
GList *keyBindings; // key & mouse button bindings [KeyBinding]
GList *popupMenuCmds; // popup menu commands [PopupMenuCmd]
GString *tabStateFile; // path for the tab state save file
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc
index 036f1116c01..6f51ad85190 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc
@@ -214,6 +214,7 @@ HTMLGen::HTMLGen(double backgroundResolutionA) {
// set up the TextOutputDev
textOutControl.mode = textOutReadingOrder;
textOutControl.html = gTrue;
+ textOutControl.splitRotatedWords = gTrue;
textOut = new TextOutputDev(NULL, &textOutControl, gFalse);
if (!textOut->isOk()) {
ok = gFalse;
@@ -492,7 +493,7 @@ void HTMLGen::appendSpans(GList *words, int firstWordIdx, int lastWordIdx,
(spanDir >= 0) ? wordIdx <= lastWordIdx : wordIdx >= lastWordIdx;
wordIdx += spanDir) {
word1 = (TextWord *)words->get(wordIdx);
- invisible = allTextInvisible || word1->isInvisible();
+ invisible = allTextInvisible || word1->isInvisible() || word1->isRotated();
if (!drawInvisibleText && invisible) {
continue;
}
@@ -513,6 +514,7 @@ void HTMLGen::appendSpans(GList *words, int firstWordIdx, int lastWordIdx,
word1->getFontInfo() != word0->getFontInfo() ||
word1->getFontSize() != word0->getFontSize() ||
word1->isInvisible() != word0->isInvisible() ||
+ word1->isRotated() != word0->isRotated() ||
vertAlign1 != vertAlign0 ||
r1 != r0 || g1 != g0 || b1 != b0) {
if (word0) {
@@ -717,6 +719,7 @@ HTMLGenFontDefn *HTMLGen::getFontFile(TextFontInfo *font,
getFontDetails(font, &family, &weight, &style, &scale);
fontSpec = GString::format("font-family:ff{0:d},{1:s}; font-weight:{2:s}; font-style:{3:s};",
nextFontFaceIdx, family, weight, style);
+ ++nextFontFaceIdx;
fontDefn = new HTMLGenFontDefn(id, fontFace, fontSpec, 1.0);
}
delete fontPath;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc
index 1fb32be0b58..5a694d7b665 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc
@@ -29,7 +29,6 @@
ImageOutputDev::ImageOutputDev(char *fileRootA, GBool dumpJPEGA,
GBool dumpRawA, GBool listA) {
fileRoot = copyString(fileRootA);
- fileName = (char *)gmalloc((int)strlen(fileRoot) + 30);
dumpJPEG = dumpJPEGA;
dumpRaw = dumpRawA;
list = listA;
@@ -39,7 +38,6 @@ ImageOutputDev::ImageOutputDev(char *fileRootA, GBool dumpJPEGA,
}
ImageOutputDev::~ImageOutputDev() {
- gfree(fileName);
gfree(fileRoot);
}
@@ -60,6 +58,7 @@ void ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
int width, int height, GBool invert,
GBool inlineImg, GBool interpolate) {
+ GString *fileName;
FILE *f;
char buf[4096];
int size, n, i;
@@ -68,11 +67,12 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
if (dumpRaw && !inlineImg) {
// open the image file
- sprintf(fileName, "%s-%04d.%s",
- fileRoot, imgNum, getRawFileExtension(str));
+ fileName = GString::format("{0:s}-{1:04d}.{2:s}",
+ fileRoot, imgNum, getRawFileExtension(str));
++imgNum;
- if (!(f = fopen(fileName, "wb"))) {
- error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
return;
}
@@ -92,10 +92,11 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
} else if (dumpJPEG && str->getKind() == strDCT && !inlineImg) {
// open the image file
- sprintf(fileName, "%s-%04d.jpg", fileRoot, imgNum);
+ fileName = GString::format("{0:s}-{1:04d}.jpg", fileRoot, imgNum);
++imgNum;
- if (!(f = fopen(fileName, "wb"))) {
- error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
return;
}
@@ -115,10 +116,11 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
} else {
// open the image file and write the PBM header
- sprintf(fileName, "%s-%04d.pbm", fileRoot, imgNum);
+ fileName = GString::format("{0:s}-{1:04d}.pbm", fileRoot, imgNum);
++imgNum;
- if (!(f = fopen(fileName, "wb"))) {
- error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
return;
}
fprintf(f, "P4\n");
@@ -144,8 +146,10 @@ void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
}
if (list) {
- writeImageInfo(width, height, state, NULL);
+ writeImageInfo(fileName, width, height, state, NULL);
}
+
+ delete fileName;
}
void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
@@ -154,6 +158,7 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
int *maskColors, GBool inlineImg,
GBool interpolate) {
GfxColorSpaceMode csMode;
+ GString *fileName;
FILE *f;
ImageStream *imgStr;
Guchar *p;
@@ -173,11 +178,12 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
if (dumpRaw && !inlineImg) {
// open the image file
- sprintf(fileName, "%s-%04d.%s",
- fileRoot, imgNum, getRawFileExtension(str));
+ fileName = GString::format("{0:s}-{1:04d}.{2:s}",
+ fileRoot, imgNum, getRawFileExtension(str));
++imgNum;
- if (!(f = fopen(fileName, "wb"))) {
- error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
return;
}
@@ -200,10 +206,11 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
!inlineImg) {
// open the image file
- sprintf(fileName, "%s-%04d.jpg", fileRoot, imgNum);
+ fileName = GString::format("{0:s}-{1:04d}.jpg", fileRoot, imgNum);
++imgNum;
- if (!(f = fopen(fileName, "wb"))) {
- error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
return;
}
@@ -224,10 +231,11 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
colorMap->getBits() == 1) {
// open the image file and write the PBM header
- sprintf(fileName, "%s-%04d.pbm", fileRoot, imgNum);
+ fileName = GString::format("{0:s}-{1:04d}.pbm", fileRoot, imgNum);
++imgNum;
- if (!(f = fopen(fileName, "wb"))) {
- error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
return;
}
fprintf(f, "P4\n");
@@ -259,10 +267,11 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
(csMode == csDeviceGray || csMode == csCalGray)) {
// open the image file and write the PGM header
- sprintf(fileName, "%s-%04d.pgm", fileRoot, imgNum);
+ fileName = GString::format("{0:s}-{1:04d}.pgm", fileRoot, imgNum);
++imgNum;
- if (!(f = fopen(fileName, "wb"))) {
- error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
return;
}
fprintf(f, "P5\n");
@@ -300,10 +309,11 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
} else {
// open the image file and write the PPM header
- sprintf(fileName, "%s-%04d.ppm", fileRoot, imgNum);
+ fileName = GString::format("{0:s}-{1:04d}.ppm", fileRoot, imgNum);
++imgNum;
- if (!(f = fopen(fileName, "wb"))) {
- error(errIO, -1, "Couldn't open image file '{0:s}'", fileName);
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
return;
}
fprintf(f, "P6\n");
@@ -343,19 +353,21 @@ void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
}
if (list) {
- writeImageInfo(width, height, state, colorMap);
+ writeImageInfo(fileName, width, height, state, colorMap);
}
+
+ delete fileName;
}
void ImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GBool maskInvert, GBool interpolate) {
drawImage(state, ref, str, width, height, colorMap,
NULL, gFalse, interpolate);
- drawImageMask(state, ref, maskStr, maskWidth, maskHeight, maskInvert,
+ drawImageMask(state, maskRef, maskStr, maskWidth, maskHeight, maskInvert,
gFalse, interpolate);
}
@@ -363,13 +375,13 @@ void ImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GfxImageColorMap *maskColorMap,
double *matte, GBool interpolate) {
drawImage(state, ref, str, width, height, colorMap,
NULL, gFalse, interpolate);
- drawImage(state, ref, maskStr, maskWidth, maskHeight, maskColorMap,
+ drawImage(state, maskRef, maskStr, maskWidth, maskHeight, maskColorMap,
NULL, gFalse, interpolate);
}
@@ -401,7 +413,8 @@ const char *ImageOutputDev::getRawFileExtension(Stream *str) {
}
}
-void ImageOutputDev::writeImageInfo(int width, int height, GfxState *state,
+void ImageOutputDev::writeImageInfo(GString *fileName,
+ int width, int height, GfxState *state,
GfxImageColorMap *colorMap) {
const char *mode;
double hdpi, vdpi, x0, y0, x1, y1;
@@ -433,7 +446,7 @@ void ImageOutputDev::writeImageInfo(int width, int height, GfxState *state,
}
printf("%s: page=%d width=%d height=%d hdpi=%.2f vdpi=%.2f %s%s bpc=%d\n",
- fileName, curPageNum, width, height, hdpi, vdpi,
+ fileName->getCString(), curPageNum, width, height, hdpi, vdpi,
mode ? "colorspace=" : "mask",
mode ? mode : "",
bpc);
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h
index 3cdae45da66..5a72925aee9 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h
@@ -85,12 +85,13 @@ public:
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr, int maskWidth, int maskHeight,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GfxImageColorMap *maskColorMap,
double *matte, GBool interpolate);
@@ -99,11 +100,11 @@ private:
Stream *getRawStream(Stream *str);
const char *getRawFileExtension(Stream *str);
- void writeImageInfo(int width, int height, GfxState *state,
+ void writeImageInfo(GString *fileName,
+ int width, int height, GfxState *state,
GfxImageColorMap *colorMap);
char *fileRoot; // root of output file names
- char *fileName; // buffer for output file names
GBool dumpJPEG; // set to dump native JPEG files
GBool dumpRaw; // set to dump raw PDF-native image files
GBool list; // set to write image info to stdout
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc
index fcb9d6d0e3a..4c4e5183026 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc
@@ -1304,7 +1304,6 @@ void JBIG2Stream::readSegments() {
Guint *refSegs;
int c1, c2, c3;
Guint i;
- GBool done;
done = gFalse;
while (!done && readULong(&segNum)) {
@@ -1723,17 +1722,18 @@ GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
"Bad delta-height value in JBIG2 symbol dictionary");
goto syntaxError;
}
- // sanity check to avoid extremely long run-times with damaged streams
- if (dh > 1000000) {
- error(errSyntaxError, getPos(),
- "Bogus delta-height value in JBIG2 symbol dictionary");
- goto syntaxError;
- }
symHeight += dh;
symWidth = 0;
totalWidth = 0;
j = i;
+ // sanity check to avoid extremely long run-times with damaged streams
+ if (symHeight > 100000) {
+ error(errSyntaxError, getPos(),
+ "Bogus symbol height value in JBIG2 symbol dictionary");
+ goto syntaxError;
+ }
+
// read the symbols in this height class
while (1) {
@@ -1760,6 +1760,13 @@ GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
goto syntaxError;
}
+ // sanity check to avoid extremely long run-times with damaged streams
+ if (symWidth > 100000) {
+ error(errSyntaxError, getPos(),
+ "Bogus symbol width value in JBIG2 symbol dictionary");
+ goto syntaxError;
+ }
+
// using a collective bitmap, so don't read a bitmap here
if (huff && !refAgg) {
symWidths[i] = symWidth;
@@ -1977,6 +1984,15 @@ void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm,
error(errSyntaxError, getPos(), "Bad size in JBIG2 text region segment");
return;
}
+ // sanity check: if the w/h/x/y values are way out of range, it likely
+ // indicates a damaged JBIG2 stream
+ if (w / 10 > pageW || h / 10 > pageH ||
+ x / 10 > pageW || y / 10 > pageH) {
+ error(errSyntaxError, getPos(),
+ "Bad size or position in JBIG2 text region segment");
+ done = gTrue;
+ return;
+ }
extCombOp = segInfoFlags & 7;
// rest of the text region header
@@ -2557,6 +2573,15 @@ void JBIG2Stream::readHalftoneRegionSeg(Guint segNum, GBool imm,
!readUByte(&segInfoFlags)) {
goto eofError;
}
+ // sanity check: if the w/h/x/y values are way out of range, it likely
+ // indicates a damaged JBIG2 stream
+ if (w / 10 > pageW || h / 10 > pageH ||
+ x / 10 > pageW || y / 10 > pageH) {
+ error(errSyntaxError, getPos(),
+ "Bad size or position in JBIG2 halftone region segment");
+ done = gTrue;
+ return;
+ }
extCombOp = segInfoFlags & 7;
// rest of the halftone region header
@@ -2720,6 +2745,15 @@ void JBIG2Stream::readGenericRegionSeg(Guint segNum, GBool imm,
"Bad bitmap size in JBIG2 generic region segment");
return;
}
+ // sanity check: if the w/h/x/y values are way out of range, it likely
+ // indicates a damaged JBIG2 stream
+ if (w / 10 > pageW || h / 10 > pageH ||
+ x / 10 > pageW || y / 10 > pageH) {
+ error(errSyntaxError, getPos(),
+ "Bad size or position in JBIG2 generic region segment");
+ done = gTrue;
+ return;
+ }
extCombOp = segInfoFlags & 7;
// rest of the generic region segment header
@@ -3623,6 +3657,15 @@ void JBIG2Stream::readGenericRefinementRegionSeg(Guint segNum, GBool imm,
"Bad size in JBIG2 generic refinement region segment");
return;
}
+ // sanity check: if the w/h/x/y values are way out of range, it likely
+ // indicates a damaged JBIG2 stream
+ if (w / 10 > pageW || h / 10 > pageH ||
+ x / 10 > pageW || y / 10 > pageH) {
+ error(errSyntaxError, getPos(),
+ "Bad size or position in JBIG2 generic refinement region segment");
+ done = gTrue;
+ return;
+ }
extCombOp = segInfoFlags & 7;
// rest of the generic refinement region segment header
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h
index 7aa4d9d8edb..cca722b4bfc 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h
@@ -128,6 +128,7 @@ private:
Guchar *dataPtr;
Guchar *dataEnd;
Guint byteCounter;
+ GBool done;
JArithmeticDecoder *arithDecoder;
JArithmeticDecoderStats *genericRegionStats;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc
index a5dd6f0db0e..d9c295bb12c 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc
@@ -20,7 +20,6 @@
#include "JPXStream.h"
//~ to do:
-// - precincts
// - ROI
// - progression order changes
// - packed packet headers
@@ -28,7 +27,6 @@
// - make sure all needed JP2/JPX subboxes are parsed (readBoxes)
// - can we assume that QCC segments must come after the QCD segment?
// - handle tilePartToEOC in readTilePartData
-// - progression orders 2, 3, and 4
// - in coefficient decoding (readCodeBlockData):
// - selective arithmetic coding bypass
// (this also affects reading the cb->dataLen array)
@@ -325,7 +323,7 @@ void JPXStream::close() {
for (r = 0; r <= tileComp->nDecompLevels; ++r) {
resLevel = &tileComp->resLevels[r];
if (resLevel->precincts) {
- for (pre = 0; pre < 1; ++pre) {
+ for (pre = 0; pre < resLevel->nPrecincts; ++pre) {
precinct = &resLevel->precincts[pre];
if (precinct->subbands) {
for (sb = 0; sb < (Guint)(r == 0 ? 1 : 3); ++sb) {
@@ -350,13 +348,13 @@ void JPXStream::close() {
gfree(precinct->subbands);
}
}
- gfree(img.tiles[i].tileComps[comp].resLevels[r].precincts);
+ gfree(resLevel->precincts);
}
}
- gfree(img.tiles[i].tileComps[comp].resLevels);
+ gfree(tileComp->resLevels);
}
}
- gfree(img.tiles[i].tileComps);
+ gfree(tile->tileComps);
}
}
gfree(img.tiles);
@@ -1036,13 +1034,6 @@ JPXDecodeResult JPXStream::readCodestream(Guint len) {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
return jpxDecodeFatalError;
}
-#if 1 //~ progression orders 2-4 are unimplemented
- if (progOrder >= 2) {
- error(errUnimplemented, -1,
- "JPX progression order {0:d} is unimplemented",
- progOrder);
- }
-#endif
codeBlockW += 2;
codeBlockH += 2;
for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
@@ -1445,7 +1436,8 @@ JPXDecodeResult JPXStream::readCodestream(Guint len) {
ok = gTrue;
while (1) {
if (!readTilePart()) {
- return jpxDecodeFatalError;
+ ok = gFalse;
+ break;
}
if (!readMarkerHdr(&segType, &segLen)) {
error(errSyntaxError, getPos(), "Error in JPX codestream");
@@ -1497,8 +1489,11 @@ GBool JPXStream::readTilePart() {
Guint style, progOrder, nLayers, multiComp, nDecompLevels;
Guint codeBlockW, codeBlockH, codeBlockStyle, transform;
Guint precinctSize, qStyle;
- Guint n, nSBs, nx, ny, sbx0, sby0, comp, segLen;
- Guint i, j, k, cbX, cbY, r, pre, sb, cbi, cbj;
+ Guint px0, py0, px1, py1;
+ Guint preCol0, preCol1, preRow0, preRow1, preCol, preRow;
+ Guint cbCol0, cbCol1, cbRow0, cbRow1, cbX, cbY;
+ Guint n, nSBs, nx, ny, comp, segLen;
+ Guint i, j, k, r, pre, sb, cbi, cbj;
int segType, level;
// process the SOT marker segment
@@ -1534,6 +1529,10 @@ GBool JPXStream::readTilePart() {
switch (segType) {
case 0x52: // COD - coding style default
cover(34);
+ if (tilePartIdx != 0) {
+ error(errSyntaxError, getPos(), "Extraneous JPX COD marker segment");
+ return gFalse;
+ }
if (!readUByte(&style) ||
!readUByte(&progOrder) ||
!readUWord(&nLayers) ||
@@ -1553,13 +1552,6 @@ GBool JPXStream::readTilePart() {
error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
return gFalse;
}
-#if 1 //~ progression orders 2-4 are unimplemented
- if (img.tiles[tileIdx].progOrder >= 2) {
- error(errUnimplemented, -1,
- "JPX progression order {0:d} is unimplemented",
- img.tiles[tileIdx].progOrder);
- }
-#endif
codeBlockW += 2;
codeBlockH += 2;
img.tiles[tileIdx].progOrder = progOrder;
@@ -1607,6 +1599,10 @@ GBool JPXStream::readTilePart() {
break;
case 0x53: // COC - coding style component
cover(35);
+ if (tilePartIdx != 0) {
+ error(errSyntaxError, getPos(), "Extraneous JPX COC marker segment");
+ return gFalse;
+ }
if ((img.nComps > 256 && !readUWord(&comp)) ||
(img.nComps <= 256 && !readUByte(&comp)) ||
comp >= img.nComps ||
@@ -1623,7 +1619,7 @@ GBool JPXStream::readTilePart() {
nDecompLevels > 31 ||
codeBlockW > 8 ||
codeBlockH > 8) {
- error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
+ error(errSyntaxError, getPos(), "Error in JPX COC marker segment");
return gFalse;
}
img.tiles[tileIdx].tileComps[comp].style =
@@ -1659,6 +1655,10 @@ GBool JPXStream::readTilePart() {
break;
case 0x5c: // QCD - quantization default
cover(36);
+ if (tilePartIdx != 0) {
+ error(errSyntaxError, getPos(), "Extraneous JPX QCD marker segment");
+ return gFalse;
+ }
if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantStyle)) {
error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
return gFalse;
@@ -1726,6 +1726,10 @@ GBool JPXStream::readTilePart() {
break;
case 0x5d: // QCC - quantization component
cover(37);
+ if (tilePartIdx != 0) {
+ error(errSyntaxError, getPos(), "Extraneous JPX QCC marker segment");
+ return gFalse;
+ }
if ((img.nComps > 256 && !readUWord(&comp)) ||
(img.nComps <= 256 && !readUByte(&comp)) ||
comp >= img.nComps ||
@@ -1786,6 +1790,10 @@ GBool JPXStream::readTilePart() {
break;
case 0x5e: // RGN - region of interest
cover(38);
+ if (tilePartIdx != 0) {
+ error(errSyntaxError, getPos(), "Extraneous JPX RGN marker segment");
+ return gFalse;
+ }
#if 1 //~ ROI is unimplemented
error(errUnimplemented, -1, "Got a JPX RGN segment");
if (segLen > 2 &&
@@ -1889,7 +1897,6 @@ GBool JPXStream::readTilePart() {
//----- initialize the tile, precincts, and code-blocks
if (tilePartIdx == 0) {
tile = &img.tiles[tileIdx];
- tile->init = gTrue;
i = tileIdx / img.nXTiles;
j = tileIdx % img.nXTiles;
if ((tile->x0 = img.xTileOffset + j * img.xTileSize) < img.xOffset) {
@@ -1919,8 +1926,6 @@ GBool JPXStream::readTilePart() {
tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->vSep);
tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep);
tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->vSep);
- tileComp->cbW = 1 << tileComp->codeBlockW;
- tileComp->cbH = 1 << tileComp->codeBlockH;
tileComp->w = jpxCeilDivPow2(tileComp->x1, reduction)
- jpxCeilDivPow2(tileComp->x0, reduction);
tileComp->h = jpxCeilDivPow2(tileComp->y1, reduction)
@@ -1939,12 +1944,26 @@ GBool JPXStream::readTilePart() {
tileComp->buf = (int *)gmallocn(n + 8, sizeof(int));
for (r = 0; r <= tileComp->nDecompLevels; ++r) {
resLevel = &tileComp->resLevels[r];
- k = r == 0 ? tileComp->nDecompLevels
- : tileComp->nDecompLevels - r + 1;
- resLevel->x0 = jpxCeilDivPow2(tileComp->x0, k);
- resLevel->y0 = jpxCeilDivPow2(tileComp->y0, k);
- resLevel->x1 = jpxCeilDivPow2(tileComp->x1, k);
- resLevel->y1 = jpxCeilDivPow2(tileComp->y1, k);
+ resLevel->x0 = jpxCeilDivPow2(tileComp->x0,
+ tileComp->nDecompLevels - r);
+ resLevel->y0 = jpxCeilDivPow2(tileComp->y0,
+ tileComp->nDecompLevels - r);
+ resLevel->x1 = jpxCeilDivPow2(tileComp->x1,
+ tileComp->nDecompLevels - r);
+ resLevel->y1 = jpxCeilDivPow2(tileComp->y1,
+ tileComp->nDecompLevels - r);
+ resLevel->codeBlockW = r == 0 ? resLevel->precinctWidth
+ : resLevel->precinctWidth - 1;
+ if (resLevel->codeBlockW > tileComp->codeBlockW) {
+ resLevel->codeBlockW = tileComp->codeBlockW;
+ }
+ resLevel->cbW = 1 << resLevel->codeBlockW;
+ resLevel->codeBlockH = r == 0 ? resLevel->precinctHeight
+ : resLevel->precinctHeight - 1;
+ if (resLevel->codeBlockH > tileComp->codeBlockH) {
+ resLevel->codeBlockH = tileComp->codeBlockH;
+ }
+ resLevel->cbH = 1 << resLevel->codeBlockH;
// the JPEG 2000 spec says that packets for empty res levels
// should all be present in the codestream (B.6, B.9, B.10),
// but it appears that encoders drop packets if the res level
@@ -1952,6 +1971,7 @@ GBool JPXStream::readTilePart() {
resLevel->empty = resLevel->x0 == resLevel->x1 ||
resLevel->y0 == resLevel->y1;
if (r == 0) {
+ nSBs = 1;
resLevel->bx0[0] = resLevel->x0;
resLevel->by0[0] = resLevel->y0;
resLevel->bx1[0] = resLevel->x1;
@@ -1960,18 +1980,19 @@ GBool JPXStream::readTilePart() {
(resLevel->bx0[0] == resLevel->bx1[0] ||
resLevel->by0[0] == resLevel->by1[0]);
} else {
- resLevel->bx0[0] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k);
- resLevel->by0[0] = resLevel->y0;
- resLevel->bx1[0] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);
- resLevel->by1[0] = resLevel->y1;
- resLevel->bx0[1] = resLevel->x0;
- resLevel->by0[1] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k);
- resLevel->bx1[1] = resLevel->x1;
- resLevel->by1[1] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);
- resLevel->bx0[2] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k);
- resLevel->by0[2] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k);
- resLevel->bx1[2] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);
- resLevel->by1[2] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);
+ nSBs = 3;
+ resLevel->bx0[0] = jpxCeilDivPow2(resLevel->x0 - 1, 1);
+ resLevel->by0[0] = jpxCeilDivPow2(resLevel->y0, 1);
+ resLevel->bx1[0] = jpxCeilDivPow2(resLevel->x1 - 1, 1);
+ resLevel->by1[0] = jpxCeilDivPow2(resLevel->y1, 1);
+ resLevel->bx0[1] = jpxCeilDivPow2(resLevel->x0, 1);
+ resLevel->by0[1] = jpxCeilDivPow2(resLevel->y0 - 1, 1);
+ resLevel->bx1[1] = jpxCeilDivPow2(resLevel->x1, 1);
+ resLevel->by1[1] = jpxCeilDivPow2(resLevel->y1 - 1, 1);
+ resLevel->bx0[2] = jpxCeilDivPow2(resLevel->x0 - 1, 1);
+ resLevel->by0[2] = jpxCeilDivPow2(resLevel->y0 - 1, 1);
+ resLevel->bx1[2] = jpxCeilDivPow2(resLevel->x1 - 1, 1);
+ resLevel->by1[2] = jpxCeilDivPow2(resLevel->y1 - 1, 1);
resLevel->empty = resLevel->empty &&
(resLevel->bx0[0] == resLevel->bx1[0] ||
resLevel->by0[0] == resLevel->by1[0]) &&
@@ -1980,135 +2001,155 @@ GBool JPXStream::readTilePart() {
(resLevel->bx0[2] == resLevel->bx1[2] ||
resLevel->by0[2] == resLevel->by1[2]);
}
- resLevel->precincts = (JPXPrecinct *)gmallocn(1, sizeof(JPXPrecinct));
- for (pre = 0; pre < 1; ++pre) {
+ preCol0 = jpxFloorDivPow2(resLevel->x0, resLevel->precinctWidth);
+ preCol1 = jpxCeilDivPow2(resLevel->x1, resLevel->precinctWidth);
+ preRow0 = jpxFloorDivPow2(resLevel->y0, resLevel->precinctHeight);
+ preRow1 = jpxCeilDivPow2(resLevel->y1, resLevel->precinctHeight);
+ resLevel->nPrecincts = (preCol1 - preCol0) * (preRow1 - preRow0);
+ resLevel->precincts = (JPXPrecinct *)gmallocn(resLevel->nPrecincts,
+ sizeof(JPXPrecinct));
+ for (pre = 0; pre < resLevel->nPrecincts; ++pre) {
resLevel->precincts[pre].subbands = NULL;
}
- for (pre = 0; pre < 1; ++pre) {
- precinct = &resLevel->precincts[pre];
- precinct->x0 = resLevel->x0;
- precinct->y0 = resLevel->y0;
- precinct->x1 = resLevel->x1;
- precinct->y1 = resLevel->y1;
- nSBs = r == 0 ? 1 : 3;
- precinct->subbands =
- (JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband));
- for (sb = 0; sb < nSBs; ++sb) {
- precinct->subbands[sb].inclusion = NULL;
- precinct->subbands[sb].zeroBitPlane = NULL;
- precinct->subbands[sb].cbs = NULL;
- }
- for (sb = 0; sb < nSBs; ++sb) {
- subband = &precinct->subbands[sb];
- subband->x0 = resLevel->bx0[sb];
- subband->y0 = resLevel->by0[sb];
- subband->x1 = resLevel->bx1[sb];
- subband->y1 = resLevel->by1[sb];
- subband->nXCBs = jpxCeilDivPow2(subband->x1,
- tileComp->codeBlockW)
- - jpxFloorDivPow2(subband->x0,
- tileComp->codeBlockW);
- subband->nYCBs = jpxCeilDivPow2(subband->y1,
- tileComp->codeBlockH)
- - jpxFloorDivPow2(subband->y0,
- tileComp->codeBlockH);
- n = subband->nXCBs > subband->nYCBs ? subband->nXCBs
- : subband->nYCBs;
- for (subband->maxTTLevel = 0, --n;
- n;
- ++subband->maxTTLevel, n >>= 1) ;
- n = 0;
- for (level = subband->maxTTLevel; level >= 0; --level) {
- nx = jpxCeilDivPow2(subband->nXCBs, level);
- ny = jpxCeilDivPow2(subband->nYCBs, level);
- n += nx * ny;
+ precinct = resLevel->precincts;
+ for (preRow = preRow0; preRow < preRow1; ++preRow) {
+ for (preCol = preCol0; preCol < preCol1; ++preCol) {
+ precinct->subbands =
+ (JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband));
+ for (sb = 0; sb < nSBs; ++sb) {
+ precinct->subbands[sb].inclusion = NULL;
+ precinct->subbands[sb].zeroBitPlane = NULL;
+ precinct->subbands[sb].cbs = NULL;
}
- subband->inclusion =
- (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode));
- subband->zeroBitPlane =
- (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode));
- for (k = 0; k < n; ++k) {
- subband->inclusion[k].finished = gFalse;
- subband->inclusion[k].val = 0;
- subband->zeroBitPlane[k].finished = gFalse;
- subband->zeroBitPlane[k].val = 0;
- }
- subband->cbs = (JPXCodeBlock *)gmallocn(subband->nXCBs *
- subband->nYCBs,
- sizeof(JPXCodeBlock));
- for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) {
- subband->cbs[k].dataLen = NULL;
- subband->cbs[k].touched = NULL;
- subband->cbs[k].arithDecoder = NULL;
- subband->cbs[k].stats = NULL;
- }
- sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW);
- sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH);
- if (r == 0) { // (NL)LL
- sbCoeffs = tileComp->data;
- } else if (sb == 0) { // (NL-r+1)HL
- sbCoeffs = tileComp->data
- + resLevel->bx1[1] - resLevel->bx0[1];
- } else if (sb == 1) { // (NL-r+1)LH
- sbCoeffs = tileComp->data
- + (resLevel->by1[0] - resLevel->by0[0]) * tileComp->w;
- } else { // (NL-r+1)HH
- sbCoeffs = tileComp->data
- + (resLevel->by1[0] - resLevel->by0[0]) * tileComp->w
- + resLevel->bx1[1] - resLevel->bx0[1];
- }
- cb = subband->cbs;
- for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
- for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
- cb->x0 = (sbx0 + cbX) << tileComp->codeBlockW;
- cb->x1 = cb->x0 + tileComp->cbW;
- if (subband->x0 > cb->x0) {
- cb->x0 = subband->x0;
- }
- if (subband->x1 < cb->x1) {
- cb->x1 = subband->x1;
- }
- cb->y0 = (sby0 + cbY) << tileComp->codeBlockH;
- cb->y1 = cb->y0 + tileComp->cbH;
- if (subband->y0 > cb->y0) {
- cb->y0 = subband->y0;
- }
- if (subband->y1 < cb->y1) {
- cb->y1 = subband->y1;
- }
- cb->seen = gFalse;
- cb->lBlock = 3;
- cb->nextPass = jpxPassCleanup;
- cb->nZeroBitPlanes = 0;
- cb->dataLenSize = 1;
- cb->dataLen = (Guint *)gmalloc(sizeof(Guint));
- if (r <= tileComp->nDecompLevels - reduction) {
- cb->coeffs = sbCoeffs
- + (cb->y0 - subband->y0) * tileComp->w
- + (cb->x0 - subband->x0);
- cb->touched = (char *)gmalloc(1 << (tileComp->codeBlockW
- + tileComp->codeBlockH));
- cb->len = 0;
- for (cbj = 0; cbj < cb->y1 - cb->y0; ++cbj) {
- for (cbi = 0; cbi < cb->x1 - cb->x0; ++cbi) {
- cb->coeffs[cbj * tileComp->w + cbi] = 0;
+ for (sb = 0; sb < nSBs; ++sb) {
+ subband = &precinct->subbands[sb];
+ if (r == 0) {
+ px0 = preCol << resLevel->precinctWidth;
+ px1 = (preCol + 1) << resLevel->precinctWidth;
+ py0 = preRow << resLevel->precinctHeight;
+ py1 = (preRow + 1) << resLevel->precinctHeight;
+ } else {
+ px0 = preCol << (resLevel->precinctWidth - 1);
+ px1 = (preCol + 1) << (resLevel->precinctWidth - 1);
+ py0 = preRow << (resLevel->precinctHeight - 1);
+ py1 = (preRow + 1) << (resLevel->precinctHeight - 1);
+ }
+ if (px0 < resLevel->bx0[sb]) {
+ px0 = resLevel->bx0[sb];
+ }
+ if (px1 > resLevel->bx1[sb]) {
+ px1 = resLevel->bx1[sb];
+ }
+ if (py0 < resLevel->by0[sb]) {
+ py0 = resLevel->by0[sb];
+ }
+ if (py1 > resLevel->by1[sb]) {
+ py1 = resLevel->by1[sb];
+ }
+ if (r == 0) { // (NL)LL
+ sbCoeffs = tileComp->data;
+ } else if (sb == 0) { // (NL-r+1)HL
+ sbCoeffs = tileComp->data
+ + resLevel->bx1[1] - resLevel->bx0[1];
+ } else if (sb == 1) { // (NL-r+1)LH
+ sbCoeffs = tileComp->data
+ + (resLevel->by1[0] - resLevel->by0[0]) * tileComp->w;
+ } else { // (NL-r+1)HH
+ sbCoeffs = tileComp->data
+ + (resLevel->by1[0] - resLevel->by0[0]) * tileComp->w
+ + (resLevel->bx1[1] - resLevel->bx0[1]);
+ }
+ cbCol0 = jpxFloorDivPow2(px0, resLevel->codeBlockW);
+ cbCol1 = jpxCeilDivPow2(px1, resLevel->codeBlockW);
+ cbRow0 = jpxFloorDivPow2(py0, resLevel->codeBlockH);
+ cbRow1 = jpxCeilDivPow2(py1, resLevel->codeBlockH);
+ subband->nXCBs = cbCol1 - cbCol0;
+ subband->nYCBs = cbRow1 - cbRow0;
+ n = subband->nXCBs > subband->nYCBs ? subband->nXCBs
+ : subband->nYCBs;
+ for (subband->maxTTLevel = 0, --n;
+ n;
+ ++subband->maxTTLevel, n >>= 1) ;
+ n = 0;
+ for (level = subband->maxTTLevel; level >= 0; --level) {
+ nx = jpxCeilDivPow2(subband->nXCBs, level);
+ ny = jpxCeilDivPow2(subband->nYCBs, level);
+ n += nx * ny;
+ }
+ subband->inclusion =
+ (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode));
+ subband->zeroBitPlane =
+ (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode));
+ for (k = 0; k < n; ++k) {
+ subband->inclusion[k].finished = gFalse;
+ subband->inclusion[k].val = 0;
+ subband->zeroBitPlane[k].finished = gFalse;
+ subband->zeroBitPlane[k].val = 0;
+ }
+ subband->cbs = (JPXCodeBlock *)gmallocn(subband->nXCBs *
+ subband->nYCBs,
+ sizeof(JPXCodeBlock));
+ for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) {
+ subband->cbs[k].dataLen = NULL;
+ subband->cbs[k].touched = NULL;
+ subband->cbs[k].arithDecoder = NULL;
+ subband->cbs[k].stats = NULL;
+ }
+ cb = subband->cbs;
+ for (cbY = cbRow0; cbY < cbRow1; ++cbY) {
+ for (cbX = cbCol0; cbX < cbCol1; ++cbX) {
+ cb->x0 = cbX << resLevel->codeBlockW;
+ cb->x1 = cb->x0 + resLevel->cbW;
+ if (cb->x0 < px0) {
+ cb->x0 = px0;
+ }
+ if (cb->x1 > px1) {
+ cb->x1 = px1;
+ }
+ cb->y0 = cbY << resLevel->codeBlockH;
+ cb->y1 = cb->y0 + resLevel->cbH;
+ if (cb->y0 < py0) {
+ cb->y0 = py0;
+ }
+ if (cb->y1 > py1) {
+ cb->y1 = py1;
+ }
+ cb->seen = gFalse;
+ cb->lBlock = 3;
+ cb->nextPass = jpxPassCleanup;
+ cb->nZeroBitPlanes = 0;
+ cb->dataLenSize = 1;
+ cb->dataLen = (Guint *)gmalloc(sizeof(Guint));
+ if (r <= tileComp->nDecompLevels - reduction) {
+ cb->coeffs = sbCoeffs
+ + (cb->y0 - resLevel->by0[sb]) * tileComp->w
+ + (cb->x0 - resLevel->bx0[sb]);
+ cb->touched = (char *)gmalloc(1 << (resLevel->codeBlockW
+ + resLevel->codeBlockH));
+ cb->len = 0;
+ for (cbj = 0; cbj < cb->y1 - cb->y0; ++cbj) {
+ for (cbi = 0; cbi < cb->x1 - cb->x0; ++cbi) {
+ cb->coeffs[cbj * tileComp->w + cbi] = 0;
+ }
}
+ memset(cb->touched, 0,
+ ((size_t)1 << (resLevel->codeBlockW
+ + resLevel->codeBlockH)));
+ } else {
+ cb->coeffs = NULL;
+ cb->touched = NULL;
+ cb->len = 0;
}
- memset(cb->touched, 0,
- ((size_t)1 << (tileComp->codeBlockW
- + tileComp->codeBlockH)));
- } else {
- cb->coeffs = NULL;
- cb->touched = NULL;
- cb->len = 0;
+ ++cb;
}
- ++cb;
}
}
+ ++precinct;
}
}
}
}
+ tile->init = gTrue;
}
return readTilePartData(tileIdx, tilePartLen, tilePartToEOC);
@@ -2384,68 +2425,87 @@ GBool JPXStream::readTilePartData(Guint tileIdx,
switch (tile->progOrder) {
case 0: // layer, resolution level, component, precinct
cover(58);
- if (++tile->comp == img.nComps) {
- tile->comp = 0;
- if (++tile->res == tile->maxNDecompLevels + 1) {
- tile->res = 0;
- if (++tile->layer == tile->nLayers) {
- tile->layer = 0;
- tile->done = gTrue;
+ resLevel = &tile->tileComps[tile->comp].resLevels[tile->res];
+ if (++tile->precinct == resLevel->nPrecincts) {
+ tile->precinct = 0;
+ if (++tile->comp == img.nComps) {
+ tile->comp = 0;
+ if (++tile->res == tile->maxNDecompLevels + 1) {
+ tile->res = 0;
+ if (++tile->layer == tile->nLayers) {
+ tile->layer = 0;
+ tile->done = gTrue;
+ }
}
}
}
break;
case 1: // resolution level, layer, component, precinct
cover(59);
- if (++tile->comp == img.nComps) {
- tile->comp = 0;
- if (++tile->layer == tile->nLayers) {
- tile->layer = 0;
- if (++tile->res == tile->maxNDecompLevels + 1) {
- tile->res = 0;
- tile->done = gTrue;
+ resLevel = &tile->tileComps[tile->comp].resLevels[tile->res];
+ if (++tile->precinct == resLevel->nPrecincts) {
+ tile->precinct = 0;
+ if (++tile->comp == img.nComps) {
+ tile->comp = 0;
+ if (++tile->layer == tile->nLayers) {
+ tile->layer = 0;
+ if (++tile->res == tile->maxNDecompLevels + 1) {
+ tile->res = 0;
+ tile->done = gTrue;
+ }
}
}
}
break;
case 2: // resolution level, precinct, component, layer
- //~ this isn't correct -- see B.12.1.3
cover(60);
+ //~ this is incorrect if there are subsampled components (?)
if (++tile->layer == tile->nLayers) {
tile->layer = 0;
if (++tile->comp == img.nComps) {
tile->comp = 0;
- if (++tile->res == tile->maxNDecompLevels + 1) {
- tile->res = 0;
- tile->done = gTrue;
+ resLevel = &tile->tileComps[tile->comp].resLevels[tile->res];
+ if (++tile->precinct == resLevel->nPrecincts) {
+ tile->precinct = 0;
+ if (++tile->res == tile->maxNDecompLevels + 1) {
+ tile->res = 0;
+ tile->done = gTrue;
+ }
}
}
}
break;
case 3: // precinct, component, resolution level, layer
- //~ this isn't correct -- see B.12.1.4
cover(61);
+ //~ this is incorrect if there are subsampled components (?)
if (++tile->layer == tile->nLayers) {
tile->layer = 0;
if (++tile->res == tile->maxNDecompLevels + 1) {
tile->res = 0;
if (++tile->comp == img.nComps) {
tile->comp = 0;
- tile->done = gTrue;
+ resLevel = &tile->tileComps[tile->comp].resLevels[tile->res];
+ if (++tile->precinct == resLevel->nPrecincts) {
+ tile->precinct = 0;
+ tile->done = gTrue;
+ }
}
}
}
break;
case 4: // component, precinct, resolution level, layer
- //~ this isn't correct -- see B.12.1.5
cover(62);
if (++tile->layer == tile->nLayers) {
tile->layer = 0;
if (++tile->res == tile->maxNDecompLevels + 1) {
tile->res = 0;
- if (++tile->comp == img.nComps) {
- tile->comp = 0;
- tile->done = gTrue;
+ resLevel = &tile->tileComps[tile->comp].resLevels[tile->res];
+ if (++tile->precinct == resLevel->nPrecincts) {
+ tile->precinct = 0;
+ if (++tile->comp == img.nComps) {
+ tile->comp = 0;
+ tile->done = gTrue;
+ }
}
}
}
@@ -2515,13 +2575,13 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched;
y0 < cb->y1;
y0 += 4, coeff0 += 4 * tileComp->w,
- touched0 += 4 << tileComp->codeBlockW) {
+ touched0 += 4 << resLevel->codeBlockW) {
for (x = cb->x0, coeff1 = coeff0, touched1 = touched0;
x < cb->x1;
++x, ++coeff1, ++touched1) {
for (y1 = 0, coeff = coeff1, touched = touched1;
y1 < 4 && y0+y1 < cb->y1;
- ++y1, coeff += tileComp->w, touched += tileComp->cbW) {
+ ++y1, coeff += tileComp->w, touched += resLevel->cbW) {
if (!*coeff) {
horiz = vert = diag = 0;
horizSign = vertSign = 2;
@@ -2590,13 +2650,13 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched;
y0 < cb->y1;
y0 += 4, coeff0 += 4 * tileComp->w,
- touched0 += 4 << tileComp->codeBlockW) {
+ touched0 += 4 << resLevel->codeBlockW) {
for (x = cb->x0, coeff1 = coeff0, touched1 = touched0;
x < cb->x1;
++x, ++coeff1, ++touched1) {
for (y1 = 0, coeff = coeff1, touched = touched1;
y1 < 4 && y0+y1 < cb->y1;
- ++y1, coeff += tileComp->w, touched += tileComp->cbW) {
+ ++y1, coeff += tileComp->w, touched += resLevel->cbW) {
if (*coeff && !*touched) {
if (*coeff == 1 || *coeff == -1) {
all = 0;
@@ -2651,16 +2711,16 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched;
y0 < cb->y1;
y0 += 4, coeff0 += 4 * tileComp->w,
- touched0 += 4 << tileComp->codeBlockW) {
+ touched0 += 4 << resLevel->codeBlockW) {
for (x = cb->x0, coeff1 = coeff0, touched1 = touched0;
x < cb->x1;
++x, ++coeff1, ++touched1) {
y1 = 0;
if (y0 + 3 < cb->y1 &&
!(*touched1) &&
- !(touched1[tileComp->cbW]) &&
- !(touched1[2 * tileComp->cbW]) &&
- !(touched1[3 * tileComp->cbW]) &&
+ !(touched1[resLevel->cbW]) &&
+ !(touched1[2 * resLevel->cbW]) &&
+ !(touched1[3 * resLevel->cbW]) &&
(x == cb->x0 || y0 == cb->y0 ||
!coeff1[-(int)tileComp->w - 1]) &&
(y0 == cb->y0 ||
@@ -2702,9 +2762,9 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
}
}
for (coeff = &coeff1[y1 * tileComp->w],
- touched = &touched1[y1 << tileComp->codeBlockW];
+ touched = &touched1[y1 << resLevel->codeBlockW];
y1 < 4 && y0 + y1 < cb->y1;
- ++y1, coeff += tileComp->w, touched += tileComp->cbW) {
+ ++y1, coeff += tileComp->w, touched += resLevel->cbW) {
if (!*touched) {
horiz = vert = diag = 0;
horizSign = vertSign = 2;
@@ -2814,15 +2874,13 @@ void JPXStream::inverseTransform(JPXTileComp *tileComp) {
int shift2;
double mu;
int val;
- Guint r, cbX, cbY, x, y;
+ Guint r, pre, cbX, cbY, x, y;
cover(68);
//----- (NL)LL subband (resolution level 0)
resLevel = &tileComp->resLevels[0];
- precinct = &resLevel->precincts[0];
- subband = &precinct->subbands[0];
// i-quant parameters
qStyle = tileComp->quantStyle & 0x1f;
@@ -2843,44 +2901,48 @@ void JPXStream::inverseTransform(JPXTileComp *tileComp) {
}
// do fixed point adjustment and dequantization on (NL)LL
- cb = subband->cbs;
- for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
- for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
- for (y = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched;
- y < cb->y1;
- ++y, coeff0 += tileComp->w, touched0 += tileComp->cbW) {
- for (x = cb->x0, coeff = coeff0, touched = touched0;
- x < cb->x1;
- ++x, ++coeff, ++touched) {
- val = *coeff;
- if (val != 0) {
- shift2 = shift - (cb->nZeroBitPlanes + cb->len + *touched);
- if (shift2 > 0) {
- cover(94);
- if (val < 0) {
- val = (val << shift2) - (1 << (shift2 - 1));
+ for (pre = 0; pre < resLevel->nPrecincts; ++pre) {
+ precinct = &resLevel->precincts[pre];
+ subband = &precinct->subbands[0];
+ cb = subband->cbs;
+ for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+ for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+ for (y = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched;
+ y < cb->y1;
+ ++y, coeff0 += tileComp->w, touched0 += resLevel->cbW) {
+ for (x = cb->x0, coeff = coeff0, touched = touched0;
+ x < cb->x1;
+ ++x, ++coeff, ++touched) {
+ val = *coeff;
+ if (val != 0) {
+ shift2 = shift - (cb->nZeroBitPlanes + cb->len + *touched);
+ if (shift2 > 0) {
+ cover(94);
+ if (val < 0) {
+ val = (val << shift2) - (1 << (shift2 - 1));
+ } else {
+ val = (val << shift2) + (1 << (shift2 - 1));
+ }
} else {
- val = (val << shift2) + (1 << (shift2 - 1));
+ cover(95);
+ val >>= -shift2;
}
- } else {
- cover(95);
- val >>= -shift2;
- }
- if (qStyle == 0) {
- cover(96);
- if (tileComp->transform == 0) {
- cover(97);
- val &= -1 << (fracBits - tileComp->prec);
+ if (qStyle == 0) {
+ cover(96);
+ if (tileComp->transform == 0) {
+ cover(97);
+ val &= -1 << (fracBits - tileComp->prec);
+ }
+ } else {
+ cover(98);
+ val = (int)((double)val * mu);
}
- } else {
- cover(98);
- val = (int)((double)val * mu);
}
+ *coeff = val;
}
- *coeff = val;
}
+ ++cb;
}
- ++cb;
}
}
@@ -2914,11 +2976,10 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
int val;
int *dataPtr, *bufPtr;
Guint nx1, nx2, ny1, ny2, offset;
- Guint x, y, sb, cbX, cbY;
+ Guint x, y, sb, pre, cbX, cbY;
qStyle = tileComp->quantStyle & 0x1f;
guard = (tileComp->quantStyle >> 5) & 7;
- precinct = &resLevel->precincts[0];
//----- compute subband bounds
@@ -2931,10 +2992,10 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
// | LH | HH | <- ny1
// +----+----+
// <- ny2
- nx1 = precinct->subbands[1].x1 - precinct->subbands[1].x0;
- nx2 = nx1 + precinct->subbands[0].x1 - precinct->subbands[0].x0;
- ny1 = precinct->subbands[0].y1 - precinct->subbands[0].y0;
- ny2 = ny1 + precinct->subbands[1].y1 - precinct->subbands[1].y0;
+ nx1 = resLevel->bx1[1] - resLevel->bx0[1];
+ nx2 = nx1 + resLevel->bx1[0] - resLevel->bx0[0];
+ ny1 = resLevel->by1[0] - resLevel->by0[0];
+ ny2 = ny1 + resLevel->by1[1] - resLevel->by0[1];
if (nx2 == 0 || ny2 == 0) {
return;
}
@@ -2965,44 +3026,48 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
}
// fixed point adjustment and dequantization
- subband = &precinct->subbands[sb];
- cb = subband->cbs;
- for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
- for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
- for (y = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched;
- y < cb->y1;
- ++y, coeff0 += tileComp->w, touched0 += tileComp->cbW) {
- for (x = cb->x0, coeff = coeff0, touched = touched0;
- x < cb->x1;
- ++x, ++coeff, ++touched) {
- val = *coeff;
- if (val != 0) {
- shift2 = shift - (cb->nZeroBitPlanes + cb->len + *touched);
- if (shift2 > 0) {
- cover(74);
- if (val < 0) {
- val = (val << shift2) - (1 << (shift2 - 1));
+
+ for (pre = 0; pre < resLevel->nPrecincts; ++pre) {
+ precinct = &resLevel->precincts[pre];
+ subband = &precinct->subbands[sb];
+ cb = subband->cbs;
+ for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+ for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+ for (y = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched;
+ y < cb->y1;
+ ++y, coeff0 += tileComp->w, touched0 += resLevel->cbW) {
+ for (x = cb->x0, coeff = coeff0, touched = touched0;
+ x < cb->x1;
+ ++x, ++coeff, ++touched) {
+ val = *coeff;
+ if (val != 0) {
+ shift2 = shift - (cb->nZeroBitPlanes + cb->len + *touched);
+ if (shift2 > 0) {
+ cover(74);
+ if (val < 0) {
+ val = (val << shift2) - (1 << (shift2 - 1));
+ } else {
+ val = (val << shift2) + (1 << (shift2 - 1));
+ }
} else {
- val = (val << shift2) + (1 << (shift2 - 1));
+ cover(75);
+ val >>= -shift2;
}
- } else {
- cover(75);
- val >>= -shift2;
- }
- if (qStyle == 0) {
- cover(76);
- if (tileComp->transform == 0) {
- val &= -1 << (fracBits - tileComp->prec);
+ if (qStyle == 0) {
+ cover(76);
+ if (tileComp->transform == 0) {
+ val &= -1 << (fracBits - tileComp->prec);
+ }
+ } else {
+ cover(77);
+ val = (int)((double)val * mu);
}
- } else {
- cover(77);
- val = (int)((double)val * mu);
}
+ *coeff = val;
}
- *coeff = val;
}
+ ++cb;
}
- ++cb;
}
}
}
@@ -3010,13 +3075,9 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
//----- inverse transform
// horizontal (row) transforms
- if (r == tileComp->nDecompLevels) {
- offset = 3 + (tileComp->x0 & 1);
- } else {
- offset = 3 + (tileComp->resLevels[r+1].x0 & 1);
- }
+ offset = 3 + (resLevel->x0 & 1);
for (y = 0, dataPtr = tileComp->data; y < ny2; ++y, dataPtr += tileComp->w) {
- if (precinct->subbands[0].x0 == precinct->subbands[1].x0) {
+ if (resLevel->bx0[0] == resLevel->bx0[1]) {
// fetch LL/LH
for (x = 0, bufPtr = tileComp->buf + offset;
x < nx1;
@@ -3050,13 +3111,9 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
}
// vertical (column) transforms
- if (r == tileComp->nDecompLevels) {
- offset = 3 + (tileComp->y0 & 1);
- } else {
- offset = 3 + (tileComp->resLevels[r+1].y0 & 1);
- }
+ offset = 3 + (resLevel->y0 & 1);
for (x = 0, dataPtr = tileComp->data; x < nx2; ++x, ++dataPtr) {
- if (precinct->subbands[1].y0 == precinct->subbands[0].y0) {
+ if (resLevel->by0[0] == resLevel->by0[1]) {
// fetch LL/HL
for (y = 0, bufPtr = tileComp->buf + offset;
y < ny1;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h
index bc5eac4f5ee..510826cd162 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h
@@ -135,7 +135,6 @@ struct JPXCodeBlock {
struct JPXSubband {
//----- computed
- Guint x0, y0, x1, y1; // bounds
Guint nXCBs, nYCBs; // number of code-blocks in the x and y
// directions
@@ -152,9 +151,6 @@ struct JPXSubband {
//------------------------------------------------------------------------
struct JPXPrecinct {
- //----- computed
- Guint x0, y0, x1, y1; // bounds of the precinct
-
//----- children
JPXSubband *subbands; // the subbands
};
@@ -165,12 +161,18 @@ struct JPXResLevel {
//----- from the COD and COC segments (main and tile)
Guint precinctWidth; // log2(precinct width)
Guint precinctHeight; // log2(precinct height)
+ Guint nPrecincts;
//----- computed
- Guint x0, y0, x1, y1; // bounds of the tile-comp (for this res level)
+ Guint x0, y0, x1, y1; // bounds of this tile-comp at this res level
Guint bx0[3], by0[3], // subband bounds
bx1[3], by1[3];
- GBool empty; // true if all subbands are zero width or height
+ Guint codeBlockW; // log2(code-block width)
+ Guint codeBlockH; // log2(code-block height)
+ Guint cbW; // code-block width
+ Guint cbH; // code-block height
+ GBool empty; // true if all subbands and precincts are
+ // zero width or height
//---- children
JPXPrecinct *precincts; // the precincts
@@ -201,8 +203,6 @@ struct JPXTileComp {
//----- computed
Guint x0, y0, x1, y1; // bounds of the tile-comp, in ref coords
Guint w, h; // data size = {x1 - x0, y1 - y0} >> reduction
- Guint cbW; // code-block width
- Guint cbH; // code-block height
//----- image data
int *data; // the decoded image data
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc
index 6ff75b0b621..0c74dbb04ea 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc
@@ -237,11 +237,6 @@ Object *Lexer::getObj(Object *obj) {
switch (c = getChar()) {
case EOF:
-#if 0
- // This breaks some PDF files, e.g., ones from Photoshop.
- case '\r':
- case '\n':
-#endif
error(errSyntaxError, getPos(), "Unterminated string");
done = gTrue;
break;
@@ -259,6 +254,16 @@ Object *Lexer::getObj(Object *obj) {
}
break;
+ case '\r':
+ // The PDF spec says that any literal end-of-line sequence
+ // (LF, CR, CR+LF) is translated to a single LF char.
+ c = lookChar();
+ if (c == '\n') {
+ getChar();
+ }
+ c2 = '\n';
+ break;
+
case '\\':
switch (c = getChar()) {
case 'n':
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc
index c223074677f..898fbc99e2b 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc
@@ -45,11 +45,12 @@ const char *objTypeNames[numObjTypes] = {
#ifdef DEBUG_MEM
#if MULTITHREADED
GAtomicCounter Object::numAlloc[numObjTypes] =
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
#else
long Object::numAlloc[numObjTypes] =
-#endif
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
#endif
+#endif // DEBUG_MEM
Object *Object::initArray(XRef *xref) {
initObj(objArray);
@@ -229,13 +230,15 @@ void Object::memCheck(FILE *f) {
long t;
t = 0;
- for (i = 0; i < numObjTypes; ++i)
+ for (i = 0; i < numObjTypes; ++i) {
t += numAlloc[i];
+ }
if (t > 0) {
fprintf(f, "Allocated objects:\n");
for (i = 0; i < numObjTypes; ++i) {
- if (numAlloc[i] > 0)
+ if (numAlloc[i] > 0) {
fprintf(f, " %-20s: %6ld\n", objTypeNames[i], numAlloc[i]);
+ }
}
}
#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc
index 6188856e8aa..a0fbb1b06c2 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc
@@ -172,6 +172,7 @@ GBool OptionalContent::evalOCObject(Object *obj, GBool *visible) {
int policy;
Ref ref;
Object obj2, obj3, obj4, obj5;
+ GBool gotOCG;
int i;
if (obj->isNull()) {
@@ -224,34 +225,40 @@ GBool OptionalContent::evalOCObject(Object *obj, GBool *visible) {
obj2.free();
return gFalse;
}
+ gotOCG = gFalse;
for (i = 0; i < obj4.arrayGetLength(); ++i) {
obj4.arrayGetNF(i, &obj5);
if (obj5.isRef()) {
ref = obj5.getRef();
- if (!(ocg = findOCG(&ref))) {
- obj5.free();
- obj4.free();
- obj3.free();
- obj2.free();
- return gFalse;
- }
- switch (policy) {
- case ocPolicyAllOn:
- *visible = *visible && ocg->getState();
- break;
- case ocPolicyAnyOn:
- *visible = *visible || ocg->getState();
- break;
- case ocPolicyAnyOff:
- *visible = *visible || !ocg->getState();
- break;
- case ocPolicyAllOff:
- *visible = *visible && !ocg->getState();
- break;
+ if ((ocg = findOCG(&ref))) {
+ gotOCG = gTrue;
+ switch (policy) {
+ case ocPolicyAllOn:
+ *visible = *visible && ocg->getState();
+ break;
+ case ocPolicyAnyOn:
+ *visible = *visible || ocg->getState();
+ break;
+ case ocPolicyAnyOff:
+ *visible = *visible || !ocg->getState();
+ break;
+ case ocPolicyAllOff:
+ *visible = *visible && !ocg->getState();
+ break;
+ }
}
}
obj5.free();
}
+ if (!gotOCG) {
+ // if the "OCGs" array is "empty or contains references only
+ // to null or deleted objects", this OCMD doesn't have any
+ // effect
+ obj4.free();
+ obj3.free();
+ obj2.free();
+ return gFalse;
+ }
obj4.free();
}
obj3.free();
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc
index adec372117d..f2be88edfdc 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc
@@ -113,7 +113,7 @@ void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GBool maskInvert, GBool interpolate) {
drawImage(state, ref, str, width, height, colorMap, NULL, gFalse,
@@ -123,7 +123,7 @@ void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GfxImageColorMap *maskColorMap,
double *matte, GBool interpolate) {
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h
index f53dab16a23..6fe7eeedb4e 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h
@@ -24,9 +24,7 @@ class GfxState;
struct GfxColor;
class GfxColorSpace;
class GfxImageColorMap;
-class GfxFunctionShading;
-class GfxAxialShading;
-class GfxRadialShading;
+class GfxShading;
class Stream;
class Links;
class Link;
@@ -61,11 +59,6 @@ public:
// operations.
virtual GBool useTilingPatternFill() { return gFalse; }
- // Does this device use functionShadedFill(), axialShadedFill(), and
- // radialShadedFill()? If this returns false, these shaded fills
- // will be reduced to a series of other drawing operations.
- virtual GBool useShadedFills() { return gFalse; }
-
// Does this device use drawForm()? If this returns false,
// form-type XObjects will be interpreted (i.e., unrolled).
virtual GBool useDrawForm() { return gFalse; }
@@ -166,12 +159,7 @@ public:
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
double xStep, double yStep) {}
- virtual GBool functionShadedFill(GfxState *state,
- GfxFunctionShading *shading)
- { return gFalse; }
- virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading)
- { return gFalse; }
- virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading)
+ virtual GBool shadedFill(GfxState *state, GfxShading *shading)
{ return gFalse; }
//----- path clipping
@@ -212,12 +200,13 @@ public:
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr, int maskWidth, int maskHeight,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GfxImageColorMap *maskColorMap,
double *matte, GBool interpolate);
@@ -253,10 +242,6 @@ public:
//----- links
virtual void processLink(Link *link) {}
-#if 1 //~tmp: turn off anti-aliasing temporarily
- virtual void setInShading(GBool sh) {}
-#endif
-
private:
double defCTM[6]; // default coordinate transform matrix
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc
index 09b5b99759a..877bcb88255 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc
@@ -26,7 +26,7 @@
#include "PDFDoc.h"
#include "Link.h"
#include "Annot.h"
-#include "Form.h"
+#include "AcroForm.h"
#include "OptionalContent.h"
#include "TileMap.h"
#include "TileCache.h"
@@ -260,9 +260,15 @@ void PDFCore::displayPage(int page, GBool scrollToTop,
if (page <= 0 || page > doc->getNumPages()) {
return;
}
- if (scrollToTop ||
- state->getDisplayMode() == displayContinuous ||
- state->getDisplayMode() == displaySideBySideContinuous) {
+ if (!scrollToTop &&
+ (state->getDisplayMode() == displayContinuous ||
+ state->getDisplayMode() == displaySideBySideContinuous)) {
+ scrollY = tileMap->getPageTopY(page)
+ + (state->getScrollY()
+ - tileMap->getPageTopY(tileMap->getFirstPage()));
+ } else if (scrollToTop ||
+ state->getDisplayMode() == displayContinuous ||
+ state->getDisplayMode() == displaySideBySideContinuous) {
scrollY = tileMap->getPageTopY(page);
} else if (scrollToBottom) {
scrollY = tileMap->getPageBottomY(page);
@@ -679,19 +685,23 @@ void PDFCore::scrollTo(int x, int y, GBool snapToPage) {
state->getDisplayMode() == displaySideBySideContinuous) {
next = state->getDisplayMode() == displaySideBySideContinuous ? 2 : 1;
topPage = tileMap->getFirstPage();
- topPageY = tileMap->getPageTopY(topPage);
- sy = state->getScrollY();
- dy = sy - topPageY;
- // note: dy can be negative here if the inter-page gap is at the
- // top of the window
- if (-16 < dy && dy < 16) {
- state->setScrollPosition(state->getScrollPage(), x, topPageY);
- } else if (topPage + next <= doc->getNumPages()) {
- topPage += next;
+ // NB: topPage can be out of bounds here, because the scroll
+ // position isn't adjusted until finishUpdate is called, below
+ if (topPage > 0 && topPage <= doc->getNumPages()) {
topPageY = tileMap->getPageTopY(topPage);
+ sy = state->getScrollY();
dy = sy - topPageY;
- if (-16 < dy && dy < 0) {
+ // note: dy can be negative here if the inter-page gap is at the
+ // top of the window
+ if (-16 < dy && dy < 16) {
state->setScrollPosition(state->getScrollPage(), x, topPageY);
+ } else if (topPage + next <= doc->getNumPages()) {
+ topPage += next;
+ topPageY = tileMap->getPageTopY(topPage);
+ dy = sy - topPageY;
+ if (-16 < dy && dy < 0) {
+ state->setScrollPosition(state->getScrollPage(), x, topPageY);
+ }
}
}
}
@@ -1100,7 +1110,7 @@ void PDFCore::setLinearSelection(int page, TextPosition *pos0,
// build the list of rectangles
//~ this doesn't handle RtL, vertical, or rotated text
- loadText(selectPage);
+ loadText(page);
rects = new GList();
if (begin.colIdx == end.colIdx &&
begin.parIdx == end.parIdx &&
@@ -1108,51 +1118,51 @@ void PDFCore::setLinearSelection(int page, TextPosition *pos0,
// same line
text->convertPosToPointUpper(&begin, &x0, &y0);
text->convertPosToPointLower(&end, &x1, &y1);
- cvtDevToUser(selectPage, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
- cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
- rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1));
+ cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
+ cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
+ rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
} else if (begin.colIdx == end.colIdx) {
// same column
text->convertPosToPointUpper(&begin, &x0, &y0);
text->convertPosToPointRightEdge(&begin, &x1, &y1);
text->convertPosToPointLeftEdge(&end, &x2, &y2);
text->convertPosToPointLower(&end, &x3, &y3);
- cvtDevToUser(selectPage, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
- cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
- rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1));
- cvtDevToUser(selectPage, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
- cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
- rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1));
- cvtDevToUser(selectPage, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux0, &uy0);
- cvtDevToUser(selectPage, (int)(x3 + 0.5), (int)(y3 + 0.5), &ux1, &uy1);
- rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1));
+ cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
+ cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
+ rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
+ cvtDevToUser(page, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
+ cvtDevToUser(page, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
+ rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
+ cvtDevToUser(page, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux0, &uy0);
+ cvtDevToUser(page, (int)(x3 + 0.5), (int)(y3 + 0.5), &ux1, &uy1);
+ rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
} else {
// different columns
text->convertPosToPointUpper(&begin, &x0, &y0);
text->convertPosToPointRightEdge(&begin, &x1, &y1);
text->getColumnLowerLeft(begin.colIdx, &x2, &y2);
- cvtDevToUser(selectPage, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
- cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
- rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1));
- cvtDevToUser(selectPage, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
- cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
- rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1));
+ cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
+ cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
+ rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
+ cvtDevToUser(page, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
+ cvtDevToUser(page, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
+ rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
for (colIdx = begin.colIdx + 1; colIdx < end.colIdx; ++colIdx) {
text->getColumnLowerLeft(colIdx, &x0, &y0);
text->getColumnUpperRight(colIdx, &x1, &y1);
- cvtDevToUser(selectPage, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
- cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux1, &uy1);
- rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1));
+ cvtDevToUser(page, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
+ cvtDevToUser(page, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux1, &uy1);
+ rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
}
text->getColumnUpperRight(end.colIdx, &x0, &y0);
text->convertPosToPointLeftEdge(&end, &x1, &y1);
text->convertPosToPointLower(&end, &x2, &y2);
- cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
- cvtDevToUser(selectPage, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
- rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1));
- cvtDevToUser(selectPage, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
- cvtDevToUser(selectPage, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
- rects->append(new SelectRect(selectPage, ux0, uy0, ux1, uy1));
+ cvtDevToUser(page, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
+ cvtDevToUser(page, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
+ rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
+ cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
+ cvtDevToUser(page, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
+ rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
}
// get window coord bboxes for old selection and new selection;
@@ -1264,8 +1274,31 @@ void PDFCore::moveSelectionDrag(int pg, int x, int y) {
}
void PDFCore::finishSelectionDrag() {
- selectPage = 0;
- selectStartX = selectStartY = 0;
+ // nothing
+}
+
+void PDFCore::selectWord(int pg, int x, int y) {
+ TextPosition endPos;
+
+ loadText(pg);
+ if (text->findWordPoints(x, y, &selectStartPos, &endPos)) {
+ selectPage = pg;
+ setLinearSelection(pg, &selectStartPos, &endPos);
+ } else {
+ selectPage = 0;
+ }
+}
+
+void PDFCore::selectLine(int pg, int x, int y) {
+ TextPosition endPos;
+
+ loadText(pg);
+ if (text->findLinePoints(x, y, &selectStartPos, &endPos)) {
+ selectPage = pg;
+ setLinearSelection(pg, &selectStartPos, &endPos);
+ } else {
+ selectPage = 0;
+ }
}
GBool PDFCore::getSelection(int *pg, double *ulx, double *uly,
@@ -1550,6 +1583,45 @@ GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
return gTrue;
}
+GList *PDFCore::findAll(Unicode *u, int len, GBool caseSensitive,
+ GBool wholeWord, int firstPage, int lastPage) {
+ GList *results = new GList();
+
+ TextOutputDev *textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
+ if (!textOut->isOk()) {
+ delete textOut;
+ return results;
+ }
+
+ for (int pg = firstPage; pg <= lastPage; ++pg) {
+ doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
+ GBool first = gTrue;
+ while (1) {
+ double xMin, yMin, xMax, yMax;
+ if (!textOut->findText(u, len, first, gTrue, !first, gFalse,
+ caseSensitive, gFalse, wholeWord,
+ &xMin, &yMin, &xMax, &yMax)) {
+ break;
+ }
+ double uxMin, uyMin, uxMax, uyMax, t;
+ textOut->cvtDevToUser(xMin, yMin, &uxMin, &uyMin);
+ textOut->cvtDevToUser(xMax, yMax, &uxMax, &uyMax);
+ if (uxMin > uxMax) {
+ t = uxMin; uxMin = uxMax; uxMax = t;
+ }
+ if (uyMin > uyMax) {
+ t = uyMin; uyMin = uyMax; uyMax = t;
+ }
+ results->append(new FindResult(pg, uxMin, uyMin, uxMax, uyMax));
+ first = gFalse;
+ }
+ }
+
+ delete textOut;
+
+ return results;
+}
+
GBool PDFCore::cvtWindowToUser(int xw, int yw,
int *pg, double *xu, double *yu) {
@@ -1683,7 +1755,7 @@ Annot *PDFCore::getAnnot(int idx) {
return annots->getAnnot(idx);
}
-FormField *PDFCore::findFormField(int pg, double x, double y) {
+AcroFormField *PDFCore::findFormField(int pg, double x, double y) {
if (!doc->getCatalog()->getForm()) {
return NULL;
}
@@ -1697,7 +1769,7 @@ int PDFCore::findFormFieldIdx(int pg, double x, double y) {
return doc->getCatalog()->getForm()->findFieldIdx(pg, x, y);
}
-FormField *PDFCore::getFormField(int idx) {
+AcroFormField *PDFCore::getFormField(int idx) {
if (!doc->getCatalog()->getForm()) {
return NULL;
}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h
index 510c01a1a76..accf19d82ef 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h
@@ -32,7 +32,7 @@ class LinkDest;
class LinkAction;
class Annot;
class Annots;
-class FormField;
+class AcroFormField;
class TextPage;
class HighlightFile;
class OptionalContentGroup;
@@ -66,6 +66,17 @@ enum SelectMode {
};
//------------------------------------------------------------------------
+// FindResult
+//------------------------------------------------------------------------
+
+struct FindResult {
+ FindResult(int pageA, double xMinA, double yMinA, double xMaxA, double yMaxA)
+ : page(pageA), xMin(xMinA), yMin(yMinA), xMax(xMaxA), yMax(yMaxA) {}
+ int page;
+ double xMin, yMin, xMax, yMax;
+};
+
+//------------------------------------------------------------------------
// PDFCore
//------------------------------------------------------------------------
@@ -176,6 +187,8 @@ public:
void startSelectionDrag(int pg, int x, int y);
void moveSelectionDrag(int pg, int x, int y);
void finishSelectionDrag();
+ void selectWord(int pg, int x, int y);
+ void selectLine(int pg, int x, int y);
// Retrieve the current selection. This function uses user
// coordinates. Returns false if there is no selection.
@@ -198,6 +211,8 @@ public:
virtual GBool findU(Unicode *u, int len, GBool caseSensitive,
GBool next, GBool backward, GBool wholeWord,
GBool onePageOnly);
+ GList *findAll(Unicode *u, int len, GBool caseSensitive,
+ GBool wholeWord, int firstPage, int lastPage);
//----- coordinate conversion
@@ -242,9 +257,9 @@ public:
Annot *findAnnot(int pg, double x, double y);
int findAnnotIdx(int pg, double x, double y);
Annot *getAnnot(int idx);
- FormField *findFormField(int pg, double x, double y);
+ AcroFormField *findFormField(int pg, double x, double y);
int findFormFieldIdx(int pg, double x, double y);
- FormField *getFormField(int idx);
+ AcroFormField *getFormField(int idx);
GBool overText(int pg, double x, double y);
void forceRedraw();
void setTileDoneCbk(void (*cbk)(void *data), void *data);
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc
index a130b164f91..6efd4aac6c4 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc
@@ -21,6 +21,7 @@
#endif
#include "gmempp.h"
#include "GString.h"
+#include "gfile.h"
#include "config.h"
#include "GlobalParams.h"
#include "Page.h"
@@ -113,6 +114,7 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
ok = setup(ownerPassword, userPassword);
}
+#if 0
#ifdef _WIN32
PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
GString *userPassword, PDFCore *coreA) {
@@ -122,14 +124,21 @@ PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
init(coreA);
+ // handle a Windows shortcut
+ wchar_t wPath[MAX_PATH + 1];
+ int n = fileNameLen < MAX_PATH ? fileNameLen : MAX_PATH;
+ memcpy(wPath, fileNameA, n * sizeof(wchar_t));
+ wPath[n] = L'\0';
+ readWindowsShortcut(wPath, MAX_PATH + 1);
+ int wPathLen = (int)wcslen(wPath);
+
// save both Unicode and 8-bit copies of the file name
fileName = new GString();
- fileNameU = (wchar_t *)gmallocn(fileNameLen + 1, sizeof(wchar_t));
- for (i = 0; i < fileNameLen; ++i) {
+ fileNameU = (wchar_t *)gmallocn(wPathLen + 1, sizeof(wchar_t));
+ memcpy(fileNameU, wPath, (wPathLen + 1) * sizeof(wchar_t));
+ for (i = 0; i < wPathLen; ++i) {
fileName->append((char)fileNameA[i]);
- fileNameU[i] = fileNameA[i];
}
- fileNameU[fileNameLen] = L'\0';
// try to open file
// NB: _wfopen is only available in NT
@@ -153,6 +162,7 @@ PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
ok = setup(ownerPassword, userPassword);
}
#endif
+#endif /* 0 */
PDFDoc::PDFDoc(char *fileNameA, GString *ownerPassword,
GString *userPassword, PDFCore *coreA) {
@@ -165,7 +175,7 @@ PDFDoc::PDFDoc(char *fileNameA, GString *ownerPassword,
/*
#ifdef _WIN32
Unicode u;
- int n, i, j;
+ int i, j;
#endif
*/
@@ -175,17 +185,19 @@ PDFDoc::PDFDoc(char *fileNameA, GString *ownerPassword,
#if defined(_WIN32)
#if 0
- n = 0;
+ wchar_t wPath[MAX_PATH + 1];
i = 0;
- while (getUTF8(fileName, &i, &u)) {
- ++n;
- }
- fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t));
- i = j = 0;
- while (j < n && getUTF8(fileName, &i, &u)) {
- fileNameU[j++] = (wchar_t)u;
+ j = 0;
+ while (j < MAX_PATH && getUTF8(fileName, &i, &u)) {
+ wPath[j++] = (wchar_t)u;
}
- fileNameU[n] = L'\0';
+ wPath[j] = L'\0';
+ readWindowsShortcut(wPath, MAX_PATH + 1);
+ int wPathLen = (int)wcslen(wPath);
+
+ fileNameU = (wchar_t *)gmallocn(wPathLen + 1, sizeof(wchar_t));
+ memcpy(fileNameU, wPath, (wPathLen + 1) * sizeof(wchar_t));
+
// NB: _wfopen is only available in NT
version.dwOSVersionInfoSize = sizeof(version);
GetVersionEx(&version);
@@ -579,6 +591,18 @@ GBool PDFDoc::saveEmbeddedFile(int idx, const char *path) {
return ret;
}
+GBool PDFDoc::saveEmbeddedFileU(int idx, const char *path) {
+ FILE *f;
+ GBool ret;
+
+ if (!(f = openFile(path, "wb"))) {
+ return gFalse;
+ }
+ ret = saveEmbeddedFile2(idx, f);
+ fclose(f);
+ return ret;
+}
+
#ifdef _WIN32
GBool PDFDoc::saveEmbeddedFile(int idx, const wchar_t *path, int pathLen) {
FILE *f;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h
index e7005b16eb2..5069cb78689 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h
@@ -185,6 +185,7 @@ public:
int getEmbeddedFileNameLength(int idx)
{ return catalog->getEmbeddedFileNameLength(idx); }
GBool saveEmbeddedFile(int idx, const char *path);
+ GBool saveEmbeddedFileU(int idx, const char *path);
#ifdef _WIN32
GBool saveEmbeddedFile(int idx, const wchar_t *path, int pathLen);
#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc
index f39a72e9015..0c39472ba85 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc
@@ -40,7 +40,7 @@
#include "XRef.h"
#include "PreScanOutputDev.h"
#include "CharCodeToUnicode.h"
-#include "Form.h"
+#include "AcroForm.h"
#include "TextString.h"
#if HAVE_SPLASH
# include "Splash.h"
@@ -1287,7 +1287,8 @@ PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA,
GBool manualCtrlA,
PSOutCustomCodeCbk customCodeCbkA,
void *customCodeCbkDataA,
- GBool honorUserUnitA) {
+ GBool honorUserUnitA,
+ GBool fileNameIsUTF8) {
FILE *f;
PSFileType fileTypeA;
@@ -1333,7 +1334,12 @@ PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA,
#endif
} else {
fileTypeA = psFile;
- if (!(f = fopen(fileName, "w"))) {
+ if (fileNameIsUTF8) {
+ f = openFile(fileName, "w");
+ } else {
+ f = fopen(fileName, "w");
+ }
+ if (!f) {
error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName);
ok = gFalse;
return;
@@ -1737,7 +1743,7 @@ void PSOutputDev::writeDocSetup(Catalog *catalog) {
Page *page;
Dict *resDict;
Annots *annots;
- Form *form;
+ AcroForm *form;
Object obj1, obj2, obj3;
GString *s;
GBool needDefaultFont;
@@ -1994,6 +2000,7 @@ void PSOutputDev::setupResources(Dict *resDict) {
}
patDict.free();
+
//----- recursively scan SMask transparency groups in ExtGState dicts
resDict->lookup("ExtGState", &gsDict);
if (gsDict.isDict()) {
@@ -2211,13 +2218,15 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
if (fontLoc->locType == gfxFontLocResident &&
fontLoc->fontType >= fontCIDType0) {
subst = gTrue;
- if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) {
- fi->ff->encoding = fontLoc->encoding->copy();
- uMap->decRefCnt();
- } else {
- error(errSyntaxError, -1,
- "Couldn't find Unicode map for 16-bit font encoding '{0:t}'",
- fontLoc->encoding);
+ if (!fi->ff->encoding) {
+ if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) {
+ fi->ff->encoding = fontLoc->encoding->copy();
+ uMap->decRefCnt();
+ } else {
+ error(errSyntaxError, -1,
+ "Couldn't find Unicode map for 16-bit font encoding '{0:t}'",
+ fontLoc->encoding);
+ }
}
}
@@ -2244,7 +2253,7 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
if (font->getType() == fontTrueType &&
!subst &&
!((Gfx8BitFont *)font)->getHasEncoding()) {
- sprintf(buf, "c%02x", i+j);
+ snprintf(buf, sizeof(buf), "c%02x", i+j);
charName = buf;
} else {
charName = ((Gfx8BitFont *)font)->getCharName(i+j);
@@ -2392,6 +2401,7 @@ PSFontFileInfo *PSOutputDev::setupExternalType1Font(GfxFont *font,
// open the font file
if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
error(errIO, -1, "Couldn't open external font file");
+ delete psName;
return NULL;
}
@@ -3623,7 +3633,7 @@ GString *PSOutputDev::copyType1PFB(Guchar *font, int fontSize) {
p[0] == 0x80 &&
(p[1] == 0x01 || p[1] == 0x02)) {
len = p[2] + (p[3] << 8) + (p[4] << 16) + (p[5] << 24);
- if (len > remain - 6) {
+ if (len < 0 || len > remain - 6) {
break;
}
if (p[1] == 0x01) {
@@ -4102,6 +4112,7 @@ void PSOutputDev::setupForm(Object *strRef, Object *strObj) {
resObj.free();
}
+
GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
int rotateA, GBool useMediaBox, GBool crop,
int sliceX, int sliceY,
@@ -4441,17 +4452,27 @@ void PSOutputDev::startPage(int pageNum, GfxState *state) {
landscape = gFalse;
} else {
rotate = (360 - state->getRotate()) % 360;
+ double scaledWidth = width * userUnit;
+ double scaledHeight = height * userUnit;
+ if (xScale0 > 0 && yScale0 > 0) {
+ scaledWidth *= xScale0;
+ scaledHeight *= yScale0;
+ }
if (rotate == 0 || rotate == 180) {
- if ((width < height && imgWidth > imgHeight && height > imgHeight) ||
- (width > height && imgWidth < imgHeight && width > imgWidth)) {
+ if ((scaledWidth < scaledHeight && imgWidth > imgHeight &&
+ scaledHeight > imgHeight) ||
+ (scaledWidth > scaledHeight && imgWidth < imgHeight &&
+ scaledWidth > imgWidth)) {
rotate += 90;
landscape = gTrue;
} else {
landscape = gFalse;
}
} else { // rotate == 90 || rotate == 270
- if ((height < width && imgWidth > imgHeight && width > imgHeight) ||
- (height > width && imgWidth < imgHeight && height > imgWidth)) {
+ if ((scaledHeight < scaledWidth && imgWidth > imgHeight &&
+ scaledWidth > imgHeight) ||
+ (scaledHeight > scaledWidth && imgWidth < imgHeight &&
+ scaledHeight > imgWidth)) {
rotate = 270 - rotate;
landscape = gTrue;
} else {
@@ -5252,6 +5273,29 @@ void PSOutputDev::tilingPatternFillL2(GfxState *state, Gfx *gfx,
noStateChanges = gFalse;
}
+GBool PSOutputDev::shadedFill(GfxState *state, GfxShading *shading) {
+ if (level != psLevel2 &&
+ level != psLevel2Sep &&
+ level != psLevel3 &&
+ level != psLevel3Sep) {
+ return gFalse;
+ }
+
+ switch (shading->getType()) {
+ case 1:
+ functionShadedFill(state, (GfxFunctionShading *)shading);
+ return gTrue;
+ case 2:
+ axialShadedFill(state, (GfxAxialShading *)shading);
+ return gTrue;
+ case 3:
+ radialShadedFill(state, (GfxRadialShading *)shading);
+ return gTrue;
+ default:
+ return gFalse;
+ }
+}
+
GBool PSOutputDev::functionShadedFill(GfxState *state,
GfxFunctionShading *shading) {
double x0, y0, x1, y1;
@@ -5957,7 +6001,7 @@ void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GBool maskInvert, GBool interpolate) {
int len;
@@ -8301,7 +8345,7 @@ void PSOutputDev::writePSString(GString *s) {
writePSChar((char)*p);
line += 2;
} else if (*p < 0x20 || *p >= 0x80) {
- sprintf(buf, "\\%03o", *p);
+ snprintf(buf, sizeof(buf), "\\%03o", *p);
writePS(buf);
line += 4;
} else {
@@ -8351,7 +8395,7 @@ GString *PSOutputDev::filterPSName(GString *name) {
c == '(' || c == ')' || c == '<' || c == '>' ||
c == '[' || c == ']' || c == '{' || c == '}' ||
c == '/' || c == '%') {
- sprintf(buf, "#%02x", c & 0xff);
+ snprintf(buf, sizeof(buf), "#%02x", c & 0xff);
name2->append(buf);
} else {
name2->append(c);
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h
index 66057a7cb74..466f81f1992 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h
@@ -38,6 +38,9 @@ class GfxICCBasedColorSpace;
class GfxIndexedColorSpace;
class GfxSeparationColorSpace;
class GfxDeviceNColorSpace;
+class GfxFunctionShading;
+class GfxAxialShading;
+class GfxRadialShading;
class PDFRectangle;
class PSOutCustomColor;
class PSOutputDev;
@@ -82,7 +85,8 @@ public:
GBool manualCtrlA = gFalse,
PSOutCustomCodeCbk customCodeCbkA = NULL,
void *customCodeCbkDataA = NULL,
- GBool honorUserUnitA = gFalse);
+ GBool honorUserUnitA = gFalse,
+ GBool fileNameIsUTF8 = gFalse);
// Open a PSOutputDev that will write to a generic stream.
PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
@@ -118,13 +122,6 @@ public:
// operations.
virtual GBool useTilingPatternFill() { return gTrue; }
- // Does this device use functionShadedFill(), axialShadedFill(), and
- // radialShadedFill()? If this returns false, these shaded fills
- // will be reduced to a series of other drawing operations.
- virtual GBool useShadedFills()
- { return level == psLevel2 || level == psLevel2Sep ||
- level == psLevel3 || level == psLevel3Sep; }
-
// Does this device use drawForm()? If this returns false,
// form-type XObjects will be interpreted (i.e., unrolled).
virtual GBool useDrawForm() { return preload; }
@@ -214,10 +211,7 @@ public:
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
double xStep, double yStep);
- virtual GBool functionShadedFill(GfxState *state,
- GfxFunctionShading *shading);
- virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading);
- virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading);
+ virtual GBool shadedFill(GfxState *state, GfxShading *shading);
//----- path clipping
virtual void clip(GfxState *state);
@@ -238,7 +232,8 @@ public:
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr, int maskWidth, int maskHeight,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
GBool maskInvert, GBool interpolate);
#if OPI_SUPPORT
@@ -342,6 +337,10 @@ private:
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
double xStep, double yStep);
+ GBool functionShadedFill(GfxState *state,
+ GfxFunctionShading *shading);
+ GBool axialShadedFill(GfxState *state, GfxAxialShading *shading);
+ GBool radialShadedFill(GfxState *state, GfxRadialShading *shading);
void doPath(GfxPath *path);
void doImageL1(Object *ref, GfxState *state,
GfxImageColorMap *colorMap,
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc
index b2820d3a2bf..ace0b33d85c 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc
@@ -14,6 +14,7 @@
#include <stddef.h>
#include "gmempp.h"
+#include "Trace.h"
#include "GlobalParams.h"
#include "Object.h"
#include "Array.h"
@@ -26,7 +27,7 @@
#include "Gfx.h"
#include "GfxState.h"
#include "Annot.h"
-#include "Form.h"
+#include "AcroForm.h"
#endif
#include "Error.h"
#include "Catalog.h"
@@ -335,7 +336,7 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
Gfx *gfx;
Object obj;
Annots *annotList;
- Form *form;
+ AcroForm *form;
int i;
if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop,
@@ -344,6 +345,8 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
return;
}
+ traceBegin(this, "begin page");
+
rotate += getRotate();
if (rotate >= 360) {
rotate -= 360;
@@ -404,7 +407,9 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
}
delete gfx;
-#endif
+#endif // PDF_PARSER_ONLY
+
+ traceEnd(this, "end page");
}
void Page::makeBox(double hDPI, double vDPI, int rotate,
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h
index ee30a1a8b47..8ec5a10995a 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h
@@ -127,6 +127,7 @@ public:
// Get page parameters.
int getNum() { return num; }
+ PageAttrs *getAttrs() { return attrs; }
PDFRectangle *getMediaBox() { return attrs->getMediaBox(); }
PDFRectangle *getCropBox() { return attrs->getCropBox(); }
GBool isCropped() { return attrs->isCropped(); }
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc
index d6f40333215..e5e00b85f51 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc
@@ -76,36 +76,7 @@ void PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
}
}
-GBool PreScanOutputDev::functionShadedFill(GfxState *state,
- GfxFunctionShading *shading) {
- if (shading->getColorSpace()->getMode() != csDeviceGray &&
- shading->getColorSpace()->getMode() != csCalGray) {
- gray = gFalse;
- }
- mono = gFalse;
- if (state->getFillOpacity() != 1 ||
- state->getBlendMode() != gfxBlendNormal) {
- transparency = gTrue;
- }
- return gTrue;
-}
-
-GBool PreScanOutputDev::axialShadedFill(GfxState *state,
- GfxAxialShading *shading) {
- if (shading->getColorSpace()->getMode() != csDeviceGray &&
- shading->getColorSpace()->getMode() != csCalGray) {
- gray = gFalse;
- }
- mono = gFalse;
- if (state->getFillOpacity() != 1 ||
- state->getBlendMode() != gfxBlendNormal) {
- transparency = gTrue;
- }
- return gTrue;
-}
-
-GBool PreScanOutputDev::radialShadedFill(GfxState *state,
- GfxRadialShading *shading) {
+GBool PreScanOutputDev::shadedFill(GfxState *state, GfxShading *shading) {
if (shading->getColorSpace()->getMode() != csDeviceGray &&
shading->getColorSpace()->getMode() != csCalGray) {
gray = gFalse;
@@ -229,7 +200,7 @@ void PreScanOutputDev::drawMaskedImage(GfxState *state, Object *ref,
Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GBool maskInvert, GBool interpolate) {
GfxColorSpace *colorSpace;
@@ -258,7 +229,7 @@ void PreScanOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GfxImageColorMap *maskColorMap,
double *matte, GBool interpolate) {
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h
index 882d8ef39f5..253733772bf 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h
@@ -46,11 +46,6 @@ public:
// operations.
virtual GBool useTilingPatternFill() { return gTrue; }
- // Does this device use functionShadedFill(), axialShadedFill(), and
- // radialShadedFill()? If this returns false, these shaded fills
- // will be reduced to a series of other drawing operations.
- virtual GBool useShadedFills() { return gTrue; }
-
// Does this device use beginType3Char/endType3Char? Otherwise,
// text in Type 3 fonts will be drawn with drawChar/drawString.
virtual GBool interpretType3Chars() { return gTrue; }
@@ -72,10 +67,7 @@ public:
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
double xStep, double yStep);
- virtual GBool functionShadedFill(GfxState *state,
- GfxFunctionShading *shading);
- virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading);
- virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading);
+ virtual GBool shadedFill(GfxState *state, GfxShading *shading);
//----- path clipping
virtual void clip(GfxState *state);
@@ -99,12 +91,13 @@ public:
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr, int maskWidth, int maskHeight,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GfxImageColorMap *maskColorMap,
double *matte, GBool interpolate);
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc
new file mode 100644
index 00000000000..d8dc24e0fc6
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc
@@ -0,0 +1,1327 @@
+//========================================================================
+//
+// ShadingImage.cc
+//
+// Copyright 2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <math.h>
+#include "Trace.h"
+#include "GfxState.h"
+#include "SplashBitmap.h"
+#include "SplashPattern.h"
+#include "SplashPath.h"
+#include "Splash.h"
+#include "ShadingImage.h"
+
+// Max recursive depth for a patch mesh shading fill.
+#define patchMaxDepth 10
+
+// Max delta allowed in any color component for a patch mesh shading
+// fill.
+#define patchColorDelta (dblToCol(1 / 256.0))
+
+SplashBitmap *ShadingImage::generateBitmap(GfxState *state,
+ GfxShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut) {
+ switch (shading->getType()) {
+ case 1:
+ return generateFunctionBitmap(state, (GfxFunctionShading *)shading,
+ mode, reverseVideo,
+ parentSplash, parentBitmap, xOut, yOut);
+ break;
+ case 2:
+ return generateAxialBitmap(state, (GfxAxialShading *)shading,
+ mode, reverseVideo,
+ parentSplash, parentBitmap, xOut, yOut);
+ break;
+ case 3:
+ return generateRadialBitmap(state, (GfxRadialShading *)shading,
+ mode, reverseVideo,
+ parentSplash, parentBitmap, xOut, yOut);
+ break;
+ case 4:
+ case 5:
+ return generateGouraudTriangleBitmap(state,
+ (GfxGouraudTriangleShading *)shading,
+ mode, reverseVideo,
+ parentSplash, parentBitmap,
+ xOut, yOut);
+ break;
+ case 6:
+ case 7:
+ return generatePatchMeshBitmap(state, (GfxPatchMeshShading *)shading,
+ mode, reverseVideo,
+ parentSplash, parentBitmap, xOut, yOut);
+ break;
+ default:
+ return NULL;
+ }
+}
+
+SplashBitmap *ShadingImage::generateFunctionBitmap(GfxState *state,
+ GfxFunctionShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut) {
+ // get the shading parameters
+ double x0, y0, x1, y1;
+ shading->getDomain(&x0, &y0, &x1, &y1);
+ double *patternMat = shading->getMatrix();
+
+ // get the clip bbox
+ double fxMin, fyMin, fxMax, fyMax;
+ state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax);
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // convert to integer coords
+ int xMin = (int)floor(fxMin);
+ int yMin = (int)floor(fyMin);
+ int xMax = (int)floor(fxMax) + 1;
+ int yMax = (int)floor(fyMax) + 1;
+ int bitmapWidth = xMax - xMin;
+ int bitmapHeight = yMax - yMin;
+
+ // allocate the bitmap
+ traceMessage("function shading fill bitmap");
+ SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode,
+ gTrue, gTrue, parentBitmap);
+ int nComps = splashColorModeNComps[mode];
+
+ // compute the domain -> device space transform = mat * CTM
+ double *ctm = state->getCTM();
+ double mat[6];
+ mat[0] = patternMat[0] * ctm[0] + patternMat[1] * ctm[2];
+ mat[1] = patternMat[0] * ctm[1] + patternMat[1] * ctm[3];
+ mat[2] = patternMat[2] * ctm[0] + patternMat[3] * ctm[2];
+ mat[3] = patternMat[2] * ctm[1] + patternMat[3] * ctm[3];
+ mat[4] = patternMat[4] * ctm[0] + patternMat[5] * ctm[2] + ctm[4];
+ mat[5] = patternMat[4] * ctm[1] + patternMat[5] * ctm[3] + ctm[5];
+
+ // compute the device space -> domain transform
+ double det = mat[0] * mat[3] - mat[1] * mat[2];
+ if (fabs(det) < 0.000001) {
+ return NULL;
+ }
+ det = 1 / det;
+ double iMat[6];
+ iMat[0] = mat[3] * det;
+ iMat[1] = -mat[1] * det;
+ iMat[2] = -mat[2] * det;
+ iMat[3] = mat[0] * det;
+ iMat[4] = (mat[2] * mat[5] - mat[3] * mat[4]) * det;
+ iMat[5] = (mat[1] * mat[4] - mat[0] * mat[5]) * det;
+
+ // fill the bitmap
+ SplashColorPtr dataPtr = bitmap->getDataPtr();
+ Guchar *alphaPtr = bitmap->getAlphaPtr();
+ for (int y = 0; y < bitmapHeight; ++y) {
+ for (int x = 0; x < bitmapWidth; ++x) {
+
+ // convert coords to the pattern domain
+ double tx = xMin + x + 0.5;
+ double ty = yMin + y + 0.5;
+ double xx = tx * iMat[0] + ty * iMat[2] + iMat[4];
+ double yy = tx * iMat[1] + ty * iMat[3] + iMat[5];
+
+ // get the color
+ if (xx >= x0 && xx <= x1 && yy >= y0 && yy <= y1) {
+ GfxColor color;
+ shading->getColor(xx, yy, &color);
+ SplashColor sColor;
+ computeShadingColor(state, mode, reverseVideo, &color, sColor);
+ for (int i = 0; i < nComps; ++i) {
+ *dataPtr++ = sColor[i];
+ }
+ *alphaPtr++ = 0xff;
+ } else {
+ dataPtr += nComps;
+ *alphaPtr++ = 0;
+ }
+ }
+ }
+
+ *xOut = xMin;
+ *yOut = yMin;
+ return bitmap;
+}
+
+SplashBitmap *ShadingImage::generateAxialBitmap(GfxState *state,
+ GfxAxialShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut) {
+ // get the shading parameters
+ double x0, y0, x1, y1;
+ shading->getCoords(&x0, &y0, &x1, &y1);
+ double t0 = shading->getDomain0();
+ double t1 = shading->getDomain1();
+ GBool ext0 = shading->getExtend0();
+ GBool ext1 = shading->getExtend1();
+ double dx = x1 - x0;
+ double dy = y1 - y0;
+ double d = dx * dx + dy * dy;
+ GBool dZero = fabs(d) < 0.0001;
+ if (!dZero) {
+ d = 1 / d;
+ }
+ if (dZero && !ext0 && !ext1) {
+ return NULL;
+ }
+
+ // get the clip bbox
+ double fxMin, fyMin, fxMax, fyMax;
+ state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax);
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // convert to integer coords
+ int xMin = (int)floor(fxMin);
+ int yMin = (int)floor(fyMin);
+ int xMax = (int)floor(fxMax) + 1;
+ int yMax = (int)floor(fyMax) + 1;
+ int bitmapWidth = xMax - xMin;
+ int bitmapHeight = yMax - yMin;
+
+ // compute the inverse CTM
+ double *ctm = state->getCTM();
+ double det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
+ if (fabs(det) < 0.000001) {
+ return NULL;
+ }
+ det = 1 / det;
+ double ictm[6];
+ ictm[0] = ctm[3] * det;
+ ictm[1] = -ctm[1] * det;
+ ictm[2] = -ctm[2] * det;
+ ictm[3] = ctm[0] * det;
+ ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+ ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+
+ // convert axis endpoints to device space
+ double xx0, yy0, xx1, yy1;
+ state->transform(x0, y0, &xx0, &yy0);
+ state->transform(x1, y1, &xx1, &yy1);
+
+ // allocate the bitmap
+ traceMessage("axial shading fill bitmap");
+ SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode,
+ gTrue, gTrue, parentBitmap);
+ int nComps = splashColorModeNComps[mode];
+
+ // special case: zero-length axis
+ if (dZero) {
+ GfxColor color;
+ if (ext0) {
+ shading->getColor(t0, &color);
+ } else {
+ shading->getColor(t1, &color);
+ }
+ SplashColor sColor;
+ computeShadingColor(state, mode, reverseVideo, &color, sColor);
+ SplashColorPtr dataPtr = bitmap->getDataPtr();
+ for (int y = 0; y < bitmapHeight; ++y) {
+ for (int x = 0; x < bitmapWidth; ++x) {
+ for (int i = 0; i < nComps; ++i) {
+ *dataPtr++ = sColor[i];
+ }
+ }
+ }
+ memset(bitmap->getAlphaPtr(), 0xff, (size_t)bitmapWidth * bitmapHeight);
+
+ // special case: horizontal axis (in device space)
+ } else if (fabs(yy0 - yy1) < 0.01) {
+ for (int x = 0; x < bitmapWidth; ++x) {
+ SplashColorPtr dataPtr = bitmap->getDataPtr() + x * nComps;
+ Guchar *alphaPtr = bitmap->getAlphaPtr() + x;
+ double tx = xMin + x + 0.5;
+ double ty = yMin + 0.5;
+ double xx = tx * ictm[0] + ty * ictm[2] + ictm[4];
+ double yy = tx * ictm[1] + ty * ictm[3] + ictm[5];
+ double s = ((xx - x0) * dx + (yy - y0) * dy) * d;
+ GBool go = gFalse;
+ if (s < 0) {
+ go = ext0;
+ } else if (s > 1) {
+ go = ext1;
+ } else {
+ go = gTrue;
+ }
+ if (go) {
+ GfxColor color;
+ if (s <= 0) {
+ shading->getColor(t0, &color);
+ } else if (s >= 1) {
+ shading->getColor(t1, &color);
+ } else {
+ double t = t0 + s * (t1 - t0);
+ shading->getColor(t, &color);
+ }
+ SplashColor sColor;
+ computeShadingColor(state, mode, reverseVideo, &color, sColor);
+ for (int y = 0; y < bitmapHeight; ++y) {
+ for (int i = 0; i < nComps; ++i) {
+ dataPtr[i] = sColor[i];
+ }
+ dataPtr += bitmap->getRowSize();
+ *alphaPtr = 0xff;
+ alphaPtr += bitmapWidth;
+ }
+ } else {
+ for (int y = 0; y < bitmapHeight; ++y) {
+ *alphaPtr = 0;
+ alphaPtr += bitmapWidth;
+ }
+ }
+ }
+
+ // special case: vertical axis (in device space)
+ } else if (fabs(xx0 - xx1) < 0.01) {
+ for (int y = 0; y < bitmapHeight; ++y) {
+ SplashColorPtr dataPtr = bitmap->getDataPtr() + y * bitmap->getRowSize();
+ Guchar *alphaPtr = bitmap->getAlphaPtr() + y * bitmapWidth;
+ double tx = xMin + 0.5;
+ double ty = yMin + y + 0.5;
+ double xx = tx * ictm[0] + ty * ictm[2] + ictm[4];
+ double yy = tx * ictm[1] + ty * ictm[3] + ictm[5];
+ double s = ((xx - x0) * dx + (yy - y0) * dy) * d;
+ GBool go = gFalse;
+ if (s < 0) {
+ go = ext0;
+ } else if (s > 1) {
+ go = ext1;
+ } else {
+ go = gTrue;
+ }
+ if (go) {
+ GfxColor color;
+ if (s <= 0) {
+ shading->getColor(t0, &color);
+ } else if (s >= 1) {
+ shading->getColor(t1, &color);
+ } else {
+ double t = t0 + s * (t1 - t0);
+ shading->getColor(t, &color);
+ }
+ SplashColor sColor;
+ computeShadingColor(state, mode, reverseVideo, &color, sColor);
+ for (int x = 0; x < bitmapWidth; ++x) {
+ for (int i = 0; i < nComps; ++i) {
+ dataPtr[i] = sColor[i];
+ }
+ dataPtr += nComps;
+ }
+ memset(alphaPtr, 0xff, bitmapWidth);
+ } else {
+ memset(alphaPtr, 0, bitmapWidth);
+ }
+ }
+
+ // general case
+ } else {
+ // pre-compute colors along the axis
+ int nColors = (int)(1.5 * sqrt((xx1 - xx0) * (xx1 - xx0)
+ + (yy1 - yy0) * (yy1 - yy0)));
+ if (nColors < 16) {
+ nColors = 16;
+ } else if (nColors > 1024) {
+ nColors = 1024;
+ }
+ SplashColorPtr sColors = (SplashColorPtr)gmallocn(nColors, nComps);
+ SplashColorPtr sColor = sColors;
+ for (int i = 0; i < nColors; ++i) {
+ double s = (double)i / (double)(nColors - 1);
+ double t = t0 + s * (t1 - t0);
+ GfxColor color;
+ shading->getColor(t, &color);
+ computeShadingColor(state, mode, reverseVideo, &color, sColor);
+ sColor += nComps;
+ }
+
+ SplashColorPtr dataPtr = bitmap->getDataPtr();
+ Guchar *alphaPtr = bitmap->getAlphaPtr();
+ for (int y = 0; y < bitmapHeight; ++y) {
+ for (int x = 0; x < bitmapWidth; ++x) {
+
+ // convert coords to user space
+ double tx = xMin + x + 0.5;
+ double ty = yMin + y + 0.5;
+ double xx = tx * ictm[0] + ty * ictm[2] + ictm[4];
+ double yy = tx * ictm[1] + ty * ictm[3] + ictm[5];
+
+ // compute the position along the axis
+ double s = ((xx - x0) * dx + (yy - y0) * dy) * d;
+ GBool go = gFalse;
+ if (s < 0) {
+ go = ext0;
+ } else if (s > 1) {
+ go = ext1;
+ } else {
+ go = gTrue;
+ }
+ if (go) {
+ if (s <= 0) {
+ sColor = sColors;
+ } else if (s >= 1) {
+ sColor = sColors + (nColors - 1) * nComps;
+ } else {
+ int i = (int)((nColors - 1) * s + 0.5);
+ sColor = sColors + i * nComps;
+ }
+ for (int i = 0; i < nComps; ++i) {
+ *dataPtr++ = sColor[i];
+ }
+ *alphaPtr++ = 0xff;
+ } else {
+ dataPtr += nComps;
+ *alphaPtr++ = 0;
+ }
+ }
+ }
+ gfree(sColors);
+ }
+
+ *xOut = xMin;
+ *yOut = yMin;
+ return bitmap;
+}
+
+SplashBitmap *ShadingImage::generateRadialBitmap(GfxState *state,
+ GfxRadialShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut) {
+ // get the shading parameters
+ double x0, y0, r0, x1, y1, r1;
+ shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
+ double t0 = shading->getDomain0();
+ double t1 = shading->getDomain1();
+ GBool ext0 = shading->getExtend0();
+ GBool ext1 = shading->getExtend1();
+ double h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
+ GBool enclosed = fabs(r1 - r0) >= h;
+
+ // get the clip bbox
+ double fxMin, fyMin, fxMax, fyMax;
+ state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax);
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // intersect with shading region (in user space): if the extend
+ // flags are false (or just the larger extend flag is false, in the
+ // "enclosed" case), we can use the bbox for the two circles
+ if ((!ext0 && !ext1) ||
+ (enclosed && !(r0 > r1 ? ext0 : ext1))) {
+ double uxMin = (x0 - r0) < (x1 - r1) ? (x0 - r0) : (x1 - r1);
+ double uxMax = (x0 + r0) > (x1 + r1) ? (x0 + r0) : (x1 + r1);
+ double uyMin = (y0 - r0) < (y1 - r1) ? (y0 - r0) : (y1 - r1);
+ double uyMax = (y0 + r0) > (y1 + r1) ? (y0 + r0) : (y1 + r1);
+ double dxMin, dyMin, dxMax, dyMax;
+ transformBBox(state, uxMin, uyMin, uxMax, uyMax,
+ &dxMin, &dyMin, &dxMax, &dyMax);
+ if (dxMin > fxMin) {
+ fxMin = dxMin;
+ }
+ if (dxMax < dxMax) {
+ fxMax = dxMax;
+ }
+ if (dyMin > fyMin) {
+ fyMin = dyMin;
+ }
+ if (dyMax < fyMax) {
+ fyMax = dyMax;
+ }
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+ }
+
+ // convert to integer coords
+ int xMin = (int)floor(fxMin);
+ int yMin = (int)floor(fyMin);
+ int xMax = (int)floor(fxMax) + 1;
+ int yMax = (int)floor(fyMax) + 1;
+ int bitmapWidth = xMax - xMin;
+ int bitmapHeight = yMax - yMin;
+
+ // compute the inverse CTM
+ double *ctm = state->getCTM();
+ double det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
+ if (fabs(det) < 0.000001) {
+ return NULL;
+ }
+ det = 1 / det;
+ double ictm[6];
+ ictm[0] = ctm[3] * det;
+ ictm[1] = -ctm[1] * det;
+ ictm[2] = -ctm[2] * det;
+ ictm[3] = ctm[0] * det;
+ ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+ ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+
+ // allocate the bitmap
+ traceMessage("radial shading fill bitmap");
+ SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode,
+ gTrue, gTrue, parentBitmap);
+ int nComps = splashColorModeNComps[mode];
+
+ // pre-compute colors along the axis
+ int nColors = (int)sqrt((double)(bitmapWidth * bitmapWidth
+ + bitmapHeight * bitmapHeight));
+ if (nColors < 16) {
+ nColors = 16;
+ } else if (nColors > 1024) {
+ nColors = 1024;
+ }
+ SplashColorPtr sColors = (SplashColorPtr)gmallocn(nColors, nComps);
+ SplashColorPtr sColor = sColors;
+ for (int i = 0; i < nColors; ++i) {
+ double s = (double)i / (double)(nColors - 1);
+ double t = t0 + s * (t1 - t0);
+ GfxColor color;
+ shading->getColor(t, &color);
+ computeShadingColor(state, mode, reverseVideo, &color, sColor);
+ sColor += nComps;
+ }
+
+ // special case: in the "enclosed" + extended case, we can fill the
+ // bitmap with the outer color and just render inside the larger
+ // circle
+ int bxMin, byMin, bxMax, byMax;
+ if (enclosed &&
+ ((r0 > r1 && ext0) || (r1 > r0 && ext1))) {
+ double uxMin, uyMin, uxMax, uyMax;
+ if (r0 > r1) {
+ sColor = sColors;
+ uxMin = x0 - r0;
+ uxMax = x0 + r0;
+ uyMin = y0 - r0;
+ uyMax = y0 + r0;
+ } else {
+ sColor = sColors + (nColors - 1) * nComps;
+ uxMin = x1 - r1;
+ uxMax = x1 + r1;
+ uyMin = y1 - r1;
+ uyMax = y1 + r1;
+ }
+
+ // convert bbox of larger circle to device space
+ double dxMin, dyMin, dxMax, dyMax;
+ transformBBox(state, uxMin, uyMin, uxMax, uyMax,
+ &dxMin, &dyMin, &dxMax, &dyMax);
+ bxMin = (int)floor(dxMin - xMin);
+ if (bxMin < 0) {
+ bxMin = 0;
+ }
+ byMin = (int)floor(dyMin - yMin);
+ if (byMin < 0) {
+ byMin = 0;
+ }
+ bxMax = (int)floor(dxMax - xMin) + 1;
+ if (bxMax > bitmapWidth) {
+ bxMax = bitmapWidth;
+ }
+ byMax = (int)floor(dyMax - yMin) + 1;
+ if (byMax > bitmapHeight) {
+ byMax = bitmapHeight;
+ }
+
+ // fill bitmap (except for the rectangle containing the larger circle)
+ SplashColorPtr dataPtr = bitmap->getDataPtr();
+ Guchar *alphaPtr = bitmap->getAlphaPtr();
+ for (int y = 0; y < bitmapHeight; ++y) {
+ for (int x = 0; x < bitmapWidth; ++x) {
+ if (y >= byMin && y < byMax && x >= bxMin && x < bxMax) {
+ dataPtr += nComps;
+ ++alphaPtr;
+ } else {
+ for (int i = 0; i < nComps; ++i) {
+ *dataPtr++ = sColor[i];
+ }
+ *alphaPtr++ = 0xff;
+ }
+ }
+ }
+
+ } else {
+ bxMin = 0;
+ byMin = 0;
+ bxMax = bitmapWidth;
+ byMax = bitmapHeight;
+ }
+
+ // render the shading into the bitmap
+ double dx = x1 - x0;
+ double dy = y1 - y0;
+ double dr = r1 - r0;
+ double r0dr = r0 * dr;
+ double r02 = r0 * r0;
+ double a = dx * dx + dy * dy - dr * dr;
+ GBool aIsZero;
+ double a2;
+ if (fabs(a) < 0.00001) {
+ aIsZero = gTrue;
+ a2 = 0;
+ } else {
+ aIsZero = gFalse;
+ a2 = 1 / (2 * a);
+ }
+ for (int y = byMin; y < byMax; ++y) {
+ SplashColorPtr dataPtr = bitmap->getDataPtr()
+ + y * bitmap->getRowSize() + bxMin * nComps;
+ Guchar *alphaPtr = bitmap->getAlphaPtr()
+ + y * bitmap->getAlphaRowSize() + bxMin;
+ for (int x = bxMin; x < bxMax; ++x) {
+
+ // convert coords to user space
+ double tx = xMin + x + 0.5;
+ double ty = yMin + y + 0.5;
+ double xx = tx * ictm[0] + ty * ictm[2] + ictm[4];
+ double yy = tx * ictm[1] + ty * ictm[3] + ictm[5];
+
+ // compute the radius of the circle at x,y
+ double b = 2 * ((xx - x0) * dx + (yy - y0) * dy + r0dr);
+ double c = (xx - x0) * (xx - x0) + (yy - y0) * (yy - y0) - r02;
+ double s = 0;
+ GBool go = gFalse;
+ if (aIsZero) {
+ if (fabs(b) < 0.000001) {
+ if (c <= 0) {
+ if (ext0) {
+ s = 0;
+ go = gTrue;
+ }
+ } else {
+ if (ext1) {
+ s = 1;
+ go = gTrue;
+ }
+ }
+ } else {
+ double s0 = c / b;
+ double rs0 = r0 + s0 * (r1 - r0);
+ if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) {
+ s = s0;
+ go = gTrue;
+ }
+ }
+ } else {
+ double e = b*b - 4*a*c;
+ if (e >= 0) {
+ double es = sqrt(e);
+ double s0 = (b + es) * a2;
+ double s1 = (b - es) * a2;
+ double rs0 = r0 + s0 * (r1 - r0);
+ double rs1 = r0 + s1 * (r1 - r0);
+ if (s0 > s1) {
+ if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) {
+ s = s0;
+ go = gTrue;
+ } else if ((s1 >= 0 || ext0) && (s1 <= 1 || ext1) && rs1 >= 0) {
+ s = s1;
+ go = gTrue;
+ }
+ } else {
+ if ((s1 >= 0 || ext0) && (s1 <= 1 || ext1) && rs1 >= 0) {
+ s = s1;
+ go = gTrue;
+ } else if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) {
+ s = s0;
+ go = gTrue;
+ }
+ }
+ }
+ }
+ if (!go) {
+ dataPtr += nComps;
+ *alphaPtr++ = 0x00;
+ continue;
+ }
+ if (s <= 0) {
+ sColor = sColors;
+ } else if (s >= 1) {
+ sColor = sColors + (nColors - 1) * nComps;
+ } else {
+ int i = (int)((nColors - 1) * s + 0.5);
+ sColor = sColors + i * nComps;
+ }
+ for (int i = 0; i < nComps; ++i) {
+ *dataPtr++ = sColor[i];
+ }
+ *alphaPtr++ = 0xff;
+ }
+ }
+
+ gfree(sColors);
+
+ *xOut = xMin;
+ *yOut = yMin;
+ return bitmap;
+}
+
+SplashBitmap *ShadingImage::generateGouraudTriangleBitmap(
+ GfxState *state,
+ GfxGouraudTriangleShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut) {
+ // get the clip bbox
+ double fxMin, fyMin, fxMax, fyMax;
+ state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax);
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // get the shading bbox
+ double tx0, ty0, tx1, ty1, dx, dy, txMin, tyMin, txMax, tyMax;
+ shading->getBBox(&tx0, &ty0, &tx1, &ty1);
+ state->transform(tx0, ty0, &dx, &dy);
+ txMin = txMax = dx;
+ tyMin = tyMax = dy;
+ state->transform(tx0, ty1, &dx, &dy);
+ if (dx < txMin) {
+ txMin = dx;
+ } else if (dx > txMax) {
+ txMax = dx;
+ }
+ if (dy < tyMin) {
+ tyMin = dy;
+ } else if (dy > tyMax) {
+ tyMax = dy;
+ }
+ state->transform(tx1, ty0, &dx, &dy);
+ if (dx < txMin) {
+ txMin = dx;
+ } else if (dx > txMax) {
+ txMax = dx;
+ }
+ if (dy < tyMin) {
+ tyMin = dy;
+ } else if (dy > tyMax) {
+ tyMax = dy;
+ }
+ state->transform(tx1, ty1, &dx, &dy);
+ if (dx < txMin) {
+ txMin = dx;
+ } else if (dx > txMax) {
+ txMax = dx;
+ }
+ if (dy < tyMin) {
+ tyMin = dy;
+ } else if (dy > tyMax) {
+ tyMax = dy;
+ }
+ if (txMin > fxMin) {
+ fxMin = txMin;
+ }
+ if (txMax < fxMax) {
+ fxMax = txMax;
+ }
+ if (tyMin > fyMin) {
+ fyMin = tyMin;
+ }
+ if (tyMax < fyMax) {
+ fyMax = tyMax;
+ }
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // convert to integer coords
+ int xMin = (int)floor(fxMin);
+ int yMin = (int)floor(fyMin);
+ int xMax = (int)floor(fxMax) + 1;
+ int yMax = (int)floor(fyMax) + 1;
+ int bitmapWidth = xMax - xMin;
+ int bitmapHeight = yMax - yMin;
+
+ // allocate the bitmap
+ traceMessage("Gouraud triangle shading fill bitmap");
+ SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode,
+ gTrue, gTrue, parentBitmap);
+
+ // clear the bitmap
+ memset(bitmap->getDataPtr(), 0, bitmap->getHeight() * bitmap->getRowSize());
+ memset(bitmap->getAlphaPtr(), 0, bitmap->getHeight() * bitmap->getWidth());
+
+ // draw the triangles
+ for (int i = 0; i < shading->getNTriangles(); ++i) {
+ double x0, y0, x1, y1, x2, y2;
+ double color0[gfxColorMaxComps];
+ double color1[gfxColorMaxComps];
+ double color2[gfxColorMaxComps];
+ shading->getTriangle(i, &x0, &y0, color0,
+ &x1, &y1, color1,
+ &x2, &y2, color2);
+ gouraudFillTriangle(state, bitmap, mode, reverseVideo,
+ xMin, yMin, xMax, yMax,
+ x0, y0, color0, x1, y1, color1, x2, y2, color2,
+ shading);
+ }
+
+ *xOut = xMin;
+ *yOut = yMin;
+ return bitmap;
+}
+
+void ShadingImage::gouraudFillTriangle(GfxState *state, SplashBitmap *bitmap,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ int xMin, int yMin, int xMax, int yMax,
+ double x0, double y0, double *color0,
+ double x1, double y1, double *color1,
+ double x2, double y2, double *color2,
+ GfxGouraudTriangleShading *shading) {
+ int nShadingComps = shading->getNComps();
+ int nBitmapComps = splashColorModeNComps[mode];
+
+ //--- transform the vertices to device space, sort by y
+ double dx0, dy0, dx1, dy1, dx2, dy2;
+ state->transform(x0, y0, &dx0, &dy0);
+ state->transform(x1, y1, &dx1, &dy1);
+ state->transform(x2, y2, &dx2, &dy2);
+ if (dy0 > dy1) {
+ double t = dx0; dx0 = dx1; dx1 = t;
+ t = dy0; dy0 = dy1; dy1 = t;
+ double *tc = color0; color0 = color1; color1 = tc;
+ }
+ if (dy1 > dy2) {
+ double t = dx1; dx1 = dx2; dx2 = t;
+ t = dy1; dy1 = dy2; dy2 = t;
+ double *tc = color1; color1 = color2; color2 = tc;
+ }
+ if (dy0 > dy1) {
+ double t = dx0; dx0 = dx1; dx1 = t;
+ t = dy0; dy0 = dy1; dy1 = t;
+ double *tc = color0; color0 = color1; color1 = tc;
+ }
+
+ //--- y loop
+ int syMin = (int)floor(dy0);
+ if (syMin < yMin) {
+ syMin = yMin;
+ }
+ int syMax = (int)floor(dy2) + 1;
+ if (syMax > yMax) {
+ syMax = yMax;
+ }
+ for (int sy = syMin; sy < syMax; ++sy) {
+
+ //--- vertical interpolation
+ double xx0, xx1;
+ double cc0[gfxColorMaxComps], cc1[gfxColorMaxComps];
+ if (sy <= dy0) {
+ xx0 = xx1 = dx0;
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc0[i] = cc1[i] = color0[i];
+ }
+ } else if (sy >= dy2) {
+ xx0 = xx1 = dx2;
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc0[i] = cc1[i] = color2[i];
+ }
+ } else {
+ if (sy <= dy1) {
+ double interp = (sy - dy0) / (dy1 - dy0);
+ xx0 = dx0 + interp * (dx1 - dx0);
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc0[i] = color0[i] + interp * (color1[i] - color0[i]);
+ }
+ } else {
+ double interp = (sy - dy1) / (dy2 - dy1);
+ xx0 = dx1 + interp * (dx2 - dx1);
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc0[i] = color1[i] + interp * (color2[i] - color1[i]);
+ }
+ }
+ double interp = (sy - dy0) / (dy2 - dy0);
+ xx1 = dx0 + interp * (dx2 - dx0);
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc1[i] = color0[i] + interp * (color2[i] - color0[i]);
+ }
+ }
+
+ //--- x loop
+ if (xx0 > xx1) {
+ double t = xx0; xx0 = xx1; xx1 = t;
+ for (int i = 0; i < nShadingComps; ++i) {
+ t = cc0[i]; cc0[i] = cc1[i]; cc1[i] = t;
+ }
+ }
+ int sxMin = (int)floor(xx0);
+ if (sxMin < xMin) {
+ sxMin = xMin;
+ }
+ int sxMax = (int)floor(xx1) + 1;
+ if (sxMax > xMax) {
+ sxMax = xMax;
+ }
+ SplashColorPtr dataPtr = bitmap->getDataPtr()
+ + (sy - yMin) * bitmap->getRowSize()
+ + (sxMin - xMin) * nBitmapComps;
+ if (sxMin < sxMax) {
+ Guchar *alphaPtr = bitmap->getAlphaPtr()
+ + (sy - yMin) * bitmap->getWidth()
+ + (sxMin - xMin);
+ memset(alphaPtr, 0xff, sxMax - sxMin);
+ }
+ for (int sx = sxMin; sx < sxMax; ++sx) {
+
+ //--- horizontal interpolation
+ double cc[gfxColorMaxComps];
+ if (sx <= xx0) {
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc[i] = cc0[i];
+ }
+ } else if (sx >= xx1) {
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc[i] = cc1[i];
+ }
+ } else {
+ for (int i = 0; i < nShadingComps; ++i) {
+ double interp = (sx - xx0) / (xx1 - xx0);
+ cc[i] = cc0[i] + interp * (cc1[i] - cc0[i]);
+ }
+ }
+
+ //--- compute color and set pixel
+ GfxColor gColor;
+ shading->getColor(cc, &gColor);
+ SplashColor sColor;
+ computeShadingColor(state, mode, reverseVideo, &gColor, sColor);
+ for (int i = 0; i < nBitmapComps; ++i) {
+ dataPtr[i] = sColor[i];
+ }
+ dataPtr += nBitmapComps;
+ }
+ }
+}
+
+SplashBitmap *ShadingImage::generatePatchMeshBitmap(
+ GfxState *state,
+ GfxPatchMeshShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut) {
+ // get the clip bbox
+ double fxMin, fyMin, fxMax, fyMax;
+ state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax);
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // get the shading bbox
+ double tx0, ty0, tx1, ty1, dx, dy, txMin, tyMin, txMax, tyMax;
+ shading->getBBox(&tx0, &ty0, &tx1, &ty1);
+ state->transform(tx0, ty0, &dx, &dy);
+ txMin = txMax = dx;
+ tyMin = tyMax = dy;
+ state->transform(tx0, ty1, &dx, &dy);
+ if (dx < txMin) {
+ txMin = dx;
+ } else if (dx > txMax) {
+ txMax = dx;
+ }
+ if (dy < tyMin) {
+ tyMin = dy;
+ } else if (dy > tyMax) {
+ tyMax = dy;
+ }
+ state->transform(tx1, ty0, &dx, &dy);
+ if (dx < txMin) {
+ txMin = dx;
+ } else if (dx > txMax) {
+ txMax = dx;
+ }
+ if (dy < tyMin) {
+ tyMin = dy;
+ } else if (dy > tyMax) {
+ tyMax = dy;
+ }
+ state->transform(tx1, ty1, &dx, &dy);
+ if (dx < txMin) {
+ txMin = dx;
+ } else if (dx > txMax) {
+ txMax = dx;
+ }
+ if (dy < tyMin) {
+ tyMin = dy;
+ } else if (dy > tyMax) {
+ tyMax = dy;
+ }
+ if (txMin > fxMin) {
+ fxMin = txMin;
+ }
+ if (txMax < fxMax) {
+ fxMax = txMax;
+ }
+ if (tyMin > fyMin) {
+ fyMin = tyMin;
+ }
+ if (tyMax < fyMax) {
+ fyMax = tyMax;
+ }
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // convert to integer coords
+ int xMin = (int)floor(fxMin);
+ int yMin = (int)floor(fyMin);
+ int xMax = (int)floor(fxMax) + 1;
+ int yMax = (int)floor(fyMax) + 1;
+ int bitmapWidth = xMax - xMin;
+ int bitmapHeight = yMax - yMin;
+
+ // allocate the bitmap
+ traceMessage("Gouraud triangle shading fill bitmap");
+ SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode,
+ gTrue, gTrue, parentBitmap);
+
+ // allocate a Splash object
+ // vector antialiasing is disabled to avoid artifacts along triangle edges
+ Splash *splash = new Splash(bitmap, gFalse,
+ parentSplash->getImageCache(),
+ parentSplash->getScreen());
+ SplashColor zero;
+ for (int i = 0; i < splashColorModeNComps[mode]; ++i) {
+ zero[i] = 0;
+ }
+ splash->clear(zero, 0x00);
+
+ // draw the patches
+ int start;
+ if (shading->getNPatches() > 128) {
+ start = 3;
+ } else if (shading->getNPatches() > 64) {
+ start = 2;
+ } else if (shading->getNPatches() > 16) {
+ start = 1;
+ } else {
+ start = 0;
+ }
+ for (int i = 0; i < shading->getNPatches(); ++i) {
+ fillPatch(state, splash, mode, reverseVideo,
+ xMin, yMin, shading->getPatch(i), shading, start);
+ }
+
+ delete splash;
+
+ *xOut = xMin;
+ *yOut = yMin;
+ return bitmap;
+}
+
+void ShadingImage::fillPatch(GfxState *state, Splash *splash,
+ SplashColorMode mode, GBool reverseVideo,
+ int xMin, int yMin,
+ GfxPatch *patch,
+ GfxPatchMeshShading *shading,
+ int depth) {
+ GfxColor c00;
+ shading->getColor(patch->color[0][0], &c00);
+ GBool stop = gFalse;
+
+ // stop subdivision at max depth
+ if (depth == patchMaxDepth) {
+ stop = gTrue;
+ }
+
+ // stop subdivision if colors are close enough
+ if (!stop) {
+ int nComps = shading->getColorSpace()->getNComps();
+ GfxColor c01, c10, c11;
+ shading->getColor(patch->color[0][1], &c01);
+ shading->getColor(patch->color[1][0], &c10);
+ shading->getColor(patch->color[1][1], &c11);
+ int i;
+ for (i = 0; i < nComps; ++i) {
+ if (abs(c00.c[i] - c01.c[i]) > patchColorDelta ||
+ abs(c01.c[i] - c11.c[i]) > patchColorDelta ||
+ abs(c11.c[i] - c10.c[i]) > patchColorDelta ||
+ abs(c10.c[i] - c00.c[i]) > patchColorDelta) {
+ break;
+ }
+ }
+ if (i == nComps) {
+ stop = gTrue;
+ }
+ }
+
+ // stop subdivision if patch is small enough
+ if (!stop) {
+ double xxMin = 0;
+ double yyMin = 0;
+ double xxMax = 0;
+ double yyMax = 0;
+ for (int j = 0; j < 4; ++j) {
+ for (int i = 0; i < 4; ++i) {
+ double xx, yy;
+ state->transformDelta(patch->x[i][j], patch->y[i][j], &xx, &yy);
+ if (i == 0 && j == 0) {
+ xxMin = xxMax = xx;
+ yyMin = yyMax = yy;
+ } else {
+ if (xx < xxMin) {
+ xxMin = xx;
+ } else if (xx > xxMax) {
+ xxMax = xx;
+ }
+ if (yy < yyMin) {
+ yyMin = yy;
+ } else if (yy > yyMax) {
+ yyMax = yy;
+ }
+ }
+ }
+ }
+ if (xxMax - xxMin < 1 && yyMax - yyMin < 1) {
+ stop = gTrue;
+ }
+ }
+
+ // draw the patch
+ if (stop) {
+ SplashColor sColor;
+ computeShadingColor(state, mode, reverseVideo, &c00, sColor);
+ splash->setFillPattern(new SplashSolidColor(sColor));
+ SplashPath *path = new SplashPath();
+ double xx0, yy0, xx1, yy1, xx2, yy2, xx3, yy3;
+ state->transform(patch->x[0][0], patch->y[0][0], &xx0, &yy0);
+ path->moveTo(xx0 - xMin, yy0 - yMin);
+ state->transform(patch->x[0][1], patch->y[0][1], &xx1, &yy1);
+ state->transform(patch->x[0][2], patch->y[0][2], &xx2, &yy2);
+ state->transform(patch->x[0][3], patch->y[0][3], &xx3, &yy3);
+ path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin,
+ xx3 - xMin, yy3 - yMin);
+ state->transform(patch->x[1][3], patch->y[1][3], &xx1, &yy1);
+ state->transform(patch->x[2][3], patch->y[2][3], &xx2, &yy2);
+ state->transform(patch->x[3][3], patch->y[3][3], &xx3, &yy3);
+ path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin,
+ xx3 - xMin, yy3 - yMin);
+ state->transform(patch->x[3][2], patch->y[3][2], &xx1, &yy1);
+ state->transform(patch->x[3][1], patch->y[3][1], &xx2, &yy2);
+ state->transform(patch->x[3][0], patch->y[3][0], &xx3, &yy3);
+ path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin,
+ xx3 - xMin, yy3 - yMin);
+ state->transform(patch->x[2][0], patch->y[2][0], &xx1, &yy1);
+ state->transform(patch->x[1][0], patch->y[1][0], &xx2, &yy2);
+ path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin,
+ xx0 - xMin, yy0 - yMin);
+ path->close();
+ splash->fill(path, gFalse);
+ delete path;
+
+ // subdivide the patch
+ } else {
+ double xx[4][8], yy[4][8];
+ for (int i = 0; i < 4; ++i) {
+ xx[i][0] = patch->x[i][0];
+ yy[i][0] = patch->y[i][0];
+ xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
+ yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
+ double xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
+ double yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
+ xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
+ yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
+ xx[i][2] = 0.5 * (xx[i][1] + xxm);
+ yy[i][2] = 0.5 * (yy[i][1] + yym);
+ xx[i][5] = 0.5 * (xxm + xx[i][6]);
+ yy[i][5] = 0.5 * (yym + yy[i][6]);
+ xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
+ yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
+ xx[i][7] = patch->x[i][3];
+ yy[i][7] = patch->y[i][3];
+ }
+ GfxPatch patch00, patch01, patch10, patch11;
+ for (int i = 0; i < 4; ++i) {
+ patch00.x[0][i] = xx[0][i];
+ patch00.y[0][i] = yy[0][i];
+ patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
+ patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
+ double xxm = 0.5 * (xx[1][i] + xx[2][i]);
+ double yym = 0.5 * (yy[1][i] + yy[2][i]);
+ patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
+ patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
+ patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
+ patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
+ patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
+ patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
+ patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
+ patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
+ patch10.x[0][i] = patch00.x[3][i];
+ patch10.y[0][i] = patch00.y[3][i];
+ patch10.x[3][i] = xx[3][i];
+ patch10.y[3][i] = yy[3][i];
+ }
+ for (int i = 4; i < 8; ++i) {
+ patch01.x[0][i-4] = xx[0][i];
+ patch01.y[0][i-4] = yy[0][i];
+ patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
+ patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
+ double xxm = 0.5 * (xx[1][i] + xx[2][i]);
+ double yym = 0.5 * (yy[1][i] + yy[2][i]);
+ patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
+ patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
+ patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
+ patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
+ patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
+ patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
+ patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
+ patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
+ patch11.x[0][i-4] = patch01.x[3][i-4];
+ patch11.y[0][i-4] = patch01.y[3][i-4];
+ patch11.x[3][i-4] = xx[3][i];
+ patch11.y[3][i-4] = yy[3][i];
+ }
+ for (int i = 0; i < shading->getNComps(); ++i) {
+ patch00.color[0][0][i] = patch->color[0][0][i];
+ patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] +
+ patch->color[0][1][i]);
+ patch01.color[0][0][i] = patch00.color[0][1][i];
+ patch01.color[0][1][i] = patch->color[0][1][i];
+ patch01.color[1][1][i] = 0.5 * (patch->color[0][1][i] +
+ patch->color[1][1][i]);
+ patch11.color[0][1][i] = patch01.color[1][1][i];
+ patch11.color[1][1][i] = patch->color[1][1][i];
+ patch11.color[1][0][i] = 0.5 * (patch->color[1][1][i] +
+ patch->color[1][0][i]);
+ patch10.color[1][1][i] = patch11.color[1][0][i];
+ patch10.color[1][0][i] = patch->color[1][0][i];
+ patch10.color[0][0][i] = 0.5 * (patch->color[1][0][i] +
+ patch->color[0][0][i]);
+ patch00.color[1][0][i] = patch10.color[0][0][i];
+ patch00.color[1][1][i] = 0.5 * (patch00.color[1][0][i] +
+ patch01.color[1][1][i]);
+ patch01.color[1][0][i] = patch00.color[1][1][i];
+ patch11.color[0][0][i] = patch00.color[1][1][i];
+ patch10.color[0][1][i] = patch00.color[1][1][i];
+ }
+ fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch00,
+ shading, depth + 1);
+ fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch10,
+ shading, depth + 1);
+ fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch01,
+ shading, depth + 1);
+ fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch11,
+ shading, depth + 1);
+ }
+}
+
+void ShadingImage::computeShadingColor(GfxState *state,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ GfxColor *color,
+ SplashColorPtr sColor) {
+ GfxGray gray;
+ GfxRGB rgb;
+#if SPLASH_CMYK
+ GfxCMYK cmyk;
+#endif
+
+ state->setFillColor(color);
+ switch (mode) {
+ case splashModeMono8:
+ state->getFillGray(&gray);
+ if (reverseVideo) {
+ gray = gfxColorComp1 - gray;
+ }
+ sColor[0] = colToByte(gray);
+ break;
+ case splashModeRGB8:
+ state->getFillRGB(&rgb);
+ if (reverseVideo) {
+ rgb.r = gfxColorComp1 - rgb.r;
+ rgb.g = gfxColorComp1 - rgb.g;
+ rgb.b = gfxColorComp1 - rgb.b;
+ }
+ sColor[0] = colToByte(rgb.r);
+ sColor[1] = colToByte(rgb.g);
+ sColor[2] = colToByte(rgb.b);
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ state->getFillCMYK(&cmyk);
+ sColor[0] = colToByte(cmyk.c);
+ sColor[1] = colToByte(cmyk.m);
+ sColor[2] = colToByte(cmyk.y);
+ sColor[3] = colToByte(cmyk.k);
+ break;
+#endif
+ case splashModeMono1:
+ case splashModeBGR8:
+ // mode cannot be Mono1 or BGR8
+ break;
+ }
+}
+
+// Transform a user space bbox to a device space bbox.
+void ShadingImage::transformBBox(GfxState *state,
+ double uxMin, double uyMin,
+ double uxMax, double uyMax,
+ double *dxMin, double *dyMin,
+ double *dxMax, double *dyMax) {
+ double tx, ty;
+ state->transform(uxMin, uyMin, &tx, &ty);
+ *dxMin = *dxMax = tx;
+ *dyMin = *dyMax = ty;
+ state->transform(uxMin, uyMax, &tx, &ty);
+ if (tx < *dxMin) {
+ *dxMin = tx;
+ } else if (tx > *dxMax) {
+ *dxMax = tx;
+ }
+ if (ty < *dyMin) {
+ *dyMin = ty;
+ } else if (ty > *dyMax) {
+ *dyMax = ty;
+ }
+ state->transform(uxMax, uyMin, &tx, &ty);
+ if (tx < *dxMin) {
+ *dxMin = tx;
+ } else if (tx > *dxMax) {
+ *dxMax = tx;
+ }
+ if (ty < *dyMin) {
+ *dyMin = ty;
+ } else if (ty > *dyMax) {
+ *dyMax = ty;
+ }
+ state->transform(uxMax, uyMax, &tx, &ty);
+ if (tx < *dxMin) {
+ *dxMin = tx;
+ } else if (tx > *dxMax) {
+ *dxMax = tx;
+ }
+ if (ty < *dyMin) {
+ *dyMin = ty;
+ } else if (ty > *dyMax) {
+ *dyMax = ty;
+ }
+}
+
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h b/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h
new file mode 100644
index 00000000000..3bbff6922a4
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h
@@ -0,0 +1,106 @@
+//========================================================================
+//
+// ShadingImage.h
+//
+// Convert shading patterns to bitmaps.
+//
+// Copyright 2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SHADINGIMAGE_H
+#define SHADINGIMAGE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+class GfxState;
+class GfxShading;
+class SplashBitmap;
+class Splash;
+
+class ShadingImage {
+public:
+
+ // Generate a [mode] bitmap for [shading], using the clipping and
+ // CTM info from [state]. Returns the bitmap and sets [xOut],[yOut]
+ // to the upper-left corner of the region in device space. Returns
+ // NULL if the clip region is empty.
+ static SplashBitmap *generateBitmap(GfxState *state, GfxShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut);
+
+
+private:
+
+ static SplashBitmap *generateFunctionBitmap(GfxState *state,
+ GfxFunctionShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut);
+ static SplashBitmap *generateAxialBitmap(GfxState *state,
+ GfxAxialShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut);
+ static SplashBitmap *generateRadialBitmap(GfxState *state,
+ GfxRadialShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut);
+ static SplashBitmap *generateGouraudTriangleBitmap(
+ GfxState *state,
+ GfxGouraudTriangleShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut);
+ static void gouraudFillTriangle(GfxState *state, SplashBitmap *bitmap,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ int xMin, int yMin, int xMax, int yMax,
+ double x0, double y0, double *color0,
+ double x1, double y1, double *color1,
+ double x2, double y2, double *color2,
+ GfxGouraudTriangleShading *shading);
+ static SplashBitmap *generatePatchMeshBitmap(GfxState *state,
+ GfxPatchMeshShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut);
+ static void fillPatch(GfxState *state, Splash *splash,
+ SplashColorMode mode, GBool reverseVideo,
+ int xMin, int yMin,
+ GfxPatch *patch,
+ GfxPatchMeshShading *shading,
+ int depth);
+ static void computeShadingColor(GfxState *state,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ GfxColor *color,
+ SplashColorPtr sColor);
+ static void transformBBox(GfxState *state,
+ double uxMin, double uyMin,
+ double uxMax, double uyMax,
+ double *dxMin, double *dyMin,
+ double *dxMax, double *dyMax);
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc
index 187ef5657b5..89c7c02a7b6 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc
@@ -17,11 +17,13 @@
#include <limits.h>
#include "gmempp.h"
#include "gfile.h"
+#include "Trace.h"
#include "GlobalParams.h"
#include "Error.h"
#include "Object.h"
#include "Gfx.h"
#include "GfxFont.h"
+#include "ShadingImage.h"
#include "Link.h"
#include "CharCodeToUnicode.h"
#include "FontEncodingTables.h"
@@ -52,6 +54,18 @@ extern "C" int unlink(char *filename);
//------------------------------------------------------------------------
+// max tile size (used in tilingPatternFill())
+// - Adobe uses a resolution-independent threshold here, of 6M sq pts
+// - xpdf uses a resolution-dependent max, but with different values
+// on 32-bit and 64-bit systems
+#if SplashBitmapRowSizeMax == INT_MAX
+# define maxTileSize 200000000
+#else
+# define maxTileSize 2000000000
+#endif
+
+//------------------------------------------------------------------------
+
// Type 3 font cache size parameters
#define type3FontCacheAssoc 8
#define type3FontCacheMaxSets 8
@@ -67,6 +81,11 @@ static SplashStrokeAdjustMode mapStrokeAdjustMode[3] = {
//------------------------------------------------------------------------
+// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
+static inline Guchar div255(int x) {
+ return (Guchar)((x + (x >> 8) + 0x80) >> 8);
+}
+
// Clip x to lie in [0, 255].
static inline Guchar clip255(int x) {
return x < 0 ? 0 : x > 255 ? 255 : (Guchar)x;
@@ -499,7 +518,7 @@ public:
int cacheAssoc; // cache associativity (glyphs per set)
Guchar *cacheData; // glyph pixmap cache
T3FontCacheTag *cacheTags; // cache tags, i.e., char codes
- GBool inUse; // set while this T3 font is in active use
+ int refCount; // active reference count for this T3 font
};
T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
@@ -541,7 +560,7 @@ T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
for (i = 0; i < cacheSets * cacheAssoc; ++i) {
cacheTags[i].mru = (Gushort)(i & (cacheAssoc - 1));
}
- inUse = gFalse;
+ refCount = 0;
}
T3FontCache::~T3FontCache() {
@@ -565,6 +584,7 @@ struct T3GlyphStack {
SplashBitmap *origBitmap;
Splash *origSplash;
double origCTM4, origCTM5;
+ SplashStrokeAdjustMode savedStrokeAdjust;
T3GlyphStack *next; // next object on stack
};
@@ -711,6 +731,9 @@ SplashOutputDev::~SplashOutputDev() {
if (bitmap) {
delete bitmap;
}
+ if (textClipPath) {
+ delete textClipPath;
+ }
}
void SplashOutputDev::startDoc(XRef *xrefA) {
@@ -763,6 +786,7 @@ void SplashOutputDev::startPage(int pageNum, GfxState *state) {
delete bitmap;
bitmap = NULL;
}
+ traceMessage("page bitmap");
bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,
colorMode != splashModeMono1, bitmapTopDown,
NULL);
@@ -1194,18 +1218,21 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
goto err1;
}
- // sanity-check the font size - skip anything larger than 20 inches
- // (this avoids problems allocating memory for the font cache)
+ // sanity-check the font size: skip anything larger than 10k x 10k,
+ // to avoid problems allocating a bitmap (note that code in
+ // SplashFont disables caching at a smaller size than this)
state->textTransformDelta(state->getFontSize(), state->getFontSize(),
&fsx, &fsy);
state->transformDelta(fsx, fsy, &fsx, &fsy);
- if (fabs(fsx) > 20 * state->getHDPI() ||
- fabs(fsy) > 20 * state->getVDPI()) {
+ if (fabs(fsx) > 20000 || fabs(fsy) > 20000) {
goto err1;
}
// check the font file cache
id = new SplashOutFontFileID(gfxFont->getID());
+ if (fontEngine->checkForBadFontFile(id)) {
+ goto err2;
+ }
if ((fontFile = fontEngine->getFontFile(id))) {
delete id;
@@ -1308,7 +1335,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
case fontType1C:
@@ -1337,7 +1364,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
case fontType1COT:
@@ -1370,7 +1397,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
case fontTrueType:
@@ -1415,7 +1442,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
case fontCIDType0:
@@ -1443,7 +1470,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
case fontCIDType0COT:
@@ -1513,7 +1540,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
case fontCIDType2:
@@ -1588,7 +1615,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) {
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
default:
@@ -1734,6 +1761,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
SplashBitmap *origBitmap, *tileBitmap;
Splash *origSplash;
SplashColor color;
+ Guint *overprintMaskBitmap;
double *ctm;
double ictm[6], tileMat[6], mat1[6], mat2[6];
double tileXMin, tileYMin, tileXMax, tileYMax;
@@ -1818,7 +1846,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
if (tileXMax - tileXMin + 0.5 > (double)INT_MAX ||
tileYMax - tileYMin + 0.5 > (double)INT_MAX ||
tileW > INT_MAX / tileH ||
- tileSize > 1000000) {
+ tileSize > maxTileSize) {
mat1[0] = mat[0];
mat1[1] = mat[1];
mat1[2] = mat[2];
@@ -1969,6 +1997,7 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
// create a temporary bitmap
origBitmap = bitmap;
origSplash = splash;
+ traceMessage("tiling pattern bitmap");
bitmap = tileBitmap = new SplashBitmap(tileW, tileH, bitmapRowPad,
colorMode, gTrue, bitmapTopDown,
origBitmap);
@@ -1978,6 +2007,20 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
color[i] = 0;
}
splash->clear(color);
+#if SPLASH_CMYK
+ // if we're doing overprint preview, we need to track the overprint
+ // mask at each pixel in the tile bitmap
+ if (globalParams->getOverprintPreview() &&
+ colorMode == splashModeCMYK8) {
+ overprintMaskBitmap = (Guint *)gmallocn(tileH, tileW * (int)sizeof(Guint));
+ memset(overprintMaskBitmap, 0, tileH * tileW * sizeof(Guint));
+ splash->setOverprintMaskBitmap(overprintMaskBitmap);
+ } else {
+ overprintMaskBitmap = NULL;
+ }
+#else // SPLASH_CMYK
+ overprintMaskBitmap = NULL;
+#endif // SPLASH_CMYK
splash->setMinLineWidth(globalParams->getMinLineWidth());
splash->setStrokeAdjust(
mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
@@ -2005,434 +2048,48 @@ void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
splash->setOverprintMask(0xffffffff);
// draw the tiles
- for (iy = iyMin; iy < iyMax; ++iy) {
- for (ix = ixMin; ix < ixMax; ++ix) {
- x = (int)(adjXMin + ix * xStepX + iy * yStepX + 0.5);
- y = (int)(adjYMin + ix * xStepY + iy * yStepY + 0.5);
- splash->composite(tileBitmap, 0, 0, x, y, tileW, tileH,
- gFalse, gFalse);
- }
- }
-
- delete tileBitmap;
-}
-
-GBool SplashOutputDev::axialShadedFill(GfxState *state,
- GfxAxialShading *shading) {
- double x0, y0, x1, y1, t0, t1;
- GBool ext0, ext1;
- double uxMin, uyMin, uxMax, uyMax, det;
- double *ctm;
- double ictm[6];
- double xMin, yMin, xMax, yMax, tx, ty, xx, yy;
- double xx0, yy0, xx1, yy1, dx, dy, d, s, t;
- GBool dZero, go;
- int ixMin, iyMin, ixMax, iyMax, bitmapWidth, bitmapHeight, nColors;
- SplashClipResult clipRes;
- SplashColorMode srcMode;
- SplashBitmap *tBitmap;
- int nComps;
- int x, y, i;
- SplashColorPtr dataPtr;
- Guchar *alphaPtr;
- GfxColor color;
- SplashColorPtr sColors, sColor;
- SplashColor sColor0;
-
-
- // get the shading parameters
- shading->getCoords(&x0, &y0, &x1, &y1);
- t0 = shading->getDomain0();
- t1 = shading->getDomain1();
- ext0 = shading->getExtend0();
- ext1 = shading->getExtend1();
- dx = x1 - x0;
- dy = y1 - y0;
- d = dx * dx + dy * dy;
- dZero = fabs(d) < 0.0001;
- if (!dZero) {
- d = 1 / d;
- }
- if (dZero && !ext0 && !ext1) {
- return gTrue;
- }
-
- // get clip region (in user space)
- state->getUserClipBBox(&uxMin, &uyMin, &uxMax, &uyMax);
- if (uxMin > uxMax || uyMin > uyMax) {
- return gTrue;
- }
-
- // convert the region to device space
- ctm = state->getCTM();
- tx = uxMin * ctm[0] + uyMin * ctm[2] + ctm[4];
- ty = uxMin * ctm[1] + uyMin * ctm[3] + ctm[5];
- xMin = xMax = tx;
- yMin = yMax = ty;
- tx = uxMin * ctm[0] + uyMax * ctm[2] + ctm[4];
- ty = uxMin * ctm[1] + uyMax * ctm[3] + ctm[5];
- if (tx < xMin) {
- xMin = tx;
- } else if (tx > xMax) {
- xMax = tx;
- }
- if (ty < yMin) {
- yMin = ty;
- } else if (ty > yMax) {
- yMax = ty;
- }
- tx = uxMax * ctm[0] + uyMin * ctm[2] + ctm[4];
- ty = uxMax * ctm[1] + uyMin * ctm[3] + ctm[5];
- if (tx < xMin) {
- xMin = tx;
- } else if (tx > xMax) {
- xMax = tx;
- }
- if (ty < yMin) {
- yMin = ty;
- } else if (ty > yMax) {
- yMax = ty;
- }
- tx = uxMax * ctm[0] + uyMax * ctm[2] + ctm[4];
- ty = uxMax * ctm[1] + uyMax * ctm[3] + ctm[5];
- if (tx < xMin) {
- xMin = tx;
- } else if (tx > xMax) {
- xMax = tx;
- }
- if (ty < yMin) {
- yMin = ty;
- } else if (ty > yMax) {
- yMax = ty;
- }
- ixMin = (int)floor(xMin);
- iyMin = (int)floor(yMin);
- ixMax = (int)floor(xMax) + 1;
- iyMax = (int)floor(yMax) + 1;
- clipRes = splash->limitRectToClipRect(&ixMin, &iyMin, &ixMax, &iyMax);
- if (clipRes == splashClipAllOutside) {
- return gTrue;
- }
-
- // allocate a bitmap
- if (colorMode == splashModeMono1) {
- srcMode = splashModeMono8;
- } else if (colorMode == splashModeBGR8) {
- srcMode = splashModeRGB8;
- } else {
- srcMode = colorMode;
- }
- bitmapWidth = ixMax - ixMin;
- bitmapHeight = iyMax - iyMin;
- tBitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1,
- srcMode, gTrue, gTrue, bitmap);
- memset(tBitmap->getAlphaPtr(), 0, (size_t)bitmapWidth * bitmapHeight);
- nComps = splashColorModeNComps[srcMode];
-
- // compute the inverse CTM
- det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
- ictm[0] = ctm[3] * det;
- ictm[1] = -ctm[1] * det;
- ictm[2] = -ctm[2] * det;
- ictm[3] = ctm[0] * det;
- ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
- ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
-
- // render the shading into the bitmap
- xx0 = x0 * ctm[0] + y0 * ctm[2] + ctm[4];
- yy0 = x0 * ctm[1] + y0 * ctm[3] + ctm[5];
- xx1 = x1 * ctm[0] + y1 * ctm[2] + ctm[4];
- yy1 = x1 * ctm[1] + y1 * ctm[3] + ctm[5];
- sColors = NULL;
-
- // special case: zero-length axis
- if (dZero) {
- if (ext0) {
- shading->getColor(t0, &color);
- } else {
- shading->getColor(t1, &color);
- }
- computeShadingColor(state, srcMode, &color, sColor0);
- dataPtr = tBitmap->getDataPtr();
- alphaPtr = tBitmap->getAlphaPtr();
- for (y = 0; y < bitmapHeight; ++y) {
- for (x = 0; x < bitmapWidth; ++x) {
- for (i = 0; i < nComps; ++i) {
- *dataPtr++ = sColor0[i];
- }
- *alphaPtr++ = 0xff;
- }
- }
-
- // special case: horizontal axis (in device space)
- } else if (fabs(yy0 - yy1) < 0.01) {
- for (x = 0; x < bitmapWidth; ++x) {
- dataPtr = tBitmap->getDataPtr() + x * nComps;
- alphaPtr = tBitmap->getAlphaPtr() + x;
- tx = ixMin + x + 0.5;
- ty = iyMin + 0.5;
- xx = tx * ictm[0] + ty * ictm[2] + ictm[4];
- yy = tx * ictm[1] + ty * ictm[3] + ictm[5];
- s = ((xx - x0) * dx + (yy - y0) * dy) * d;
- go = gFalse;
- if (s < 0) {
- go = ext0;
- } else if (s > 1) {
- go = ext1;
- } else {
- go = gTrue;
- }
- if (!go) {
- continue;
- }
- if (s <= 0) {
- shading->getColor(t0, &color);
- } else if (s >= 1) {
- shading->getColor(t1, &color);
- } else {
- t = t0 + s * (t1 - t0);
- shading->getColor(t, &color);
- }
- computeShadingColor(state, srcMode, &color, sColor0);
- for (y = 0; y < bitmapHeight; ++y) {
- for (i = 0; i < nComps; ++i) {
- dataPtr[i] = sColor0[i];
- }
- *alphaPtr = 0xff;
- dataPtr += tBitmap->getRowSize();
- alphaPtr += bitmapWidth;
+ if (tileW == 1 && tileH == 1 &&
+ fabs(xStepX * yStepY - xStepY * yStepX) < 0.9) {
+ // if the tile is 1x1 pixel, and the stepping completely fills the
+ // area, just composite the 1x1 image across the clip region
+ // (this avoids performance problems in cases where the step size
+ // is very small) (we compare to 0.9 instead of 1.0 to avoid fp
+ // jitter issues)
+ ixMin = (int)floor(clipXMin);
+ ixMax = (int)floor(clipXMax) + 1;
+ iyMin = (int)floor(clipYMin);
+ iyMax = (int)floor(clipYMax) + 1;
+ for (iy = iyMin; iy < iyMax; ++iy) {
+ for (ix = ixMin; ix < ixMax; ++ix) {
+ splash->composite(tileBitmap, 0, 0, ix, iy, tileW, tileH,
+ gFalse, gFalse);
}
}
-
- // special case: vertical axis (in device space)
- } else if (fabs(xx0 - xx1) < 0.01) {
- dataPtr = tBitmap->getDataPtr();
- alphaPtr = tBitmap->getAlphaPtr();
- for (y = 0; y < bitmapHeight; ++y) {
- tx = ixMin + 0.5;
- ty = iyMin + y + 0.5;
- xx = tx * ictm[0] + ty * ictm[2] + ictm[4];
- yy = tx * ictm[1] + ty * ictm[3] + ictm[5];
- s = ((xx - x0) * dx + (yy - y0) * dy) * d;
- go = gFalse;
- if (s < 0) {
- go = ext0;
- } else if (s > 1) {
- go = ext1;
- } else {
- go = gTrue;
- }
- if (!go) {
- continue;
- }
- if (s <= 0) {
- shading->getColor(t0, &color);
- } else if (s >= 1) {
- shading->getColor(t1, &color);
- } else {
- t = t0 + s * (t1 - t0);
- shading->getColor(t, &color);
- }
- computeShadingColor(state, srcMode, &color, sColor0);
- for (x = 0; x < bitmapWidth; ++x) {
- for (i = 0; i < nComps; ++i) {
- *dataPtr++ = sColor0[i];
- }
- *alphaPtr++ = 0xff;
- }
- }
-
- // general case
} else {
- // pre-compute colors along the axis
- nColors = (int)(1.5 * sqrt((xx1 - xx0) * (xx1 - xx0)
- + (yy1 - yy0) * (yy1 - yy0)));
- if (nColors < 16) {
- nColors = 16;
- } else if (nColors > 1024) {
- nColors = 1024;
- }
- sColors = (SplashColorPtr)gmallocn(nColors, nComps);
- sColor = sColors;
- for (i = 0; i < nColors; ++i) {
- s = (double)i / (double)(nColors - 1);
- t = t0 + s * (t1 - t0);
- shading->getColor(t, &color);
- computeShadingColor(state, srcMode, &color, sColor);
- sColor += nComps;
- }
-
- dataPtr = tBitmap->getDataPtr();
- alphaPtr = tBitmap->getAlphaPtr();
- for (y = 0; y < bitmapHeight; ++y) {
- for (x = 0; x < bitmapWidth; ++x) {
-
- // convert coords to user space
- tx = ixMin + x + 0.5;
- ty = iyMin + y + 0.5;
- xx = tx * ictm[0] + ty * ictm[2] + ictm[4];
- yy = tx * ictm[1] + ty * ictm[3] + ictm[5];
-
- // compute the position along the axis
- s = ((xx - x0) * dx + (yy - y0) * dy) * d;
- go = gFalse;
- if (s < 0) {
- go = ext0;
- } else if (s > 1) {
- go = ext1;
+ for (iy = iyMin; iy < iyMax; ++iy) {
+ for (ix = ixMin; ix < ixMax; ++ix) {
+ x = (int)(adjXMin + ix * xStepX + iy * yStepX + 0.5);
+ y = (int)(adjYMin + ix * xStepY + iy * yStepY + 0.5);
+ if (overprintMaskBitmap) {
+ splash->compositeWithOverprint(tileBitmap, overprintMaskBitmap,
+ 0, 0, x, y, tileW, tileH,
+ gFalse, gFalse);
} else {
- go = gTrue;
- }
- if (!go) {
- dataPtr += nComps;
- ++alphaPtr;
- continue;
+ splash->composite(tileBitmap, 0, 0, x, y, tileW, tileH,
+ gFalse, gFalse);
}
- if (s <= 0) {
- sColor = sColors;
- } else if (s >= 1) {
- sColor = sColors + (nColors - 1) * nComps;
- } else {
- i = (int)((nColors - 1) * s + 0.5);
- sColor = sColors + i * nComps;
- }
- for (i = 0; i < nComps; ++i) {
- *dataPtr++ = sColor[i];
- }
- *alphaPtr++ = 0xff;
}
}
}
- // composite the bitmap
- setOverprintMask(state, state->getFillColorSpace(),
- state->getFillOverprint(), state->getOverprintMode(),
- NULL);
- splash->composite(tBitmap, 0, 0, ixMin, iyMin, bitmapWidth, bitmapHeight,
- clipRes == splashClipAllInside, gFalse);
-
- gfree(sColors);
- delete tBitmap;
-
- return gTrue;
+ gfree(overprintMaskBitmap);
+ delete tileBitmap;
}
-GBool SplashOutputDev::radialShadedFill(GfxState *state,
- GfxRadialShading *shading) {
- double x0, y0, r0, x1, y1, r1, t0, t1, h;
- GBool ext0, ext1, enclosed;
- double uxMin, uyMin, uxMax, uyMax, det;
- double *ctm;
- double ictm[6];
- double xMin, yMin, xMax, yMax, tx, ty, xx, yy;
- double dx, dy, dr, r0dr, r02, a, a2, b, c, e, es, s, s0, s1, rs0, rs1, t;
- GBool aIsZero, go;
- int ixMin, iyMin, ixMax, iyMax, bitmapWidth, bitmapHeight, nColors;
- int bxMin, byMin, bxMax, byMax;
- SplashClipResult clipRes;
- SplashColorMode srcMode;
- SplashBitmap *tBitmap;
- int nComps;
- int x, y, i;
- SplashColorPtr dataPtr;
- Guchar *alphaPtr;
- GfxColor color;
- SplashColorPtr sColors, sColor;
-
-
- // get the shading parameters
- shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
- t0 = shading->getDomain0();
- t1 = shading->getDomain1();
- ext0 = shading->getExtend0();
- ext1 = shading->getExtend1();
- h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
- enclosed = fabs(r1 - r0) >= h;
-
- // get clip region (in user space)
- state->getUserClipBBox(&uxMin, &uyMin, &uxMax, &uyMax);
- if (uxMin > uxMax || uyMin > uyMax) {
- return gTrue;
- }
+GBool SplashOutputDev::shadedFill(GfxState *state, GfxShading *shading) {
- // intersect with shading region (in user space): if the extend
- // flags are false (or just the larger extend flag is false, in the
- // "enclosed" case), we can use the bbox for the two circles
- if ((!ext0 && !ext1) ||
- (enclosed && !(r0 > r1 ? ext0 : ext1))) {
- tx = (x0 - r0) < (x1 - r1) ? (x0 - r0) : (x1 - r1);
- if (tx > uxMin) {
- uxMin = tx;
- }
- tx = (x0 + r0) > (x1 + r1) ? (x0 + r0) : (x1 + r1);
- if (tx < uxMax) {
- uxMax = tx;
- }
- ty = (y0 - r0) < (y1 - r1) ? (y0 - r0) : (y1 - r1);
- if (ty > uyMin) {
- uyMin = ty;
- }
- ty = (y0 + r0) > (y1 + r1) ? (y0 + r0) : (y1 + r1);
- if (ty < uyMax) {
- uyMax = ty;
- }
- }
- if (uxMin > uxMax || uyMin > uyMax) {
- return gTrue;
- }
-
- // convert the region to device space
- ctm = state->getCTM();
- tx = uxMin * ctm[0] + uyMin * ctm[2] + ctm[4];
- ty = uxMin * ctm[1] + uyMin * ctm[3] + ctm[5];
- xMin = xMax = tx;
- yMin = yMax = ty;
- tx = uxMin * ctm[0] + uyMax * ctm[2] + ctm[4];
- ty = uxMin * ctm[1] + uyMax * ctm[3] + ctm[5];
- if (tx < xMin) {
- xMin = tx;
- } else if (tx > xMax) {
- xMax = tx;
- }
- if (ty < yMin) {
- yMin = ty;
- } else if (ty > yMax) {
- yMax = ty;
- }
- tx = uxMax * ctm[0] + uyMin * ctm[2] + ctm[4];
- ty = uxMax * ctm[1] + uyMin * ctm[3] + ctm[5];
- if (tx < xMin) {
- xMin = tx;
- } else if (tx > xMax) {
- xMax = tx;
- }
- if (ty < yMin) {
- yMin = ty;
- } else if (ty > yMax) {
- yMax = ty;
- }
- tx = uxMax * ctm[0] + uyMax * ctm[2] + ctm[4];
- ty = uxMax * ctm[1] + uyMax * ctm[3] + ctm[5];
- if (tx < xMin) {
- xMin = tx;
- } else if (tx > xMax) {
- xMax = tx;
- }
- if (ty < yMin) {
- yMin = ty;
- } else if (ty > yMax) {
- yMax = ty;
- }
- ixMin = (int)floor(xMin);
- iyMin = (int)floor(yMin);
- ixMax = (int)floor(xMax) + 1;
- iyMax = (int)floor(yMax) + 1;
- clipRes = splash->limitRectToClipRect(&ixMin, &iyMin, &ixMax, &iyMax);
- if (clipRes == splashClipAllOutside) {
- return gTrue;
- }
-
- // allocate a bitmap
+ // generate the bitmap
+ SplashColorMode srcMode;
if (colorMode == splashModeMono1) {
srcMode = splashModeMono8;
} else if (colorMode == splashModeBGR8) {
@@ -2440,287 +2097,36 @@ GBool SplashOutputDev::radialShadedFill(GfxState *state,
} else {
srcMode = colorMode;
}
- bitmapWidth = ixMax - ixMin;
- bitmapHeight = iyMax - iyMin;
- tBitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1,
- srcMode, gTrue, gTrue, bitmap);
- memset(tBitmap->getAlphaPtr(), 0, tBitmap->getAlphaRowSize() * bitmapHeight);
- nComps = splashColorModeNComps[srcMode];
-
- // compute the inverse CTM
- det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
- ictm[0] = ctm[3] * det;
- ictm[1] = -ctm[1] * det;
- ictm[2] = -ctm[2] * det;
- ictm[3] = ctm[0] * det;
- ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
- ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
-
- // pre-compute colors along the axis
- nColors = (int)sqrt((double)(bitmapWidth * bitmapWidth
- + bitmapHeight * bitmapHeight));
- if (nColors < 16) {
- nColors = 16;
- } else if (nColors > 1024) {
- nColors = 1024;
- }
- sColors = (SplashColorPtr)gmallocn(nColors, nComps);
- sColor = sColors;
- for (i = 0; i < nColors; ++i) {
- s = (double)i / (double)(nColors - 1);
- t = t0 + s * (t1 - t0);
- shading->getColor(t, &color);
- computeShadingColor(state, srcMode, &color, sColor);
- sColor += nComps;
- }
-
- // special case: in the "enclosed" + extended case, we can fill the
- // bitmap with the outer color and just render inside the larger
- // circle
- if (enclosed &&
- ((r0 > r1 && ext0) || (r1 > r0 && ext1))) {
- if (r0 > r1) {
- sColor = sColors;
- uxMin = x0 - r0;
- uxMax = x0 + r0;
- uyMin = y0 - r0;
- uyMax = y0 + r0;
- } else {
- sColor = sColors + (nColors - 1) * nComps;
- uxMin = x1 - r1;
- uxMax = x1 + r1;
- uyMin = y1 - r1;
- uyMax = y1 + r1;
- }
-
- // convert bbox of larger circle to device space
- tx = uxMin * ctm[0] + uyMin * ctm[2] + ctm[4];
- ty = uxMin * ctm[1] + uyMin * ctm[3] + ctm[5];
- xMin = xMax = tx;
- yMin = yMax = ty;
- tx = uxMin * ctm[0] + uyMax * ctm[2] + ctm[4];
- ty = uxMin * ctm[1] + uyMax * ctm[3] + ctm[5];
- if (tx < xMin) {
- xMin = tx;
- } else if (tx > xMax) {
- xMax = tx;
- }
- if (ty < yMin) {
- yMin = ty;
- } else if (ty > yMax) {
- yMax = ty;
- }
- tx = uxMax * ctm[0] + uyMin * ctm[2] + ctm[4];
- ty = uxMax * ctm[1] + uyMin * ctm[3] + ctm[5];
- if (tx < xMin) {
- xMin = tx;
- } else if (tx > xMax) {
- xMax = tx;
- }
- if (ty < yMin) {
- yMin = ty;
- } else if (ty > yMax) {
- yMax = ty;
- }
- tx = uxMax * ctm[0] + uyMax * ctm[2] + ctm[4];
- ty = uxMax * ctm[1] + uyMax * ctm[3] + ctm[5];
- if (tx < xMin) {
- xMin = tx;
- } else if (tx > xMax) {
- xMax = tx;
- }
- if (ty < yMin) {
- yMin = ty;
- } else if (ty > yMax) {
- yMax = ty;
- }
- bxMin = (int)floor(xMin - ixMin);
- if (bxMin < 0) {
- bxMin = 0;
- }
- byMin = (int)floor(yMin - iyMin);
- if (byMin < 0) {
- byMin = 0;
- }
- bxMax = (int)floor(xMax - ixMin) + 1;
- if (bxMax > bitmapWidth) {
- bxMax = bitmapWidth;
- }
- byMax = (int)floor(yMax - iyMin) + 1;
- if (byMax > bitmapHeight) {
- byMax = bitmapHeight;
- }
-
- // fill bitmap (except for the rectangle containing the larger circle)
- dataPtr = tBitmap->getDataPtr();
- alphaPtr = tBitmap->getAlphaPtr();
- for (y = 0; y < bitmapHeight; ++y) {
- for (x = 0; x < bitmapWidth; ++x) {
- if (y >= byMin && y < byMax && x >= bxMin && x < bxMax) {
- dataPtr += nComps;
- ++alphaPtr;
- } else {
- for (i = 0; i < nComps; ++i) {
- *dataPtr++ = sColor[i];
- }
- *alphaPtr++ = 0xff;
- }
- }
- }
-
- } else {
- bxMin = 0;
- byMin = 0;
- bxMax = bitmapWidth;
- byMax = bitmapHeight;
- }
-
- // render the shading into the bitmap
- dx = x1 - x0;
- dy = y1 - y0;
- dr = r1 - r0;
- r0dr = r0 * dr;
- r02 = r0 * r0;
- a = dx * dx + dy * dy - dr * dr;
- if (fabs(a) < 0.00001) {
- aIsZero = gTrue;
- a2 = 0;
- } else {
- aIsZero = gFalse;
- a2 = 1 / (2 * a);
- }
- for (y = byMin; y < byMax; ++y) {
- dataPtr = tBitmap->getDataPtr()
- + y * tBitmap->getRowSize() + bxMin * nComps;
- alphaPtr = tBitmap->getAlphaPtr() + y * tBitmap->getAlphaRowSize() + bxMin;
- for (x = bxMin; x < bxMax; ++x) {
-
- // convert coords to user space
- tx = ixMin + x + 0.5;
- ty = iyMin + y + 0.5;
- xx = tx * ictm[0] + ty * ictm[2] + ictm[4];
- yy = tx * ictm[1] + ty * ictm[3] + ictm[5];
-
- // compute the radius of the circle at x,y
- b = 2 * ((xx - x0) * dx + (yy - y0) * dy + r0dr);
- c = (xx - x0) * (xx - x0) + (yy - y0) * (yy - y0) - r02;
- s = 0;
- go = gFalse;
- if (aIsZero) {
- if (fabs(b) < 0.000001) {
- if (c <= 0) {
- if (ext0) {
- s = 0;
- go = gTrue;
- }
- } else {
- if (ext1) {
- s = 1;
- go = gTrue;
- }
- }
- } else {
- s0 = c / b;
- rs0 = r0 + s0 * (r1 - r0);
- if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) {
- s = s0;
- go = gTrue;
- }
- }
- } else {
- e = b*b - 4*a*c;
- if (e >= 0) {
- es = sqrt(e);
- s0 = (b + es) * a2;
- s1 = (b - es) * a2;
- rs0 = r0 + s0 * (r1 - r0);
- rs1 = r0 + s1 * (r1 - r0);
- if (s0 > s1) {
- if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) {
- s = s0;
- go = gTrue;
- } else if ((s1 >= 0 || ext0) && (s1 <= 1 || ext1) && rs1 >= 0) {
- s = s1;
- go = gTrue;
- }
- } else {
- if ((s1 >= 0 || ext0) && (s1 <= 1 || ext1) && rs1 >= 0) {
- s = s1;
- go = gTrue;
- } else if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) {
- s = s0;
- go = gTrue;
- }
- }
- }
- }
- if (!go) {
- dataPtr += nComps;
- ++alphaPtr;
- continue;
- }
- if (s <= 0) {
- sColor = sColors;
- } else if (s >= 1) {
- sColor = sColors + (nColors - 1) * nComps;
- } else {
- i = (int)((nColors - 1) * s + 0.5);
- sColor = sColors + i * nComps;
- }
- for (i = 0; i < nComps; ++i) {
- *dataPtr++ = sColor[i];
- }
- *alphaPtr++ = 0xff;
- }
+ int x, y;
+ SplashBitmap *tBitmap = ShadingImage::generateBitmap(state, shading, srcMode,
+ reverseVideo,
+ splash, bitmap, &x, &y);
+ if (!tBitmap) {
+ // clip region is empty - nothing to draw
+ return gTrue;
}
- // composite the bitmap
- setOverprintMask(state, state->getFillColorSpace(),
- state->getFillOverprint(), state->getOverprintMode(),
- NULL);
- splash->composite(tBitmap, 0, 0, ixMin, iyMin, bitmapWidth, bitmapHeight,
- clipRes == splashClipAllInside, gFalse);
+ // check clipping and composite the bitmap
+ int xMin = x;
+ int yMin = y;
+ int xMax = x + tBitmap->getWidth();
+ int yMax = y + tBitmap->getHeight();
+ SplashClipResult clipRes = splash->limitRectToClipRect(&xMin, &yMin,
+ &xMax, &yMax);
+ if (clipRes != splashClipAllOutside) {
+ setOverprintMask(state, state->getFillColorSpace(),
+ state->getFillOverprint(), state->getOverprintMode(),
+ NULL);
+ splash->composite(tBitmap, xMin - x, yMin - y, xMin, yMin,
+ xMax - xMin, yMax - yMin,
+ clipRes == splashClipAllInside, gFalse);
+ }
- gfree(sColors);
delete tBitmap;
return gTrue;
}
-void SplashOutputDev::computeShadingColor(GfxState *state,
- SplashColorMode mode,
- GfxColor *color,
- SplashColorPtr sColor) {
- GfxGray gray;
- GfxRGB rgb;
-#if SPLASH_CMYK
- GfxCMYK cmyk;
-#endif
-
- state->setFillColor(color);
- switch (mode) {
- case splashModeMono8:
- state->getFillGray(&gray);
- getColor(gray, sColor);
- break;
- case splashModeRGB8:
- state->getFillRGB(&rgb);
- getColor(&rgb, sColor);
- break;
-#if SPLASH_CMYK
- case splashModeCMYK8:
- state->getFillCMYK(&cmyk);
- getColor(&cmyk, sColor);
- break;
-#endif
- case splashModeMono1:
- case splashModeBGR8:
- // mode cannot be Mono1 or BGR8
- break;
- }
-}
-
-
void SplashOutputDev::clip(GfxState *state) {
SplashPath *path;
@@ -2799,6 +2205,7 @@ void SplashOutputDev::drawChar(GfxState *state, double x, double y,
if (skipHorizText || skipRotatedText) {
state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
+ // this matches the 'diagonal' test in TextPage::updateFont()
horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
fabs(m[2]) < 0.001 && m[3] < 0;
if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
@@ -2924,7 +2331,7 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
}
if (!(gfxFont = state->getFont())) {
- return gFalse;
+ return gTrue;
}
fontID = gfxFont->getID();
ctm = state->getCTM();
@@ -2954,7 +2361,7 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
}
} else {
for (j = nT3Fonts - 1; j >= 0; --j) {
- if (!t3FontCache[j]->inUse) {
+ if (t3FontCache[j]->refCount == 0) {
break;
}
}
@@ -3038,7 +2445,11 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
}
}
- t3Font->inUse = gTrue;
+ if (t3Font->refCount > 1000) {
+ error(errSyntaxError, -1, "Type 3 CharProcs nested too deeply");
+ return gTrue;
+ }
+ ++t3Font->refCount;
// push a new Type 3 glyph record
t3gs = new T3GlyphStack();
@@ -3050,6 +2461,10 @@ GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
t3GlyphStack->cacheData = NULL;
t3GlyphStack->haveDx = gFalse;
t3GlyphStack->doNotCache = gFalse;
+#if 1 //~t3-sa
+ t3GlyphStack->savedStrokeAdjust = splash->getStrokeAdjust();
+ splash->setStrokeAdjust(splashStrokeAdjustOff);
+#endif
return gFalse;
}
@@ -3074,9 +2489,12 @@ void SplashOutputDev::endType3Char(GfxState *state) {
drawType3Glyph(state, t3GlyphStack->cache,
t3GlyphStack->cacheTag, t3GlyphStack->cacheData);
}
+#if 1 //~t3-sa
+ splash->setStrokeAdjust(t3GlyphStack->savedStrokeAdjust);
+#endif
t3gs = t3GlyphStack;
t3GlyphStack = t3gs->next;
- t3gs->cache->inUse = gFalse;
+ --t3gs->cache->refCount;
delete t3gs;
}
@@ -3186,6 +2604,7 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
// create the temporary bitmap
if (colorMode == splashModeMono1) {
colorMode = splashModeMono1;
+ traceMessage("T3 glyph bitmap");
bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
splashModeMono1, gFalse, gTrue, bitmap);
splash = new Splash(bitmap, gFalse,
@@ -3196,6 +2615,7 @@ void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
color[0] = 0xff;
} else {
colorMode = splashModeMono8;
+ traceMessage("T3 glyph bitmap");
bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
splashModeMono8, gFalse, gTrue, bitmap);
splash = new Splash(bitmap, vectorAntialias,
@@ -3299,7 +2719,7 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
imgMaskData.height = height;
imgMaskData.y = 0;
- imgTag = makeImageTag(ref);
+ imgTag = makeImageTag(ref, gfxRenderingIntentRelativeColorimetric, NULL);
splash->fillImageMask(imgTag,
&imageMaskSrc, &imgMaskData, width, height, mat,
t3GlyphStack != NULL, interpolate);
@@ -3344,6 +2764,7 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
imgMaskData.width = width;
imgMaskData.height = height;
imgMaskData.y = 0;
+ traceMessage("image mask soft mask bitmap");
maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
1, splashModeMono8, gFalse, gTrue, bitmap);
maskSplash = new Splash(maskBitmap, gTrue, splash->getImageCache());
@@ -3354,7 +2775,7 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
maskColor[0] = 0xff;
maskSplash->setFillPattern(new SplashSolidColor(maskColor));
- imgTag = makeImageTag(ref);
+ imgTag = makeImageTag(ref, gfxRenderingIntentRelativeColorimetric, NULL);
maskSplash->fillImageMask(imgTag, &imageMaskSrc, &imgMaskData,
width, height, mat, gFalse, interpolate);
delete imgTag;
@@ -3643,7 +3064,8 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
srcMode = colorMode;
}
src = maskColors ? &alphaImageSrc : &imageSrc;
- imgTag = makeImageTag(ref);
+ imgTag = makeImageTag(ref, state->getRenderingIntent(),
+ colorMap->getColorSpace());
splash->drawImage(imgTag,
src, &imgData, srcMode, maskColors ? gTrue : gFalse,
width, height, mat, interpolate);
@@ -3775,9 +3197,9 @@ GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
Stream *str, int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr, int maskWidth,
- int maskHeight, GBool maskInvert,
- GBool interpolate) {
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GBool maskInvert, GBool interpolate) {
GfxImageColorMap *maskColorMap;
Object maskDecode, decodeLow, decodeHigh;
double *ctm;
@@ -3817,7 +3239,7 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
new GfxDeviceGrayColorSpace());
maskDecode.free();
drawSoftMaskedImage(state, ref, str, width, height, colorMap,
- maskStr, maskWidth, maskHeight, maskColorMap,
+ maskRef, maskStr, maskWidth, maskHeight, maskColorMap,
NULL, interpolate);
delete maskColorMap;
@@ -3837,6 +3259,7 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
imgMaskData.width = maskWidth;
imgMaskData.height = maskHeight;
imgMaskData.y = 0;
+ traceMessage("masked image bitmap");
maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1,
gFalse, gTrue, bitmap);
maskSplash = new Splash(maskBitmap, gFalse, splash->getImageCache());
@@ -3931,7 +3354,8 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
} else {
srcMode = colorMode;
}
- imgTag = makeImageTag(ref);
+ imgTag = makeImageTag(ref, state->getRenderingIntent(),
+ colorMap->getColorSpace());
splash->drawImage(imgTag,
&maskedImageSrc, &imgData, srcMode, gTrue,
width, height, mat, interpolate);
@@ -4055,7 +3479,7 @@ GBool SplashOutputDev::softMaskMatteImageSrc(void *data,
void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
Stream *str, int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GfxImageColorMap *maskColorMap,
double *matte, GBool interpolate) {
@@ -4147,18 +3571,13 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
#endif
}
//~ could add the matteImgData.lookup special case
- if (colorMap->getBits() <= 8) {
- n = 1 << maskColorMap->getBits();
- } else {
- // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit
- n = 1 << 8;
- }
matteImgData.colorMode = colorMode;
matteImgData.invert = reverseVideo && reverseVideoInvertImages;
matteImgData.width = width;
matteImgData.height = height;
matteImgData.y = 0;
- imgTag = makeImageTag(ref);
+ imgTag = makeImageTag(ref, state->getRenderingIntent(),
+ colorMap->getColorSpace());
splash->drawImage(imgTag, &softMaskMatteImageSrc, &matteImgData,
srcMode, gTrue, width, height, mat, interpolate);
delete imgTag;
@@ -4186,13 +3605,19 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
imgMaskData.width = maskWidth;
imgMaskData.height = maskHeight;
imgMaskData.y = 0;
- n = 1 << maskColorMap->getBits();
+ if (maskColorMap->getBits() <= 8) {
+ n = 1 << maskColorMap->getBits();
+ } else {
+ // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit
+ n = 1 << 8;
+ }
imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
for (i = 0; i < n; ++i) {
pix = (Guchar)i;
maskColorMap->getGray(&pix, &gray, state->getRenderingIntent());
imgMaskData.lookup[i] = colToByte(gray);
}
+ traceMessage("soft masked image bitmap");
maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
1, splashModeMono8, gFalse, gTrue, bitmap);
maskSplash = new Splash(maskBitmap, vectorAntialias,
@@ -4273,7 +3698,8 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
}
}
- imgTag = makeImageTag(ref);
+ imgTag = makeImageTag(ref, state->getRenderingIntent(),
+ colorMap->getColorSpace());
splash->drawImage(imgTag,
&imageSrc, &imgData, srcMode, gFalse, width, height, mat,
interpolate);
@@ -4288,11 +3714,14 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
}
}
-GString *SplashOutputDev::makeImageTag(Object *ref) {
- if (!ref || !ref->isRef()) {
+GString *SplashOutputDev::makeImageTag(Object *ref, GfxRenderingIntent ri,
+ GfxColorSpace *colorSpace) {
+ if (!ref || !ref->isRef() ||
+ (colorSpace && colorSpace->isDefaultColorSpace())) {
return NULL;
}
- return GString::format("{0:d}_{1:d}", ref->getRefNum(), ref->getRefGen());
+ return GString::format("{0:d}_{1:d}_{2:d}",
+ ref->getRefNum(), ref->getRefGen(), (int)ri);
}
void SplashOutputDev::reduceImageResolution(Stream *str, double *ctm,
@@ -4471,14 +3900,16 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
ty = bh - 1;
}
w = (int)ceil(xMax) - tx + 1;
- if (tx + w > bw) {
+ // NB bw and tx are both non-negative, so 'bw - tx' can't overflow
+ if (bw - tx < w) {
w = bw - tx;
}
if (w < 1) {
w = 1;
}
h = (int)ceil(yMax) - ty + 1;
- if (ty + h > bh) {
+ // NB bh and ty are both non-negative, so 'bh - ty' can't overflow
+ if (bh - ty < h) {
h = bh - ty;
}
if (h < 1) {
@@ -4524,6 +3955,7 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
}
// create the temporary bitmap
+ traceMessage("t-group bitmap");
bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
bitmapTopDown, transpGroup->origBitmap);
splash = new Splash(bitmap, vectorAntialias,
@@ -4535,32 +3967,70 @@ void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
splash->setEnablePathSimplification(
globalParams->getEnablePathSimplification());
copyState(transpGroup->origSplash, gTrue);
+ if (!isolated || knockout) {
+ // non-isolated and knockout groups nested in another group will
+ // read the parent group bitmap, so we need to force any deferred
+ // initialization on the parent
+ transpGroup->origSplash->forceDeferredInit(ty, h);
+ }
if (isolated) {
- for (i = 0; i < splashMaxColorComps; ++i) {
- color[i] = 0;
+ // isolated group
+ backdropBitmap = transpGroup->origBitmap;
+ transpGroup->backdropBitmap = NULL;
+ if (forSoftMask) {
+ // setSoftMask uses the whole bitmap, not just the mod region,
+ // so we can't use the deferred initialization optimization
+ for (i = 0; i < splashMaxColorComps; ++i) {
+ color[i] = 0;
+ }
+ splash->clear(color, 0);
+ splash->setInTransparencyGroup(backdropBitmap, tx, ty,
+ splashGroupDestPreInit,
+ gFalse, knockout);
+ } else {
+ splash->setInTransparencyGroup(backdropBitmap, tx, ty,
+ splashGroupDestInitZero,
+ gFalse, knockout);
}
- splash->clear(color, 0);
- } else {
- splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
- }
- if (!isolated &&
- transpGroup->origBitmap->getAlphaPtr() &&
- transpGroup->origSplash->getInNonIsolatedGroup() &&
- colorMode != splashModeMono1) {
- // when drawing a non-isolated group into another non-isolated group,
+ } else if (transpGroup->origBitmap->getAlphaPtr() &&
+ transpGroup->origSplash->getInNonIsolatedGroup() &&
+ colorMode != splashModeMono1) {
+ // non-isolated group drawn in another non-isolated group:
// compute a backdrop bitmap with corrected alpha values
+ traceMessage("t-group backdrop bitmap");
backdropBitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
bitmapTopDown, transpGroup->origBitmap);
transpGroup->origSplash->blitCorrectedAlpha(backdropBitmap,
tx, ty, 0, 0, w, h);
transpGroup->backdropBitmap = backdropBitmap;
- splash->setInTransparencyGroup(backdropBitmap, 0, 0,
- !isolated, knockout);
+ if (forSoftMask) {
+ // setSoftMask uses the whole bitmap, not just the mod region,
+ // so we can't use the deferred initialization optimization
+ splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
+ splash->setInTransparencyGroup(backdropBitmap, 0, 0,
+ splashGroupDestPreInit,
+ gTrue, knockout);
+ } else {
+ splash->setInTransparencyGroup(backdropBitmap, 0, 0,
+ splashGroupDestInitCopy,
+ gTrue, knockout);
+ }
} else {
+ // other non-isolated group
backdropBitmap = transpGroup->origBitmap;
transpGroup->backdropBitmap = NULL;
- splash->setInTransparencyGroup(backdropBitmap, tx, ty,
- !isolated, knockout);
+ if (forSoftMask) {
+ // setSoftMask uses the whole bitmap, not just the mod region,
+ // so we can't use the deferred initialization optimization
+ splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
+ splash->setInTransparencyGroup(backdropBitmap, tx, ty,
+ splashGroupDestPreInit,
+ gTrue, knockout);
+ } else {
+ splash->setInTransparencyGroup(backdropBitmap, tx, ty,
+ splashGroupDestInitCopy,
+ gTrue, knockout);
+ }
}
splash->clearModRegion();
transpGroup->tBitmap = bitmap;
@@ -4632,14 +4102,17 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
Splash *tSplash;
SplashTransparencyGroup *transpGroup;
SplashColor color;
- SplashColorPtr p;
+ SplashColorPtr p, colorPtr, colorPtr2;
+ Guchar *alphaPtr;
GfxGray gray;
GfxRGB rgb;
#if SPLASH_CMYK
GfxCMYK cmyk;
#endif
double backdrop, backdrop2, lum, lum2;
- int tx, ty, x, y;
+ Guchar lum8;
+ SplashBitmapRowSize rowSize;
+ int tw, th, tNComps, tx, ty, x, y;
tx = transpGroupStack->tx;
ty = transpGroupStack->ty;
@@ -4709,51 +4182,72 @@ void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
backdrop2 = backdrop;
}
+ traceMessage("soft mask bitmap");
softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
1, splashModeMono8, gFalse, gTrue, bitmap);
memset(softMask->getDataPtr(), (int)(backdrop2 * 255.0 + 0.5),
softMask->getRowSize() * softMask->getHeight());
if (tx < softMask->getWidth() && ty < softMask->getHeight()) {
p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
- for (y = 0; y < tBitmap->getHeight(); ++y) {
- for (x = 0; x < tBitmap->getWidth(); ++x) {
- if (alpha) {
- lum = tBitmap->getAlpha(x, y) / 255.0;
- } else {
- tBitmap->getPixel(x, y, color);
+ tw = tBitmap->getWidth();
+ th = tBitmap->getHeight();
+ rowSize = softMask->getRowSize();
+ if (alpha) {
+ alphaPtr = tBitmap->getAlphaPtr();
+ for (y = 0; y < th; ++y) {
+ for (x = 0; x < tw; ++x) {
+ lum = *alphaPtr++ / 255.0;
+ if (transferFunc) {
+ transferFunc->transform(&lum, &lum2);
+ } else {
+ lum2 = lum;
+ }
+ p[x] = (Guchar)(lum2 * 255.0 + 0.5);
+ }
+ p += rowSize;
+ }
+ } else {
+ colorPtr = tBitmap->getDataPtr();
+ tNComps = splashColorModeNComps[tBitmap->getMode()];
+ lum8 = 0; // make gcc happy
+ for (y = 0; y < th; ++y) {
+ colorPtr2 = colorPtr;
+ for (x = 0; x < tw; ++x) {
// convert to luminosity
switch (tBitmap->getMode()) {
case splashModeMono1:
+ lum8 = 0;
+ break;
case splashModeMono8:
- lum = color[0] / 255.0;
+ lum8 = colorPtr2[0];
break;
case splashModeRGB8:
case splashModeBGR8:
- lum = (0.3 / 255.0) * color[0] +
- (0.59 / 255.0) * color[1] +
- (0.11 / 255.0) * color[2];
+ // [0.3, 0.59, 0.11] * 255 = [77, 150, 28]
+ lum8 = div255(77 * colorPtr2[0] +
+ 150 * colorPtr2[1] +
+ 28 * colorPtr2[1]);
break;
#if SPLASH_CMYK
case splashModeCMYK8:
- lum = (1 - color[3] / 255.0)
- - (0.3 / 255.0) * color[0]
- - (0.59 / 255.0) * color[1]
- - (0.11 / 255.0) * color[2];
- if (lum < 0) {
- lum = 0;
- }
+ lum8 = clip255(255 - colorPtr2[3]
+ - div255(77 * colorPtr2[0] +
+ 150 * colorPtr2[1] +
+ 28 * colorPtr2[2]));
break;
#endif
}
+ if (transferFunc) {
+ lum = lum8 / 255.0;
+ transferFunc->transform(&lum, &lum2);
+ lum8 = (Guchar)(lum2 * 255.0 + 0.5);
+ }
+ p[x] = lum8;
+ colorPtr2 += tNComps;
}
- if (transferFunc) {
- transferFunc->transform(&lum, &lum2);
- } else {
- lum2 = lum;
- }
- p[x] = (Guchar)(lum2 * 255.0 + 0.5);
+ p += rowSize;
+ colorPtr += tBitmap->getRowSize();
}
- p += softMask->getRowSize();
}
}
splash->setSoftMask(softMask);
@@ -4993,6 +4487,10 @@ void SplashOutputDev::copyState(Splash *oldSplash, GBool copyColors) {
}
#if 1 //~tmp: turn off anti-aliasing temporarily
+// This was originally used with gradient shadings -- that's no longer
+// necessary, now that shadings are all converted to device space
+// images. It's still used with knockout groups, however, because the
+// rasterizer doesn't properly separate opacity and shape.
void SplashOutputDev::setInShading(GBool sh) {
splash->setInShading(sh);
}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h
index 2215f883d32..cbe47b61cb7 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h
@@ -69,11 +69,6 @@ public:
// operations.
virtual GBool useTilingPatternFill() { return gTrue; }
- // Does this device use functionShadedFill(), axialShadedFill(), and
- // radialShadedFill()? If this returns false, these shaded fills
- // will be reduced to a series of other drawing operations.
- virtual GBool useShadedFills() { return gTrue; }
-
// Does this device use beginType3Char/endType3Char? Otherwise,
// text in Type 3 fonts will be drawn with drawChar/drawString.
virtual GBool interpretType3Chars() { return gTrue; }
@@ -123,8 +118,7 @@ public:
double *mat, double *bbox,
int x0, int y0, int x1, int y1,
double xStep, double yStep);
- virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading);
- virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading);
+ virtual GBool shadedFill(GfxState *state, GfxShading *shading);
//----- path clipping
virtual void clip(GfxState *state);
@@ -156,12 +150,13 @@ public:
virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr, int maskWidth, int maskHeight,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
GBool maskInvert, GBool interpolate);
virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GfxImageColorMap *maskColorMap,
double *matte, GBool interpolate);
@@ -240,10 +235,6 @@ public:
// Get the screen parameters.
SplashScreenParams *getScreenParams() { return &screenParams; }
-#if 1 //~tmp: turn off anti-aliasing temporarily
- virtual void setInShading(GBool sh);
-#endif
-
private:
void setupScreenParams(double hDPI, double vDPI);
@@ -260,10 +251,6 @@ private:
void setOverprintMask(GfxState *state, GfxColorSpace *colorSpace,
GBool overprintFlag, int overprintMode,
GfxColor *singleColor);
- void computeShadingColor(GfxState *state,
- SplashColorMode mode,
- GfxColor *color,
- SplashColorPtr sColor);
SplashPath *convertPath(GfxState *state, GfxPath *path,
GBool dropEmptySubpaths);
void doUpdateFont(GfxState *state);
@@ -279,7 +266,8 @@ private:
static GBool softMaskMatteImageSrc(void *data,
SplashColorPtr colorLine,
Guchar *alphaLine);
- GString *makeImageTag(Object *ref);
+ GString *makeImageTag(Object *ref, GfxRenderingIntent ri,
+ GfxColorSpace *colorSpace);
void reduceImageResolution(Stream *str, double *mat,
int *width, int *height);
void clearMaskRegion(GfxState *state,
@@ -287,6 +275,9 @@ private:
double xMin, double yMin,
double xMax, double yMax);
void copyState(Splash *oldSplash, GBool copyColors);
+#if 1 //~tmp: turn off anti-aliasing temporarily
+ void setInShading(GBool sh);
+#endif
SplashColorMode colorMode;
int bitmapRowPad;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc
index a7c13c9eab3..601add89be4 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc
@@ -1317,6 +1317,7 @@ void LZWStream::reset() {
eof = gFalse;
inputBits = 0;
clearTable();
+ totalIn = totalOut = 0;
}
GBool LZWStream::processNextCode() {
@@ -1382,6 +1383,14 @@ GBool LZWStream::processNextCode() {
nextBits = 12;
}
prevCode = code;
+ totalOut += seqLength;
+
+ // check for a 'decompression bomb'
+ if (totalOut > 50000000 && totalIn < totalOut / 250) {
+ error(errSyntaxError, getPos(), "Decompression bomb in flate stream");
+ eof = gTrue;
+ return gFalse;
+ }
// reset buffer
seqIndex = 0;
@@ -1405,6 +1414,7 @@ int LZWStream::getCode() {
return EOF;
inputBuf = (inputBuf << 8) | (c & 0xff);
inputBits += 8;
+ ++totalIn;
}
code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1);
inputBits -= nextBits;
@@ -2268,7 +2278,6 @@ short CCITTFaxStream::lookBits(int n) {
GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent,
GBool okToReadStream) {
GString *s;
- char s1[50];
if (psLevel < 2) {
return NULL;
@@ -2278,8 +2287,7 @@ GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent,
}
s->append(indent)->append("<< ");
if (encoding != 0) {
- sprintf(s1, "/K %d ", encoding);
- s->append(s1);
+ s->appendf("/K {0:d} ", encoding);
}
if (endOfLine) {
s->append("/EndOfLine true ");
@@ -2287,11 +2295,9 @@ GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent,
if (byteAlign) {
s->append("/EncodedByteAlign true ");
}
- sprintf(s1, "/Columns %d ", columns);
- s->append(s1);
+ s->appendf("/Columns {0:d} ", columns);
if (rows != 0) {
- sprintf(s1, "/Rows %d ", rows);
- s->append(s1);
+ s->appendf("/Rows {0:d} ", rows);
}
if (!endOfBlock) {
s->append("/EndOfBlock false ");
@@ -4059,7 +4065,7 @@ GBool DCTStream::readQuantTables() {
error(errSyntaxError, getPos(), "Bad DCT quantization table");
return gFalse;
}
- if (index == numQuantTables) {
+ if (index >= numQuantTables) {
numQuantTables = index + 1;
}
for (i = 0; i < 64; ++i) {
@@ -4951,6 +4957,8 @@ void FlateStream::reset() {
endOfBlock = eof = gTrue;
cmf = str->getChar();
flg = str->getChar();
+ totalIn = 2;
+ totalOut = 0;
if (cmf == EOF || flg == EOF)
return;
if ((cmf & 0x0f) != 0x08) {
@@ -5017,7 +5025,7 @@ int FlateStream::getRawChar() {
}
int FlateStream::getBlock(char *blk, int size) {
- int n;
+ int n, k;
if (pred) {
return pred->getBlock(blk, size);
@@ -5031,11 +5039,17 @@ int FlateStream::getBlock(char *blk, int size) {
}
readSome();
}
- while (remain && n < size) {
- blk[n++] = buf[index];
- index = (index + 1) & flateMask;
- --remain;
+ k = remain;
+ if (size - n < k) {
+ k = size - n;
+ }
+ if (flateWindow - index < k) {
+ k = flateWindow - index;
}
+ memcpy(blk + n, buf + index, k);
+ n += k;
+ index = (index + k) & flateMask;
+ remain -= k;
}
return n;
}
@@ -5061,7 +5075,7 @@ GBool FlateStream::isBinary(GBool last) {
void FlateStream::readSome() {
int code1, code2;
int len, dist;
- int i, j, k;
+ int src, dest, n1, n2, n3, i, j, k;
int c;
if (endOfBlock) {
@@ -5090,12 +5104,78 @@ void FlateStream::readSome() {
if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF)
goto err;
dist = distDecode[code1].first + code2;
- i = index;
- j = (index - dist) & flateMask;
- for (k = 0; k < len; ++k) {
- buf[i] = buf[j];
- i = (i + 1) & flateMask;
- j = (j + 1) & flateMask;
+ dest = index;
+ src = (index - dist) & flateMask;
+ // the following is an optimized version of:
+ // for (k = 0; k < len; ++k) {
+ // buf[dest] = buf[src];
+ // dest = (dest + 1) & flateMask;
+ // src = (src + 1) & flateMask;
+ // }
+ if (dest + len <= flateWindow) {
+ if (src + len <= flateWindow) {
+ for (k = 0; k < len; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ } else {
+ n1 = flateWindow - src;
+ n2 = len - n1;
+ for (k = 0; k < n1; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ dest = dest + n1;
+ src = 0;
+ for (k = 0; k < n2; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ }
+ } else {
+ if (src + len <= flateWindow) {
+ n1 = flateWindow - dest;
+ n2 = len - n1;
+ for (k = 0; k < n1; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ dest = 0;
+ src = src + n1;
+ for (k = 0; k < n2; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ } else if (src < dest) {
+ n1 = flateWindow - dest;
+ n2 = dest - src;
+ n3 = len - n1 - n2;
+ for (k = 0; k < n1; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ dest = 0;
+ src = src + n1;
+ for (k = 0; k < n2; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ dest = n2;
+ src = 0;
+ for (k = 0; k < n3; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ } else {
+ n1 = flateWindow - src;
+ n2 = src - dest;
+ n3 = len - n1 - n2;
+ for (k = 0; k < n1; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ dest = dest + n1;
+ src = 0;
+ for (k = 0; k < n2; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ dest = 0;
+ src = n2;
+ for (k = 0; k < n3; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ }
}
remain = len;
}
@@ -5113,6 +5193,15 @@ void FlateStream::readSome() {
blockLen -= len;
if (blockLen == 0)
endOfBlock = gTrue;
+ totalIn += remain;
+ }
+ totalOut += remain;
+
+ // check for a 'decompression bomb'
+ if (totalOut > 50000000 && totalIn < totalOut / 250) {
+ error(errSyntaxError, getPos(), "Decompression bomb in flate stream");
+ endOfBlock = eof = gTrue;
+ remain = 0;
}
return;
@@ -5164,6 +5253,7 @@ GBool FlateStream::startBlock() {
"Bad uncompressed block length in flate stream");
codeBuf = 0;
codeSize = 0;
+ totalIn += 4;
// compressed block with fixed codes
} else if (blockHdr == 1) {
@@ -5358,6 +5448,7 @@ int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) {
}
codeBuf |= (c & 0xff) << codeSize;
codeSize += 8;
+ ++totalIn;
}
code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)];
if (codeSize == 0 || codeSize < code->len || code->len == 0) {
@@ -5376,6 +5467,7 @@ int FlateStream::getCodeWord(int bits) {
return EOF;
codeBuf |= (c & 0xff) << codeSize;
codeSize += 8;
+ ++totalIn;
}
c = codeBuf & ((1 << bits) - 1);
codeBuf >>= bits;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h
index 38a8dd3497c..59cafadc14f 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h
@@ -501,6 +501,8 @@ private:
int seqLength; // length of current sequence
int seqIndex; // index into current sequence
GBool first; // first code after a table clear
+ unsigned long long totalIn; // total number of encoded bytes read so far
+ unsigned long long totalOut; // total number of bytes decoded so far
GBool processNextCode();
void clearTable();
@@ -814,6 +816,8 @@ private:
int blockLen; // remaining length of uncompressed block
GBool endOfBlock; // set when end of block is reached
GBool eof; // set when end of stream is reached
+ unsigned long long totalIn; // total number of encoded bytes read so far
+ unsigned long long totalOut; // total number of bytes decoded so far
static int // code length code reordering
codeLenCodeMap[flateMaxCodeLenCodes];
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc
index 4f594384915..9ec906c7379 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc
@@ -26,6 +26,7 @@
#include "gmempp.h"
#include "GString.h"
#include "GList.h"
+#include "gfile.h"
#include "config.h"
#include "Error.h"
#include "GlobalParams.h"
@@ -88,11 +89,13 @@
// Inter-character spacing that varies by less than this multiple of
// font size is assumed to be equivalent.
#define uniformSpacing 0.07
+#define tableModeUniformSpacing 0.14
// Typical word spacing, as a fraction of font size. This will be
// added to the minimum inter-character spacing, to account for wide
// character spacing.
#define wordSpacing 0.1
+#define tableModeWordSpacing 0.2
// Minimum paragraph indent from left margin, as a fraction of font
// size.
@@ -114,6 +117,9 @@
// value multiplied by font size.
#define simpleLayoutGapThreshold 0.7
+// Minimum overlap in simple2 mode.
+#define simple2MinOverlap 0.2
+
// Table cells (TextColumns) are allowed to overlap by this much
// in table layout mode (as a fraction of cell width or height).
#define tableCellOverlapSlack 0.05
@@ -157,9 +163,28 @@
// rectangles, in order to work around flakey ascent values in fonts.
#define selectionAscent 0.8
+// Grid size used to bin sort characters for overlap detection.
+#define overlapGridWidth 20
+#define overlapGridHeight 20
+
+// Minimum character bbox overlap (horizontal and vertical) as a
+// fraction of character bbox width/height for a character to be
+// treated as overlapping.
+#define minCharOverlap 0.3
+
#define maxUnicodeLen 16
//------------------------------------------------------------------------
+
+static inline double dmin(double x, double y) {
+ return x < y ? x : y;
+}
+
+static inline double dmax(double x, double y) {
+ return x > y ? x : y;
+}
+
+//------------------------------------------------------------------------
// TextChar
//------------------------------------------------------------------------
@@ -168,31 +193,36 @@ public:
TextChar(Unicode cA, int charPosA, int charLenA,
double xMinA, double yMinA, double xMaxA, double yMaxA,
- int rotA, GBool clippedA, GBool invisibleA,
+ int rotA, GBool rotatedA, GBool clippedA, GBool invisibleA,
TextFontInfo *fontA, double fontSizeA,
double colorRA, double colorGA, double colorBA);
static int cmpX(const void *p1, const void *p2);
static int cmpY(const void *p1, const void *p2);
+ static int cmpCharPos(const void *p1, const void *p2);
Unicode c;
int charPos;
int charLen;
double xMin, yMin, xMax, yMax;
- Guchar rot;
- char clipped;
- char invisible;
- char spaceAfter;
TextFontInfo *font;
double fontSize;
double colorR,
colorG,
colorB;
+
+ // group the byte-size fields to minimize object size
+ Guchar rot;
+ char rotated;
+ char clipped;
+ char invisible;
+ char spaceAfter;
+ char overlap;
};
TextChar::TextChar(Unicode cA, int charPosA, int charLenA,
double xMinA, double yMinA, double xMaxA, double yMaxA,
- int rotA, GBool clippedA, GBool invisibleA,
+ int rotA, GBool rotatedA, GBool clippedA, GBool invisibleA,
TextFontInfo *fontA, double fontSizeA,
double colorRA, double colorGA, double colorBA) {
double t;
@@ -228,6 +258,7 @@ TextChar::TextChar(Unicode cA, int charPosA, int charLenA,
yMax = 1e8;
}
rot = (Guchar)rotA;
+ rotated = (char)rotatedA;
clipped = (char)clippedA;
invisible = (char)invisibleA;
spaceAfter = (char)gFalse;
@@ -236,6 +267,7 @@ TextChar::TextChar(Unicode cA, int charPosA, int charLenA,
colorR = colorRA;
colorG = colorGA;
colorB = colorBA;
+ overlap = gFalse;
}
int TextChar::cmpX(const void *p1, const void *p2) {
@@ -264,6 +296,12 @@ int TextChar::cmpY(const void *p1, const void *p2) {
}
}
+int TextChar::cmpCharPos(const void *p1, const void *p2) {
+ const TextChar *ch1 = *(const TextChar **)p1;
+ const TextChar *ch2 = *(const TextChar **)p2;
+ return ch1->charPos - ch2->charPos;
+}
+
//------------------------------------------------------------------------
// TextBlock
//------------------------------------------------------------------------
@@ -408,6 +446,40 @@ void TextBlock::updateBounds(int childIdx) {
}
//------------------------------------------------------------------------
+// TextCharLine
+//------------------------------------------------------------------------
+
+class TextCharLine {
+public:
+
+ TextCharLine(int rotA);
+ ~TextCharLine();
+ void add(TextChar *ch);
+
+ GList *chars;
+ double yMin, yMax;
+ int rot;
+ TextCharLine *next, *prev;
+};
+
+TextCharLine::TextCharLine(int rotA) {
+ chars = new GList();
+ yMin = yMax = 0;
+ rot = rotA;
+ next = prev = NULL;
+}
+
+TextCharLine::~TextCharLine() {
+ delete chars;
+}
+
+void TextCharLine::add(TextChar *ch) {
+ chars->append(ch);
+ yMin = ch->yMin;
+ yMax = ch->yMax;
+}
+
+//------------------------------------------------------------------------
// TextGaps
//------------------------------------------------------------------------
@@ -539,8 +611,12 @@ TextOutputControl::TextOutputControl() {
html = gFalse;
clipText = gFalse;
discardDiagonalText = gFalse;
+ discardRotatedText = gFalse;
discardInvisibleText = gFalse;
discardClippedText = gFalse;
+ splitRotatedWords = gFalse;
+ overlapHandling = textOutIgnoreOverlaps;
+ separateLargeChars = gTrue;
insertBOM = gFalse;
marginLeft = 0;
marginRight = 0;
@@ -602,13 +678,15 @@ TextFontInfo::~TextFontInfo() {
}
GBool TextFontInfo::matches(GfxState *state) {
- Ref *id;
+ Ref id;
- if (!state->getFont()) {
- return gFalse;
+ if (state->getFont()) {
+ id = *state->getFont()->getID();
+ } else {
+ id.num = -1;
+ id.gen = -1;
}
- id = state->getFont()->getID();
- return id->num == fontID.num && id->gen == fontID.gen;
+ return id.num == fontID.num && id.gen == fontID.gen;
}
//------------------------------------------------------------------------
@@ -618,11 +696,12 @@ GBool TextFontInfo::matches(GfxState *state) {
// Build a TextWord object, using chars[start .. start+len-1].
// (If rot >= 2, the chars list is in reverse order.)
TextWord::TextWord(GList *chars, int start, int lenA,
- int rotA, int dirA, GBool spaceAfterA) {
+ int rotA, GBool rotatedA, int dirA, GBool spaceAfterA) {
TextChar *ch;
int i;
- rot = rotA;
+ rot = (char)rotA;
+ rotated = (char)rotatedA;
len = lenA;
text = (Unicode *)gmallocn(len, sizeof(Unicode));
edge = (double *)gmallocn(len + 1, sizeof(double));
@@ -680,8 +759,8 @@ TextWord::TextWord(GList *chars, int start, int lenA,
ch = (TextChar *)chars->get(start);
font = ch->font;
fontSize = ch->fontSize;
- dir = dirA;
- spaceAfter = spaceAfterA;
+ dir = (char)dirA;
+ spaceAfter = (char)spaceAfterA;
underlined = gFalse;
link = NULL;
colorR = ch->colorR;
@@ -1057,6 +1136,7 @@ TextPage::TextPage(TextOutputControl *controlA) {
curFontSize = 0;
curRot = 0;
diagonal = gFalse;
+ rotated = gFalse;
nTinyChars = 0;
actualText = NULL;
actualTextLen = 0;
@@ -1108,6 +1188,7 @@ void TextPage::clear() {
curFontSize = 0;
curRot = 0;
diagonal = gFalse;
+ rotated = gFalse;
nTinyChars = 0;
gfree(actualText);
actualText = NULL;
@@ -1219,7 +1300,12 @@ void TextPage::updateFont(GfxState *state) {
m[2] = m2[2];
m[3] = m2[3];
}
- if (fabs(m[0]) >= fabs(m[1])) {
+ if (curFontSize == 0) {
+ // special case - if the font size is zero, just assume plain
+ // horizontal text
+ curRot = 0;
+ diagonal = gFalse;
+ } else if (fabs(m[0]) >= fabs(m[1])) {
if (m[0] > 0) {
curRot = 0;
} else {
@@ -1234,6 +1320,9 @@ void TextPage::updateFont(GfxState *state) {
}
diagonal = fabs(m[0]) > diagonalThreshold * fabs(m[1]);
}
+ // this matches the 'horiz' test in SplashOutputDev::drawChar()
+ rotated = !(m[0] > 0 && fabs(m[1]) < 0.001 &&
+ fabs(m[2]) < 0.001 && m[3] < 0);
}
void TextPage::addChar(GfxState *state, double x, double y,
@@ -1260,8 +1349,9 @@ void TextPage::addChar(GfxState *state, double x, double y,
return;
}
- // throw away diagonal chars
- if (control.discardDiagonalText && diagonal) {
+ // throw away diagonal/rotated chars
+ if ((control.discardDiagonalText && diagonal) ||
+ (control.discardRotatedText && rotated)) {
charPos += nBytes;
return;
}
@@ -1299,9 +1389,15 @@ void TextPage::addChar(GfxState *state, double x, double y,
}
// skip space, tab, and non-breaking space characters
- if (uLen == 1 && (u[0] == (Unicode)0x20 ||
- u[0] == (Unicode)0x09 ||
- u[0] == (Unicode)0xa0)) {
+ // (ActualText spans can result in multiple space chars)
+ for (i = 0; i < uLen; ++i) {
+ if (u[i] != (Unicode)0x20 &&
+ u[i] != (Unicode)0x09 &&
+ u[i] != (Unicode)0xa0) {
+ break;
+ }
+ }
+ if (i == uLen && uLen >= 1) {
charPos += nBytes;
if (chars->getLength() > 0) {
((TextChar *)chars->get(chars->getLength() - 1))->spaceAfter =
@@ -1400,7 +1496,7 @@ void TextPage::addChar(GfxState *state, double x, double y,
}
chars->append(new TextChar(uBuf[j], charPos, nBytes,
xMin, yMin, xMax, yMax,
- curRot, clipped,
+ curRot, rotated, clipped,
state->getRender() == 3 || alpha < 0.001,
curFont, curFontSize,
colToDbl(rgb.r), colToDbl(rgb.g),
@@ -1503,6 +1599,10 @@ void TextPage::write(void *outputStream, TextOutputFunc outputFunc) {
writeSimpleLayout(outputStream, outputFunc, uMap, space, spaceLen,
eol, eolLen);
break;
+ case textOutSimple2Layout:
+ writeSimple2Layout(outputStream, outputFunc, uMap, space, spaceLen,
+ eol, eolLen);
+ break;
case textOutLinePrinter:
writeLinePrinter(outputStream, outputFunc, uMap, space, spaceLen,
eol, eolLen);
@@ -1530,11 +1630,20 @@ void TextPage::writeReadingOrder(void *outputStream,
TextColumn *col;
TextParagraph *par;
TextLine *line;
+ GList *overlappingChars;
GList *columns;
GBool primaryLR;
GString *s;
int colIdx, parIdx, lineIdx, rot, n;
+#if 0 //~debug
+ dumpChars(chars);
+#endif
+ if (control.overlapHandling != textOutIgnoreOverlaps) {
+ overlappingChars = separateOverlappingText(chars);
+ } else {
+ overlappingChars = NULL;
+ }
rot = rotateChars(chars);
primaryLR = checkPrimaryLR(chars);
tree = splitChars(chars);
@@ -1553,6 +1662,12 @@ void TextPage::writeReadingOrder(void *outputStream,
rotateUnderlinesAndLinks(rot);
generateUnderlinesAndLinks(columns);
}
+ if (overlappingChars) {
+ if (overlappingChars->getLength() > 0) {
+ columns->append(buildOverlappingTextColumn(overlappingChars));
+ }
+ deleteGList(overlappingChars, TextChar);
+ }
#if 0 //~debug
dumpColumns(columns);
#endif
@@ -1585,23 +1700,43 @@ void TextPage::writeReadingOrder(void *outputStream,
GList *TextPage::makeColumns() {
TextBlock *tree;
+ GList *overlappingChars;
GList *columns;
GBool primaryLR;
int rot;
- rot = rotateChars(chars);
- primaryLR = checkPrimaryLR(chars);
- if ((tree = splitChars(chars))) {
- columns = buildColumns(tree, primaryLR);
- delete tree;
+ if (control.mode == textOutSimple2Layout) {
+ primaryLR = checkPrimaryLR(chars);
+ rotateCharsToZero(chars);
+ columns = buildSimple2Columns(chars);
+ unrotateCharsFromZero(chars);
+ unrotateColumnsFromZero(columns);
} else {
- // no text
- columns = new GList();
- }
- unrotateChars(chars, rot);
- unrotateColumns(columns, rot);
- if (control.html) {
- generateUnderlinesAndLinks(columns);
+ if (control.overlapHandling != textOutIgnoreOverlaps) {
+ overlappingChars = separateOverlappingText(chars);
+ } else {
+ overlappingChars = NULL;
+ }
+ rot = rotateChars(chars);
+ primaryLR = checkPrimaryLR(chars);
+ if ((tree = splitChars(chars))) {
+ columns = buildColumns(tree, primaryLR);
+ delete tree;
+ } else {
+ // no text
+ columns = new GList();
+ }
+ unrotateChars(chars, rot);
+ unrotateColumns(columns, rot);
+ if (control.html) {
+ generateUnderlinesAndLinks(columns);
+ }
+ if (overlappingChars) {
+ if (overlappingChars->getLength() > 0) {
+ columns->append(buildOverlappingTextColumn(overlappingChars));
+ }
+ deleteGList(overlappingChars, TextChar);
+ }
}
return columns;
}
@@ -1618,6 +1753,7 @@ void TextPage::writePhysLayout(void *outputStream,
TextColumn *col;
TextParagraph *par;
TextLine *line;
+ GList *overlappingChars;
GList *columns;
GBool primaryLR;
int ph, colIdx, parIdx, lineIdx, rot, y, i;
@@ -1628,6 +1764,11 @@ void TextPage::writePhysLayout(void *outputStream,
#if 0 //~debug
dumpUnderlines();
#endif
+ if (control.overlapHandling != textOutIgnoreOverlaps) {
+ overlappingChars = separateOverlappingText(chars);
+ } else {
+ overlappingChars = NULL;
+ }
rot = rotateChars(chars);
primaryLR = checkPrimaryLR(chars);
tree = splitChars(chars);
@@ -1700,6 +1841,29 @@ void TextPage::writePhysLayout(void *outputStream,
gfree(outLen);
deleteGList(columns, TextColumn);
+
+ if (overlappingChars) {
+ if (overlappingChars->getLength() > 0) {
+ TextColumn *col = buildOverlappingTextColumn(overlappingChars);
+ (*outputFunc)(outputStream, eol, eolLen);
+ for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ GString *s = new GString();
+ encodeFragment(line->text, line->len, uMap, primaryLR, s);
+ s->append(eol, eolLen);
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ delete s;
+ }
+ if (parIdx < col->paragraphs->getLength() - 1) {
+ (*outputFunc)(outputStream, eol, eolLen);
+ }
+ }
+ delete col;
+ }
+ deleteGList(overlappingChars, TextChar);
+ }
}
void TextPage::writeSimpleLayout(void *outputStream,
@@ -1762,6 +1926,46 @@ void TextPage::writeSimpleLayout(void *outputStream,
deleteGList(superLines, TextSuperLine);
}
+void TextPage::writeSimple2Layout(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ GList *columns;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ GString *out;
+ GBool primaryLR;
+ int colIdx, parIdx, lineIdx;
+
+ primaryLR = checkPrimaryLR(chars);
+ rotateCharsToZero(chars);
+#if 0 //~debug
+ dumpChars(chars);
+#endif
+ columns = buildSimple2Columns(chars);
+ unrotateCharsFromZero(chars);
+ unrotateColumnsFromZero(columns);
+
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ out = new GString();
+ encodeFragment(line->text, line->len, uMap, primaryLR, out);
+ (*outputFunc)(outputStream, out->getCString(), out->getLength());
+ delete out;
+ (*outputFunc)(outputStream, eol, eolLen);
+ }
+ }
+ }
+
+ deleteGList(columns, TextColumn);
+}
+
void TextPage::writeLinePrinter(void *outputStream,
TextOutputFunc outputFunc,
UnicodeMap *uMap,
@@ -1778,7 +1982,8 @@ void TextPage::writeLinePrinter(void *outputStream,
rot = rotateChars(chars);
chars->sort(&TextChar::cmpX);
- removeDuplicates(chars, 0);
+ // don't call removeDuplicates here, because it expects to be
+ // working on a secondary list that doesn't own the TextChar objects
chars->sort(&TextChar::cmpY);
// get character pitch
@@ -1796,7 +2001,7 @@ void TextPage::writeLinePrinter(void *outputStream,
ch->yMin + ascentAdjustFactor * (ch->yMax - ch->yMin) <
ch2->yMax - descentAdjustFactor * (ch2->yMax - ch2->yMin)) {
delta = fabs(ch2->xMin - ch->xMin);
- if (delta > 0 && delta < pitch) {
+ if (delta > 0.01 && delta < pitch) {
pitch = delta;
}
}
@@ -2135,6 +2340,53 @@ int TextPage::rotateChars(GList *charsA) {
return rot;
}
+// Rotate all chars to zero rotation. This leaves the TextChar.rot
+// fields unchanged.
+void TextPage::rotateCharsToZero(GList *charsA) {
+ TextChar *ch;
+ double xMin, yMin, xMax, yMax;
+ int i;
+
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ switch (ch->rot) {
+ case 0:
+ default:
+ break;
+ case 1:
+ xMin = ch->yMin;
+ xMax = ch->yMax;
+ yMin = pageWidth - ch->xMax;
+ yMax = pageWidth - ch->xMin;
+ ch->xMin = xMin;
+ ch->xMax = xMax;
+ ch->yMin = yMin;
+ ch->yMax = yMax;
+ break;
+ case 2:
+ xMin = pageWidth - ch->xMax;
+ xMax = pageWidth - ch->xMin;
+ yMin = pageHeight - ch->yMax;
+ yMax = pageHeight - ch->yMin;
+ ch->xMin = xMin;
+ ch->xMax = xMax;
+ ch->yMin = yMin;
+ ch->yMax = yMax;
+ break;
+ case 3:
+ xMin = pageHeight - ch->yMax;
+ xMax = pageHeight - ch->yMin;
+ yMin = ch->xMin;
+ yMax = ch->xMax;
+ ch->xMin = xMin;
+ ch->xMax = xMax;
+ ch->yMin = yMin;
+ ch->yMax = yMax;
+ break;
+ }
+ }
+}
+
// Rotate the TextUnderlines and TextLinks to match the transform
// performed by rotateChars().
void TextPage::rotateUnderlinesAndLinks(int rot) {
@@ -2286,6 +2538,224 @@ void TextPage::unrotateChars(GList *charsA, int rot) {
}
}
+// Undo the coordinate transform performed by rotateCharsToZero().
+void TextPage::unrotateCharsFromZero(GList *charsA) {
+ TextChar *ch;
+ double xMin, yMin, xMax, yMax;
+ int i;
+
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ switch (ch->rot) {
+ case 0:
+ default:
+ break;
+ case 1:
+ xMin = pageWidth - ch->yMax;
+ xMax = pageWidth - ch->yMin;
+ yMin = ch->xMin;
+ yMax = ch->xMax;
+ ch->xMin = xMin;
+ ch->xMax = xMax;
+ ch->yMin = yMin;
+ ch->yMax = yMax;
+ break;
+ case 2:
+ xMin = pageWidth - ch->xMax;
+ xMax = pageWidth - ch->xMin;
+ yMin = pageHeight - ch->yMax;
+ yMax = pageHeight - ch->yMin;
+ ch->xMin = xMin;
+ ch->xMax = xMax;
+ ch->yMin = yMin;
+ ch->yMax = yMax;
+ break;
+ case 3:
+ xMin = ch->yMin;
+ xMax = ch->yMax;
+ yMin = pageHeight - ch->xMax;
+ yMax = pageHeight - ch->xMin;
+ ch->xMin = xMin;
+ ch->xMax = xMax;
+ ch->yMin = yMin;
+ ch->yMax = yMax;
+ break;
+ }
+ }
+}
+
+// Undo the coordinate transform performed by rotateCharsToZero().
+void TextPage::unrotateColumnsFromZero(GList *columns) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextWord *word;
+ double xMin, yMin, xMax, yMax;
+ int colIdx, parIdx, lineIdx, wordIdx, i;
+
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ switch (col->getRotation()) {
+ case 0:
+ default:
+ break;
+ case 1:
+ xMin = pageWidth - col->yMax;
+ xMax = pageWidth - col->yMin;
+ yMin = col->xMin;
+ yMax = col->xMax;
+ col->xMin = xMin;
+ col->xMax = xMax;
+ col->yMin = yMin;
+ col->yMax = yMax;
+ for (parIdx = 0;
+ parIdx < col->paragraphs->getLength();
+ ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ xMin = pageWidth - par->yMax;
+ xMax = pageWidth - par->yMin;
+ yMin = par->xMin;
+ yMax = par->xMax;
+ par->xMin = xMin;
+ par->xMax = xMax;
+ par->yMin = yMin;
+ par->yMax = yMax;
+ for (lineIdx = 0;
+ lineIdx < par->lines->getLength();
+ ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ xMin = pageWidth - line->yMax;
+ xMax = pageWidth - line->yMin;
+ yMin = line->xMin;
+ yMax = line->xMax;
+ line->xMin = xMin;
+ line->xMax = xMax;
+ line->yMin = yMin;
+ line->yMax = yMax;
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ word = (TextWord *)line->words->get(wordIdx);
+ xMin = pageWidth - word->yMax;
+ xMax = pageWidth - word->yMin;
+ yMin = word->xMin;
+ yMax = word->xMax;
+ word->xMin = xMin;
+ word->xMax = xMax;
+ word->yMin = yMin;
+ word->yMax = yMax;
+ }
+ }
+ }
+ break;
+ case 2:
+ xMin = pageWidth - col->xMax;
+ xMax = pageWidth - col->xMin;
+ yMin = pageHeight - col->yMax;
+ yMax = pageHeight - col->yMin;
+ col->xMin = xMin;
+ col->xMax = xMax;
+ col->yMin = yMin;
+ col->yMax = yMax;
+ for (parIdx = 0;
+ parIdx < col->paragraphs->getLength();
+ ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ xMin = pageWidth - par->xMax;
+ xMax = pageWidth - par->xMin;
+ yMin = pageHeight - par->yMax;
+ yMax = pageHeight - par->yMin;
+ par->xMin = xMin;
+ par->xMax = xMax;
+ par->yMin = yMin;
+ par->yMax = yMax;
+ for (lineIdx = 0;
+ lineIdx < par->lines->getLength();
+ ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ xMin = pageWidth - line->xMax;
+ xMax = pageWidth - line->xMin;
+ yMin = pageHeight - line->yMax;
+ yMax = pageHeight - line->yMin;
+ line->xMin = xMin;
+ line->xMax = xMax;
+ line->yMin = yMin;
+ line->yMax = yMax;
+ for (i = 0; i <= line->len; ++i) {
+ line->edge[i] = pageWidth - line->edge[i];
+ }
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ word = (TextWord *)line->words->get(wordIdx);
+ xMin = pageWidth - word->xMax;
+ xMax = pageWidth - word->xMin;
+ yMin = pageHeight - word->yMax;
+ yMax = pageHeight - word->yMin;
+ word->xMin = xMin;
+ word->xMax = xMax;
+ word->yMin = yMin;
+ word->yMax = yMax;
+ for (i = 0; i <= word->len; ++i) {
+ word->edge[i] = pageWidth - word->edge[i];
+ }
+ }
+ }
+ }
+ break;
+ case 3:
+ xMin = col->yMin;
+ xMax = col->yMax;
+ yMin = pageHeight - col->xMax;
+ yMax = pageHeight - col->xMin;
+ col->xMin = xMin;
+ col->xMax = xMax;
+ col->yMin = yMin;
+ col->yMax = yMax;
+ for (parIdx = 0;
+ parIdx < col->paragraphs->getLength();
+ ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ xMin = par->yMin;
+ xMax = par->yMax;
+ yMin = pageHeight - par->xMax;
+ yMax = pageHeight - par->xMin;
+ par->xMin = xMin;
+ par->xMax = xMax;
+ par->yMin = yMin;
+ par->yMax = yMax;
+ for (lineIdx = 0;
+ lineIdx < par->lines->getLength();
+ ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ xMin = line->yMin;
+ xMax = line->yMax;
+ yMin = pageHeight - line->xMax;
+ yMax = pageHeight - line->xMin;
+ line->xMin = xMin;
+ line->xMax = xMax;
+ line->yMin = yMin;
+ line->yMax = yMax;
+ for (i = 0; i <= line->len; ++i) {
+ line->edge[i] = pageHeight - line->edge[i];
+ }
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ word = (TextWord *)line->words->get(wordIdx);
+ xMin = word->yMin;
+ xMax = word->yMax;
+ yMin = pageHeight - word->xMax;
+ yMax = pageHeight - word->xMin;
+ word->xMin = xMin;
+ word->xMax = xMax;
+ word->yMin = yMin;
+ word->yMax = yMax;
+ for (i = 0; i <= word->len; ++i) {
+ word->edge[i] = pageHeight - word->edge[i];
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
// Undo the coordinate transform performed by rotateChars().
void TextPage::unrotateColumns(GList *columns, int rot) {
TextColumn *col;
@@ -2601,7 +3071,8 @@ void TextPage::removeDuplicates(GList *charsA, int rot) {
int i, j;
if (rot & 1) {
- for (i = 0; i < charsA->getLength(); ++i) {
+ i = 0;
+ while (i < charsA->getLength()) {
ch = (TextChar *)charsA->get(i);
xDelta = dupMaxSecDelta * ch->fontSize;
yDelta = dupMaxPriDelta * ch->fontSize;
@@ -2615,6 +3086,11 @@ void TextPage::removeDuplicates(GList *charsA, int rot) {
fabs(ch2->xMin - ch->xMin) < xDelta &&
fabs(ch2->xMax - ch->xMax) < xDelta &&
fabs(ch2->yMax - ch->yMax) < yDelta) {
+ if (ch->invisible && !ch2->invisible) {
+ charsA->del(i);
+ --i;
+ break;
+ }
if (ch2->spaceAfter) {
ch->spaceAfter = (char)gTrue;
}
@@ -2623,9 +3099,11 @@ void TextPage::removeDuplicates(GList *charsA, int rot) {
++j;
}
}
+ ++i;
}
} else {
- for (i = 0; i < charsA->getLength(); ++i) {
+ i = 0;
+ while (i < charsA->getLength()) {
ch = (TextChar *)charsA->get(i);
xDelta = dupMaxPriDelta * ch->fontSize;
yDelta = dupMaxSecDelta * ch->fontSize;
@@ -2639,15 +3117,257 @@ void TextPage::removeDuplicates(GList *charsA, int rot) {
fabs(ch2->xMax - ch->xMax) < xDelta &&
fabs(ch2->yMin - ch->yMin) < yDelta &&
fabs(ch2->yMax - ch->yMax) < yDelta) {
+ if (ch->invisible && !ch2->invisible) {
+ charsA->del(i);
+ --i;
+ break;
+ }
+ if (ch2->spaceAfter) {
+ ch->spaceAfter = (char)gTrue;
+ }
charsA->del(j);
} else {
++j;
}
}
+ ++i;
}
}
}
+struct TextCharNode {
+ TextCharNode(TextChar *chA, TextCharNode *nextA): ch(chA), next(nextA) {}
+ TextChar *ch;
+ TextCharNode *next;
+};
+
+// Separate out any overlapping text. If handling is
+// textOutAppendOverlaps, return a list of the overlapping chars; else
+// delete them and return NULL.
+GList *TextPage::separateOverlappingText(GList *charsA) {
+ // bin-sort the TextChars
+ TextCharNode *grid[overlapGridHeight][overlapGridWidth];
+ for (int y = 0; y < overlapGridHeight; ++y) {
+ for (int x = 0; x < overlapGridWidth; ++x) {
+ grid[y][x] = NULL;
+ }
+ }
+ for (int i = 0; i < charsA->getLength(); ++i) {
+ TextChar *ch = (TextChar *)charsA->get(i);
+ int y0 = (int)floor(overlapGridHeight * ch->yMin / pageHeight);
+ int y1 = (int)ceil(overlapGridHeight * ch->yMax / pageHeight);
+ int x0 = (int)floor(overlapGridWidth * ch->xMin / pageWidth);
+ int x1 = (int)ceil(overlapGridWidth * ch->yMin / pageWidth);
+ if (y0 < 0) {
+ y0 = 0;
+ }
+ if (y1 >= overlapGridHeight) {
+ y1 = overlapGridHeight - 1;
+ }
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= overlapGridWidth) {
+ x1 = overlapGridWidth - 1;
+ }
+ for (int y = y0; y <= y1; ++y) {
+ for (int x = x0; x <= x1; ++x) {
+ grid[y][x] = new TextCharNode(ch, grid[y][x]);
+ }
+ }
+ }
+
+ // look for overlaps in each cell
+ GBool foundOverlaps = gFalse;
+ for (int y = 0; y < overlapGridHeight; ++y) {
+ for (int x = 0; x < overlapGridWidth; ++x) {
+ for (TextCharNode *p0 = grid[y][x]; p0; p0 = p0->next) {
+ for (TextCharNode *p1 = p0->next; p1; p1 = p1->next) {
+ if (p0->ch->colorR != p1->ch->colorR ||
+ p0->ch->colorG != p1->ch->colorG ||
+ p0->ch->colorB != p1->ch->colorB) {
+ double ovx = (dmin(p0->ch->xMax, p1->ch->xMax)
+ - dmax(p0->ch->xMin, p1->ch->xMin))
+ / dmin(p0->ch->xMax - p0->ch->xMin,
+ p1->ch->xMax - p1->ch->xMin);
+ double ovy = (dmin(p0->ch->yMax, p1->ch->yMax)
+ - dmax(p0->ch->yMin, p1->ch->yMin))
+ / dmin(p0->ch->yMax - p0->ch->yMin,
+ p1->ch->yMax - p1->ch->yMin);
+ if (ovx > minCharOverlap && ovy > minCharOverlap) {
+ // assume the lighter colored text is extraneous
+ if (p0->ch->colorR + p0->ch->colorG + p0->ch->colorB
+ < p1->ch->colorR + p1->ch->colorG + p1->ch->colorB) {
+ p1->ch->overlap = gTrue;
+ } else {
+ p0->ch->overlap = gTrue;
+ }
+ foundOverlaps = gTrue;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // find overlapped strings
+ GList *overlapChars = NULL;
+ if (control.overlapHandling == textOutAppendOverlaps) {
+ overlapChars = new GList();
+ }
+ if (foundOverlaps) {
+ charsA->sort(&TextChar::cmpCharPos);
+ int i = 0;
+ while (i < charsA->getLength()) {
+ TextChar *ch0 = (TextChar *)charsA->get(i);
+ if (ch0->overlap) {
+ int j0, j1;
+ for (j0 = i - 1; j0 >= 0; --j0) {
+ TextChar *ch1 = (TextChar *)charsA->get(j0);
+ if (ch1->colorR != ch0->colorR ||
+ ch1->colorG != ch0->colorG ||
+ ch1->colorB != ch0->colorB ||
+ ch1->rot != ch0->rot) {
+ break;
+ }
+ }
+ ++j0;
+ for (j1 = i + 1; j1 < charsA->getLength(); ++j1) {
+ TextChar *ch1 = (TextChar *)charsA->get(j1);
+ if (ch1->colorR != ch0->colorR ||
+ ch1->colorG != ch0->colorG ||
+ ch1->colorB != ch0->colorB ||
+ ch1->rot != ch0->rot) {
+ break;
+ }
+ }
+ --j1;
+ for (int j = j0; j <= j1; ++j) {
+ if (overlapChars) {
+ overlapChars->append(charsA->get(j0));
+ } else {
+ delete (TextChar *)charsA->get(j0);
+ }
+ charsA->del(j0);
+ }
+ i = j0;
+ } else {
+ ++i;
+ }
+ }
+ }
+
+ // free memory
+ for (int y = 0; y < overlapGridHeight; ++y) {
+ for (int x = 0; x < overlapGridWidth; ++x) {
+ TextCharNode *p0 = grid[y][x];
+ while (p0) {
+ TextCharNode *p1 = p0->next;
+ delete p0;
+ p0 = p1;
+ }
+ }
+ }
+
+ return overlapChars;
+}
+
+// Construct a TextColumn from the list of separated overlapping
+// chars.
+TextColumn *TextPage::buildOverlappingTextColumn(GList *overlappingChars) {
+ GList *pars = new GList();
+ GList *lines = new GList();
+ GList *words = new GList();
+ int wordStart = 0;
+ double lineXMin = 0, lineYMin = 0, lineXMax = 0, lineYMax = 0;
+ double colXMin = 0, colYMin = 0, colXMax = 0, colYMax = 0;
+ for (int i = 0; i < overlappingChars->getLength(); ++i) {
+ TextChar *ch = (TextChar *)overlappingChars->get(i);
+ TextChar *chNext = NULL;
+ if (i + 1 < overlappingChars->getLength()) {
+ chNext = (TextChar *)overlappingChars->get(i + 1);
+ }
+ double sp = 0;
+ double dy = 0;
+ if (chNext) {
+ switch (ch->rot) {
+ case 0:
+ default:
+ sp = chNext->xMin - ch->xMax;
+ dy = chNext->yMin - ch->yMin;
+ break;
+ case 1:
+ sp = chNext->yMin - ch->yMax;
+ dy = chNext->xMax - ch->xMax;
+ break;
+ case 2:
+ sp = ch->xMin - chNext->xMax;
+ dy = ch->yMax - chNext->yMax;
+ break;
+ case 3:
+ sp = ch->yMin - chNext->yMax;
+ dy = ch->xMin - chNext->xMin;
+ break;
+ }
+ }
+ // the +1 here allows for a space character after ch
+ GBool parBreak = !chNext ||
+ chNext->rot != ch->rot ||
+ chNext->charPos > ch->charPos + ch->charLen + 1;
+ GBool lineBreak = parBreak ||
+ sp < -rawModeCharOverlap * ch->fontSize ||
+ fabs(dy) > rawModeLineDelta * ch->fontSize;
+ GBool wordBreak = lineBreak ||
+ ch->spaceAfter ||
+ sp > rawModeWordSpacing * ch->fontSize;
+ if (!wordBreak) {
+ continue;
+ }
+ TextWord *word = new TextWord(overlappingChars, wordStart,
+ i - wordStart + 1, ch->rot, ch->rotated,
+ getCharDirection(ch), !lineBreak);
+ words->append(word);
+ if (words->getLength() == 0) {
+ lineXMin = word->xMin;
+ lineYMin = word->yMin;
+ lineXMax = word->xMax;
+ lineYMax = word->yMax;
+ } else {
+ lineXMin = dmin(lineXMin, word->xMin);
+ lineYMin = dmin(lineYMin, word->yMin);
+ lineXMax = dmax(lineXMax, word->xMax);
+ lineYMax = dmax(lineYMax, word->yMax);
+ }
+ wordStart = i + 1;
+ if (!lineBreak) {
+ continue;
+ }
+ lines->append(new TextLine(words, lineXMin, lineYMin, lineXMax, lineYMax,
+ ((TextWord *)words->get(0))->fontSize));
+ words = new GList();
+ if (!parBreak) {
+ continue;
+ }
+ TextParagraph *par = new TextParagraph(lines, gFalse);
+ pars->append(par);
+ if (pars->getLength() == 0) {
+ colXMin = par->xMin;
+ colYMin = par->yMin;
+ colXMax = par->xMax;
+ colYMax = par->yMax;
+ } else {
+ colXMin = dmin(colXMin, par->xMin);
+ colYMin = dmin(colYMin, par->yMin);
+ colXMax = dmax(colXMax, par->xMax);
+ colYMax = dmax(colYMax, par->yMax);
+ }
+ lines = new GList();
+ }
+ delete words;
+ delete lines;
+ return new TextColumn(pars, colXMin, colYMin, colXMax, colYMax);
+}
+
// Split the characters into trees of TextBlocks, one tree for each
// rotation. Merge into a single tree (with the primary rotation).
TextBlock *TextPage::splitChars(GList *charsA) {
@@ -2860,12 +3580,15 @@ TextBlock *TextPage::split(GList *charsA, int rot) {
// largeCharSize could otherwise have slightly different values
// here and where it's used below to do the large char partition
// (because it gets truncated from 80 to 64 bits when spilled)
- largeCharSize = (int)(largeCharThreshold * avgFontSize * 256) / 256.0;
nLargeChars = 0;
- for (i = 0; i < charsA->getLength(); ++i) {
- ch = (TextChar *)charsA->get(i);
- if (ch->fontSize > largeCharSize) {
- ++nLargeChars;
+ largeCharSize = 0;
+ if (control.separateLargeChars) {
+ largeCharSize = (int)(largeCharThreshold * avgFontSize * 256) / 256.0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (ch->fontSize > largeCharSize) {
+ ++nLargeChars;
+ }
}
}
@@ -3092,7 +3815,7 @@ void TextPage::findGaps(GList *charsA, int rot,
if (xMin * invSplitPrecision < 0.5 * INT_MIN ||
xMax * invSplitPrecision > 0.5 * INT_MAX ||
yMin * invSplitPrecision < 0.5 * INT_MIN ||
- xMax * invSplitPrecision > 0.5 * INT_MAX) {
+ yMax * invSplitPrecision > 0.5 * INT_MAX) {
return;
}
// add some slack to the array bounds to avoid floating point
@@ -3543,7 +4266,7 @@ TextColumn *TextPage::buildColumn(TextBlock *blk) {
int i;
lines = new GList();
- buildLines(blk, lines);
+ buildLines(blk, lines, gFalse);
spaceThresh = paragraphSpacingThreshold * getAverageLineSpacing(lines);
@@ -3712,43 +4435,231 @@ double TextPage::getLineSpacing(TextLine *line0, TextLine *line1) {
return sp;
}
-void TextPage::buildLines(TextBlock *blk, GList *lines) {
+void TextPage::buildLines(TextBlock *blk, GList *lines,
+ GBool splitSuperLines) {
TextLine *line;
int i;
- switch (blk->tag) {
- case blkTagLine:
+ if (blk->tag == blkTagLine ||
+ (blk->tag == blkTagSuperLine && !splitSuperLines)) {
line = buildLine(blk);
if (blk->rot == 1 || blk->rot == 2) {
lines->insert(0, line);
} else {
lines->append(line);
}
- break;
- case blkTagSuperLine:
- case blkTagColumn:
- case blkTagMulticolumn: // multicolumn should never happen here
+ } else {
for (i = 0; i < blk->children->getLength(); ++i) {
- buildLines((TextBlock *)blk->children->get(i), lines);
+ buildLines((TextBlock *)blk->children->get(i), lines, splitSuperLines);
}
- break;
}
}
+GList *TextPage::buildSimple2Columns(GList *charsA) {
+ GList *columns, *paragraphs, *lines;
+ TextParagraph *paragraph;
+ int rot;
+
+ charsA->sort(&TextChar::cmpX);
+ columns = new GList();
+ for (rot = 0; rot < 4; ++rot) {
+ lines = buildSimple2Lines(charsA, rot);
+ if (lines->getLength() == 0) {
+ delete lines;
+ continue;
+ }
+ paragraph = new TextParagraph(lines, gFalse);
+ paragraphs = new GList();
+ paragraphs->append(paragraph);
+ columns->append(new TextColumn(paragraphs,
+ paragraph->xMin, paragraph->yMin,
+ paragraph->xMax, paragraph->yMax));
+ }
+ return columns;
+}
+
+GList *TextPage::buildSimple2Lines(GList *charsA, int rot) {
+ GList *openCharLines, *lines;
+ TextCharLine *firstCharLine, *lastCharLine, *charLine, *p;
+ TextChar *ch;
+ TextLine *line;
+ double bestOverlap, overlap, xMin, yMin, xMax, yMax;
+ int bestLine, i, j, k, m;
+
+ firstCharLine = lastCharLine = NULL;
+ openCharLines = new GList();
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (ch->rot != rot) {
+ continue;
+ }
+
+ // find the first open line with line.yMax > ch.yMin
+ j = -1;
+ k = openCharLines->getLength();
+ while (j < k - 1) {
+ // invariants: openLines[j].yMax <= ch.yMin (or j = -1)
+ // openLines[k].yMax > ch.yMin (or k = nOpenLines)
+ // j < k - 1
+ m = j + (k - j) / 2;
+ charLine = (TextCharLine *)openCharLines->get(m);
+ if (charLine->yMax <= ch->yMin) {
+ j = m;
+ } else {
+ k = m;
+ }
+ }
+
+ // check overlap for all overlapping lines
+ // i.e., all lines with line.yMin < ch.yMax and line.yMax > ch.yMin
+ bestLine = -1;
+ bestOverlap = 0;
+ for (; k < openCharLines->getLength(); ++k) {
+ charLine = (TextCharLine *)openCharLines->get(k);
+ if (charLine->yMin >= ch->yMax) {
+ break;
+ }
+ overlap = ((ch->yMax < charLine->yMax ? ch->yMax : charLine->yMax)
+ - (ch->yMin > charLine->yMin ? ch->yMin : charLine->yMin))
+ / (ch->yMax - ch->yMin);
+ if (overlap > bestOverlap) {
+ bestLine = k;
+ bestOverlap = overlap;
+ }
+ }
+
+ // found an overlapping line
+ if (bestLine >= 0 && bestOverlap > simple2MinOverlap) {
+ k = bestLine;
+ charLine = (TextCharLine *)openCharLines->get(k);
+
+ // else insert a new line immediately before line k
+ } else {
+ charLine = new TextCharLine(ch->rot);
+ if (k < openCharLines->getLength()) {
+ p = (TextCharLine *)openCharLines->get(k);
+ if (p->prev) {
+ p->prev->next = charLine;
+ charLine->prev = p->prev;
+ } else {
+ firstCharLine = charLine;
+ }
+ p->prev = charLine;
+ charLine->next = p;
+ } else {
+ if (lastCharLine) {
+ lastCharLine->next = charLine;
+ charLine->prev = lastCharLine;
+ } else {
+ firstCharLine = charLine;
+ }
+ lastCharLine = charLine;
+ }
+ openCharLines->insert(k, charLine);
+ }
+
+ // add the char to the line
+ charLine->add(ch);
+ charLine->yMin = ch->yMin;
+ charLine->yMax = ch->yMax;
+
+ // update open lines before k
+ j = k - 1;
+ while (j >= 0) {
+ charLine = (TextCharLine *)openCharLines->get(j);
+ if (charLine->yMax <= ch->yMin) {
+ break;
+ }
+ charLine->yMax = ch->yMin;
+ if (charLine->yMin < charLine->yMax) {
+ break;
+ }
+ openCharLines->del(j);
+ --j;
+ }
+
+ // update open lines after k
+ j = k + 1;
+ while (j < openCharLines->getLength()) {
+ charLine = (TextCharLine *)openCharLines->get(j);
+ if (charLine->yMin >= ch->yMax) {
+ break;
+ }
+ charLine->yMin = ch->yMax;
+ if (charLine->yMin < charLine->yMax) {
+ break;
+ }
+ openCharLines->del(j);
+ }
+ }
+
+ // build TextLine objects
+ lines = new GList();
+ for (charLine = firstCharLine; charLine; charLine = p) {
+ xMin = yMin = xMax = yMax = 0;
+ for (j = 0; j < charLine->chars->getLength(); ++j) {
+ ch = (TextChar *)charLine->chars->get(j);
+ if (j == 0) {
+ xMin = ch->xMin;
+ yMin = ch->yMin;
+ xMax = ch->xMax;
+ yMax = ch->yMax;
+ } else {
+ if (ch->xMin < xMin) {
+ xMin = ch->xMin;
+ }
+ if (ch->yMin < yMin) {
+ yMin = ch->yMin;
+ }
+ if (ch->xMax < xMax) {
+ xMax = ch->xMax;
+ }
+ if (ch->yMax < yMax) {
+ yMax = ch->yMax;
+ }
+ }
+ }
+ // the chars have been rotated to 0, without changing the
+ // TextChar.rot values, so we need to tell buildLine to use rot=0,
+ // and then set the word and line rotation correctly afterward
+ line = buildLine(charLine->chars, 0, xMin, yMin, xMax, yMax);
+ line->rot = charLine->rot;
+ for (i = 0; i < line->words->getLength(); ++i) {
+ ((TextWord *)line->words->get(i))->rot = (char)charLine->rot;
+ }
+ lines->append(line);
+ p = charLine->next;
+ delete charLine;
+ }
+
+ delete openCharLines;
+
+ return lines;
+}
+
TextLine *TextPage::buildLine(TextBlock *blk) {
GList *charsA;
+
+ charsA = new GList();
+ getLineChars(blk, charsA);
+ TextLine *line = buildLine(charsA, blk->rot,
+ blk->xMin, blk->yMin, blk->xMax, blk->yMax);
+ delete charsA;
+ return line;
+}
+
+TextLine *TextPage::buildLine(GList *charsA, int rot,
+ double xMin, double yMin,
+ double xMax, double yMax) {
GList *words;
TextChar *ch, *ch2;
TextWord *word;
double wordSp, lineFontSize, sp;
int dir, dir2;
- GBool spaceAfter, spaceBefore;
+ GBool rotated, spaceAfter, spaceBefore;
int i, j;
- charsA = new GList();
- getLineChars(blk, charsA);
-
- wordSp = computeWordSpacingThreshold(charsA, blk->rot);
+ wordSp = computeWordSpacingThreshold(charsA, rot);
words = new GList();
lineFontSize = 0;
@@ -3758,10 +4669,11 @@ TextLine *TextPage::buildLine(TextBlock *blk) {
sp = wordSp - 1;
spaceAfter = gFalse;
dir = getCharDirection((TextChar *)charsA->get(i));
+ rotated = ((TextChar *)charsA->get(i))->rotated;
for (j = i+1; j < charsA->getLength(); ++j) {
ch = (TextChar *)charsA->get(j-1);
ch2 = (TextChar *)charsA->get(j);
- sp = (blk->rot & 1) ? (ch2->yMin - ch->yMax) : (ch2->xMin - ch->xMax);
+ sp = (rot & 1) ? (ch2->yMin - ch->yMax) : (ch2->xMin - ch->xMax);
if (sp > wordSp) {
spaceAfter = gTrue;
break;
@@ -3775,6 +4687,7 @@ TextLine *TextPage::buildLine(TextBlock *blk) {
dir2 = getCharDirection(ch2);
if (ch->font != ch2->font ||
fabs(ch->fontSize - ch2->fontSize) > 0.01 ||
+ (control.splitRotatedWords && ch2->rotated != rotated) ||
(dir && dir2 && dir2 != dir) ||
(control.mode == textOutRawOrder &&
ch2->charPos != ch->charPos + ch->charLen)) {
@@ -3785,11 +4698,11 @@ TextLine *TextPage::buildLine(TextBlock *blk) {
}
sp = wordSp - 1;
}
- word = new TextWord(charsA, i, j - i, blk->rot, dir,
- (blk->rot >= 2) ? spaceBefore : spaceAfter);
+ word = new TextWord(charsA, i, j - i, rot, rotated, dir,
+ (rot >= 2) ? spaceBefore : spaceAfter);
spaceBefore = spaceAfter;
i = j;
- if (blk->rot >= 2) {
+ if (rot >= 2) {
words->insert(0, word);
} else {
words->append(word);
@@ -3799,10 +4712,7 @@ TextLine *TextPage::buildLine(TextBlock *blk) {
}
}
- delete charsA;
-
- return new TextLine(words, blk->xMin, blk->yMin, blk->xMax, blk->yMax,
- lineFontSize);
+ return new TextLine(words, xMin, yMin, xMax, yMax, lineFontSize);
}
void TextPage::getLineChars(TextBlock *blk, GList *charsA) {
@@ -3822,10 +4732,19 @@ void TextPage::getLineChars(TextBlock *blk, GList *charsA) {
// spaces.
double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) {
TextChar *ch, *ch2;
+ double uniformSp, wordSp;
double avgFontSize;
double minAdjGap, maxAdjGap, minSpGap, maxSpGap, minGap, maxGap, gap, gap2;
int i;
+ if (control.mode == textOutTableLayout) {
+ uniformSp = tableModeUniformSpacing;
+ wordSp = tableModeWordSpacing;
+ } else {
+ uniformSp = uniformSpacing;
+ wordSp = wordSpacing;
+ }
+
avgFontSize = 0;
minGap = maxGap = 0;
minAdjGap = minSpGap = 1;
@@ -3876,13 +4795,13 @@ double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) {
// (3) otherwise assume it's a single word (technically it could be
// either "ABC" or "A B C", but it's essentially impossible to
// tell)
- if (maxGap - minGap < uniformSpacing * avgFontSize) {
+ if (maxGap - minGap < uniformSp * avgFontSize) {
if (minSpGap <= maxSpGap) {
if (minAdjGap <= maxAdjGap &&
minSpGap - maxAdjGap > 0.01) {
return 0.5 * (maxAdjGap + minSpGap);
} else if (minAdjGap > maxAdjGap &&
- maxSpGap - minSpGap < uniformSpacing * avgFontSize) {
+ maxSpGap - minSpGap < uniformSp * avgFontSize) {
return minSpGap - 1;
}
}
@@ -3890,7 +4809,7 @@ double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) {
// if there is some variation in spacing, but it's small, assume
// there are some inter-word spaces
- } else if (maxGap - minGap < wordSpacing * avgFontSize) {
+ } else if (maxGap - minGap < wordSp * avgFontSize) {
return 0.5 * (minGap + maxGap);
// if there is a large variation in spacing, use the SpGap/AdjGap
@@ -3901,12 +4820,12 @@ double TextPage::computeWordSpacingThreshold(GList *charsA, int rot) {
} else {
if (minAdjGap <= maxAdjGap &&
minSpGap <= maxSpGap &&
- minSpGap - maxAdjGap > uniformSpacing * avgFontSize) {
- gap = wordSpacing * avgFontSize;
+ minSpGap - maxAdjGap > uniformSp * avgFontSize) {
+ gap = wordSp * avgFontSize;
gap2 = 0.5 * (minSpGap - minGap);
return minGap + (gap < gap2 ? gap : gap2);
} else {
- return minGap + wordSpacing * avgFontSize;
+ return minGap + wordSp * avgFontSize;
}
}
}
@@ -4069,15 +4988,9 @@ void TextPage::buildSuperLines(TextBlock *blk, GList *superLines) {
GList *lines;
int i;
- if (blk->tag == blkTagLine) {
+ if (blk->tag == blkTagLine || blk->tag == blkTagSuperLine) {
lines = new GList();
- buildLines(blk, lines);
- superLines->append(new TextSuperLine(lines));
- } else if (blk->tag == blkTagSuperLine) {
- lines = new GList();
- for (i = 0; i < blk->children->getLength(); ++i) {
- buildLines((TextBlock *)blk->children->get(i), lines);
- }
+ buildLines(blk, lines, gTrue);
superLines->append(new TextSuperLine(lines));
} else {
for (i = 0; i < blk->children->getLength(); ++i) {
@@ -4681,6 +5594,59 @@ GBool TextPage::findPointNear(double x, double y, TextPosition *pos) {
return gTrue;
}
+GBool TextPage::findWordPoints(double x, double y,
+ TextPosition *startPos, TextPosition *endPos) {
+ TextPosition pos;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ int startCharIdx, endCharIdx;
+
+ if (!findPointInside(x, y, &pos)) {
+ return gFalse;
+ }
+ col = (TextColumn *)findCols->get(pos.colIdx);
+ par = (TextParagraph *)col->getParagraphs()->get(pos.parIdx);
+ line = (TextLine *)par->getLines()->get(pos.lineIdx);
+
+ for (startCharIdx = pos.charIdx;
+ startCharIdx > 0 && line->text[startCharIdx - 1] != 0x20;
+ --startCharIdx) ;
+ *startPos = pos;
+ startPos->charIdx = startCharIdx;
+
+ for (endCharIdx = pos.charIdx;
+ endCharIdx < line->len && line->text[endCharIdx] != 0x20;
+ ++endCharIdx) ;
+ *endPos = pos;
+ endPos->charIdx = endCharIdx;
+
+ return gTrue;
+}
+
+GBool TextPage::findLinePoints(double x, double y,
+ TextPosition *startPos, TextPosition *endPos) {
+ TextPosition pos;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+
+ if (!findPointInside(x, y, &pos)) {
+ return gFalse;
+ }
+ col = (TextColumn *)findCols->get(pos.colIdx);
+ par = (TextParagraph *)col->getParagraphs()->get(pos.parIdx);
+ line = (TextLine *)par->getLines()->get(pos.lineIdx);
+
+ *startPos = pos;
+ startPos->charIdx = 0;
+
+ *endPos = pos;
+ endPos->charIdx = line->len;
+
+ return gTrue;
+}
+
// Find the position in [col] corresponding to [x],[y]. The column,
// [col], was found by findPointInside() or findPointNear().
void TextPage::findPointInColumn(TextColumn *col, double x, double y,
@@ -4860,33 +5826,56 @@ TextWordList *TextPage::makeWordListForChars(GList *charList) {
TextParagraph *par;
TextLine *line;
TextWord *word;
+ GList *overlappingChars;
GList *words;
GBool primaryLR;
int rot, colIdx, parIdx, lineIdx, wordIdx;
#if 0 //~debug
- dumpCharList(charList);
+ dumpChars(charList);
#endif
- rot = rotateChars(charList);
- primaryLR = checkPrimaryLR(charList);
- tree = splitChars(charList);
+
+ if (control.mode == textOutSimple2Layout) {
+ rot = 0;
+ primaryLR = checkPrimaryLR(chars);
+ rotateCharsToZero(chars);
+ columns = buildSimple2Columns(chars);
+ unrotateCharsFromZero(chars);
+ unrotateColumnsFromZero(columns);
+
+ } else {
+ if (control.overlapHandling != textOutIgnoreOverlaps) {
+ overlappingChars = separateOverlappingText(chars);
+ } else {
+ overlappingChars = NULL;
+ }
+ rot = rotateChars(charList);
+ primaryLR = checkPrimaryLR(charList);
+ tree = splitChars(charList);
#if 0 //~debug
- dumpTree(tree);
+ dumpTree(tree);
#endif
- if (!tree) {
- // no text
- unrotateChars(charList, rot);
- return new TextWordList(new GList(), gTrue);
- }
- columns = buildColumns(tree, primaryLR);
+ if (!tree) {
+ // no text
+ unrotateChars(charList, rot);
+ return new TextWordList(new GList(), gTrue);
+ }
+ columns = buildColumns(tree, primaryLR);
#if 0 //~debug
- dumpColumns(columns, gTrue);
+ dumpColumns(columns, gTrue);
#endif
- delete tree;
- unrotateChars(charList, rot);
- if (control.html) {
- rotateUnderlinesAndLinks(rot);
- generateUnderlinesAndLinks(columns);
+ delete tree;
+ unrotateChars(charList, rot);
+ if (control.html) {
+ rotateUnderlinesAndLinks(rot);
+ generateUnderlinesAndLinks(columns);
+ }
+ if (overlappingChars) {
+ if (overlappingChars->getLength() > 0) {
+ columns->append(buildOverlappingTextColumn(overlappingChars));
+ }
+ deleteGList(overlappingChars, TextChar);
+ }
}
words = new GList();
@@ -4897,8 +5886,12 @@ TextWordList *TextPage::makeWordListForChars(GList *charList) {
for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) {
line = (TextLine *)par->lines->get(lineIdx);
for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
- word = (TextWord *)line->words->get(wordIdx);
- words->append(word->copy());
+ word = ((TextWord *)line->words->get(wordIdx))->copy();
+ if (wordIdx == line->words->getLength() - 1 &&
+ !line->getHyphenated()) {
+ word->spaceAfter = gTrue;
+ }
+ words->append(word);
}
}
}
@@ -4906,6 +5899,7 @@ TextWordList *TextPage::makeWordListForChars(GList *charList) {
switch (control.mode) {
case textOutReadingOrder:
+ case textOutSimple2Layout:
// already in reading order
break;
case textOutPhysLayout:
@@ -4958,7 +5952,8 @@ void TextPage::dumpTree(TextBlock *tree, int indent) {
tree->type == blkLeaf ? "leaf" :
tree->type == blkHorizSplit ? "horiz" : "vert",
tree->tag == blkTagMulticolumn ? "multicolumn" :
- tree->tag == blkTagColumn ? "column" : "line",
+ tree->tag == blkTagColumn ? "column" :
+ tree->tag == blkTagSuperLine ? "superline" : "line",
tree->smallSplit,
tree->rot, tree->xMin, tree->yMin, tree->xMax, tree->yMax);
if (tree->type == blkLeaf) {
@@ -5001,18 +5996,18 @@ void TextPage::dumpColumns(GList *columns, GBool dumpWords) {
word = (TextWord *)line->words->get(wordIdx);
printf(" word: xMin=%g yMin=%g xMax=%g yMax=%g\n",
word->xMin, word->yMin, word->xMax, word->yMax);
- printf(" ");
+ printf(" '");
for (i = 0; i < word->len; ++i) {
printf("%c", word->text[i] & 0xff);
}
- printf("\n");
+ printf("'\n");
}
} else {
- printf(" ");
+ printf(" '");
for (i = 0; i < line->len; ++i) {
printf("%c", line->text[i] & 0xff);
}
- printf("\n");
+ printf("'\n");
}
}
}
@@ -5041,7 +6036,7 @@ static void outputToFile(void *stream, const char *text, int len) {
}
TextOutputDev::TextOutputDev(char *fileName, TextOutputControl *controlA,
- GBool append) {
+ GBool append, GBool fileNameIsUTF8) {
text = NULL;
control = *controlA;
ok = gTrue;
@@ -5055,15 +6050,22 @@ TextOutputDev::TextOutputDev(char *fileName, TextOutputControl *controlA,
// keep DOS from munging the end-of-line characters
setmode(fileno(stdout), O_BINARY);
#endif
- } else if ((outputStream = fopen(fileName, append ? "ab" : "wb"))) {
- needClose = gTrue;
} else {
- error(errIO, -1, "Couldn't open text file '{0:s}'", fileName);
- ok = gFalse;
- return;
+ if (fileNameIsUTF8) {
+ outputStream = openFile(fileName, append ? "ab" : "wb");
+ } else {
+ outputStream = fopen(fileName, append ? "ab" : "wb");
+ }
+ if (!outputStream) {
+ error(errIO, -1, "Couldn't open text file '{0:s}'", fileName);
+ ok = gFalse;
+ return;
+ }
+ needClose = gTrue;
}
outputFunc = &outputToFile;
} else {
+ outputFunc = NULL;
outputStream = NULL;
}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h
index 0f00cf5ae7b..a1d19c22d7d 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h
@@ -42,12 +42,19 @@ enum TextOutputMode {
textOutReadingOrder, // format into reading order
textOutPhysLayout, // maintain original physical layout
textOutSimpleLayout, // simple one-column physical layout
+ textOutSimple2Layout, // simple one-column physical layout
textOutTableLayout, // similar to PhysLayout, but optimized
// for tables
textOutLinePrinter, // strict fixed-pitch/height layout
textOutRawOrder // keep text in content stream order
};
+enum TextOutputOverlapHandling {
+ textOutIgnoreOverlaps, // no special handling for overlaps
+ textOutAppendOverlaps, // append overlapping text to main text
+ textOutDiscardOverlaps // discard overlapping text
+};
+
class TextOutputControl {
public:
@@ -66,8 +73,17 @@ public:
// in after forming columns
GBool discardDiagonalText; // discard all text that's not close to
// 0/90/180/270 degrees
+ GBool discardRotatedText; // discard all text that's not horizontal
+ // (0 degrees)
GBool discardInvisibleText; // discard all invisible characters
GBool discardClippedText; // discard all clipped characters
+ GBool splitRotatedWords; // do not combine horizontal and
+ // non-horizontal chars in a single
+ // word
+ TextOutputOverlapHandling // how to handle overlapping text
+ overlapHandling;
+ GBool separateLargeChars; // separate "large" characters from
+ // "regular" characters
GBool insertBOM; // insert a Unicode BOM at the start of
// the text output
double marginLeft, // characters outside the margins are
@@ -124,7 +140,7 @@ class TextWord {
public:
TextWord(GList *chars, int start, int lenA,
- int rotA, int dirA, GBool spaceAfterA);
+ int rotA, GBool rotatedA, int dirA, GBool spaceAfterA);
~TextWord();
TextWord *copy() { return new TextWord(this); }
@@ -144,6 +160,7 @@ public:
double *xMaxA, double *yMaxA);
double getFontSize() { return fontSize; }
int getRotation() { return rot; }
+ GBool isRotated() { return (GBool)rotated; }
int getCharPos() { return charPos[0]; }
int getCharLen() { return charPos[len] - charPos[0]; }
int getDirection() { return dir; }
@@ -158,8 +175,6 @@ private:
static int cmpYX(const void *p1, const void *p2);
static int cmpCharPos(const void *p1, const void *p2);
- int rot; // rotation, multiple of 90 degrees
- // (0, 1, 2, or 3)
double xMin, xMax; // bounding box x coordinates
double yMin, yMax; // bounding box y coordinates
Unicode *text; // the text
@@ -171,19 +186,22 @@ private:
int len; // number of characters
TextFontInfo *font; // font information
double fontSize; // font size
- int dir; // character direction (+1 = left-to-right;
- // -1 = right-to-left; 0 = neither)
- GBool spaceAfter; // set if there is a space between this
- // word and the next word on the line
-
- GBool underlined;
TextLink *link;
-
double colorR, // word color
colorG,
colorB;
GBool invisible; // set for invisible text (render mode 3)
+ // group the byte-size fields to minimize object size
+ Guchar rot; // rotation, multiple of 90 degrees
+ // (0, 1, 2, or 3)
+ char rotated; // set if this word is non-horizontal
+ char dir; // character direction (+1 = left-to-right;
+ // -1 = right-to-left; 0 = neither)
+ char spaceAfter; // set if there is a space between this
+ // word and the next word on the line
+ char underlined;
+
friend class TextBlock;
friend class TextLine;
friend class TextPage;
@@ -209,6 +227,7 @@ public:
GList *getWords() { return words; }
int getLength() { return len; }
double getEdge(int idx) { return edge[idx]; }
+ GBool getHyphenated() { return hyphenated; }
private:
@@ -407,6 +426,16 @@ public:
// are no columns.
GBool findPointNear(double x, double y, TextPosition *pos);
+ // Find the start and end of a word inside a column. Returns false
+ // if x,y fall outside all columns.
+ GBool findWordPoints(double x, double y,
+ TextPosition *startPos, TextPosition *endPos);
+
+ // Find the start and end of a line inside a column. Returns false
+ // if x,y fall outside all columns.
+ GBool findLinePoints(double x, double y,
+ TextPosition *startPos, TextPosition *endPos);
+
// Get the upper point of a TextPosition.
void convertPosToPointUpper(TextPosition *pos, double *x, double *y);
@@ -478,6 +507,11 @@ private:
UnicodeMap *uMap,
char *space, int spaceLen,
char *eol, int eolLen);
+ void writeSimple2Layout(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
void writeLinePrinter(void *outputStream,
TextOutputFunc outputFunc,
UnicodeMap *uMap,
@@ -493,12 +527,17 @@ private:
// analysis
int rotateChars(GList *charsA);
+ void rotateCharsToZero(GList *charsA);
void rotateUnderlinesAndLinks(int rot);
void unrotateChars(GList *charsA, int rot);
+ void unrotateCharsFromZero(GList *charsA);
+ void unrotateColumnsFromZero(GList *columns);
void unrotateColumns(GList *columns, int rot);
void unrotateWords(GList *words, int rot);
GBool checkPrimaryLR(GList *charsA);
void removeDuplicates(GList *charsA, int rot);
+ GList *separateOverlappingText(GList *charsA);
+ TextColumn *buildOverlappingTextColumn(GList *overlappingChars);
TextBlock *splitChars(GList *charsA);
TextBlock *split(GList *charsA, int rot);
GList *getChars(GList *charsA, double xMin, double yMin,
@@ -522,8 +561,12 @@ private:
double getLineIndent(TextLine *line, TextBlock *blk);
double getAverageLineSpacing(GList *lines);
double getLineSpacing(TextLine *line0, TextLine *line1);
- void buildLines(TextBlock *blk, GList *lines);
+ void buildLines(TextBlock *blk, GList *lines, GBool splitSuperLines);
+ GList *buildSimple2Columns(GList *charsA);
+ GList *buildSimple2Lines(GList *charsA, int rot);
TextLine *buildLine(TextBlock *blk);
+ TextLine *buildLine(GList *charsA, int rot,
+ double xMin, double yMin, double xMax, double yMax);
void getLineChars(TextBlock *blk, GList *charsA);
double computeWordSpacingThreshold(GList *charsA, int rot);
int getCharDirection(TextChar *ch);
@@ -563,6 +606,7 @@ private:
int curRot; // current rotation
GBool diagonal; // set if rotation is not close to
// 0/90/180/270 degrees
+ GBool rotated; // set if text is not horizontal (0 degrees)
int nTinyChars; // number of "tiny" chars seen so far
Unicode *actualText; // current "ActualText" span
int actualTextLen;
@@ -605,7 +649,7 @@ public:
// is maintained. If <rawOrder> is true, the text is kept in
// content stream order.
TextOutputDev(char *fileName, TextOutputControl *controlA,
- GBool append);
+ GBool append, GBool fileNameIsUTF8 = gFalse);
// Create a TextOutputDev which will write to a generic stream. If
// <physLayoutA> is true, the original physical layout of the text
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc
index 4701fed98eb..8e54a1d5471 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc
@@ -201,3 +201,13 @@ GString *TextString::toPDFTextString() {
}
return s;
}
+
+GString *TextString::toUTF8() {
+ GString *s = new GString();
+ for (int i = 0; i < len; ++i) {
+ char buf[8];
+ int n = mapUTF8(u[i], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ return s;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h b/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h
index c5e7e613af8..c296d7d6d7a 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h
@@ -56,6 +56,9 @@ public:
// Create a PDF text string from a TextString.
GString *toPDFTextString();
+ // Convert a TextString to UTF-8.
+ GString *toUTF8();
+
private:
void expand(int delta);
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc
index 95f0e983a31..9ae636226ae 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc
@@ -22,6 +22,7 @@
WebFont::WebFont(GfxFont *gfxFontA, XRef *xref) {
GfxFontType type;
+ Ref id;
gfxFont = gfxFontA;
fontBuf = NULL;
@@ -29,23 +30,25 @@ WebFont::WebFont(GfxFont *gfxFontA, XRef *xref) {
ffType1C = NULL;
isOpenType = gFalse;
- type = gfxFont->getType();
- if (type == fontTrueType ||
- type == fontTrueTypeOT ||
- type == fontCIDType2 ||
- type == fontCIDType2OT) {
- if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) {
- ffTrueType = FoFiTrueType::make(fontBuf, fontLength, 0);
- }
- } else if (type == fontType1C ||
- type == fontCIDType0C) {
- if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) {
- ffType1C = FoFiType1C::make(fontBuf, fontLength);
- }
- } else if (type == fontType1COT ||
- type == fontCIDType0COT) {
- if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) {
- isOpenType = gTrue;
+ if (gfxFont->getEmbeddedFontID(&id)) {
+ type = gfxFont->getType();
+ if (type == fontTrueType ||
+ type == fontTrueTypeOT ||
+ type == fontCIDType2 ||
+ type == fontCIDType2OT) {
+ if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) {
+ ffTrueType = FoFiTrueType::make(fontBuf, fontLength, 0);
+ }
+ } else if (type == fontType1C ||
+ type == fontCIDType0C) {
+ if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) {
+ ffType1C = FoFiType1C::make(fontBuf, fontLength);
+ }
+ } else if (type == fontType1COT ||
+ type == fontCIDType0COT) {
+ if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) {
+ isOpenType = gTrue;
+ }
}
}
}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc
deleted file mode 100644
index 3b88270c798..00000000000
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc
+++ /dev/null
@@ -1,3245 +0,0 @@
-//========================================================================
-//
-// XFAForm.cc
-//
-// Copyright 2012 Glyph & Cog, LLC
-//
-//========================================================================
-
-#include <aconf.h>
-
-#ifdef USE_GCC_PRAGMAS
-#pragma implementation
-#endif
-
-#include <stdlib.h>
-#include "gmem.h"
-#include "gmempp.h"
-#include "GString.h"
-#include "GList.h"
-#include "GHash.h"
-#include "Error.h"
-#include "Object.h"
-#include "PDFDoc.h"
-#include "Gfx.h"
-#include "GfxFont.h"
-#include "Zoox.h"
-#include "PDF417Barcode.h"
-#include "UTF8.h"
-#include "XFAForm.h"
-
-#ifdef _WIN32
-# undef strcasecmp
-# undef strncasecmp
-# define strcasecmp _stricmp
-# define strncasecmp _strnicmp
-#endif
-
-//------------------------------------------------------------------------
-
-// 5 bars + 5 spaces -- each can be wide (1) or narrow (0)
-// (there are always exactly 3 wide elements;
-// the last space is always narrow)
-static Guchar code3Of9Data[128][10] = {
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 0x00
- { 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, 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, 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, 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, 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 }, // 0x10
- { 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, 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, 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, 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, 0, 0 },
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- { 0, 1, 1, 0, 0, 0, 1, 0, 0, 0 }, // ' ' = 0x20
- { 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, 1, 0, 1, 0, 1, 0, 0, 0, 0 }, // '$' = 0x24
- { 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, // '%' = 0x25
- { 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, 0, 0, 0, 0, 0, 0, 0, 0 },
- { 0, 1, 0, 0, 1, 0, 1, 0, 0, 0 }, // '*' = 0x2a
- { 0, 1, 0, 0, 0, 1, 0, 1, 0, 0 }, // '+' = 0x2b
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0 }, // '-' = 0x2d
- { 1, 1, 0, 0, 0, 0, 1, 0, 0, 0 }, // '.' = 0x2e
- { 0, 1, 0, 1, 0, 0, 0, 1, 0, 0 }, // '/' = 0x2f
- { 0, 0, 0, 1, 1, 0, 1, 0, 0, 0 }, // '0' = 0x30
- { 1, 0, 0, 1, 0, 0, 0, 0, 1, 0 }, // '1'
- { 0, 0, 1, 1, 0, 0, 0, 0, 1, 0 }, // '2'
- { 1, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, // '3'
- { 0, 0, 0, 1, 1, 0, 0, 0, 1, 0 }, // '4'
- { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 }, // '5'
- { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 }, // '6'
- { 0, 0, 0, 1, 0, 0, 1, 0, 1, 0 }, // '7'
- { 1, 0, 0, 1, 0, 0, 1, 0, 0, 0 }, // '8'
- { 0, 0, 1, 1, 0, 0, 1, 0, 0, 0 }, // '9'
- { 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, 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, 0, 0, 0, 0, 0, 0 }, // 0x40
- { 1, 0, 0, 0, 0, 1, 0, 0, 1, 0 }, // 'A' = 0x41
- { 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 }, // 'B'
- { 1, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, // 'C'
- { 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 }, // 'D'
- { 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // 'E'
- { 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 }, // 'F'
- { 0, 0, 0, 0, 0, 1, 1, 0, 1, 0 }, // 'G'
- { 1, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, // 'H'
- { 0, 0, 1, 0, 0, 1, 1, 0, 0, 0 }, // 'I'
- { 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, // 'J'
- { 1, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, // 'K'
- { 0, 0, 1, 0, 0, 0, 0, 1, 1, 0 }, // 'L'
- { 1, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, // 'M'
- { 0, 0, 0, 0, 1, 0, 0, 1, 1, 0 }, // 'N'
- { 1, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, // 'O'
- { 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 }, // 'P' = 0x50
- { 0, 0, 0, 0, 0, 0, 1, 1, 1, 0 }, // 'Q'
- { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, // 'R'
- { 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 }, // 'S'
- { 0, 0, 0, 0, 1, 0, 1, 1, 0, 0 }, // 'T'
- { 1, 1, 0, 0, 0, 0, 0, 0, 1, 0 }, // 'U'
- { 0, 1, 1, 0, 0, 0, 0, 0, 1, 0 }, // 'V'
- { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, // 'W'
- { 0, 1, 0, 0, 1, 0, 0, 0, 1, 0 }, // 'X'
- { 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, // 'Y'
- { 0, 1, 1, 0, 1, 0, 0, 0, 0, 0 }, // 'Z'
- { 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, 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 }, // 0x60
- { 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, 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, 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, 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, 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 }, // 0x70
- { 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, 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, 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, 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, 0, 0 },
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
-};
-
-// 3 bars + 3 spaces -- each can be 1, 2, 3, or 4 units wide
-static Guchar code128Data[107][6] = {
- { 2, 1, 2, 2, 2, 2 },
- { 2, 2, 2, 1, 2, 2 },
- { 2, 2, 2, 2, 2, 1 },
- { 1, 2, 1, 2, 2, 3 },
- { 1, 2, 1, 3, 2, 2 },
- { 1, 3, 1, 2, 2, 2 },
- { 1, 2, 2, 2, 1, 3 },
- { 1, 2, 2, 3, 1, 2 },
- { 1, 3, 2, 2, 1, 2 },
- { 2, 2, 1, 2, 1, 3 },
- { 2, 2, 1, 3, 1, 2 },
- { 2, 3, 1, 2, 1, 2 },
- { 1, 1, 2, 2, 3, 2 },
- { 1, 2, 2, 1, 3, 2 },
- { 1, 2, 2, 2, 3, 1 },
- { 1, 1, 3, 2, 2, 2 },
- { 1, 2, 3, 1, 2, 2 },
- { 1, 2, 3, 2, 2, 1 },
- { 2, 2, 3, 2, 1, 1 },
- { 2, 2, 1, 1, 3, 2 },
- { 2, 2, 1, 2, 3, 1 },
- { 2, 1, 3, 2, 1, 2 },
- { 2, 2, 3, 1, 1, 2 },
- { 3, 1, 2, 1, 3, 1 },
- { 3, 1, 1, 2, 2, 2 },
- { 3, 2, 1, 1, 2, 2 },
- { 3, 2, 1, 2, 2, 1 },
- { 3, 1, 2, 2, 1, 2 },
- { 3, 2, 2, 1, 1, 2 },
- { 3, 2, 2, 2, 1, 1 },
- { 2, 1, 2, 1, 2, 3 },
- { 2, 1, 2, 3, 2, 1 },
- { 2, 3, 2, 1, 2, 1 },
- { 1, 1, 1, 3, 2, 3 },
- { 1, 3, 1, 1, 2, 3 },
- { 1, 3, 1, 3, 2, 1 },
- { 1, 1, 2, 3, 1, 3 },
- { 1, 3, 2, 1, 1, 3 },
- { 1, 3, 2, 3, 1, 1 },
- { 2, 1, 1, 3, 1, 3 },
- { 2, 3, 1, 1, 1, 3 },
- { 2, 3, 1, 3, 1, 1 },
- { 1, 1, 2, 1, 3, 3 },
- { 1, 1, 2, 3, 3, 1 },
- { 1, 3, 2, 1, 3, 1 },
- { 1, 1, 3, 1, 2, 3 },
- { 1, 1, 3, 3, 2, 1 },
- { 1, 3, 3, 1, 2, 1 },
- { 3, 1, 3, 1, 2, 1 },
- { 2, 1, 1, 3, 3, 1 },
- { 2, 3, 1, 1, 3, 1 },
- { 2, 1, 3, 1, 1, 3 },
- { 2, 1, 3, 3, 1, 1 },
- { 2, 1, 3, 1, 3, 1 },
- { 3, 1, 1, 1, 2, 3 },
- { 3, 1, 1, 3, 2, 1 },
- { 3, 3, 1, 1, 2, 1 },
- { 3, 1, 2, 1, 1, 3 },
- { 3, 1, 2, 3, 1, 1 },
- { 3, 3, 2, 1, 1, 1 },
- { 3, 1, 4, 1, 1, 1 },
- { 2, 2, 1, 4, 1, 1 },
- { 4, 3, 1, 1, 1, 1 },
- { 1, 1, 1, 2, 2, 4 },
- { 1, 1, 1, 4, 2, 2 },
- { 1, 2, 1, 1, 2, 4 },
- { 1, 2, 1, 4, 2, 1 },
- { 1, 4, 1, 1, 2, 2 },
- { 1, 4, 1, 2, 2, 1 },
- { 1, 1, 2, 2, 1, 4 },
- { 1, 1, 2, 4, 1, 2 },
- { 1, 2, 2, 1, 1, 4 },
- { 1, 2, 2, 4, 1, 1 },
- { 1, 4, 2, 1, 1, 2 },
- { 1, 4, 2, 2, 1, 1 },
- { 2, 4, 1, 2, 1, 1 },
- { 2, 2, 1, 1, 1, 4 },
- { 4, 1, 3, 1, 1, 1 },
- { 2, 4, 1, 1, 1, 2 },
- { 1, 3, 4, 1, 1, 1 },
- { 1, 1, 1, 2, 4, 2 },
- { 1, 2, 1, 1, 4, 2 },
- { 1, 2, 1, 2, 4, 1 },
- { 1, 1, 4, 2, 1, 2 },
- { 1, 2, 4, 1, 1, 2 },
- { 1, 2, 4, 2, 1, 1 },
- { 4, 1, 1, 2, 1, 2 },
- { 4, 2, 1, 1, 1, 2 },
- { 4, 2, 1, 2, 1, 1 },
- { 2, 1, 2, 1, 4, 1 },
- { 2, 1, 4, 1, 2, 1 },
- { 4, 1, 2, 1, 2, 1 },
- { 1, 1, 1, 1, 4, 3 },
- { 1, 1, 1, 3, 4, 1 },
- { 1, 3, 1, 1, 4, 1 },
- { 1, 1, 4, 1, 1, 3 },
- { 1, 1, 4, 3, 1, 1 },
- { 4, 1, 1, 1, 1, 3 },
- { 4, 1, 1, 3, 1, 1 },
- { 1, 1, 3, 1, 4, 1 },
- { 1, 1, 4, 1, 3, 1 },
- { 3, 1, 1, 1, 4, 1 },
- { 4, 1, 1, 1, 3, 1 },
- { 2, 1, 1, 4, 1, 2 }, // start code A
- { 2, 1, 1, 2, 1, 4 }, // start code B
- { 2, 1, 1, 2, 3, 2 }, // start code C
- { 2, 3, 3, 1, 1, 1 } // stop code (without final bar)
-};
-
-//------------------------------------------------------------------------
-
-class XFATableInfo {
-public:
-
- XFATableInfo(ZxAttr *columnWidthsAttr);
- ~XFATableInfo();
- void computeRowHeight(ZxElement *rowSubform);
-
- int nColumns; // number of columns
- double *columnRight; // right edge (x coord) of each column
- int rowIdx; // current row index
- int columnIdx; // current column index
- double rowTop; // top edge (y coord) of current row
- double rowHeight; // height of current row (max cell height
- // so far)
-};
-
-XFATableInfo::XFATableInfo(ZxAttr *columnWidthsAttr) {
- GString *s;
- double w;
- int i, columnRightSize;
-
- nColumns = 0;
- columnRight = NULL;
- columnRightSize = 0;
- if (columnWidthsAttr) {
- s = columnWidthsAttr->getValue();
- i = 0;
- while (1) {
- for (;
- i < s->getLength() &&
- (s->getChar(i) == ' ' || s->getChar(i) == '\t' ||
- s->getChar(i) == '\r' || s->getChar(i) == '\n');
- ++i) ;
- if (i == s->getLength()) {
- break;
- }
- w = XFAFormField::getMeasurement(s, i);
- if (nColumns == columnRightSize) {
- columnRightSize = columnRightSize ? 2 * columnRightSize : 8;
- columnRight = (double *)greallocn(columnRight, columnRightSize,
- sizeof(double));
- }
- columnRight[nColumns] = (nColumns > 0 ? columnRight[nColumns - 1] : 0) + w;
- ++nColumns;
- for (++i;
- i < s->getLength() &&
- !(s->getChar(i) == ' ' || s->getChar(i) == '\t' ||
- s->getChar(i) == '\r' || s->getChar(i) == '\n');
- ++i) ;
- }
- }
- rowIdx = -1;
- columnIdx = 0;
- rowTop = rowHeight = 0;
-}
-
-XFATableInfo::~XFATableInfo() {
- gfree(columnRight);
-}
-
-void XFATableInfo::computeRowHeight(ZxElement *rowSubform) {
- ZxNode *child;
- ZxAttr *attr;
- double h;
-
- rowHeight = 0;
- for (child = rowSubform->getFirstChild();
- child;
- child = child->getNextChild()) {
- if (child->isElement("field") || child->isElement("draw")) {
- if (!(attr = ((ZxElement *)child)->findAttr("h"))) {
- attr = ((ZxElement *)child)->findAttr("minH");
- }
- h = XFAFormField::getMeasurement(attr, 0);
- if (h > rowHeight) {
- rowHeight = h;
- }
- }
- }
-}
-
-//------------------------------------------------------------------------
-// XFAForm
-//------------------------------------------------------------------------
-
-XFAForm *XFAForm::load(PDFDoc *docA, Catalog *catalog,
- Object *acroFormObj, Object *xfaObj) {
- XFAForm *xfaForm;
- XFAFormField *field;
- ZxDoc *xmlA;
- ZxElement *tmpl;
- Object catDict, resourceDictA, obj1;
- GString *data;
- GBool fullXFAA;
- GString *name, *fullName;
- GHash *nameCount, *nameIdx, *fullNameCount, *fullNameIdx;
- char buf[4096];
- int n, i;
-
- if (catalog->getNumPages() == 0) {
- return NULL;
- }
-
- docA->getXRef()->getCatalog(&catDict);
- catDict.dictLookup("NeedsRendering", &obj1);
- fullXFAA = obj1.isBool() && obj1.getBool();
- obj1.free();
- catDict.free();
-
- if (xfaObj->isStream()) {
- data = new GString();
- xfaObj->streamReset();
- while ((n = xfaObj->getStream()->getBlock(buf, sizeof(buf))) > 0) {
- data->append(buf, n);
- }
- } else if (xfaObj->isArray()) {
- data = new GString();
- for (i = 1; i < xfaObj->arrayGetLength(); i += 2) {
- if (!xfaObj->arrayGet(i, &obj1)->isStream()) {
- error(errSyntaxError, -1, "XFA array element is wrong type");
- obj1.free();
- delete data;
- return NULL;
- }
- obj1.streamReset();
- while ((n = obj1.getStream()->getBlock(buf, sizeof(buf))) > 0) {
- data->append(buf, n);
- }
- obj1.free();
- }
- } else {
- error(errSyntaxError, -1, "XFA object is wrong type");
- return NULL;
- }
-
- xmlA = ZxDoc::loadMem(data->getCString(), data->getLength());
- delete data;
- if (!xmlA) {
- error(errSyntaxError, -1, "Invalid XML in XFA form");
- return NULL;
- }
-
- if (acroFormObj->isDict()) {
- acroFormObj->dictLookup("DR", &resourceDictA);
- }
-
- xfaForm = new XFAForm(docA, catalog->getNumPages(),
- xmlA, &resourceDictA, fullXFAA);
-
- resourceDictA.free();
-
- if (xfaForm->xml->getRoot()) {
- if ((tmpl = xfaForm->xml->getRoot()->findFirstChildElement("template"))) {
- xfaForm->curPageNum = 0;
- xfaForm->curXOffset = xfaForm->curYOffset = 0;
- name = new GString();
- fullName = new GString();
- nameCount = new GHash();
- nameIdx = new GHash();
- fullNameCount = new GHash();
- fullNameIdx = new GHash();
- xfaForm->scanNode(tmpl, name, fullName, gFalse, NULL,
- nameCount, nameIdx, fullNameCount, fullNameIdx,
- NULL, catalog);
- delete nameCount;
- delete nameIdx;
- delete fullNameCount;
- delete fullNameIdx;
- delete name;
- delete fullName;
-
- // apply pageOffsetX/Y (the pageSet/pageArea/contentArea offset)
- // to all fields
- if (xfaForm->pageSetNPages >= 1 &&
- xfaForm->pageSetNPages < xfaForm->nPages) {
- for (i = xfaForm->pageSetNPages + 1; i <= xfaForm->nPages; ++i) {
- xfaForm->pageOffsetX[i - 1] =
- xfaForm->pageOffsetX[xfaForm->pageSetNPages - 1];
- xfaForm->pageOffsetY[i - 1] =
- xfaForm->pageOffsetY[xfaForm->pageSetNPages - 1];
- }
- }
- for (i = 0; i < xfaForm->fields->getLength(); ++i) {
- field = (XFAFormField *)xfaForm->fields->get(i);
- if (field->pageNum >= 1 && field->pageNum <= xfaForm->nPages) {
- field->xOffset += xfaForm->pageOffsetX[field->pageNum - 1];
- field->yOffset += xfaForm->pageOffsetY[field->pageNum - 1];
- }
- }
- }
- }
-
- return xfaForm;
-}
-
-XFAForm::XFAForm(PDFDoc *docA, int nPagesA, ZxDoc *xmlA,
- Object *resourceDictA, GBool fullXFAA):
- Form(docA) {
- int pg;
-
- xml = xmlA;
- fields = new GList();
- resourceDictA->copy(&resourceDict);
- fullXFA = fullXFAA;
- nPages = nPagesA;
- pageSetNPages = 0;
- pageOffsetX = (double *)gmallocn(nPages, sizeof(double));
- pageOffsetY = (double *)gmallocn(nPages, sizeof(double));
- for (pg = 0; pg < nPages; ++pg) {
- pageOffsetX[pg] = pageOffsetY[pg] = 0;
- }
-}
-
-XFAForm::~XFAForm() {
- delete xml;
- deleteGList(fields, XFAFormField);
- resourceDict.free();
- gfree(pageOffsetX);
- gfree(pageOffsetY);
-}
-
-// Scan <elem>. Constructs the node's name and full name. If <elem>
-// is a field, creates an XFAFormField; else scans <elem>'s children.
-void XFAForm::scanNode(ZxElement *elem,
- GString *parentName, GString *parentFullName,
- GBool inPageSet, XFATableInfo *tableInfo,
- GHash *nameCount, GHash *nameIdx,
- GHash *fullNameCount, GHash *fullNameIdx,
- GString *exclGroupName, Catalog *catalog) {
- ZxAttr *attr;
- GString *name, *fullName, *namePart, *fullNamePart;
- GHash *childNameCount, *childNameIdx, *childFullNameCount, *childFullNameIdx;
- int colSpan, i;
-
- if (elem->isElement("template")) {
- name = new GString("form");
- fullName = new GString("form");
- childNameCount = new GHash();
- scanNames(elem, childNameCount);
- childNameIdx = new GHash();
- childFullNameCount = new GHash();
- scanFullNames(elem, childFullNameCount);
- childFullNameIdx = new GHash();
- } else {
- if ((namePart = getNodeName(elem))) {
- name = GString::format("{0:t}.{1:t}", parentName, namePart);
- if (nameCount->lookupInt(namePart) > 1) {
- i = nameIdx->lookupInt(namePart);
- name->appendf("[{0:d}]", i);
- nameIdx->replace(namePart, i + 1);
- }
- childNameCount = new GHash();
- scanNames(elem, childNameCount);
- childNameIdx = new GHash();
- } else {
- name = parentName->copy();
- childNameCount = nameCount;
- childNameIdx = nameIdx;
- }
- if ((fullNamePart = getNodeFullName(elem))) {
- fullName = GString::format("{0:t}.{1:t}", parentFullName, fullNamePart);
- if (fullNameCount->lookupInt(fullNamePart) > 1) {
- i = fullNameIdx->lookupInt(fullNamePart);
- fullName->appendf("[{0:d}]", i);
- fullNameIdx->replace(fullNamePart, i + 1);
- }
- childFullNameCount = new GHash();
- scanFullNames(elem, childFullNameCount);
- childFullNameIdx = new GHash();
- } else {
- fullName = parentFullName->copy();
- childFullNameCount = fullNameCount;
- childFullNameIdx = fullNameIdx;
- }
- }
-
- if (tableInfo && (elem->isElement("field") || elem->isElement("draw"))) {
- if ((attr = elem->findAttr("colSpan"))) {
- colSpan = atoi(attr->getValue()->getCString());
- } else {
- colSpan = 1;
- }
- } else {
- colSpan = 0;
- }
-
- if (elem->isElement("field")) {
- scanField(elem, name, fullName, exclGroupName,
- inPageSet, tableInfo, colSpan, catalog);
- } else {
- scanNonField(elem, name, fullName, inPageSet, tableInfo, colSpan,
- childNameCount, childNameIdx,
- childFullNameCount, childFullNameIdx,
- catalog);
- }
-
- if (tableInfo) {
- tableInfo->columnIdx += colSpan;
- }
-
- delete name;
- delete fullName;
- if (childNameCount != nameCount) {
- delete childNameCount;
- }
- if (childNameIdx != nameIdx) {
- delete childNameIdx;
- }
- if (childFullNameCount != fullNameCount) {
- delete childFullNameCount;
- }
- if (childFullNameIdx != fullNameIdx) {
- delete childFullNameIdx;
- }
-}
-
-// Traverse all children of <elem>, incrementing nameCount[name] for
-// each named child. Traversal stops at each named child.
-void XFAForm::scanNames(ZxElement *elem, GHash *nameCount) {
- ZxNode *node;
- ZxElement *child;
- GString *namePart;
-
- for (node = elem->getFirstChild(); node; node = node->getNextChild()) {
- if (node->isElement()) {
- child = (ZxElement *)node;
- if ((namePart = getNodeName(child))) {
- if (nodeIsBindGlobal(child)) {
- nameCount->replace(namePart, 1);
- } else {
- nameCount->replace(namePart, nameCount->lookupInt(namePart) + 1);
- }
- } else {
- scanNames(child, nameCount);
- }
- }
- }
-}
-
-// Traverse all children of <elem>, incrementing fullNameCount[name]
-// for each full-named child. Traversal stops at each full-named
-// child.
-void XFAForm::scanFullNames(ZxElement *elem, GHash *fullNameCount) {
- ZxNode *node;
- ZxElement *child;
- GString *fullNamePart;
-
- for (node = elem->getFirstChild(); node; node = node->getNextChild()) {
- if (node->isElement()) {
- child = (ZxElement *)node;
- if ((fullNamePart = getNodeFullName(child))) {
- if (nodeIsBindGlobal(child)) {
- fullNameCount->replace(fullNamePart, 1);
- } else {
- fullNameCount->replace(fullNamePart,
- fullNameCount->lookupInt(fullNamePart) + 1);
- }
- } else {
- scanFullNames(child, fullNameCount);
- }
- }
- }
-}
-
-void XFAForm::scanField(ZxElement *elem, GString *name, GString *fullName,
- GString *exclGroupName, GBool inPageSet,
- XFATableInfo *tableInfo, int colSpan,
- Catalog *catalog) {
- double xSubOffset, ySubOffset, columnWidth, rowHeight;
-
- if (curPageNum == 0) {
- curPageNum = 1;
- }
-
- xSubOffset = ySubOffset = 0;
- columnWidth = rowHeight = 0;
- if (tableInfo) {
- if (tableInfo->columnIdx > 0 &&
- tableInfo->columnIdx <= tableInfo->nColumns) {
- xSubOffset = tableInfo->columnRight[tableInfo->columnIdx - 1];
- }
- if (tableInfo->columnIdx + colSpan <= tableInfo->nColumns) {
- columnWidth = tableInfo->columnRight[tableInfo->columnIdx + colSpan - 1]
- - xSubOffset;
- }
- ySubOffset = tableInfo->rowTop;
- rowHeight = tableInfo->rowHeight;
- curXOffset += xSubOffset;
- curYOffset += ySubOffset;
- }
-
- fields->append(new XFAFormField(this, elem, name->copy(), fullName->copy(),
- exclGroupName ? exclGroupName->copy()
- : (GString *)NULL,
- curPageNum, curXOffset, curYOffset,
- columnWidth, rowHeight));
-
- if (tableInfo) {
- curXOffset -= xSubOffset;
- curYOffset -= ySubOffset;
- }
-}
-
-void XFAForm::scanNonField(ZxElement *elem, GString *name, GString *fullName,
- GBool inPageSet,
- XFATableInfo *tableInfo, int colSpan,
- GHash *nameCount, GHash *nameIdx,
- GHash *fullNameCount, GHash *fullNameIdx,
- Catalog *catalog) {
- XFATableInfo *newTableInfo;
- ZxElement *brk, *contentArea;
- ZxNode *child;
- ZxAttr *attr;
- PDFRectangle *box;
- GString *exclGroupName;
- double xSubOffset, ySubOffset;
- int savedPageNum;
-
- newTableInfo = tableInfo;
-
- if (elem->isElement("subform")) {
-
- // update page number
- if (((brk = elem->findFirstChildElement("breakBefore")) &&
- (attr = brk->findAttr("targetType")) &&
- !attr->getValue()->cmp("pageArea")) ||
- ((brk = elem->findFirstChildElement("break")) &&
- (attr = brk->findAttr("before")) &&
- !attr->getValue()->cmp("pageArea")) ||
- (curPageNum < nPages &&
- (attr = elem->findAttr("w")) &&
- (box = catalog->getPage(curPageNum + 1)->getMediaBox()) &&
- XFAFormField::getMeasurement(attr, 0) == box->x2 - box->x1 &&
- (attr = elem->findAttr("h")) &&
- XFAFormField::getMeasurement(attr, 0) == box->y2 - box->y1)) {
- if (curPageNum < nPages) {
- ++curPageNum;
- }
- }
-
- // update tableInfo
- if ((attr = elem->findAttr("layout"))) {
- if (!attr->getValue()->cmp("table")) {
- newTableInfo = new XFATableInfo(elem->findAttr("columnWidths"));
- newTableInfo->rowIdx = -1;
- newTableInfo->columnIdx = 0;
- } else if (tableInfo && !attr->getValue()->cmp("row")) {
- ++tableInfo->rowIdx;
- tableInfo->columnIdx = 0;
- tableInfo->rowTop += tableInfo->rowHeight;
- tableInfo->computeRowHeight(elem);
- }
- }
-
- // update position
- xSubOffset = XFAFormField::getMeasurement(elem->findAttr("x"), 0);
- ySubOffset = XFAFormField::getMeasurement(elem->findAttr("y"), 0);
- curXOffset += xSubOffset;
- curYOffset += ySubOffset;
-
- } else if (elem->isElement("area") ||
- elem->isElement("exclGroup")) {
- xSubOffset = XFAFormField::getMeasurement(elem->findAttr("x"), 0);
- ySubOffset = XFAFormField::getMeasurement(elem->findAttr("y"), 0);
- curXOffset += xSubOffset;
- curYOffset += ySubOffset;
-
- } else {
- xSubOffset = ySubOffset = 0;
- }
-
- savedPageNum = curPageNum;
- if (elem->isElement("pageSet")) {
- inPageSet = gTrue;
- curPageNum = 0;
-
- } else if (elem->isElement("pageArea")) {
- if (inPageSet) {
- if (curPageNum < nPages) {
- ++curPageNum;
- }
- if ((contentArea = elem->findFirstChildElement("contentArea"))) {
- pageOffsetX[curPageNum - 1] =
- XFAFormField::getMeasurement(contentArea->findAttr("x"), 0);
- pageOffsetY[curPageNum - 1] =
- XFAFormField::getMeasurement(contentArea->findAttr("y"), 0);
- // looks like the contentArea offset (pageOffsetX/Y) should
- // not be added to fields defined inside the pageArea
- // element (?)
- xSubOffset -= pageOffsetX[curPageNum - 1];
- ySubOffset -= pageOffsetY[curPageNum - 1];
- curXOffset -= pageOffsetX[curPageNum - 1];
- curYOffset -= pageOffsetY[curPageNum - 1];
- }
- }
- }
-
- if (elem->isElement("exclGroup")) {
- exclGroupName = name;
- } else {
- exclGroupName = NULL;
- }
-
- for (child = elem->getFirstChild(); child; child = child->getNextChild()) {
- if (child->isElement()) {
- scanNode((ZxElement *)child, name, fullName, inPageSet,
- newTableInfo, nameCount, nameIdx, fullNameCount, fullNameIdx,
- exclGroupName, catalog);
- }
- }
-
- curXOffset -= xSubOffset;
- curYOffset -= ySubOffset;
-
- if (newTableInfo != tableInfo) {
- delete newTableInfo;
- }
-
- if (elem->isElement("pageSet")) {
- pageSetNPages = curPageNum;
- curPageNum = savedPageNum;
- inPageSet = gFalse;
- }
-}
-
-GString *XFAForm::getNodeName(ZxElement *elem) {
- ZxElement *bindElem;
- ZxAttr *attr;
-
- if (!(elem->getType()->cmp("field") &&
- (bindElem = elem->findFirstChildElement("bind")) &&
- (attr = bindElem->findAttr("match")) &&
- !attr->getValue()->cmp("none")) &&
- !elem->isElement("area") &&
- (attr = elem->findAttr("name"))) {
- return attr->getValue();
- }
- return NULL;
-}
-
-GString *XFAForm::getNodeFullName(ZxElement *elem) {
- ZxAttr *attr;
-
- if (!elem->isElement("area") &&
- (attr = elem->findAttr("name"))) {
- return attr->getValue();
- }
- return NULL;
-}
-
-GBool XFAForm::nodeIsBindGlobal(ZxElement *elem) {
- ZxElement *bindElem;
- ZxAttr *attr;
-
- return (bindElem = elem->findFirstChildElement("bind")) &&
- (attr = bindElem->findAttr("match")) &&
- !attr->getValue()->cmp("global");
-}
-
-void XFAForm::draw(int pageNum, Gfx *gfx, GBool printing) {
- GfxFontDict *fontDict;
- Object obj1;
- int i;
-
- // build the font dictionary
- fontDict = NULL;
- if (resourceDict.isDict()) {
- if (resourceDict.dictLookup("Font", &obj1)->isDict()) {
- fontDict = new GfxFontDict(doc->getXRef(), NULL, obj1.getDict());
- }
- obj1.free();
- }
-
- for (i = 0; i < fields->getLength(); ++i) {
- ((XFAFormField *)fields->get(i))->draw(pageNum, gfx, printing, fontDict);
- }
-
- delete fontDict;
-}
-
-int XFAForm::getNumFields() {
- return fields->getLength();
-}
-
-FormField *XFAForm::getField(int idx) {
- return (XFAFormField *)fields->get(idx);
-}
-
-//------------------------------------------------------------------------
-// XFAFormField
-//------------------------------------------------------------------------
-
-XFAFormField::XFAFormField(XFAForm *xfaFormA, ZxElement *xmlA,
- GString *nameA, GString *fullNameA,
- GString *exclGroupNameA,
- int pageNumA, double xOffsetA, double yOffsetA,
- double columnWidthA, double rowHeightA) {
- xfaForm = xfaFormA;
- xml = xmlA;
- name = nameA;
- fullName = fullNameA;
- exclGroupName = exclGroupNameA;
- pageNum = pageNumA;
- xOffset = xOffsetA;
- yOffset = yOffsetA;
- columnWidth = columnWidthA;
- rowHeight = rowHeightA;
-}
-
-XFAFormField::~XFAFormField() {
- delete name;
- delete fullName;
- if (exclGroupName) {
- delete exclGroupName;
- }
-}
-
-int XFAFormField::getPageNum() {
- return pageNum;
-}
-
-const char *XFAFormField::getType() {
- ZxElement *uiElem;
- ZxNode *node;
-
- if ((uiElem = xml->findFirstChildElement("ui"))) {
- for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) {
- if (node->isElement("textEdit")) {
- return "Text";
- } else if (node->isElement("numericEdit")) {
- return "Numeric";
- } else if (node->isElement("dateTimeEdit")) {
- return "DateTime";
- } else if (node->isElement("choiceList")) {
- return "ChoiceList";
- } else if (node->isElement("checkButton")) {
- return "CheckButton";
- } else if (node->isElement("barcode")) {
- return "BarCode";
- }
- //~ other field types go here
- }
- }
- return NULL;
-}
-
-Unicode *XFAFormField::getName(int *length) {
- //~ assumes name is UTF-8
- return utf8ToUnicode(name, length);
-}
-
-Unicode *XFAFormField::getValue(int *length) {
- ZxElement *uiElem;
- ZxNode *node;
- GString *s;
-
- //~ assumes value is UTF-8
- s = NULL;
- if ((uiElem = xml->findFirstChildElement("ui"))) {
- for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) {
- if (node->isElement("textEdit")) {
- s = getFieldValue("text");
- break;
- } else if (node->isElement("numericEdit")) {
- //~ not sure if this is correct
- s = getFieldValue("text");
- break;
- } else if (node->isElement("dateTimeEdit")) {
- s = getFieldValue("text");
- break;
- } else if (node->isElement("checkButton")) {
- if (!(s = getFieldValue("integer"))) {
- s = getFieldValue("text");
- }
- break;
- } else if (node->isElement("barcode")) {
- s = getFieldValue("text");
- break;
- }
- //~ other field types go here
- }
- }
- if (!s) {
- return NULL;
- }
- return utf8ToUnicode(s, length);
-}
-
-Unicode *XFAFormField::utf8ToUnicode(GString *s, int *length) {
- Unicode *u;
- int n, size, c0, c1, c2, c3, c4, c5, i;
-
- n = size = 0;
- u = NULL;
- i = 0;
- while (i < s->getLength()) {
- if (n == size) {
- size = size ? size * 2 : 16;
- u = (Unicode *)greallocn(u, size, sizeof(Unicode));
- }
- c0 = s->getChar(i++) & 0xff;
- if (c0 <= 0x7f) {
- u[n++] = c0;
- } else if (c0 <= 0xdf && i < n) {
- c1 = s->getChar(i++) & 0xff;
- u[n++] = ((c0 & 0x1f) << 6) | (c1 & 0x3f);
- } else if (c0 <= 0xef && i+1 < n) {
- c1 = s->getChar(i++) & 0xff;
- c2 = s->getChar(i++) & 0xff;
- u[n++] = ((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) | (c2 & 0x3f);
- } else if (c0 <= 0xf7 && i+2 < n) {
- c1 = s->getChar(i++) & 0xff;
- c2 = s->getChar(i++) & 0xff;
- c3 = s->getChar(i++) & 0xff;
- u[n++] = ((c0 & 0x07) << 18) | ((c1 & 0x3f) << 12) | ((c2 & 0x3f) << 6)
- | (c3 & 0x3f);
- } else if (c0 <= 0xfb && i+3 < n) {
- c1 = s->getChar(i++) & 0xff;
- c2 = s->getChar(i++) & 0xff;
- c3 = s->getChar(i++) & 0xff;
- c4 = s->getChar(i++) & 0xff;
- u[n++] = ((c0 & 0x03) << 24) | ((c1 & 0x3f) << 18) | ((c2 & 0x3f) << 12)
- | ((c3 & 0x3f) << 6) | (c4 & 0x3f);
- } else if (c0 <= 0xfd && i+4 < n) {
- c1 = s->getChar(i++) & 0xff;
- c2 = s->getChar(i++) & 0xff;
- c3 = s->getChar(i++) & 0xff;
- c4 = s->getChar(i++) & 0xff;
- c5 = s->getChar(i++) & 0xff;
- u[n++] = ((c0 & 0x01) << 30) | ((c1 & 0x3f) << 24) | ((c2 & 0x3f) << 18)
- | ((c3 & 0x3f) << 12) | ((c4 & 0x3f) << 6) | (c5 & 0x3f);
- } else {
- u[n++] = '?';
- }
- }
- *length = n;
- return u;
-}
-
-void XFAFormField::getBBox(double *llx, double *lly,
- double *urx, double *ury) {
- double xfaX, xfaY, xfaW, xfaH, pdfX, pdfY, pdfW, pdfH;
- int pdfRot;
-
- getRectangle(&xfaX, &xfaY, &xfaW, &xfaH,
- &pdfX, &pdfY, &pdfW, &pdfH, &pdfRot);
- *llx = pdfX;
- *lly = pdfY;
- *urx = pdfX + pdfW;
- *ury = pdfY + pdfH;
-}
-
-void XFAFormField::getFont(Ref *fontID, double *fontSize) {
- ZxElement *fontElem;
- ZxAttr *attr;
- GBool bold, italic;
-
- fontID->num = fontID->gen = -1;
- *fontSize = 0;
- if ((fontElem = xml->findFirstChildElement("font"))) {
- bold = italic = gFalse;
- if ((attr = fontElem->findAttr("weight"))) {
- if (!attr->getValue()->cmp("bold")) {
- bold = gTrue;
- }
- }
- if ((attr = fontElem->findAttr("posture"))) {
- if (!attr->getValue()->cmp("italic")) {
- italic = gTrue;
- }
- }
- if ((attr = fontElem->findAttr("typeface"))) {
- *fontID = findFontName(attr->getValue(), bold, italic);
- }
- if ((attr = fontElem->findAttr("size"))) {
- *fontSize = getMeasurement(attr, 0);
- }
- }
-}
-
-void XFAFormField::getColor(double *red, double *green, double *blue) {
- ZxElement *fontElem, *fillElem, *colorElem;
- ZxAttr *attr;
- int r, g, b;
-
- *red = *green = *blue = 0;
- if ((fontElem = xml->findFirstChildElement("font"))) {
- if ((fillElem = fontElem->findFirstChildElement("fill"))) {
- if ((colorElem = fillElem->findFirstChildElement("color"))) {
- if ((attr = colorElem->findAttr("value"))) {
- if (sscanf(attr->getValue()->getCString(), "%d,%d,%d",
- &r, &g, &b) == 3) {
- *red = r / 255.0;
- *green = g / 255.0;
- *blue = b / 255.0;
- }
- }
- }
- }
- }
-}
-
-void XFAFormField::draw(int pageNumA, Gfx *gfx, GBool printing,
- GfxFontDict *fontDict) {
- ZxElement *uiElem;
- ZxNode *node;
- ZxAttr *attr;
- GString *appearBuf;
- MemStream *appearStream;
- Object appearDict, appearance, resourceDict, resourceSubdict;
- Object fontResources, fontResource, defaultFont;
- Object obj1, obj2;
- char *resType, *fontName;
- double mat[6];
- double xfaX, xfaY, xfaW, xfaH, pdfX, pdfY, pdfW, pdfH;
- int rot3, i;
-
- if (pageNumA != pageNum) {
- return;
- }
-
- // check the 'presence' attribute
- if ((attr = xml->findAttr("presence"))) {
- if (!attr->getValue()->cmp("hidden") ||
- !attr->getValue()->cmp("invisible")) {
- return;
- }
- }
-
- getRectangle(&xfaX, &xfaY, &xfaW, &xfaH, &pdfX, &pdfY, &pdfW, &pdfH, &rot3);
-
- // generate transform matrix
- switch (rot3) {
- case 0:
- default:
- mat[0] = 1; mat[1] = 0;
- mat[2] = 0; mat[3] = 1;
- mat[4] = 0; mat[5] = 0;
- break;
- case 90:
- mat[0] = 0; mat[1] = 1;
- mat[2] = -1; mat[3] = 0;
- mat[4] = xfaH; mat[5] = 0;
- break;
- case 180:
- mat[0] = -1; mat[1] = 0;
- mat[2] = 0; mat[3] = -1;
- mat[4] = xfaW; mat[5] = xfaH;
- break;
- case 270:
- mat[0] = 0; mat[1] = -1;
- mat[2] = 1; mat[3] = 0;
- mat[4] = 0; mat[5] = xfaW;
- break;
- }
-
- // get the appearance stream data
- appearBuf = new GString();
-#if 0 //~ for debugging
- double debugPad = 0;
- appearBuf->appendf("q 1 1 0 rg {0:.4f} {1:.4f} {2:.4f} {3:.4f} re f Q\n",
- debugPad, debugPad, xfaW - 2*debugPad, xfaH - 2*debugPad);
-#endif
- if ((uiElem = xml->findFirstChildElement("ui"))) {
- for (node = uiElem->getFirstChild(); node; node = node->getNextChild()) {
- if (node->isElement("textEdit") ||
- node->isElement("numericEdit")) {
- drawTextEdit(fontDict, xfaW, xfaH, rot3, appearBuf);
- break;
- } else if (node->isElement("dateTimeEdit")) {
- drawTextEdit(fontDict, xfaW, xfaH, rot3, appearBuf);
- break;
- } else if (node->isElement("choiceList")) {
- drawTextEdit(fontDict, xfaW, xfaH, rot3, appearBuf);
- break;
- } else if (node->isElement("checkButton")) {
- drawCheckButton(fontDict, xfaW, xfaH, rot3, appearBuf);
- break;
- } else if (node->isElement("barcode")) {
- drawBarCode(fontDict, xfaW, xfaH, rot3, appearBuf);
- break;
- }
- //~ other field types go here
- }
- } else {
- drawTextEdit(fontDict, xfaW, xfaH, rot3, appearBuf);
- }
-
- // create the appearance stream
- appearDict.initDict(xfaForm->doc->getXRef());
- appearDict.dictAdd(copyString("Length"),
- obj1.initInt(appearBuf->getLength()));
- appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
- obj1.initArray(xfaForm->doc->getXRef());
- obj1.arrayAdd(obj2.initReal(0));
- obj1.arrayAdd(obj2.initReal(0));
- obj1.arrayAdd(obj2.initReal(xfaW));
- obj1.arrayAdd(obj2.initReal(xfaH));
- appearDict.dictAdd(copyString("BBox"), &obj1);
- obj1.initArray(xfaForm->doc->getXRef());
- obj1.arrayAdd(obj2.initReal(mat[0]));
- obj1.arrayAdd(obj2.initReal(mat[1]));
- obj1.arrayAdd(obj2.initReal(mat[2]));
- obj1.arrayAdd(obj2.initReal(mat[3]));
- obj1.arrayAdd(obj2.initReal(mat[4]));
- obj1.arrayAdd(obj2.initReal(mat[5]));
- appearDict.dictAdd(copyString("Matrix"), &obj1);
- // NB: we need to deep-copy the resource dict and font resource
- // subdict, because the font subdict is modified, and multiple
- // threads can be sharing these objects.
- resourceDict.initDict(xfaForm->doc->getXRef());
- if (xfaForm->resourceDict.isDict()) {
- for (i = 0; i < xfaForm->resourceDict.dictGetLength(); ++i) {
- resType = xfaForm->resourceDict.dictGetKey(i);
- if (strcmp(resType, "Font")) {
- xfaForm->resourceDict.dictGetVal(i, &resourceSubdict);
- resourceDict.dictAdd(copyString(resType), &resourceSubdict);
- }
- }
- }
- fontResources.initDict(xfaForm->doc->getXRef());
- if (xfaForm->resourceDict.isDict() &&
- xfaForm->resourceDict.dictLookup("Font", &resourceSubdict)->isDict()) {
- for (i = 0; i < resourceSubdict.dictGetLength(); ++i) {
- fontName = resourceSubdict.dictGetKey(i);
- resourceSubdict.dictGetVal(i, &fontResource);
- fontResources.dictAdd(copyString(fontName), &fontResource);
- }
- resourceSubdict.free();
- }
- defaultFont.initDict(xfaForm->doc->getXRef());
- defaultFont.dictAdd(copyString("Type"), obj1.initName("Font"));
- defaultFont.dictAdd(copyString("Subtype"), obj1.initName("Type1"));
- defaultFont.dictAdd(copyString("BaseFont"), obj1.initName("Helvetica"));
- defaultFont.dictAdd(copyString("Encoding"), obj1.initName("WinAnsiEncoding"));
- fontResources.dictAdd(copyString("xpdf_default_font"), &defaultFont);
- resourceDict.dictAdd(copyString("Font"), &fontResources);
- appearDict.dictAdd(copyString("Resources"), &resourceDict);
- appearStream = new MemStream(appearBuf->getCString(), 0,
- appearBuf->getLength(), &appearDict);
- appearance.initStream(appearStream);
- gfx->drawAnnot(&appearance, NULL, pdfX, pdfY, pdfX + pdfW, pdfY + pdfH);
- appearance.free();
- delete appearBuf;
-}
-
-void XFAFormField::getRectangle(double *xfaX, double *xfaY,
- double *xfaW, double *xfaH,
- double *pdfX, double *pdfY,
- double *pdfW, double *pdfH,
- int *pdfRot) {
- Page *page;
- PDFRectangle *pageRect;
- ZxElement *captionElem, *paraElem, *marginElem;
- ZxAttr *attr;
- double x2, y2, w2, h2, t;
- double anchorX, anchorY;
- int pageRot, rot;
-
- page = xfaForm->doc->getCatalog()->getPage(pageNum);
- pageRect = page->getMediaBox();
- pageRot = page->getRotate();
-
- anchorX = 0;
- anchorY = 0;
- if ((attr = xml->findAttr("anchorType"))) {
- if (!attr->getValue()->cmp("topLeft")) {
- anchorX = 0;
- anchorY = 0;
- } else if (!attr->getValue()->cmp("topCenter")) {
- anchorX = 0.5;
- anchorY = 0;
- } else if (!attr->getValue()->cmp("topRight")) {
- anchorX = 1;
- anchorY = 0;
- } else if (!attr->getValue()->cmp("middleLeft")) {
- anchorX = 0;
- anchorY = 0.5;
- } else if (!attr->getValue()->cmp("middleCenter")) {
- anchorX = 0.5;
- anchorY = 0.5;
- } else if (!attr->getValue()->cmp("middleRight")) {
- anchorX = 1;
- anchorY = 0.5;
- } else if (!attr->getValue()->cmp("bottomLeft")) {
- anchorX = 0;
- anchorY = 1;
- } else if (!attr->getValue()->cmp("bottomCenter")) {
- anchorX = 0.5;
- anchorY = 1;
- } else if (!attr->getValue()->cmp("bottomRight")) {
- anchorX = 1;
- anchorY = 1;
- }
- }
- *xfaX = getMeasurement(xml->findAttr("x"), 0) + xOffset;
- *xfaY = getMeasurement(xml->findAttr("y"), 0) + yOffset;
- if (!(attr = xml->findAttr("w"))) {
- attr = xml->findAttr("minW");
- }
- *xfaW = getMeasurement(attr, 0);
- if (*xfaW < columnWidth) {
- *xfaW = columnWidth;
- }
- if (!(attr = xml->findAttr("h"))) {
- attr = xml->findAttr("minH");
- }
- *xfaH = getMeasurement(attr, 0);
- if (*xfaH < rowHeight) {
- *xfaH = rowHeight;
- }
- if ((attr = xml->findAttr("rotate"))) {
- rot = atoi(attr->getValue()->getCString());
- if ((rot %= 360) < 0) {
- rot += 360;
- }
- } else {
- rot = 0;
- }
-
- // look for <caption reserve="...."> -- add the caption width/height
- // (plus a margin allowance)
- if ((captionElem = xml->findFirstChildElement("caption"))) {
- if ((attr = captionElem->findAttr("reserve"))) {
- t = getMeasurement(attr, 0);
- if ((attr = captionElem->findAttr("placement"))) {
- if (!attr->getValue()->cmp("left")) {
- *xfaX += t + 1.5;
- *xfaW -= t + 1.5;
- } else if (!attr->getValue()->cmp("right")) {
- *xfaW -= t + 1.5;
- } else if (!attr->getValue()->cmp("top")) {
- *xfaY += t;
- *xfaH -= t;
- } else if (!attr->getValue()->cmp("bottom")) {
- *xfaH -= t;
- }
- } else {
- // default is placement="left"
- *xfaX += t + 1.5;
- *xfaW -= t + 1.5;
- }
- }
- }
-
- // look for <margin>
- if ((marginElem = xml->findFirstChildElement("margin"))) {
- if ((attr = marginElem->findAttr("leftInset"))) {
- t = getMeasurement(attr, 0);
- *xfaX += t;
- *xfaW -= t;
- }
- if ((attr = marginElem->findAttr("rightInset"))) {
- t = getMeasurement(attr, 0);
- *xfaW -= t;
- }
- if ((attr = marginElem->findAttr("topInset"))) {
- t = getMeasurement(attr, 0);
- *xfaY += t;
- *xfaH -= t;
- }
- if ((attr = marginElem->findAttr("bottomInset"))) {
- t = getMeasurement(attr, 0);
- *xfaH -= t;
- }
- }
-
- // look for <para> -- add the margins
- if ((paraElem = xml->findFirstChildElement("para"))) {
- if ((attr = paraElem->findAttr("marginLeft"))) {
- t = getMeasurement(attr, 0);
- *xfaX += t;
- *xfaW -= t;
- }
- if ((attr = paraElem->findAttr("marginRight"))) {
- t = getMeasurement(attr, 0);
- *xfaW -= t;
- }
- if ((attr = paraElem->findAttr("spaceAbove"))) {
- t = getMeasurement(attr, 0);
- *xfaY += t;
- *xfaH -= t;
- }
- if ((attr = paraElem->findAttr("spaceBelow"))) {
- t = getMeasurement(attr, 0);
- *xfaH -= t;
- }
- }
-
- // get annot rect (UL corner, width, height) in XFA coords
- // notes:
- // - XFA coordinates are top-left origin, after page rotation
- // - XFA coordinates are dependent on choice of anchor point
- // and field rotation
- switch (rot) {
- case 0:
- default:
- x2 = *xfaX - anchorX * *xfaW;
- y2 = *xfaY - anchorY * *xfaH;
- w2 = *xfaW;
- h2 = *xfaH;
- break;
- case 90:
- x2 = *xfaX - anchorY * *xfaH;
- y2 = *xfaY - (1 - anchorX) * *xfaW;
- w2 = *xfaH;
- h2 = *xfaW;
- break;
- case 180:
- x2 = *xfaX - (1 - anchorX) * *xfaW;
- y2 = *xfaY - (1 - anchorY) * *xfaH;
- w2 = *xfaW;
- h2 = *xfaH;
- break;
- case 270:
- x2 = *xfaX - (1 - anchorY) * *xfaH;
- y2 = *xfaY - anchorX * *xfaW;
- w2 = *xfaH;
- h2 = *xfaW;
- break;
- }
-
- // convert annot rect to PDF coords (LL corner, width, height),
- // taking page rotation into account
- switch (pageRot) {
- case 0:
- default:
- *pdfX = pageRect->x1 + x2;
- *pdfY = pageRect->y2 - (y2 + h2);
- *pdfW = w2;
- *pdfH = h2;
- break;
- case 90:
- *pdfX = pageRect->x1 + y2;
- *pdfY = pageRect->y1 + x2;
- *pdfW = h2;
- *pdfH = w2;
- break;
- case 180:
- *pdfX = pageRect->x2 - (x2 + w2);
- *pdfY = pageRect->y1 + y2;
- *pdfW = w2;
- *pdfH = h2;
- break;
- case 270:
- *pdfX = pageRect->x2 - (y2 + h2);
- *pdfY = pageRect->y1 + (x2 + w2);
- *pdfW = h2;
- *pdfH = w2;
- break;
- }
- *pdfRot = (rot + pageRot) % 360;
-}
-
-void XFAFormField::drawTextEdit(GfxFontDict *fontDict,
- double w, double h, int rot,
- GString *appearBuf) {
- ZxElement *formatElem, *pictureElem;
- ZxNode *pictureChildElem;
- ZxElement *valueElem, *textElem, *uiElem, *textEditElem, *combElem;
- ZxElement *fontElem, *paraElem;
- ZxAttr *attr;
- GString *picture, *value, *formattedValue, *fontName;
- double fontSize;
- int maxChars, combCells;
- GBool multiLine, bold, italic;
- XFAHorizAlign hAlign;
- XFAVertAlign vAlign;
-
- if (!(value = getFieldValue("text"))) {
- return;
- }
-
- uiElem = xml->findFirstChildElement("ui");
-
- //--- picture formatting
- if (uiElem &&
- (formatElem = xml->findFirstChildElement("format")) &&
- (pictureElem = formatElem->findFirstChildElement("picture")) &&
- (pictureChildElem = pictureElem->getFirstChild()) &&
- pictureChildElem->isCharData()) {
- picture = ((ZxCharData *)pictureChildElem)->getData();
- if (uiElem->findFirstChildElement("dateTimeEdit")) {
- formattedValue = pictureFormatDateTime(value, picture);
- } else if (uiElem->findFirstChildElement("numericEdit")) {
- formattedValue = pictureFormatNumber(value, picture);
- } else if (uiElem->findFirstChildElement("textEdit")) {
- formattedValue = pictureFormatText(value, picture);
- } else {
- formattedValue = value->copy();
- }
- } else {
- formattedValue = value->copy();
- }
-
- maxChars = 0;
- if ((valueElem = xml->findFirstChildElement("value")) &&
- (textElem = valueElem->findFirstChildElement("text")) &&
- (attr = textElem->findAttr("maxChars"))) {
- maxChars = atoi(attr->getValue()->getCString());
- }
-
- multiLine = gFalse;
- combCells = 0;
- if (uiElem &&
- (textEditElem = uiElem->findFirstChildElement("textEdit"))) {
- if ((attr = textEditElem->findAttr("multiLine")) &&
- !attr->getValue()->cmp("1")) {
- multiLine = gTrue;
- }
- if ((combElem = textEditElem->findFirstChildElement("comb"))) {
- if ((attr = combElem->findAttr("numberOfCells"))) {
- combCells = atoi(attr->getValue()->getCString());
- } else {
- combCells = maxChars;
- }
- }
- }
-
- fontName = NULL;
- fontSize = 10;
- bold = gFalse;
- italic = gFalse;
- if ((fontElem = xml->findFirstChildElement("font"))) {
- if ((attr = fontElem->findAttr("typeface"))) {
- fontName = attr->getValue()->copy();
- }
- if ((attr = fontElem->findAttr("weight"))) {
- if (!attr->getValue()->cmp("bold")) {
- bold = gTrue;
- }
- }
- if ((attr = fontElem->findAttr("posture"))) {
- if (!attr->getValue()->cmp("italic")) {
- italic = gTrue;
- }
- }
- if ((attr = fontElem->findAttr("size"))) {
- fontSize = getMeasurement(attr, fontSize);
- }
- }
- if (!fontName) {
- fontName = new GString("Courier");
- }
-
- hAlign = xfaHAlignLeft;
- vAlign = xfaVAlignTop;
- if ((paraElem = xml->findFirstChildElement("para"))) {
- if ((attr = paraElem->findAttr("hAlign"))) {
- if (!attr->getValue()->cmp("left")) {
- hAlign = xfaHAlignLeft;
- } else if (!attr->getValue()->cmp("center")) {
- hAlign = xfaHAlignCenter;
- } else if (!attr->getValue()->cmp("right")) {
- hAlign = xfaHAlignRight;
- }
- //~ other hAlign values (justify, justifyAll, radix) are
- //~ currently unsupported
- }
- if ((attr = paraElem->findAttr("vAlign"))) {
- if (!attr->getValue()->cmp("top")) {
- vAlign = xfaVAlignTop;
- } else if (!attr->getValue()->cmp("bottom")) {
- vAlign = xfaVAlignBottom;
- } else if (!attr->getValue()->cmp("middle")) {
- vAlign = xfaVAlignMiddle;
- }
- }
- }
-
- drawText(formattedValue, multiLine, combCells,
- fontName, bold, italic, fontSize,
- hAlign, vAlign, 0, 0, w, h, gFalse, fontDict, appearBuf);
- delete fontName;
- delete formattedValue;
-}
-
-void XFAFormField::drawCheckButton(GfxFontDict *fontDict,
- double w, double h, int rot,
- GString *appearBuf) {
- ZxElement *items;
- ZxNode *firstItem, *firstItemVal;
- const char *itemType;
- const char *onValue;
- GString *value;
- double x, y;
-
- // get the "on" value for this check button
- itemType = "integer";
- onValue = "1";
- if ((items = xml->findFirstChildElement("items"))) {
- if ((firstItem = items->getFirstChild())) {
- if (firstItem->isElement("text") && firstItem->getFirstChild()) {
- itemType = "text";
- if ((firstItemVal = firstItem->getFirstChild())->isCharData()) {
- onValue = ((ZxCharData *)firstItemVal)->getData()->getCString();
- }
- } else if (firstItem->isElement("integer") &&
- firstItem->getFirstChild()) {
- itemType = "integer";
- if ((firstItemVal = firstItem->getFirstChild())->isCharData()) {
- onValue = ((ZxCharData *)firstItemVal)->getData()->getCString();
- }
- }
- }
- }
-
- // compare its value to the "on" value
- if (!(value = getFieldValue(itemType))) {
- return;
- }
- if (value->cmp(onValue)) {
- return;
- }
-
- // adjust shape to be square
- x = y = 0;
- if (w > h) {
- x += 0.5 * (w - h);
- w = h;
- } else {
- y += 0.5 * (h - w);
- h = w;
- }
-
- // margins
- x += 1.5;
- w -= 3;
- y += 1.5;
- h -= 3;
-
- //~ this should look at the "shape" and "mark" attributes
-
- appearBuf->appendf("0.5 w {0:.4f} {1:.4f} m {2:.4f} {3:.4f} l {0:.4f} {3:.4f} m {2:.4f} {1:.4f} l S\n",
- x, y, x + w, y + h);
-}
-
-void XFAFormField::drawBarCode(GfxFontDict *fontDict,
- double w, double h, int rot,
- GString *appearBuf) {
- ZxElement *uiElem, *barcodeElem, *fontElem;
- ZxAttr *attr;
- GString *value, *value2, *barcodeType, *textLocation, *fontName, *s1, *s2;
- XFAVertAlign textAlign;
- double wideNarrowRatio, moduleWidth, moduleHeight, fontSize;
- double yText, wText, yBarcode, hBarcode, wNarrow, xx;
- GBool doText;
- int dataLength, errorCorrectionLevel, checksum;
- GBool bold, italic;
- char *p;
- int i, j, c;
-
- //--- get field value
- if (!(value = getFieldValue("text"))) {
- return;
- }
-
- //--- get font
- fontName = NULL;
- fontSize = 0.2 * h;
- bold = gFalse;
- italic = gFalse;
- if ((fontElem = xml->findFirstChildElement("font"))) {
- if ((attr = fontElem->findAttr("typeface"))) {
- fontName = attr->getValue()->copy();
- }
- if ((attr = fontElem->findAttr("weight"))) {
- if (!attr->getValue()->cmp("bold")) {
- bold = gTrue;
- }
- }
- if ((attr = fontElem->findAttr("posture"))) {
- if (!attr->getValue()->cmp("italic")) {
- italic = gTrue;
- }
- }
- if ((attr = fontElem->findAttr("size"))) {
- fontSize = getMeasurement(attr, fontSize);
- }
- }
- if (!fontName) {
- fontName = new GString("Courier");
- }
-
- //--- get field attributes
- barcodeType = NULL;
- wideNarrowRatio = 3;
- moduleWidth = 0;
- moduleHeight = 0;
- dataLength = 0;
- errorCorrectionLevel = 0;
- textLocation = NULL;
- if ((uiElem = xml->findFirstChildElement("ui")) &&
- (barcodeElem = uiElem->findFirstChildElement("barcode"))) {
- if ((attr = barcodeElem->findAttr("type"))) {
- barcodeType = attr->getValue();
- }
- if ((attr = barcodeElem->findAttr("wideNarrowRatio"))) {
- s1 = attr->getValue();
- if ((p = strchr(s1->getCString(), ':'))) {
- s2 = new GString(s1, 0, (int)(p - s1->getCString()));
- wideNarrowRatio = atof(p + 1);
- if (wideNarrowRatio == 0) {
- wideNarrowRatio = 1;
- }
- wideNarrowRatio = atof(s2->getCString()) / wideNarrowRatio;
- delete s2;
- } else {
- wideNarrowRatio = atof(s1->getCString());
- }
- }
- if ((attr = barcodeElem->findAttr("moduleWidth"))) {
- moduleWidth = getMeasurement(attr, (0.25 / 25.4) * 72.0); // 0.25mm
- }
- if ((attr = barcodeElem->findAttr("moduleHeight"))) {
- moduleHeight = getMeasurement(attr, (0.5 / 25.4) * 72.0); // 0.5mm
- }
- if ((attr = barcodeElem->findAttr("dataLength"))) {
- dataLength = atoi(attr->getValue()->getCString());
- }
- if ((attr = barcodeElem->findAttr("errorCorrectionLevel"))) {
- errorCorrectionLevel = atoi(attr->getValue()->getCString());
- }
- if ((attr = barcodeElem->findAttr("textLocation"))) {
- textLocation = attr->getValue();
- }
- }
- if (!barcodeType) {
- error(errSyntaxError, -1, "Missing 'type' attribute in XFA barcode field");
- delete fontName;
- return;
- }
-
- //--- compute the embedded text type position
- doText = gTrue;
- yText = yBarcode = hBarcode = 0;
- if (textLocation && !textLocation->cmp("above")) {
- textAlign = xfaVAlignTop;
- yText = h;
- yBarcode = 0;
- hBarcode = h - fontSize;
- } else if (textLocation && !textLocation->cmp("belowEmbedded")) {
- textAlign = xfaVAlignBottom;
- yText = 0;
- yBarcode = 0;
- hBarcode = h;
- } else if (textLocation && !textLocation->cmp("aboveEmbedded")) {
- textAlign = xfaVAlignTop;
- yText = h;
- yBarcode = 0;
- hBarcode = h;
- } else if (textLocation && !textLocation->cmp("none")) {
- textAlign = xfaVAlignBottom; // make gcc happy
- doText = gFalse;
- } else { // default is "below"
- textAlign = xfaVAlignBottom;
- yText = 0;
- yBarcode = fontSize;
- hBarcode = h - fontSize;
- }
- wText = w;
-
- //--- remove extraneous start/stop chars
- value2 = value->copy();
- if (!barcodeType->cmp("code3Of9")) {
- if (value2->getLength() >= 1 && value2->getChar(0) == '*') {
- value2->del(0);
- }
- if (value2->getLength() >= 1 &&
- value2->getChar(value2->getLength() - 1) == '*') {
- value2->del(value2->getLength() - 1);
- }
- }
-
- //--- draw the bar code
- if (!barcodeType->cmp("code3Of9")) {
- if (!dataLength) {
- error(errSyntaxError, -1,
- "Missing 'dataLength' attribute in XFA barcode field");
- goto err;
- }
- appearBuf->append("0 g\n");
- wNarrow = w / ((7 + 3 * wideNarrowRatio) * (dataLength + 2));
- xx = 0;
- for (i = -1; i <= value2->getLength(); ++i) {
- if (i < 0 || i >= value2->getLength()) {
- c = '*';
- } else {
- c = value2->getChar(i) & 0x7f;
- }
- for (j = 0; j < 10; j += 2) {
- appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
- xx, yBarcode,
- (code3Of9Data[c][j] ? wideNarrowRatio : 1) * wNarrow,
- hBarcode);
- xx += ((code3Of9Data[c][j] ? wideNarrowRatio : 1) +
- (code3Of9Data[c][j+1] ? wideNarrowRatio : 1)) * wNarrow;
- }
- }
- // center the text on the drawn barcode (not the max length barcode)
- wText = (value2->getLength() + 2) * (7 + 3 * wideNarrowRatio) * wNarrow;
- } else if (!barcodeType->cmp("code128B")) {
- if (!dataLength) {
- error(errSyntaxError, -1,
- "Missing 'dataLength' attribute in XFA barcode field");
- goto err;
- }
- appearBuf->append("0 g\n");
- wNarrow = w / (11 * (dataLength + 3) + 2);
- xx = 0;
- checksum = 0;
- for (i = -1; i <= value2->getLength() + 1; ++i) {
- if (i == -1) {
- // start code B
- c = 104;
- checksum += c;
- } else if (i == value2->getLength()) {
- // checksum
- c = checksum % 103;
- } else if (i == value2->getLength() + 1) {
- // stop code
- c = 106;
- } else {
- c = value2->getChar(i) & 0xff;
- if (c >= 32 && c <= 127) {
- c -= 32;
- } else {
- c = 0;
- }
- checksum += (i + 1) * c;
- }
- for (j = 0; j < 6; j += 2) {
- appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
- xx, yBarcode,
- code128Data[c][j] * wNarrow, hBarcode);
- xx += (code128Data[c][j] + code128Data[c][j+1]) * wNarrow;
- }
- }
- // final bar of the stop code
- appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
- xx, yBarcode, 2 * wNarrow, hBarcode);
- // center the text on the drawn barcode (not the max length barcode)
- wText = (11 * (value2->getLength() + 3) + 2) * wNarrow;
- } else if (!barcodeType->cmp("pdf417")) {
- drawPDF417Barcode(w, h, moduleWidth, moduleHeight, errorCorrectionLevel,
- value2, appearBuf);
- doText = gFalse;
- } else {
- error(errSyntaxError, -1,
- "Unimplemented barcode type '{0:t}' in XFA barcode field",
- barcodeType);
- }
- //~ add other barcode types here
-
- //--- draw the embedded text
- if (doText) {
- appearBuf->append("0 g\n");
- drawText(value2, gFalse, 0,
- fontName, bold, italic, fontSize,
- xfaHAlignCenter, textAlign, 0, yText, wText, h, gTrue,
- fontDict, appearBuf);
- }
-
- err:
- delete fontName;
- delete value2;
-}
-
-Object *XFAFormField::getResources(Object *res) {
- return xfaForm->resourceDict.copy(res);
-}
-
-double XFAFormField::getMeasurement(ZxAttr *attr, double defaultVal) {
- GString *s;
-
- if (!attr) {
- return defaultVal;
- }
- s = attr->getValue();
- return getMeasurement(s, 0);
-}
-
-double XFAFormField::getMeasurement(GString *s, int begin) {
- double val, mul;
- GBool neg;
- int i;
-
- i = begin;
- neg = gFalse;
- if (i < s->getLength() && s->getChar(i) == '+') {
- ++i;
- } else if (i < s->getLength() && s->getChar(i) == '-') {
- neg = gTrue;
- ++i;
- }
- val = 0;
- while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') {
- val = val * 10 + s->getChar(i) - '0';
- ++i;
- }
- if (i < s->getLength() && s->getChar(i) == '.') {
- ++i;
- mul = 0.1;
- while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') {
- val += mul * (s->getChar(i) - '0');
- mul *= 0.1;
- ++i;
- }
- }
- if (neg) {
- val = -val;
- }
- if (i+1 < s->getLength()) {
- if (s->getChar(i) == 'i' && s->getChar(i+1) == 'n') {
- val *= 72;
- } else if (s->getChar(i) == 'p' && s->getChar(i+1) == 't') {
- // no change
- } else if (s->getChar(i) == 'c' && s->getChar(i+1) == 'm') {
- val *= 72 / 2.54;
- } else if (s->getChar(i) == 'm' && s->getChar(i+1) == 'm') {
- val *= 72 / 25.4;
- } else {
- // default to inches
- val *= 72;
- }
- } else {
- // default to inches
- val *= 72;
- }
- return val;
-}
-
-GString *XFAFormField::getFieldValue(const char *valueChildType) {
- ZxElement *valueElem, *datasets, *data, *formElem, *elem;
- char *p;
-
- // check the <datasets> packet
- p = name->getCString();
- if (xfaForm->xml->getRoot() && !strncmp(p, "form.", 5)) {
- if ((datasets =
- xfaForm->xml->getRoot()->findFirstChildElement("xfa:datasets")) &&
- (data = datasets->findFirstChildElement("xfa:data"))) {
- elem = findFieldInDatasets(data, p + 5);
- if (elem &&
- elem->getFirstChild() &&
- elem->getFirstChild()->isCharData() &&
- ((ZxCharData *)elem->getFirstChild())->getData()->getLength() > 0) {
- return ((ZxCharData *)elem->getFirstChild())->getData();
- }
- }
- }
- if (exclGroupName) {
- p = exclGroupName->getCString();
- if (xfaForm->xml->getRoot() && !strncmp(p, "form.", 5)) {
- if ((datasets =
- xfaForm->xml->getRoot()->findFirstChildElement("xfa:datasets")) &&
- (data = datasets->findFirstChildElement("xfa:data"))) {
- elem = findFieldInDatasets(data, p + 5);
- if (elem &&
- elem->getFirstChild() &&
- elem->getFirstChild()->isCharData() &&
- ((ZxCharData *)elem->getFirstChild())->getData()->getLength() > 0) {
- return ((ZxCharData *)elem->getFirstChild())->getData();
- }
- }
- }
- }
-
- // check the <form> element
- p = fullName->getCString();
- if (xfaForm->xml->getRoot() && !strncmp(p, "form.", 5)) {
- if ((formElem = xfaForm->xml->getRoot()->findFirstChildElement("form"))) {
- elem = findFieldInFormElem(formElem, p + 5);
- if (elem &&
- (valueElem = elem->findFirstChildElement("value")) &&
- (elem = valueElem->findFirstChildElement(valueChildType))) {
- if (elem->getFirstChild() &&
- elem->getFirstChild()->isCharData() &&
- ((ZxCharData *)elem->getFirstChild())
- ->getData()->getLength() > 0) {
- return ((ZxCharData *)elem->getFirstChild())->getData();
- }
- }
- }
- }
-
- // check the <value> element within the field
- if ((valueElem = xml->findFirstChildElement("value")) &&
- (elem = valueElem->findFirstChildElement(valueChildType))) {
- if (elem->getFirstChild() &&
- elem->getFirstChild()->isCharData() &&
- ((ZxCharData *)elem->getFirstChild())->getData()->getLength() > 0) {
- return ((ZxCharData *)elem->getFirstChild())->getData();
- }
- }
-
- return NULL;
-}
-
-ZxElement *XFAFormField::findFieldInDatasets(ZxElement *elem, char *partName) {
- ZxNode *node;
- ZxElement *result;
- GString *nodeName;
- char *next;
- int curIdx, idx, n;
-
- curIdx = 0;
- for (node = elem->getFirstChild(); node; node = node->getNextChild()) {
- if (node->isElement()) {
- nodeName = ((ZxElement *)node)->getType();
- n = nodeName->getLength();
- if (!strncmp(partName, nodeName->getCString(), n)) {
- if (partName[n] == '[') {
- idx = atoi(partName + n + 1);
- if (idx == curIdx) {
- for (++n; partName[n] && partName[n-1] != ']'; ++n) ;
- } else {
- ++curIdx;
- continue;
- }
- }
- if (!partName[n]) {
- return (ZxElement *)node;
- } else if (partName[n] == '.') {
- if ((result = findFieldInDatasets((ZxElement *)node,
- partName + n + 1))) {
- return result;
- }
- break;
- }
- }
- }
- }
-
- // search for an "ancestor match"
- if ((next = strchr(partName, '.'))) {
- return findFieldInDatasets(elem, next + 1);
- }
-
- return NULL;
-}
-
-ZxElement *XFAFormField::findFieldInFormElem(ZxElement *elem, char *partName) {
- ZxElement *elem2;
- ZxNode *node;
- ZxAttr *attr;
- GString *nodeName;
- int curIdx, idx, n;
-
- curIdx = 0;
- for (node = elem->getFirstChild(); node; node = node->getNextChild()) {
- if ((node->isElement("subform") || node->isElement("field")) &&
- ((attr = ((ZxElement *)node)->findAttr("name")))) {
- nodeName = attr->getValue();
- n = nodeName->getLength();
- if (!strncmp(partName, nodeName->getCString(), n)) {
- if (partName[n] == '[') {
- idx = atoi(partName + n + 1);
- if (idx == curIdx) {
- for (++n; partName[n] && partName[n-1] != ']'; ++n) ;
- } else {
- ++curIdx;
- continue;
- }
- }
- if (!partName[n]) {
- return (ZxElement *)node;
- } else if (partName[n] == '.') {
- return findFieldInFormElem((ZxElement *)node, partName + n + 1);
- }
- }
- } else if (node->isElement("subform")) {
- if ((elem2 = findFieldInFormElem((ZxElement *)node, partName))) {
- return elem2;
- }
- }
- }
- return NULL;
-}
-
-void XFAFormField::transform(int rot, double w, double h,
- double *wNew, double *hNew, GString *appearBuf) {
- switch (rot) {
- case 0:
- default:
- appearBuf->appendf("1 0 0 1 0 {0:.4f} cm\n", -h);
- break;
- case 90:
- appearBuf->appendf("0 1 -1 0 {0:.4f} 0 cm\n", w);
- *wNew = h;
- *hNew = w;
- break;
- case 180:
- appearBuf->appendf("-1 0 0 -1 {0:.4f} {1:.4f} cm\n", w, h);
- *wNew = w;
- *hNew = h;
- break;
- case 270:
- appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", h);
- *wNew = h;
- *hNew = w;
- break;
- }
-}
-
-void XFAFormField::drawText(GString *text, GBool multiLine, int combCells,
- GString *fontName, GBool bold,
- GBool italic, double fontSize,
- XFAHorizAlign hAlign, XFAVertAlign vAlign,
- double x, double y, double w, double h,
- GBool whiteBackground,
- GfxFontDict *fontDict, GString *appearBuf) {
- GString *text2;
- GfxFont *font;
- const char *fontTag;
- GString *s;
- Unicode u;
- double yTop, xx, yy, tw, charWidth, lineHeight;
- double ascent, descent, rectX, rectY, rectW, rectH, blkH;
- int nLines, line, i, j, k, c, rectI;
-
- // convert UTF-8 to Latin1
- //~ this currently drops all non-Latin1 characters
- text2 = new GString();
- i = 0;
- while (getUTF8(text, &i, &u)) {
- if (u <= 0xff) {
- text2->append((char)u);
- } else {
- text2->append('?');
- }
- }
-
- // find the font
- if ((font = findFont(fontDict, fontName, bold, italic))) {
- fontTag = font->getTag()->getCString();
- ascent = font->getAscent() * fontSize;
- descent = font->getDescent() * fontSize;
- } else {
- error(errSyntaxError, -1, "Couldn't find a font for '{0:t}', {1:s}, {2:s} used in XFA field",
- fontName, bold ? "bold" : "non-bold",
- italic ? "italic" : "non-italic");
- fontTag = "xpdf_default_font";
- ascent = 0.718 * fontSize;
- descent = -0.207 * fontSize;
- }
-
- // setup
- rectW = rectH = 0;
- rectI = appearBuf->getLength();
- appearBuf->append("BT\n");
- appearBuf->appendf("/{0:s} {1:.2f} Tf\n", fontTag, fontSize);
-
- // multi-line text
- if (multiLine) {
-
- // compute line height
- lineHeight = 1.2 * fontSize;
-
- // handle bottom/middle alignment
- if (vAlign == xfaVAlignBottom || vAlign == xfaVAlignMiddle) {
- nLines = 0;
- i = 0;
- while (i < text2->getLength()) {
- getNextLine(text2, i, font, fontSize, w, &j, &tw, &k);
- ++nLines;
- i = k;
- }
- blkH = nLines * lineHeight - (lineHeight - fontSize);
- if (vAlign == xfaVAlignBottom) {
- yTop = y + blkH;
- } else {
- yTop = y + 0.5 * (h + blkH);
- }
- if (yTop > y + h) {
- yTop = y + h;
- }
- } else {
- yTop = y + h;
- }
-
- // write a series of lines of text
- line = 0;
- i = 0;
- while (i < text2->getLength()) {
-
- getNextLine(text2, i, font, fontSize, w, &j, &tw, &k);
- if (tw > rectW) {
- rectW = tw;
- }
-
- // compute text start position
- switch (hAlign) {
- case xfaHAlignLeft:
- default:
- xx = x;
- break;
- case xfaHAlignCenter:
- xx = x + 0.5 * (w - tw);
- break;
- case xfaHAlignRight:
- xx = x + w - tw;
- break;
- }
- yy = yTop - line * lineHeight - ascent;
-
- // draw the line
- appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n", xx, yy);
- appearBuf->append('(');
- for (; i < j; ++i) {
- c = text2->getChar(i) & 0xff;
- if (c == '(' || c == ')' || c == '\\') {
- appearBuf->append('\\');
- appearBuf->append((char)c);
- } else if (c < 0x20 || c >= 0x80) {
- appearBuf->appendf("\\{0:03o}", c);
- } else {
- appearBuf->append((char)c);
- }
- }
- appearBuf->append(") Tj\n");
-
- // next line
- i = k;
- ++line;
- }
- rectH = line * lineHeight;
- rectY = y + h - rectH;
-
- // comb formatting
- } else if (combCells > 0) {
-
- // compute comb spacing
- tw = w / combCells;
-
- // compute text start position
- switch (hAlign) {
- case xfaHAlignLeft:
- default:
- xx = x;
- break;
- case xfaHAlignCenter:
- xx = x + (int)(0.5 * (combCells - text2->getLength())) * tw;
- break;
- case xfaHAlignRight:
- xx = x + w - text2->getLength() * tw;
- break;
- }
- rectW = text2->getLength() * tw;
- switch (vAlign) {
- case xfaVAlignTop:
- default:
- yy = y + h - ascent;
- break;
- case xfaVAlignMiddle:
- yy = y + 0.5 * (h - (ascent + descent));
- break;
- case xfaVAlignBottom:
- yy = y - descent;
- break;
- }
- rectY = yy + descent;
- rectH = ascent - descent;
-
- // write the text string
- for (i = 0; i < text2->getLength(); ++i) {
- c = text2->getChar(i) & 0xff;
- if (font && !font->isCIDFont()) {
- charWidth = fontSize * ((Gfx8BitFont *)font)->getWidth((Guchar)c);
- appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n",
- xx + i * tw + 0.5 * (tw - charWidth), yy);
- } else {
- appearBuf->appendf("1 0 0 1 {0:.4f} {1:.4f} Tm\n",
- xx + i * tw, yy);
- }
- appearBuf->append('(');
- if (c == '(' || c == ')' || c == '\\') {
- appearBuf->append('\\');
- appearBuf->append((char)c);
- } else if (c < 0x20 || c >= 0x80) {
- appearBuf->appendf("{0:.4f} 0 Td\n", w);
- } else {
- appearBuf->append((char)c);
- }
- appearBuf->append(") Tj\n");
- }
-
- // regular (non-comb) formatting
- } else {
-
- // compute string width
- if (font && !font->isCIDFont()) {
- tw = 0;
- for (i = 0; i < text2->getLength(); ++i) {
- tw += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i));
- }
- } else {
- // otherwise, make a crude estimate
- tw = text2->getLength() * 0.5;
- }
- tw *= fontSize;
- rectW = tw;
-
- // compute text start position
- switch (hAlign) {
- case xfaHAlignLeft:
- default:
- xx = x;
- break;
- case xfaHAlignCenter:
- xx = x + 0.5 * (w - tw);
- break;
- case xfaHAlignRight:
- xx = x + w - tw;
- break;
- }
- switch (vAlign) {
- case xfaVAlignTop:
- default:
- yy = y + h - ascent;
- break;
- case xfaVAlignMiddle:
- yy = y + 0.5 * (h - (ascent + descent));
- break;
- case xfaVAlignBottom:
- yy = y - descent;
- break;
- }
- rectY = yy + descent;
- rectH = ascent - descent;
- appearBuf->appendf("{0:.4f} {1:.4f} Td\n", xx, yy);
-
- // write the text string
- appearBuf->append('(');
- for (i = 0; i < text2->getLength(); ++i) {
- c = text2->getChar(i) & 0xff;
- if (c == '(' || c == ')' || c == '\\') {
- appearBuf->append('\\');
- appearBuf->append((char)c);
- } else if (c < 0x20 || c >= 0x80) {
- appearBuf->appendf("\\{0:03o}", c);
- } else {
- appearBuf->append((char)c);
- }
- }
- appearBuf->append(") Tj\n");
- }
-
- // cleanup
- appearBuf->append("ET\n");
-
- // draw a white rectangle behind the text
- if (whiteBackground) {
- switch (hAlign) {
- case xfaHAlignLeft:
- default:
- rectX = x;
- break;
- case xfaHAlignCenter:
- rectX = x + 0.5 * (w - rectW);
- break;
- case xfaHAlignRight:
- rectX = x + w - rectW;
- break;
- }
- rectX -= 0.25 * fontSize;
- rectW += 0.5 * fontSize;
- rectY -= 0.1 * fontSize;
- rectH += 0.2 * fontSize;
- s = GString::format("q 1 g {0:.4f} {1:.4f} {2:.4f} {3:.4f} re f Q\n",
- rectX, rectY, rectW, rectH);
- appearBuf->insert(rectI, s);
- delete s;
- }
-
- delete text2;
-}
-
-// Searches <fontDict> for a font matching(<fontName>, <bold>,
-// <italic>).
-GfxFont *XFAFormField::findFont(GfxFontDict *fontDict, GString *fontName,
- GBool bold, GBool italic) {
- GString *reqName, *testName;
- GfxFont *font;
- GBool foundName, foundBold, foundItalic;
- char *p;
- char c;
- int i, j;
-
- if (!fontDict) {
- return NULL;
- }
-
- reqName = new GString();
- for (i = 0; i < fontName->getLength(); ++i) {
- c = fontName->getChar(i);
- if (c != ' ') {
- reqName->append(c);
- }
- }
-
- for (i = 0; i < fontDict->getNumFonts(); ++i) {
- font = fontDict->getFont(i);
- if (!font || !font->getName()) {
- continue;
- }
- testName = new GString();
- for (j = 0; j < font->getName()->getLength(); ++j) {
- c = font->getName()->getChar(j);
- if (c != ' ') {
- testName->append(c);
- }
- }
- foundName = foundBold = foundItalic = gFalse;
- for (p = testName->getCString(); *p; ++p) {
- if (!strncasecmp(p, reqName->getCString(), reqName->getLength())) {
- foundName = gTrue;
- }
- if (!strncasecmp(p, "bold", 4)) {
- foundBold = gTrue;
- }
- if (!strncasecmp(p, "italic", 6) || !strncasecmp(p, "oblique", 7)) {
- foundItalic = gTrue;
- }
- }
- delete testName;
- if (foundName && foundBold == bold && foundItalic == italic) {
- delete reqName;
- return font;
- }
- }
-
- delete reqName;
- return NULL;
-}
-
-// Searches the font resource dict for a font matching(<name>, <bold>,
-// <italic>), and returns the font object ID. This does not require a
-// GfxFontDict, and it does not do any parsing of font objects beyond
-// looking at BaseFont.
-Ref XFAFormField::findFontName(GString *name, GBool bold, GBool italic) {
- Object fontDictObj, fontObj, baseFontObj, fontRef;
- Ref fontID;
- GString *reqName, *testName;
- GBool foundName, foundBold, foundItalic;
- char *p;
- char c;
- int i;
-
- fontID.num = fontID.gen = -1;
-
- // remove space chars from the requested name
- reqName = new GString();
- for (i = 0; i < name->getLength(); ++i) {
- c = name->getChar(i);
- if (c != ' ') {
- reqName->append(c);
- }
- }
-
- if (xfaForm->resourceDict.isDict()) {
- if (xfaForm->resourceDict.dictLookup("Font", &fontDictObj)->isDict()) {
- for (i = 0; i < fontDictObj.dictGetLength() && fontID.num < 0; ++i) {
- fontDictObj.dictGetVal(i, &fontObj);
- if (fontObj.dictLookup("BaseFont", &baseFontObj)->isName()) {
-
- // remove space chars from the font name
- testName = new GString();
- for (p = baseFontObj.getName(); *p; ++p) {
- if (*p != ' ') {
- testName->append(p);
- }
- }
-
- // compare the names
- foundName = foundBold = foundItalic = gFalse;
- for (p = testName->getCString(); *p; ++p) {
- if (!strncasecmp(p, reqName->getCString(), reqName->getLength())) {
- foundName = gTrue;
- }
- if (!strncasecmp(p, "bold", 4)) {
- foundBold = gTrue;
- }
- if (!strncasecmp(p, "italic", 6) || !strncasecmp(p, "oblique", 7)) {
- foundItalic = gTrue;
- }
- }
- delete testName;
- if (foundName && foundBold == bold && foundItalic == italic) {
- if (fontObj.dictGetValNF(i, &fontRef)) {
- fontID = fontRef.getRef();
- }
- fontRef.free();
- }
- }
-
- baseFontObj.free();
- fontObj.free();
- }
- }
- fontDictObj.free();
- }
- delete reqName;
-
- return fontID;
-}
-
-// Figure out how much text will fit on the next line. Returns:
-// *end = one past the last character to be included
-// *width = width of the characters start .. end-1
-// *next = index of first character on the following line
-void XFAFormField::getNextLine(GString *text, int start,
- GfxFont *font, double fontSize, double wMax,
- int *end, double *width, int *next) {
- double w, dw;
- int j, k, c;
-
- // figure out how much text will fit on the line
- //~ what does Adobe do with tabs?
- w = 0;
- for (j = start; j < text->getLength() && w <= wMax; ++j) {
- c = text->getChar(j) & 0xff;
- if (c == 0x0a || c == 0x0d) {
- break;
- }
- if (font && !font->isCIDFont()) {
- dw = ((Gfx8BitFont *)font)->getWidth((Guchar)c) * fontSize;
- } else {
- // otherwise, make a crude estimate
- dw = 0.5 * fontSize;
- }
- w += dw;
- }
- if (w > wMax) {
- for (k = j; k > start && text->getChar(k-1) != ' '; --k) ;
- for (; k > start && text->getChar(k-1) == ' '; --k) ;
- if (k > start) {
- j = k;
- }
- if (j == start) {
- // handle the pathological case where the first character is
- // too wide to fit on the line all by itself
- j = start + 1;
- }
- }
- *end = j;
-
- // compute the width
- w = 0;
- for (k = start; k < j; ++k) {
- if (font && !font->isCIDFont()) {
- dw = ((Gfx8BitFont *)font)->getWidth(text->getChar(k)) * fontSize;
- } else {
- // otherwise, make a crude estimate
- dw = 0.5 * fontSize;
- }
- w += dw;
- }
- *width = w;
-
- // next line
- while (j < text->getLength() && text->getChar(j) == ' ') {
- ++j;
- }
- if (j < text->getLength() && text->getChar(j) == 0x0d) {
- ++j;
- }
- if (j < text->getLength() && text->getChar(j) == 0x0a) {
- ++j;
- }
- *next = j;
-}
-
-//------------------------------------------------------------------------
-// 'picture' formatting
-//------------------------------------------------------------------------
-
-class XFAPictureNode {
-public:
- virtual ~XFAPictureNode() {}
- virtual GBool isLiteral() { return gFalse; }
- virtual GBool isSign() { return gFalse; }
- virtual GBool isDigit() { return gFalse; }
- virtual GBool isDecPt() { return gFalse; }
- virtual GBool isSeparator() { return gFalse; }
- virtual GBool isYear() { return gFalse; }
- virtual GBool isMonth() { return gFalse; }
- virtual GBool isDay() { return gFalse; }
- virtual GBool isHour() { return gFalse; }
- virtual GBool isMinute() { return gFalse; }
- virtual GBool isSecond() { return gFalse; }
- virtual GBool isChar() { return gFalse; }
-};
-
-class XFAPictureLiteral: public XFAPictureNode {
-public:
- XFAPictureLiteral(GString *sA) { s = sA; }
- virtual ~XFAPictureLiteral() { delete s; }
- virtual GBool isLiteral() { return gTrue; }
- GString *s;
-};
-
-class XFAPictureSign: public XFAPictureNode {
-public:
- XFAPictureSign(char cA) { c = cA; }
- virtual GBool isSign() { return gTrue; }
- char c;
-};
-
-class XFAPictureDigit: public XFAPictureNode {
-public:
- XFAPictureDigit(char cA) { c = cA; pos = 0; }
- virtual GBool isDigit() { return gTrue; }
- char c;
- int pos;
-};
-
-class XFAPictureDecPt: public XFAPictureNode {
-public:
- XFAPictureDecPt() { }
- virtual GBool isDecPt() { return gTrue; }
-};
-
-class XFAPictureSeparator: public XFAPictureNode {
-public:
- XFAPictureSeparator() { }
- virtual GBool isSeparator() { return gTrue; }
-};
-
-class XFAPictureYear: public XFAPictureNode {
-public:
- XFAPictureYear(int nDigitsA) { nDigits = nDigitsA; }
- virtual GBool isYear() { return gTrue; }
- int nDigits;
-};
-
-class XFAPictureMonth: public XFAPictureNode {
-public:
- XFAPictureMonth(int nDigitsA) { nDigits = nDigitsA; }
- virtual GBool isMonth() { return gTrue; }
- int nDigits;
-};
-
-class XFAPictureDay: public XFAPictureNode {
-public:
- XFAPictureDay(int nDigitsA) { nDigits = nDigitsA; }
- virtual GBool isDay() { return gTrue; }
- int nDigits;
-};
-
-class XFAPictureHour: public XFAPictureNode {
-public:
- XFAPictureHour(GBool is24HourA, int nDigitsA)
- { is24Hour = is24HourA; nDigits = nDigitsA; }
- virtual GBool isHour() { return gTrue; }
- GBool is24Hour;
- int nDigits;
-};
-
-class XFAPictureMinute: public XFAPictureNode {
-public:
- XFAPictureMinute(int nDigitsA) { nDigits = nDigitsA; }
- virtual GBool isMinute() { return gTrue; }
- int nDigits;
-};
-
-class XFAPictureSecond: public XFAPictureNode {
-public:
- XFAPictureSecond(int nDigitsA) { nDigits = nDigitsA; }
- virtual GBool isSecond() { return gTrue; }
- int nDigits;
-};
-
-class XFAPictureChar: public XFAPictureNode {
-public:
- XFAPictureChar() {}
- virtual GBool isChar() { return gTrue; }
-};
-
-GString *XFAFormField::pictureFormatDateTime(GString *value, GString *picture) {
- GList *pic;
- XFAPictureNode *node;
- GString *ret, *s;
- char c;
- int year, month, day, hour, min, sec;
- int len, picStart, picEnd, u, n, i, j;
-
- len = value->getLength();
- if (len == 0) {
- return value->copy();
- }
-
- //--- parse the value
-
- // expected format is yyyy(-mm(-dd)?)?Thh(:mm(:ss)?)?
- // where:
- // - the '-'s and ':'s are optional
- // - the 'T' is literal
- // - we're ignoring optional time zone info at the end
- // (if the value is not in this canonical format, we just punt and
- // return the value string)
- //~ another option would be to parse the value following the
- //~ <ui><picture> element
- year = month = day = hour = min = sec = 0;
- i = 0;
- if (!(i + 4 <= len && isValidInt(value, i, 4))) {
- return value->copy();
- }
- year = convertInt(value, i, 4);
- i += 4;
- if (i < len && value->getChar(i) == '-') {
- ++i;
- }
- if (i + 2 <= len && isValidInt(value, i, 2)) {
- month = convertInt(value, i, 2);
- i += 2;
- if (i < len && value->getChar(i) == '-') {
- ++i;
- }
- if (i + 2 <= len && isValidInt(value, i, 2)) {
- day = convertInt(value, i, 2);
- i += 2;
- }
- }
- if (i < len) {
- if (value->getChar(i) != 'T') {
- return value->copy();
- }
- ++i;
- if (!(i + 2 <= len && isValidInt(value, i, 2))) {
- return value->copy();
- }
- hour = convertInt(value, i, 2);
- i += 2;
- if (i < len && value->getChar(i) == ':') {
- ++i;
- }
- if (i + 2 <= len && isValidInt(value, i, 2)) {
- min = convertInt(value, i, 2);
- i += 2;
- if (i < len && value->getChar(i) == ':') {
- ++i;
- }
- if (i + 2 <= len && isValidInt(value, i, 2)) {
- sec = convertInt(value, i, 2);
- i += 2;
- }
- }
- }
- if (i < len) {
- return value->copy();
- }
-
- //--- skip the category and locale in the picture
-
- picStart = 0;
- picEnd = picture->getLength();
- for (i = 0; i < picture->getLength(); ++i) {
- c = picture->getChar(i);
- if (c == '{') {
- picStart = i + 1;
- for (picEnd = picStart;
- picEnd < picture->getLength() && picture->getChar(picEnd) != '}';
- ++picEnd) ;
- break;
- } else if (!((c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- c == '(' ||
- c == ')')) {
- break;
- }
- }
-
- //--- parse the picture
-
- pic = new GList();
- i = picStart;
- while (i < picEnd) {
- c = picture->getChar(i);
- ++i;
- if (c == '\'') {
- s = new GString();
- while (i < picEnd) {
- c = picture->getChar(i);
- if (c == '\'') {
- ++i;
- if (i < picEnd && picture->getChar(i) == '\'') {
- s->append('\'');
- ++i;
- } else {
- break;
- }
- } else if (c == '\\') {
- ++i;
- if (i == picEnd) {
- break;
- }
- c = picture->getChar(i);
- ++i;
- if (c == 'u' && i+4 <= picEnd) {
- u = 0;
- for (j = 0; j < 4; ++j, ++i) {
- c = picture->getChar(i);
- u <<= 4;
- if (c >= '0' && c <= '9') {
- u += c - '0';
- } else if (c >= 'a' && c <= 'f') {
- u += c - 'a' + 10;
- } else if (c >= 'A' && c <= 'F') {
- u += c - 'A' + 10;
- }
- }
- //~ this should convert to UTF-8 (?)
- if (u <= 0xff) {
- s->append((char)u);
- }
- } else {
- s->append(c);
- }
- } else {
- s->append(c);
- }
- }
- pic->append(new XFAPictureLiteral(s));
- } else if (c == ',' || c == '-' || c == ':' ||
- c == '/' || c == '.' || c == ' ') {
- s = new GString();
- s->append(c);
- pic->append(new XFAPictureLiteral(s));
- } else if (c == 'Y') {
- for (n = 1; n < 4 && i < picEnd && picture->getChar(i) == 'Y'; ++n, ++i) ;
- pic->append(new XFAPictureYear(n));
- } else if (c == 'M') {
- for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'M'; ++n, ++i) ;
- pic->append(new XFAPictureMonth(n));
- } else if (c == 'D') {
- for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'D'; ++n, ++i) ;
- pic->append(new XFAPictureDay(n));
- } else if (c == 'h') {
- for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'h'; ++n, ++i) ;
- pic->append(new XFAPictureHour(gFalse, n));
- } else if (c == 'H') {
- for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'H'; ++n, ++i) ;
- pic->append(new XFAPictureHour(gTrue, n));
- } else if (c == 'M') {
- for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'M'; ++n, ++i) ;
- pic->append(new XFAPictureMinute(n));
- } else if (c == 'S') {
- for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'S'; ++n, ++i) ;
- pic->append(new XFAPictureSecond(n));
- }
- }
-
- //--- generate formatted text
-
- ret = new GString();
- for (i = 0; i < pic->getLength(); ++i) {
- node = (XFAPictureNode *)pic->get(i);
- if (node->isLiteral()) {
- ret->append(((XFAPictureLiteral *)node)->s);
- } else if (node->isYear()) {
- if (((XFAPictureYear *)node)->nDigits == 2) {
- if (year >= 1930 && year < 2030) {
- ret->appendf("{0:02d}", year % 100);
- } else {
- ret->append("??");
- }
- } else {
- ret->appendf("{0:04d}", year);
- }
- } else if (node->isMonth()) {
- if (((XFAPictureMonth *)node)->nDigits == 1) {
- ret->appendf("{0:d}", month);
- } else {
- ret->appendf("{0:02d}", month);
- }
- } else if (node->isDay()) {
- if (((XFAPictureDay *)node)->nDigits == 1) {
- ret->appendf("{0:d}", day);
- } else {
- ret->appendf("{0:02d}", day);
- }
- } else if (node->isHour()) {
- if (((XFAPictureHour *)node)->is24Hour) {
- n = hour;
- } else {
- n = hour % 12;
- if (n == 0) {
- n = 12;
- }
- }
- if (((XFAPictureHour *)node)->nDigits == 1) {
- ret->appendf("{0:d}", n);
- } else {
- ret->appendf("{0:02d}", n);
- }
- } else if (node->isMinute()) {
- if (((XFAPictureMinute *)node)->nDigits == 1) {
- ret->appendf("{0:d}", min);
- } else {
- ret->appendf("{0:02d}", min);
- }
- } else if (node->isSecond()) {
- if (((XFAPictureSecond *)node)->nDigits == 1) {
- ret->appendf("{0:d}", sec);
- } else {
- ret->appendf("{0:02d}", sec);
- }
- }
- }
- deleteGList(pic, XFAPictureNode);
-
- return ret;
-}
-
-GString *XFAFormField::pictureFormatNumber(GString *value, GString *picture) {
- GList *pic;
- XFAPictureNode *node;
- GString *ret, *s;
- GBool neg, haveDigits;
- char c;
- int start, decPt, trailingZero, len;
- int picStart, picEnd, u, pos, i, j;
-
- len = value->getLength();
- if (len == 0) {
- return value->copy();
- }
-
- //--- parse the value
-
- // -nnnn.nnnn0000
- // ^ ^ ^ ^
- // | | | +-- len
- // | | +------ trailingZero
- // | +----------- decPt
- // +--------------- start
- start = 0;
- neg = gFalse;
- if (value->getChar(start) == '-') {
- neg = gTrue;
- ++start;
- } else if (value->getChar(start) == '+') {
- ++start;
- }
- for (decPt = start; decPt < len && value->getChar(decPt) != '.'; ++decPt) ;
- for (trailingZero = len;
- trailingZero > decPt && value->getChar(trailingZero - 1) == '0';
- --trailingZero) ;
-
- //--- skip the category and locale in the picture
-
- picStart = 0;
- picEnd = picture->getLength();
- for (i = 0; i < picture->getLength(); ++i) {
- c = picture->getChar(i);
- if (c == '{') {
- picStart = i + 1;
- for (picEnd = picStart;
- picEnd < picture->getLength() && picture->getChar(picEnd) != '}';
- ++picEnd) ;
- break;
- } else if (!((c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- c == '(' ||
- c == ')')) {
- break;
- }
- }
-
- //--- parse the picture
-
- pic = new GList();
- i = picStart;
- while (i < picEnd) {
- c = picture->getChar(i);
- ++i;
- if (c == '\'') {
- s = new GString();
- while (i < picEnd) {
- c = picture->getChar(i);
- if (c == '\'') {
- ++i;
- if (i < picEnd && picture->getChar(i) == '\'') {
- s->append('\'');
- ++i;
- } else {
- break;
- }
- } else if (c == '\\') {
- ++i;
- if (i == picEnd) {
- break;
- }
- c = picture->getChar(i);
- ++i;
- if (c == 'u' && i+4 <= picEnd) {
- u = 0;
- for (j = 0; j < 4; ++j, ++i) {
- c = picture->getChar(i);
- u <<= 4;
- if (c >= '0' && c <= '9') {
- u += c - '0';
- } else if (c >= 'a' && c <= 'F') {
- u += c - 'a' + 10;
- } else if (c >= 'A' && c <= 'F') {
- u += c - 'A' + 10;
- }
- }
- //~ this should convert to UTF-8 (?)
- if (u <= 0xff) {
- s->append((char)u);
- }
- } else {
- s->append(c);
- }
- } else {
- s->append(c);
- ++i;
- }
- }
- pic->append(new XFAPictureLiteral(s));
- } else if (c == '-' || c == ':' || c == '/' || c == ' ') {
- s = new GString();
- s->append(c);
- pic->append(new XFAPictureLiteral(s));
- } else if (c == 's' || c == 'S') {
- pic->append(new XFAPictureSign(c));
- } else if (c == 'Z' || c == 'z' || c == '8' || c == '9') {
- pic->append(new XFAPictureDigit(c));
- } else if (c == '.') {
- pic->append(new XFAPictureDecPt());
- } else if (c == ',') {
- pic->append(new XFAPictureSeparator());
- }
- }
- for (i = 0; i < pic->getLength(); ++i) {
- node = (XFAPictureNode *)pic->get(i);
- if (node->isDecPt()) {
- break;
- }
- }
- pos = 0;
- for (j = i - 1; j >= 0; --j) {
- node = (XFAPictureNode *)pic->get(j);
- if (node->isDigit()) {
- ((XFAPictureDigit *)node)->pos = pos;
- ++pos;
- }
- }
- pos = -1;
- for (j = i + 1; j < pic->getLength(); ++j) {
- node = (XFAPictureNode *)pic->get(j);
- if (node->isDigit()) {
- ((XFAPictureDigit *)node)->pos = pos;
- --pos;
- }
- }
-
- //--- generate formatted text
-
- ret = new GString();
- haveDigits = gFalse;
- for (i = 0; i < pic->getLength(); ++i) {
- node = (XFAPictureNode *)pic->get(i);
- if (node->isLiteral()) {
- ret->append(((XFAPictureLiteral *)node)->s);
- } else if (node->isSign()) {
- if (((XFAPictureSign *)node)->c == 'S') {
- ret->append(neg ? '-' : ' ');
- } else {
- if (neg) {
- ret->append('-');
- }
- }
- } else if (node->isDigit()) {
- pos = ((XFAPictureDigit *)node)->pos;
- c = ((XFAPictureDigit *)node)->c;
- if (pos >= 0 && pos < decPt - start) {
- ret->append(value->getChar(decPt - 1 - pos));
- haveDigits = gTrue;
- } else if (pos < 0 && -pos <= trailingZero - decPt - 1) {
- ret->append(value->getChar(decPt - pos));
- haveDigits = gTrue;
- } else if (c == '8' &&
- pos < 0 &&
- -pos <= len - decPt - 1) {
- ret->append('0');
- haveDigits = gTrue;
- } else if (c == '9') {
- ret->append('0');
- haveDigits = gTrue;
- } else if (c == 'Z' && pos >= 0) {
- ret->append(' ');
- }
- } else if (node->isDecPt()) {
- if (!(i+1 < pic->getLength() &&
- ((XFAPictureNode *)pic->get(i+1))->isDigit() &&
- ((XFAPictureDigit *)pic->get(i+1))->c == 'z') ||
- trailingZero > decPt + 1) {
- ret->append('.');
- }
- } else if (node->isSeparator()) {
- if (haveDigits) {
- ret->append(',');
- }
- }
- }
- deleteGList(pic, XFAPictureNode);
-
- return ret;
-}
-
-GString *XFAFormField::pictureFormatText(GString *value, GString *picture) {
- GList *pic;
- XFAPictureNode *node;
- GString *ret, *s;
- char c;
- int len, picStart, picEnd, u, i, j;
-
- len = value->getLength();
- if (len == 0) {
- return value->copy();
- }
-
- //--- skip the category and locale in the picture
-
- picStart = 0;
- picEnd = picture->getLength();
- for (i = 0; i < picture->getLength(); ++i) {
- c = picture->getChar(i);
- if (c == '{') {
- picStart = i + 1;
- for (picEnd = picStart;
- picEnd < picture->getLength() && picture->getChar(picEnd) != '}';
- ++picEnd) ;
- break;
- } else if (!((c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- c == '(' ||
- c == ')')) {
- break;
- }
- }
-
- //--- parse the picture
-
- pic = new GList();
- i = picStart;
- while (i < picEnd) {
- c = picture->getChar(i);
- ++i;
- if (c == '\'') {
- s = new GString();
- while (i < picEnd) {
- c = picture->getChar(i);
- if (c == '\'') {
- ++i;
- if (i < picEnd && picture->getChar(i) == '\'') {
- s->append('\'');
- ++i;
- } else {
- break;
- }
- } else if (c == '\\') {
- ++i;
- if (i == picEnd) {
- break;
- }
- c = picture->getChar(i);
- ++i;
- if (c == 'u' && i+4 <= picEnd) {
- u = 0;
- for (j = 0; j < 4; ++j, ++i) {
- c = picture->getChar(i);
- u <<= 4;
- if (c >= '0' && c <= '9') {
- u += c - '0';
- } else if (c >= 'a' && c <= 'F') {
- u += c - 'a' + 10;
- } else if (c >= 'A' && c <= 'F') {
- u += c - 'A' + 10;
- }
- }
- //~ this should convert to UTF-8 (?)
- if (u <= 0xff) {
- s->append((char)u);
- }
- } else {
- s->append(c);
- }
- } else {
- s->append(c);
- ++i;
- }
- }
- pic->append(new XFAPictureLiteral(s));
- } else if (c == ',' || c == '-' || c == ':' ||
- c == '/' || c == '.' || c == ' ') {
- s = new GString();
- s->append(c);
- pic->append(new XFAPictureLiteral(s));
- } else if (c == 'A' || c == 'X' || c == 'O' || c == '0' || c == '9') {
- pic->append(new XFAPictureChar());
- }
- }
-
- //--- generate formatted text
-
- ret = new GString();
- j = 0;
- for (i = 0; i < pic->getLength(); ++i) {
- node = (XFAPictureNode *)pic->get(i);
- if (node->isLiteral()) {
- ret->append(((XFAPictureLiteral *)node)->s);
- } else if (node->isChar()) {
- // if there are more chars in the picture than in the value,
- // Adobe renders the value as-is, without picture formatting
- if (j >= value->getLength()) {
- delete ret;
- ret = value->copy();
- break;
- }
- ret->append(value->getChar(j));
- ++j;
- }
- }
- deleteGList(pic, XFAPictureNode);
-
- return ret;
-}
-
-GBool XFAFormField::isValidInt(GString *s, int start, int len) {
- int i;
-
- for (i = 0; i < len; ++i) {
- if (!(start + i < s->getLength() &&
- s->getChar(start + i) >= '0' &&
- s->getChar(start + i) <= '9')) {
- return gFalse;
- }
- }
- return gTrue;
-}
-
-int XFAFormField::convertInt(GString *s, int start, int len) {
- char c;
- int x, i;
-
- x = 0;
- for (i = 0; i < len && start + i < s->getLength(); ++i) {
- c = s->getChar(start + i);
- if (c < '0' || c > '9') {
- break;
- }
- x = x * 10 + (c - '0');
- }
- return x;
-}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h
deleted file mode 100644
index 2f56e728a98..00000000000
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h
+++ /dev/null
@@ -1,181 +0,0 @@
-//========================================================================
-//
-// XFAForm.h
-//
-// Copyright 2012 Glyph & Cog, LLC
-//
-//========================================================================
-
-#ifndef XFAFORM_H
-#define XFAFORM_H
-
-#include <aconf.h>
-
-#ifdef USE_GCC_PRAGMAS
-#pragma interface
-#endif
-
-#include "Form.h"
-
-class GHash;
-class XFATableInfo;
-class ZxDoc;
-class ZxElement;
-class ZxAttr;
-
-//------------------------------------------------------------------------
-
-enum XFAHorizAlign {
- xfaHAlignLeft,
- xfaHAlignCenter,
- xfaHAlignRight
-};
-
-enum XFAVertAlign {
- xfaVAlignTop,
- xfaVAlignBottom,
- xfaVAlignMiddle
-};
-
-//------------------------------------------------------------------------
-
-class XFAForm: public Form {
-public:
-
- static XFAForm *load(PDFDoc *docA, Catalog *catalog,
- Object *acroFormObj, Object *xfaObj);
-
- virtual ~XFAForm();
-
- virtual const char *getType() { return "XFA"; }
-
- virtual void draw(int pageNum, Gfx *gfx, GBool printing);
-
- virtual int getNumFields();
- virtual FormField *getField(int idx);
-
-private:
-
- XFAForm(PDFDoc *docA, int nPagesA, ZxDoc *xmlA,
- Object *resourceDictA, GBool fullXFAA);
- void scanNode(ZxElement *elem,
- GString *parentName, GString *parentFullName,
- GBool inPageSet, XFATableInfo *tableInfo,
- GHash *nameCount, GHash *nameIdx,
- GHash *fullNameCount, GHash *fullNameIdx,
- GString *exclGroupName, Catalog *catalog);
- void scanNames(ZxElement *elem, GHash *nameCount);
- void scanFullNames(ZxElement *elem, GHash *fullNameCount);
- void scanField(ZxElement *elem, GString *name, GString *fullName,
- GString *exclGroupName, GBool inPageSet,
- XFATableInfo *tableInfo, int colSpan,
- Catalog *catalog);
- void scanNonField(ZxElement *elem, GString *name, GString *fullName,
- GBool inPageSet,
- XFATableInfo *tableInfo, int colSpan,
- GHash *nameCount, GHash *nameIdx,
- GHash *fullNameCount, GHash *fullNameIdx,
- Catalog *catalog);
- GString *getNodeName(ZxElement *elem);
- GString *getNodeFullName(ZxElement *elem);
- GBool nodeIsBindGlobal(ZxElement *elem);
-
- ZxDoc *xml;
- GList *fields; // [XFAFormField]
- Object resourceDict;
- GBool fullXFA; // true for "Full XFA", false for
- // "XFA Foreground"
- int nPages; // number of pages in PDF file
- double *pageOffsetX, // x,y offset for each page
- *pageOffsetY;
- int pageSetNPages; // number of pages in pageSet element
- int curPageNum; // current page number - used by scanFields()
- double curXOffset, // current x,y offset - used by scanFields()
- curYOffset;
-
- friend class XFAFormField;
-};
-
-//------------------------------------------------------------------------
-
-class XFAFormField: public FormField {
-public:
-
- XFAFormField(XFAForm *xfaFormA, ZxElement *xmlA,
- GString *nameA, GString *fullNameA, GString *exclGroupNameA,
- int pageNumA, double xOffsetA, double yOffsetA,
- double columnWidthA, double rowHeightA);
-
- virtual ~XFAFormField();
-
- virtual int getPageNum();
- virtual const char *getType();
- virtual Unicode *getName(int *length);
- virtual Unicode *getValue(int *length);
- virtual void getBBox(double *llx, double *lly, double *urx, double *ury);
- virtual void getFont(Ref *fontID, double *fontSize);
- virtual void getColor(double *red, double *green, double *blue);
-
- virtual Object *getResources(Object *res);
-
-private:
-
- Unicode *utf8ToUnicode(GString *s, int *length);
- void draw(int pageNumA, Gfx *gfx, GBool printing, GfxFontDict *fontDict);
- void getRectangle(double *xfaX, double *xfaY,
- double *xfaW, double *xfaH,
- double *pdfX, double *pdfY,
- double *pdfW, double *pdfH,
- int *pdfRot);
- void drawTextEdit(GfxFontDict *fontDict,
- double w, double h, int rot,
- GString *appearBuf);
- void drawCheckButton(GfxFontDict *fontDict,
- double w, double h, int rot,
- GString *appearBuf);
- void drawBarCode(GfxFontDict *fontDict,
- double w, double h, int rot,
- GString *appearBuf);
- static double getMeasurement(ZxAttr *attr, double defaultVal);
- static double getMeasurement(GString *s, int begin);
- GString *getFieldValue(const char *valueChildType);
- ZxElement *findFieldInDatasets(ZxElement *elem, char *partName);
- ZxElement *findFieldInFormElem(ZxElement *elem, char *partName);
- void transform(int rot, double w, double h,
- double *wNew, double *hNew, GString *appearBuf);
- void drawText(GString *text, GBool multiLine, int combCells,
- GString *fontName, GBool bold,
- GBool italic, double fontSize,
- XFAHorizAlign hAlign, XFAVertAlign vAlign,
- double x, double y, double w, double h,
- GBool whiteBackground,
- GfxFontDict *fontDict, GString *appearBuf);
- GfxFont *findFont(GfxFontDict *fontDict, GString *fontName,
- GBool bold, GBool italic);
- Ref findFontName(GString *name, GBool bold, GBool italic);
- void getNextLine(GString *text, int start,
- GfxFont *font, double fontSize, double wMax,
- int *end, double *width, int *next);
- GString *pictureFormatDateTime(GString *value, GString *picture);
- GString *pictureFormatNumber(GString *value, GString *picture);
- GString *pictureFormatText(GString *value, GString *picture);
- GBool isValidInt(GString *s, int start, int len);
- int convertInt(GString *s, int start, int len);
-
- XFAForm *xfaForm;
- ZxElement *xml;
- GString *name;
- GString *fullName;
- GString *exclGroupName;
- int pageNum;
- double xOffset, yOffset;
- double columnWidth; // column width, if this field is in a
- // table; otherwise zero
- double rowHeight; // row height, if this field is in a
- // table; otherwise zero
-
- friend class XFAForm;
- friend class XFATableInfo;
-};
-
-#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc
new file mode 100644
index 00000000000..6928fa9c9a8
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc
@@ -0,0 +1,675 @@
+//========================================================================
+//
+// XFAScanner.cc
+//
+// Copyright 2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "GString.h"
+#include "GHash.h"
+#include "Object.h"
+#include "Error.h"
+#include "Zoox.h"
+#include "XFAScanner.h"
+
+//------------------------------------------------------------------------
+
+// fields have two names:
+//
+// name:
+// - nodes with bind=global set the index to 0 ("foo[0]") regardless
+// of the number of nodes with the same name
+// - nodes with bind=none are dropped from the name
+// - <area> nodes are dropped from the name
+// - used for field value lookup in <xfa:datasets>
+//
+// fullName:
+// - all named nodes are treated the same, regardless of bind=global
+// or bind=none
+// - <area> nodes are included in the name, but don't reset the
+// numbering (i.e., <area> nodes are "transparent" with respect to
+// node numbering)
+// - used for field value lookup in <form>
+// - used for matching with AcroForm names
+//
+// Both names use indexes on all nodes, even if there's only one node
+// with the name -- this isn't correct for XFA naming, but it matches
+// the AcroForm behavior.
+
+//------------------------------------------------------------------------
+
+XFAFieldLayoutInfo::XFAFieldLayoutInfo(XFAFieldLayoutHAlign hAlignA,
+ XFAFieldLayoutVAlign vAlignA) {
+ hAlign = hAlignA;
+ vAlign = vAlignA;
+}
+
+//------------------------------------------------------------------------
+
+XFAFieldPictureInfo::XFAFieldPictureInfo(XFAFieldPictureSubtype subtypeA,
+ GString *formatA) {
+ subtype = subtypeA;
+ format = formatA;
+}
+
+XFAFieldPictureInfo::~XFAFieldPictureInfo() {
+ delete format;
+}
+
+//------------------------------------------------------------------------
+
+XFAFieldBarcodeInfo::XFAFieldBarcodeInfo(GString *barcodeTypeA,
+ double wideNarrowRatioA,
+ double moduleWidthA,
+ double moduleHeightA,
+ int dataLengthA,
+ int errorCorrectionLevelA,
+ GString *textLocationA) {
+ barcodeType = barcodeTypeA;
+ wideNarrowRatio = wideNarrowRatioA;
+ moduleWidth = moduleWidthA;
+ moduleHeight = moduleHeightA;
+ dataLength = dataLengthA;
+ errorCorrectionLevel = errorCorrectionLevelA;
+ textLocation = textLocationA;
+}
+
+XFAFieldBarcodeInfo::~XFAFieldBarcodeInfo() {
+ delete barcodeType;
+ delete textLocation;
+}
+
+//------------------------------------------------------------------------
+
+XFAField::XFAField(GString *nameA, GString *fullNameA, GString *valueA,
+ XFAFieldLayoutInfo *layoutInfoA,
+ XFAFieldPictureInfo *pictureInfoA,
+ XFAFieldBarcodeInfo *barcodeInfoA)
+ : name(nameA)
+ , fullName(fullNameA)
+ , value(valueA)
+ , layoutInfo(layoutInfoA)
+ , pictureInfo(pictureInfoA)
+ , barcodeInfo(barcodeInfoA)
+{
+}
+
+XFAField::~XFAField() {
+ delete name;
+ delete fullName;
+ delete value;
+ delete layoutInfo;
+ delete pictureInfo;
+ delete barcodeInfo;
+}
+
+//------------------------------------------------------------------------
+
+XFAScanner *XFAScanner::load(Object *xfaObj) {
+ GString *xfaData = readXFAStreams(xfaObj);
+ if (!xfaData) {
+ return NULL;
+ }
+ ZxDoc *xml = ZxDoc::loadMem(xfaData->getCString(), xfaData->getLength());
+ delete xfaData;
+ if (!xml) {
+ error(errSyntaxError, -1, "Invalid XML in XFA form");
+ return NULL;
+ }
+
+ XFAScanner *scanner = new XFAScanner();
+
+ if (xml->getRoot()) {
+ GHash *formValues = scanner->scanFormValues(xml->getRoot());
+ ZxElement *dataElem = NULL;
+ ZxElement *datasets =
+ xml->getRoot()->findFirstChildElement("xfa:datasets");
+ if (datasets) {
+ dataElem = datasets->findFirstChildElement("xfa:data");
+ }
+ ZxElement *tmpl = xml->getRoot()->findFirstChildElement("template");
+ if (tmpl) {
+ scanner->scanNode(tmpl, NULL, NULL, NULL, NULL, NULL,
+ dataElem, formValues);
+ }
+ deleteGHash(formValues, GString);
+ }
+
+ delete xml;
+
+ return scanner;
+}
+
+XFAScanner::XFAScanner() {
+ fields = new GHash();
+}
+
+XFAScanner::~XFAScanner() {
+ deleteGHash(fields, XFAField);
+}
+
+XFAField *XFAScanner::findField(GString *acroFormFieldName) {
+ return (XFAField *)fields->lookup(acroFormFieldName);
+}
+
+GString *XFAScanner::readXFAStreams(Object *xfaObj) {
+ GString *data = new GString();
+ char buf[4096];
+ int n;
+ if (xfaObj->isStream()) {
+ xfaObj->streamReset();
+ while ((n = xfaObj->getStream()->getBlock(buf, sizeof(buf))) > 0) {
+ data->append(buf, n);
+ }
+ } else if (xfaObj->isArray()) {
+ for (int i = 1; i < xfaObj->arrayGetLength(); i += 2) {
+ Object obj;
+ if (!xfaObj->arrayGet(i, &obj)->isStream()) {
+ error(errSyntaxError, -1, "XFA array element is wrong type");
+ obj.free();
+ delete data;
+ return NULL;
+ }
+ obj.streamReset();
+ while ((n = obj.getStream()->getBlock(buf, sizeof(buf))) > 0) {
+ data->append(buf, n);
+ }
+ obj.free();
+ }
+ } else {
+ error(errSyntaxError, -1, "XFA object is wrong type");
+ return NULL;
+ }
+ return data;
+}
+
+GHash *XFAScanner::scanFormValues(ZxElement *xmlRoot) {
+ GHash *formValues = new GHash(gTrue);
+ ZxElement *formElem = xmlRoot->findFirstChildElement("form");
+ if (formElem) {
+ scanFormNode(formElem, NULL, formValues);
+ }
+ return formValues;
+}
+
+void XFAScanner::scanFormNode(ZxElement *elem, GString *fullName,
+ GHash *formValues) {
+ GHash *fullNameIdx = new GHash();
+ for (ZxNode *node = elem->getFirstChild();
+ node;
+ node = node->getNextChild()) {
+ if (node->isElement("value")) {
+ if (fullName) {
+ ZxNode *child1Node = ((ZxElement *)node)->getFirstChild();
+ if (child1Node && child1Node->isElement()) {
+ ZxNode *child2Node = ((ZxElement *)child1Node)->getFirstChild();
+ if (child2Node && child2Node->isCharData()) {
+ formValues->add(fullName->copy(),
+ ((ZxCharData *)child2Node)->getData()->copy());
+ }
+ }
+ }
+ } else if (node->isElement()) {
+ ZxAttr *nameAttr = ((ZxElement *)node)->findAttr("name");
+ if (nameAttr && (node->isElement("subform") ||
+ node->isElement("field"))) {
+ GString *nodeName = nameAttr->getValue();
+ GString *childFullName;
+ if (fullName) {
+ childFullName = GString::format("{0:t}.{1:t}", fullName, nodeName);
+ } else {
+ childFullName = nodeName->copy();
+ }
+ int idx = fullNameIdx->lookupInt(nodeName);
+ childFullName->appendf("[{0:d}]", idx);
+ fullNameIdx->replace(nodeName, idx + 1);
+ scanFormNode((ZxElement *)node, childFullName, formValues);
+ delete childFullName;
+ } else if (node->isElement("subform")) {
+ scanFormNode((ZxElement *)node, fullName, formValues);
+ }
+ }
+ }
+ delete fullNameIdx;
+}
+
+void XFAScanner::scanNode(ZxElement *elem,
+ GString *parentName, GString *parentFullName,
+ GHash *nameIdx, GHash *fullNameIdx,
+ GString *exclGroupName, ZxElement *dataElem,
+ GHash *formValues) {
+ GString *nodeName = getNodeName(elem);
+ GHash *childNameIdx;
+ if (!nameIdx || nodeName) {
+ childNameIdx = new GHash();
+ } else {
+ childNameIdx = nameIdx;
+ }
+ GString *nodeFullName = getNodeFullName(elem);
+ GHash *childFullNameIdx;
+ if (!fullNameIdx || (nodeFullName && !elem->isElement("area"))) {
+ childFullNameIdx = new GHash();
+ } else {
+ childFullNameIdx = fullNameIdx;
+ }
+
+ GString *childName;
+ if (nodeName) {
+ if (parentName) {
+ childName = GString::format("{0:t}.{1:t}", parentName, nodeName);
+ } else {
+ childName = nodeName->copy();
+ }
+ int idx = nameIdx->lookupInt(nodeName);
+ nameIdx->replace(nodeName, idx + 1);
+ if (nodeIsBindGlobal(elem)) {
+ childName->appendf("[0]");
+ } else {
+ childName->appendf("[{0:d}]", idx);
+ }
+ } else {
+ childName = parentName;
+ }
+ GString *childFullName;
+ if (nodeFullName) {
+ if (parentFullName) {
+ childFullName = GString::format("{0:t}.{1:t}",
+ parentFullName, nodeFullName);
+ } else {
+ childFullName = nodeFullName->copy();
+ }
+ int idx = fullNameIdx->lookupInt(nodeFullName);
+ fullNameIdx->replace(nodeFullName, idx + 1);
+ childFullName->appendf("[{0:d}]", idx);
+ } else {
+ childFullName = parentFullName;
+ }
+
+ if (elem->isElement("field")) {
+ scanField(elem, childName, childFullName, exclGroupName,
+ dataElem, formValues);
+ } else {
+ GString *childExclGroupName;
+ if (elem->isElement("exclGroup")) {
+ childExclGroupName = childName;
+ } else {
+ childExclGroupName = NULL;
+ }
+ for (ZxNode *child = elem->getFirstChild();
+ child;
+ child = child->getNextChild()) {
+ if (child->isElement()) {
+ scanNode((ZxElement *)child, childName, childFullName,
+ childNameIdx, childFullNameIdx, childExclGroupName,
+ dataElem, formValues);
+ }
+ }
+ }
+
+ if (childName != parentName) {
+ delete childName;
+ }
+ if (childFullName != parentFullName) {
+ delete childFullName;
+ }
+ if (childNameIdx != nameIdx) {
+ delete childNameIdx;
+ }
+ if (childFullNameIdx != fullNameIdx) {
+ delete childFullNameIdx;
+ }
+}
+
+void XFAScanner::scanField(ZxElement *elem, GString *name, GString *fullName,
+ GString *exclGroupName, ZxElement *dataElem,
+ GHash *formValues) {
+ GString *value = getFieldValue(elem, name, fullName, exclGroupName,
+ dataElem, formValues);
+ XFAFieldLayoutInfo *layoutInfo = getFieldLayoutInfo(elem);
+ XFAFieldPictureInfo *pictureInfo = getFieldPictureInfo(elem);
+ XFAFieldBarcodeInfo *barcodeInfo = getFieldBarcodeInfo(elem);
+ XFAField *field = new XFAField(name->copy(), fullName->copy(), value,
+ layoutInfo, pictureInfo, barcodeInfo);
+ fields->add(field->fullName, field);
+}
+
+GString *XFAScanner::getFieldValue(ZxElement *elem, GString *name,
+ GString *fullName, GString *exclGroupName,
+ ZxElement *dataElem, GHash *formValues) {
+ GString *val = NULL;
+
+ //--- check the <xfa:datasets> packet
+ val = getDatasetsValue(name->getCString(), dataElem);
+ if (!val && exclGroupName) {
+ val = (GString *)getDatasetsValue(exclGroupName->getCString(), dataElem);
+ }
+
+ //--- check the <form> element
+ if (!val) {
+ val = (GString *)formValues->lookup(fullName);
+ }
+
+ //--- check the <value> element within the field
+ if (!val) {
+ ZxElement *valueElem = elem->findFirstChildElement("value");
+ if (valueElem) {
+ ZxNode *child1Node = valueElem->getFirstChild();
+ if (child1Node && child1Node->isElement()) {
+ ZxNode *child2Node = ((ZxElement *)child1Node)->getFirstChild();
+ if (child2Node && child2Node->isCharData()) {
+ val = ((ZxCharData *)child2Node)->getData();
+ }
+ }
+ }
+ }
+
+ //--- get the checkbutton item value
+ GString *checkbuttonItem = NULL;
+ ZxElement *uiElem = elem->findFirstChildElement("ui");
+ if (uiElem) {
+ ZxNode *uiChild = uiElem->getFirstChild();
+ if (uiChild && uiChild->isElement("checkButton")) {
+ ZxElement *itemsElem = elem->findFirstChildElement("items");
+ if (itemsElem) {
+ ZxNode *node1 = itemsElem->getFirstChild();
+ if (node1 && node1->isElement()) {
+ ZxNode *node2 = ((ZxElement *)node1)->getFirstChild();
+ if (node2 && node2->isCharData()) {
+ checkbuttonItem = ((ZxCharData *)node2)->getData();
+ }
+ }
+ }
+ }
+ }
+ // convert XFA checkbutton value to AcroForm-style On/Off value
+ if (checkbuttonItem && val) {
+ if (val->cmp(checkbuttonItem)) {
+ val = new GString("Off");
+ } else {
+ val = new GString("On");
+ }
+ } else if (val) {
+ val = val->copy();
+ }
+
+ return val;
+}
+
+GString *XFAScanner::getDatasetsValue(char *partName, ZxElement *elem) {
+ if (!elem) {
+ return NULL;
+ }
+
+ // partName = xxxx[nn].yyyy----
+ char *p = strchr(partName, '[');
+ if (!p) {
+ return NULL;
+ }
+ int partLen = (int)(p - partName);
+ int idx = atoi(p + 1);
+ p = strchr(p + 1, '.');
+ if (p) {
+ ++p;
+ }
+
+ int curIdx = 0;
+ for (ZxNode *node = elem->getFirstChild();
+ node;
+ node = node->getNextChild()) {
+ if (!node->isElement()) {
+ continue;
+ }
+ GString *nodeName = ((ZxElement *)node)->getType();
+ if (nodeName->getLength() != partLen ||
+ strncmp(nodeName->getCString(), partName, partLen)) {
+ continue;
+ }
+ if (curIdx != idx) {
+ ++curIdx;
+ continue;
+ }
+ if (p) {
+ GString *val = getDatasetsValue(p, (ZxElement *)node);
+ if (val) {
+ return val;
+ }
+ break;
+ } else {
+ ZxNode *child = ((ZxElement *)node)->getFirstChild();
+ if (!child || !child->isCharData()) {
+ return NULL;
+ }
+ return ((ZxCharData *)child)->getData();
+ }
+ }
+
+ // search for an 'ancestor match'
+ if (p) {
+ return getDatasetsValue(p, elem);
+ }
+
+ return NULL;
+}
+
+XFAFieldLayoutInfo *XFAScanner::getFieldLayoutInfo(ZxElement *elem) {
+ ZxElement *paraElem = elem->findFirstChildElement("para");
+ if (!paraElem) {
+ return NULL;
+ }
+ XFAFieldLayoutHAlign hAlign = xfaFieldLayoutHAlignLeft;
+ ZxAttr *hAlignAttr = paraElem->findAttr("hAlign");
+ if (hAlignAttr) {
+ if (!hAlignAttr->getValue()->cmp("left")) {
+ hAlign = xfaFieldLayoutHAlignLeft;
+ } else if (!hAlignAttr->getValue()->cmp("center")) {
+ hAlign = xfaFieldLayoutHAlignCenter;
+ } else if (!hAlignAttr->getValue()->cmp("right")) {
+ hAlign = xfaFieldLayoutHAlignRight;
+ }
+ }
+ XFAFieldLayoutVAlign vAlign = xfaFieldLayoutVAlignTop;
+ ZxAttr *vAlignAttr = paraElem->findAttr("vAlign");
+ if (vAlignAttr) {
+ if (!vAlignAttr->getValue()->cmp("top")) {
+ vAlign = xfaFieldLayoutVAlignTop;
+ } else if (!vAlignAttr->getValue()->cmp("middle")) {
+ vAlign = xfaFieldLayoutVAlignMiddle;
+ } else if (!vAlignAttr->getValue()->cmp("bottom")) {
+ vAlign = xfaFieldLayoutVAlignBottom;
+ }
+ }
+ return new XFAFieldLayoutInfo(hAlign, vAlign);
+}
+
+XFAFieldPictureInfo *XFAScanner::getFieldPictureInfo(ZxElement *elem) {
+ ZxElement *uiElem = elem->findFirstChildElement("ui");
+ if (!uiElem) {
+ return NULL;
+ }
+ XFAFieldPictureSubtype subtype;
+ if (uiElem->findFirstChildElement("dateTimeEdit")) {
+ subtype = xfaFieldPictureDateTime;
+ } else if (uiElem->findFirstChildElement("numericEdit")) {
+ subtype = xfaFieldPictureNumeric;
+ } else if (uiElem->findFirstChildElement("textEdit")) {
+ subtype = xfaFieldPictureText;
+ } else {
+ return NULL;
+ }
+
+ ZxElement *formatElem, *pictureElem;
+ ZxNode *pictureChildNode;
+ if (!(formatElem = elem->findFirstChildElement("format")) ||
+ !(pictureElem = formatElem->findFirstChildElement("picture")) ||
+ !(pictureChildNode = pictureElem->getFirstChild()) ||
+ !pictureChildNode->isCharData()) {
+ return NULL;
+ }
+ GString *format = ((ZxCharData *)pictureChildNode)->getData()->copy();
+
+ return new XFAFieldPictureInfo(subtype, format);
+}
+
+XFAFieldBarcodeInfo *XFAScanner::getFieldBarcodeInfo(ZxElement *elem) {
+ ZxElement *uiElem, *barcodeElem;
+ if (!(uiElem = elem->findFirstChildElement("ui")) ||
+ !(barcodeElem = uiElem->findFirstChildElement("barcode"))) {
+ return NULL;
+ }
+
+ ZxAttr *attr;
+ if (!(attr = barcodeElem->findAttr("type"))) {
+ return NULL;
+ }
+ GString *barcodeType = attr->getValue()->copy();
+
+ double wideNarrowRatio = 3;
+ if ((attr = barcodeElem->findAttr("wideNarrowRatio"))) {
+ char *s = attr->getValue()->getCString();
+ char *colon = strchr(s, ':');
+ if (colon) {
+ GString *numStr = new GString(s, (int)(colon - s));
+ double num = atof(numStr->getCString());
+ delete numStr;
+ double den = atof(colon + 1);
+ if (den == 0) {
+ wideNarrowRatio = num;
+ } else {
+ wideNarrowRatio = num / den;
+ }
+ } else {
+ wideNarrowRatio = atof(s);
+ }
+ }
+
+ double moduleWidth = (0.25 / 25.4) * 72.0; // 0.25mm
+ if ((attr = barcodeElem->findAttr("moduleWidth"))) {
+ moduleWidth = getMeasurement(attr->getValue());
+ }
+
+ double moduleHeight = (5.0 / 25.4) * 72.0; // 5mm
+ if ((attr = barcodeElem->findAttr("moduleHeight"))) {
+ moduleHeight = getMeasurement(attr->getValue());
+ }
+
+ int dataLength = 0;
+ if ((attr = barcodeElem->findAttr("dataLength"))) {
+ dataLength = atoi(attr->getValue()->getCString());
+ }
+
+ int errorCorrectionLevel = 0;
+ if ((attr = barcodeElem->findAttr("errorCorrectionLevel"))) {
+ errorCorrectionLevel = atoi(attr->getValue()->getCString());
+ }
+
+ GString *textLocation;
+ if ((attr = barcodeElem->findAttr("textLocation"))) {
+ textLocation = attr->getValue()->copy();
+ } else {
+ textLocation = new GString("below");
+ }
+
+ return new XFAFieldBarcodeInfo(barcodeType, wideNarrowRatio,
+ moduleWidth, moduleHeight, dataLength,
+ errorCorrectionLevel, textLocation);
+}
+
+double XFAScanner::getMeasurement(GString *s) {
+ int i = 0;
+ GBool neg = gFalse;
+ if (i < s->getLength() && s->getChar(i) == '+') {
+ ++i;
+ } else if (i < s->getLength() && s->getChar(i) == '-') {
+ neg = gTrue;
+ ++i;
+ }
+ double val = 0;
+ while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') {
+ val = val * 10 + s->getChar(i) - '0';
+ ++i;
+ }
+ if (i < s->getLength() && s->getChar(i) == '.') {
+ ++i;
+ double mul = 0.1;
+ while (i < s->getLength() &&
+ s->getChar(i) >= '0' && s->getChar(i) <= '9') {
+ val += mul * (s->getChar(i) - '0');
+ mul *= 0.1;
+ ++i;
+ }
+ }
+ if (neg) {
+ val = -val;
+ }
+ if (i+1 < s->getLength()) {
+ if (s->getChar(i) == 'i' && s->getChar(i+1) == 'n') {
+ val *= 72;
+ } else if (s->getChar(i) == 'p' && s->getChar(i+1) == 't') {
+ // no change
+ } else if (s->getChar(i) == 'c' && s->getChar(i+1) == 'm') {
+ val *= 72 / 2.54;
+ } else if (s->getChar(i) == 'm' && s->getChar(i+1) == 'm') {
+ val *= 72 / 25.4;
+ } else {
+ // default to inches
+ val *= 72;
+ }
+ } else {
+ // default to inches
+ val *= 72;
+ }
+ return val;
+}
+
+GString *XFAScanner::getNodeName(ZxElement *elem) {
+ if (elem->isElement("template") ||
+ elem->isElement("area") ||
+ elem->isElement("draw")) {
+ return NULL;
+ }
+ if (!elem->isElement("field") && nodeIsBindNone(elem)) {
+ return NULL;
+ }
+ ZxAttr *nameAttr = elem->findAttr("name");
+ if (!nameAttr) {
+ return NULL;
+ }
+ return nameAttr->getValue();
+}
+
+GString *XFAScanner::getNodeFullName(ZxElement *elem) {
+ if (elem->isElement("template") ||
+ elem->isElement("draw")) {
+ return NULL;
+ }
+ ZxAttr *nameAttr = elem->findAttr("name");
+ if (!nameAttr) {
+ return NULL;
+ }
+ return nameAttr->getValue();
+}
+
+GBool XFAScanner::nodeIsBindGlobal(ZxElement *elem) {
+ ZxElement *bindElem = elem->findFirstChildElement("bind");
+ if (!bindElem) {
+ return gFalse;
+ }
+ ZxAttr *attr = bindElem->findAttr("match");
+ return attr && !attr->getValue()->cmp("global");
+}
+
+GBool XFAScanner::nodeIsBindNone(ZxElement *elem) {
+ ZxElement *bindElem = elem->findFirstChildElement("bind");
+ if (!bindElem) {
+ return gFalse;
+ }
+ ZxAttr *attr = bindElem->findAttr("match");
+ return attr && !attr->getValue()->cmp("none");
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h
new file mode 100644
index 00000000000..00ee6f7fcee
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h
@@ -0,0 +1,166 @@
+//========================================================================
+//
+// XFAScanner.h
+//
+// Copyright 2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef XFASCANNER_H
+#define XFASCANNER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class GHash;
+class ZxElement;
+
+//------------------------------------------------------------------------
+
+enum XFAFieldLayoutHAlign {
+ xfaFieldLayoutHAlignLeft,
+ xfaFieldLayoutHAlignCenter,
+ xfaFieldLayoutHAlignRight
+};
+
+enum XFAFieldLayoutVAlign {
+ xfaFieldLayoutVAlignTop,
+ xfaFieldLayoutVAlignMiddle,
+ xfaFieldLayoutVAlignBottom
+};
+
+class XFAFieldLayoutInfo {
+public:
+
+ XFAFieldLayoutInfo(XFAFieldLayoutHAlign hAlignA,
+ XFAFieldLayoutVAlign vAlignA);
+
+ XFAFieldLayoutHAlign hAlign;
+ XFAFieldLayoutVAlign vAlign;
+};
+
+//------------------------------------------------------------------------
+
+enum XFAFieldPictureSubtype {
+ xfaFieldPictureDateTime,
+ xfaFieldPictureNumeric,
+ xfaFieldPictureText
+};
+
+class XFAFieldPictureInfo {
+public:
+
+ XFAFieldPictureInfo(XFAFieldPictureSubtype subtypeA, GString *formatA);
+ ~XFAFieldPictureInfo();
+
+ XFAFieldPictureSubtype subtype;
+ GString *format; // picture format string
+};
+
+//------------------------------------------------------------------------
+
+class XFAFieldBarcodeInfo {
+public:
+
+ XFAFieldBarcodeInfo(GString *barcodeTypeA, double wideNarrowRatioA,
+ double moduleWidthA, double moduleHeightA,
+ int dataLengthA, int errorCorrectionLevelA,
+ GString *textLocationA);
+ ~XFAFieldBarcodeInfo();
+
+ GString *barcodeType;
+ double wideNarrowRatio;
+ double moduleWidth;
+ double moduleHeight;
+ int dataLength;
+ int errorCorrectionLevel;
+ GString *textLocation;
+};
+
+//------------------------------------------------------------------------
+
+class XFAField {
+public:
+
+ XFAField(GString *nameA, GString *fullNameA, GString *valueA,
+ XFAFieldLayoutInfo *layoutInfoA,
+ XFAFieldPictureInfo *pictureInfoA,
+ XFAFieldBarcodeInfo *barcodeInfoA);
+ ~XFAField();
+
+ // Get the field's value, or NULL if it doesn't have a value. Sets
+ // *[length] to the length of the Unicode string.
+ GString *getValue() { return value; }
+
+ // Return a pointer to the field's picture formatting info object,
+ // or NULL if the field doesn't have picture formatting.
+ XFAFieldPictureInfo *getPictureInfo() { return pictureInfo; }
+
+ // Return a pointer to the field's layout info object, or NULL if
+ // the field doesn't have layout info.
+ XFAFieldLayoutInfo *getLayoutInfo() { return layoutInfo; }
+
+ // Return a pointer to the field's barcode info object, or NULL if
+ // the field isn't a barcode.
+ XFAFieldBarcodeInfo *getBarcodeInfo() { return barcodeInfo; }
+
+private:
+
+ friend class XFAScanner;
+
+ GString *name; // UTF-8
+ GString *fullName; // UTF-8
+ GString *value; // UTF-8
+ XFAFieldLayoutInfo *layoutInfo;
+ XFAFieldPictureInfo *pictureInfo;
+ XFAFieldBarcodeInfo *barcodeInfo;
+};
+
+//------------------------------------------------------------------------
+
+class XFAScanner {
+public:
+
+ static XFAScanner *load(Object *xfaObj);
+
+ virtual ~XFAScanner();
+
+ // Find an XFA field matchined the specified AcroForm field name.
+ // Returns NULL if there is no matching field.
+ XFAField *findField(GString *acroFormFieldName);
+
+private:
+
+ XFAScanner();
+ static GString *readXFAStreams(Object *xfaObj);
+ GHash *scanFormValues(ZxElement *xmlRoot);
+ void scanFormNode(ZxElement *elem, GString *fullName,
+ GHash *formValues);
+ void scanNode(ZxElement *elem,
+ GString *parentName, GString *parentFullName,
+ GHash *nameIdx, GHash *fullNameIdx,
+ GString *exclGroupName, ZxElement *xmlRoot,
+ GHash *formValues);
+ void scanField(ZxElement *elem, GString *name, GString *fullName,
+ GString *exclGroupName, ZxElement *xmlRoot,
+ GHash *formValues);
+ GString *getFieldValue(ZxElement *elem, GString *name,
+ GString *fullName, GString *exclGroupName,
+ ZxElement *xmlRoot, GHash *formValues);
+ GString *getDatasetsValue(char *partName, ZxElement *elem);
+ XFAFieldLayoutInfo *getFieldLayoutInfo(ZxElement *elem);
+ XFAFieldPictureInfo *getFieldPictureInfo(ZxElement *elem);
+ XFAFieldBarcodeInfo *getFieldBarcodeInfo(ZxElement *elem);
+ double getMeasurement(GString *s);
+ GString *getNodeName(ZxElement *elem);
+ GString *getNodeFullName(ZxElement *elem);
+ GBool nodeIsBindGlobal(ZxElement *elem);
+ GBool nodeIsBindNone(ZxElement *elem);
+
+ GHash *fields; // [XFAField]
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc
index d6b43cd3bea..0c364638b39 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc
@@ -350,7 +350,7 @@ XRef::XRef(BaseStream *strA, GBool repair) {
// read the xref table
posSet = new XRefPosSet();
- while (readXRef(&pos, posSet)) ;
+ while (readXRef(&pos, posSet, gFalse)) ;
xrefTablePosLen = posSet->getLength();
xrefTablePos = (GFileOffset *)gmallocn(xrefTablePosLen,
sizeof(GFileOffset));
@@ -438,8 +438,10 @@ GFileOffset XRef::getStartXref() {
}
// Read one xref table section. Also reads the associated trailer
-// dictionary, and returns the prev pointer (if any).
-GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet) {
+// dictionary, and returns the prev pointer (if any). The [hybrid]
+// flag is true when following the XRefStm link in a hybrid-reference
+// file.
+GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet, GBool hybrid) {
Parser *parser;
Object obj;
GBool more;
@@ -461,47 +463,44 @@ GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet) {
for (i = 0; i < n && Lexer::isSpace(buf[i]); ++i) ;
// parse an old-style xref table
- if (i + 4 < n &&
+ if (!hybrid &&
+ i + 4 < n &&
buf[i] == 'x' && buf[i+1] == 'r' && buf[i+2] == 'e' && buf[i+3] == 'f' &&
Lexer::isSpace(buf[i+4])) {
more = readXRefTable(pos, i + 5, posSet);
// parse an xref stream
- } else if (i < n && buf[i] >= '0' && buf[i] <= '9') {
+ } else {
obj.initNull();
parser = new Parser(NULL,
new Lexer(NULL,
str->makeSubStream(start + *pos, gFalse, 0, &obj)),
gTrue);
if (!parser->getObj(&obj, gTrue)->isInt()) {
- goto err2;
+ goto err;
}
obj.free();
if (!parser->getObj(&obj, gTrue)->isInt()) {
- goto err2;
+ goto err;
}
obj.free();
if (!parser->getObj(&obj, gTrue)->isCmd("obj")) {
- goto err2;
+ goto err;
}
obj.free();
if (!parser->getObj(&obj)->isStream()) {
- goto err2;
+ goto err;
}
- more = readXRefStream(obj.getStream(), pos);
+ more = readXRefStream(obj.getStream(), pos, hybrid);
obj.free();
delete parser;
-
- } else {
- goto err1;
}
return more;
- err2:
+ err:
obj.free();
delete parser;
- err1:
ok = gFalse;
return gFalse;
}
@@ -662,7 +661,7 @@ GBool XRef::readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet) {
//~ this can be a 64-bit int (?)
if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
pos2 = (GFileOffset)(Guint)obj2.getInt();
- readXRef(&pos2, posSet);
+ readXRef(&pos2, posSet, gTrue);
if (!ok) {
obj2.free();
obj.free();
@@ -679,7 +678,7 @@ GBool XRef::readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet) {
return gFalse;
}
-GBool XRef::readXRefStream(Stream *xrefStr, GFileOffset *pos) {
+GBool XRef::readXRefStream(Stream *xrefStr, GFileOffset *pos, GBool hybrid) {
Dict *dict;
int w[3];
GBool more;
@@ -857,125 +856,250 @@ GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
// Attempt to construct an xref table for a damaged file.
GBool XRef::constructXRef() {
- Parser *parser;
- Object newTrailerDict, obj;
- char buf[256];
- GFileOffset pos;
- int num, gen;
- int newSize;
- int streamEndsSize;
- char *p;
- int i;
- GBool gotRoot;
-
- gfree(entries);
- size = 0;
- entries = NULL;
-
- gotRoot = gFalse;
- streamEndsLen = streamEndsSize = 0;
-
+ int *streamObjNums = NULL;
+ int streamObjNumsLen = 0;
+ int streamObjNumsSize = 0;
+ int lastObjNum = -1;
+ rootNum = -1;
+ int streamEndsSize = 0;
+ streamEndsLen = 0;
+ char buf[4096 + 1];
str->reset();
+ GFileOffset bufPos = start;
+ char *p = buf;
+ char *end = buf;
+ GBool startOfLine = gTrue;
+ GBool eof = gFalse;
while (1) {
- pos = str->getPos();
- if (!str->getLine(buf, 256)) {
+ if (end - p < 256 && !eof) {
+ memcpy(buf, p, end - p);
+ bufPos += p - buf;
+ p = buf + (end - p);
+ int n = (int)(buf + 4096 - p);
+ int m = str->getBlock(p, n);
+ end = p + m;
+ *end = '\0';
+ p = buf;
+ eof = m < n;
+ }
+ if (p == end && eof) {
break;
}
- p = buf;
+ if (startOfLine && !strncmp(p, "trailer", 7)) {
+ constructTrailerDict((GFileOffset)(bufPos + (p + 7 - buf)));
+ p += 7;
+ startOfLine = gFalse;
+ } else if (startOfLine && !strncmp(p, "endstream", 9)) {
+ if (streamEndsLen == streamEndsSize) {
+ streamEndsSize += 64;
+ streamEnds = (GFileOffset *)greallocn(streamEnds, streamEndsSize,
+ sizeof(GFileOffset));
+ }
+ streamEnds[streamEndsLen++] = (GFileOffset)(bufPos + (p - buf));
+ p += 9;
+ startOfLine = gFalse;
+ } else if (startOfLine && *p >= '0' && *p <= '9') {
+ p = constructObjectEntry(p, (GFileOffset)(bufPos + (p - buf)),
+ &lastObjNum);
+ startOfLine = gFalse;
+ } else if (p[0] == '>' && p[1] == '>') {
+ p += 2;
+ startOfLine = gFalse;
+ // skip any PDF whitespace except for '\0'
+ while (*p == '\t' || *p == '\n' || *p == '\x0c' ||
+ *p == '\r' || *p == ' ') {
+ startOfLine = *p == '\n' || *p == '\r';
+ ++p;
+ }
+ if (!strncmp(p, "stream", 6)) {
+ if (lastObjNum >= 0) {
+ if (streamObjNumsLen == streamObjNumsSize) {
+ streamObjNumsSize += 64;
+ streamObjNums = (int *)greallocn(streamObjNums, streamObjNumsSize,
+ sizeof(int));
+ }
+ streamObjNums[streamObjNumsLen++] = lastObjNum;
+ }
+ p += 6;
+ startOfLine = gFalse;
+ }
+ } else {
+ startOfLine = *p == '\n' || *p == '\r';
+ ++p;
+ }
+ }
- // skip whitespace
- while (*p && Lexer::isSpace(*p & 0xff)) ++p;
+ // read each stream object, check for xref or object stream
+ for (int i = 0; i < streamObjNumsLen; ++i) {
+ Object obj;
+ fetch(streamObjNums[i], entries[streamObjNums[i]].gen, &obj);
+ if (obj.isStream()) {
+ Dict *dict = obj.streamGetDict();
+ Object type;
+ dict->lookup("Type", &type);
+ if (type.isName("XRef")) {
+ saveTrailerDict(dict, gTrue);
+ } else if (type.isName("ObjStm")) {
+ constructObjectStreamEntries(&obj, streamObjNums[i]);
+ }
+ type.free();
+ }
+ obj.free();
+ }
+
+ gfree(streamObjNums);
- // got trailer dictionary
- if (!strncmp(p, "trailer", 7)) {
- obj.initNull();
- parser = new Parser(NULL,
+ if (rootNum < 0) {
+ error(errSyntaxError, -1, "Couldn't find trailer dictionary");
+ return gFalse;
+ }
+ return gTrue;
+}
+
+// Attempt to construct a trailer dict at [pos] in the stream.
+void XRef::constructTrailerDict(GFileOffset pos) {
+ Object newTrailerDict, obj;
+ obj.initNull();
+ Parser *parser =
+ new Parser(NULL,
new Lexer(NULL,
- str->makeSubStream(pos + 7, gFalse, 0, &obj)),
+ str->makeSubStream(pos, gFalse, 0, &obj)),
gFalse);
- parser->getObj(&newTrailerDict);
- if (newTrailerDict.isDict()) {
- newTrailerDict.dictLookupNF("Root", &obj);
- if (obj.isRef()) {
- rootNum = obj.getRefNum();
- rootGen = obj.getRefGen();
- if (!trailerDict.isNone()) {
- trailerDict.free();
- }
- newTrailerDict.copy(&trailerDict);
- gotRoot = gTrue;
- }
- obj.free();
- }
- newTrailerDict.free();
- delete parser;
+ parser->getObj(&newTrailerDict);
+ if (newTrailerDict.isDict()) {
+ saveTrailerDict(newTrailerDict.getDict(), gFalse);
+ }
+ newTrailerDict.free();
+ delete parser;
+}
- // look for object
- } else if (isdigit(*p & 0xff)) {
- num = atoi(p);
- if (num > 0) {
- do {
- ++p;
- } while (*p && isdigit(*p & 0xff));
- if (isspace(*p & 0xff)) {
- do {
- ++p;
- } while (*p && isspace(*p & 0xff));
- if (isdigit(*p & 0xff)) {
- gen = atoi(p);
- do {
- ++p;
- } while (*p && isdigit(*p & 0xff));
- if (isspace(*p & 0xff)) {
- do {
- ++p;
- } while (*p && isspace(*p & 0xff));
- if (!strncmp(p, "obj", 3)) {
- if (num >= size) {
- newSize = (num + 1 + 255) & ~255;
- if (newSize < 0) {
- error(errSyntaxError, -1, "Bad object number");
- return gFalse;
- }
- entries = (XRefEntry *)
- greallocn(entries, newSize, sizeof(XRefEntry));
- for (i = size; i < newSize; ++i) {
- entries[i].offset = (GFileOffset)-1;
- entries[i].type = xrefEntryFree;
- }
- size = newSize;
- }
- if (entries[num].type == xrefEntryFree ||
- gen >= entries[num].gen) {
- entries[num].offset = pos - start;
- entries[num].gen = gen;
- entries[num].type = xrefEntryUncompressed;
- if (num > last) {
- last = num;
- }
- }
- }
- }
- }
- }
+// If [dict] "looks like" a trailer dict (i.e., has a Root entry),
+// save it as the trailer dict.
+void XRef::saveTrailerDict(Dict *dict, GBool isXRefStream) {
+ Object obj;
+ dict->lookupNF("Root", &obj);
+ if (obj.isRef()) {
+ int newRootNum = obj.getRefNum();
+ // the xref stream scanning code runs after all objects are found,
+ // so we can check for a valid root object number at that point
+ if (!isXRefStream || newRootNum <= last) {
+ rootNum = newRootNum;
+ rootGen = obj.getRefGen();
+ if (!trailerDict.isNone()) {
+ trailerDict.free();
}
+ trailerDict.initDict(dict);
+ }
+ }
+ obj.free();
+}
- } else if (!strncmp(p, "endstream", 9)) {
- if (streamEndsLen == streamEndsSize) {
- streamEndsSize += 64;
- streamEnds = (GFileOffset *)greallocn(streamEnds, streamEndsSize,
- sizeof(GFileOffset));
+// Look for an object header ("nnn ggg obj") at [p]. The first
+// character at *[p] is a digit. [pos] is the position of *[p].
+char *XRef::constructObjectEntry(char *p, GFileOffset pos, int *objNum) {
+ // we look for non-end-of-line space characters here, to deal with
+ // situations like:
+ // nnn <-- garbage digits on a line
+ // nnn nnn obj <-- actual object
+ // and we also ignore '\0' (because it's used to terminate the
+ // buffer in this damage-scanning code)
+ int num = 0;
+ do {
+ num = (num * 10) + (*p - '0');
+ ++p;
+ } while (*p >= '0' && *p <= '9' && num < 100000000);
+ if (*p != '\t' && *p != '\x0c' && *p != ' ') {
+ return p;
+ }
+ do {
+ ++p;
+ } while (*p == '\t' || *p == '\x0c' || *p == ' ');
+ if (!(*p >= '0' && *p <= '9')) {
+ return p;
+ }
+ int gen = 0;
+ do {
+ gen = (gen * 10) + (*p - '0');
+ ++p;
+ } while (*p >= '0' && *p <= '9' && gen < 100000000);
+ if (*p != '\t' && *p != '\x0c' && *p != ' ') {
+ return p;
+ }
+ do {
+ ++p;
+ } while (*p == '\t' || *p == '\x0c' || *p == ' ');
+ if (strncmp(p, "obj", 3)) {
+ return p;
+ }
+
+ if (constructXRefEntry(num, gen, pos - start, xrefEntryUncompressed)) {
+ *objNum = num;
+ }
+
+ return p;
+}
+
+// Read the header from an object stream, and add xref entries for all
+// of its objects.
+void XRef::constructObjectStreamEntries(Object *objStr, int objStrObjNum) {
+ Object obj1, obj2;
+
+ // get the object count
+ if (!objStr->streamGetDict()->lookup("N", &obj1)->isInt()) {
+ obj1.free();
+ return;
+ }
+ int nObjects = obj1.getInt();
+ obj1.free();
+ if (nObjects <= 0 || nObjects > 1000000) {
+ return;
+ }
+
+ // parse the header: object numbers and offsets
+ Parser *parser = new Parser(NULL,
+ new Lexer(NULL, objStr->getStream()->copy()),
+ gFalse);
+ for (int i = 0; i < nObjects; ++i) {
+ parser->getObj(&obj1, gTrue);
+ parser->getObj(&obj2, gTrue);
+ if (obj1.isInt() && obj2.isInt()) {
+ int num = obj1.getInt();
+ if (num >= 0 && num < 1000000) {
+ constructXRefEntry(num, i, objStrObjNum, xrefEntryCompressed);
}
- streamEnds[streamEndsLen++] = pos;
}
+ obj2.free();
+ obj1.free();
}
+ delete parser;
+}
- if (gotRoot) {
- return gTrue;
+GBool XRef::constructXRefEntry(int num, int gen, GFileOffset pos,
+ XRefEntryType type) {
+ if (num >= size) {
+ int newSize = (num + 1 + 255) & ~255;
+ if (newSize < 0) {
+ return gFalse;
+ }
+ entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
+ for (int i = size; i < newSize; ++i) {
+ entries[i].offset = (GFileOffset)-1;
+ entries[i].type = xrefEntryFree;
+ }
+ size = newSize;
}
- error(errSyntaxError, -1, "Couldn't find trailer dictionary");
- return gFalse;
+ if (entries[num].type == xrefEntryFree ||
+ gen >= entries[num].gen) {
+ entries[num].offset = pos;
+ entries[num].gen = gen;
+ entries[num].type = type;
+ if (num > last) {
+ last = num;
+ }
+ }
+
+ return gTrue;
}
void XRef::setEncryption(int permFlagsA, GBool ownerPasswordOkA,
@@ -1153,6 +1277,9 @@ GBool XRef::getObjectStreamObject(int objStrNum, int objIdx,
gLockMutex(&objStrsMutex);
#endif
if (!(objStr = getObjectStream(objStrNum))) {
+#if MULTITHREADED
+ gUnlockMutex(&objStrsMutex);
+#endif
return gFalse;
}
cleanObjectStreamCache();
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h
index 9e63dcf04da..00a8644f3a9 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h
@@ -166,11 +166,17 @@ private:
#endif
GFileOffset getStartXref();
- GBool readXRef(GFileOffset *pos, XRefPosSet *posSet);
+ GBool readXRef(GFileOffset *pos, XRefPosSet *posSet, GBool hybrid);
GBool readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet);
+ GBool readXRefStream(Stream *xrefStr, GFileOffset *pos, GBool hybrid);
GBool readXRefStreamSection(Stream *xrefStr, int *w, int first, int n);
- GBool readXRefStream(Stream *xrefStr, GFileOffset *pos);
GBool constructXRef();
+ void constructTrailerDict(GFileOffset pos);
+ void saveTrailerDict(Dict *dict, GBool isXRefStream);
+ char *constructObjectEntry(char *p, GFileOffset pos, int *objNum);
+ void constructObjectStreamEntries(Object *objStr, int objStrObjNum);
+ GBool constructXRefEntry(int num, int gen, GFileOffset pos,
+ XRefEntryType type);
GBool getObjectStreamObject(int objStrNum, int objIdx,
int objNum, Object *obj);
ObjectStream *getObjectStream(int objStrNum);
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc
index ba6e11496ae..9e4fb47900c 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc
@@ -430,7 +430,8 @@ ZxAttr *ZxDoc::parseAttr() {
GString *name, *value;
const char *start;
char quote, c;
- int x, n;
+ unsigned int x;
+ int n;
name = parseName();
parseSpace();
@@ -559,7 +560,8 @@ void ZxDoc::parseCharData(ZxElement *par) {
GString *data;
const char *start;
char c;
- int x, n;
+ unsigned int x;
+ int n;
data = new GString();
while (parsePtr < parseEnd && *parsePtr != '<') {
@@ -638,7 +640,7 @@ void ZxDoc::parseCharData(ZxElement *par) {
par->addChild(new ZxCharData(data, true));
}
-void ZxDoc::appendUTF8(GString *s, int c) {
+void ZxDoc::appendUTF8(GString *s, unsigned int c) {
if (c <= 0x7f) {
s->append((char)c);
} else if (c <= 0x7ff) {
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h
index 49f9f6b1da8..c04ca95064b 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h
@@ -103,7 +103,7 @@ private:
ZxAttr *parseAttr();
void parseContent(ZxElement *par);
void parseCharData(ZxElement *par);
- void appendUTF8(GString *s, int c);
+ void appendUTF8(GString *s, unsigned int c);
void parseCDSect(ZxNode *par);
void parseMisc(ZxNode *par);
void parseComment(ZxNode *par);
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/config.h b/Build/source/libs/xpdf/xpdf-src/xpdf/config.h
index 3dbed2ae93c..39cade1682c 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/config.h
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/config.h
@@ -2,7 +2,7 @@
//
// config.h
//
-// Copyright 1996-2019 Glyph & Cog, LLC
+// Copyright 1996-2021 Glyph & Cog, LLC
//
//========================================================================
@@ -14,13 +14,13 @@
//------------------------------------------------------------------------
// xpdf version
-#define xpdfVersion "4.02"
-#define xpdfVersionNum 4.02
+#define xpdfVersion "4.03"
+#define xpdfVersionNum 4.03
#define xpdfMajorVersion 4
-#define xpdfMinorVersion 2
+#define xpdfMinorVersion 3
#define xpdfUpdateVersion 0
#define xpdfMajorVersionStr "4"
-#define xpdfMinorVersionStr "2"
+#define xpdfMinorVersionStr "3"
#define xpdfUpdateVersionStr "0"
// supported PDF version
@@ -28,11 +28,11 @@
#define supportedPDFVersionNum 2.0
// copyright notice
-#define xpdfCopyright "Copyright 1996-2019 Glyph & Cog, LLC"
+#define xpdfCopyright "Copyright 1996-2021 Glyph & Cog, LLC"
// Windows resource file stuff
-#define winxpdfVersion "WinXpdf 4.02"
-#define xpdfCopyrightAmp "Copyright 1996-2019 Glyph && Cog, LLC"
+#define winxpdfVersion "WinXpdf 4.03"
+#define xpdfCopyrightAmp "Copyright 1996-2021 Glyph && Cog, LLC"
//------------------------------------------------------------------------
// paper size
@@ -59,12 +59,14 @@
#endif
// system config file name (set via the configure script)
-#ifdef SYSTEM_XPDFRC
-#define xpdfSysConfigFile SYSTEM_XPDFRC
-#else
+#if defined(_WIN32)
// under Windows, we get the directory with the executable and then
// append this file name
#define xpdfSysConfigFile "xpdfrc"
+#elif defined(SYSTEM_XPDFRC)
+#define xpdfSysConfigFile SYSTEM_XPDFRC
+#else
+#define xpdfSysConfigFile "/etc/xpdfrc"
#endif
//------------------------------------------------------------------------
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc
index 34fd439bd76..e8b74381017 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc
@@ -16,6 +16,7 @@
#include "PDFDoc.h"
#include "CharTypes.h"
#include "UnicodeMap.h"
+#include "UTF8.h"
#include "Error.h"
#include "config.h"
@@ -67,8 +68,7 @@ int main(int argc, char *argv[]) {
PDFDoc *doc;
Unicode *name;
char uBuf[8];
- char path[1024];
- char *p;
+ GString *path;
GBool ok;
int exitCode;
int nFiles, nameLen, n, i, j;
@@ -84,7 +84,7 @@ int main(int argc, char *argv[]) {
ok = gFalse;
}
if (!ok || argc != 2 || printVersion || printHelp) {
- fprintf(stderr, "pdfdetach version %s\n", xpdfVersion);
+ fprintf(stderr, "pdfdetach version %s [www.xpdfreader.com]\n", xpdfVersion);
fprintf(stderr, "%s\n", xpdfCopyright);
if (!printVersion) {
printUsage("pdfdetach", "<PDF-file>", argDesc);
@@ -148,32 +148,24 @@ int main(int argc, char *argv[]) {
} else if (saveAll) {
for (i = 0; i < nFiles; ++i) {
if (savePath[0]) {
- n = (int)strlen(savePath);
- if (n > (int)sizeof(path) - 2) {
- n = sizeof(path) - 2;
- }
- memcpy(path, savePath, n);
- path[n] = '/';
- p = path + n + 1;
+ path = new GString(savePath);
+ path->append('/');
} else {
- p = path;
+ path = new GString();
}
name = doc->getEmbeddedFileName(i);
nameLen = doc->getEmbeddedFileNameLength(i);
for (j = 0; j < nameLen; ++j) {
- n = uMap->mapUnicode(name[j], uBuf, sizeof(uBuf));
- if (p + n >= path + sizeof(path)) {
- break;
- }
- memcpy(p, uBuf, n);
- p += n;
+ n = mapUTF8(name[j], uBuf, sizeof(uBuf));
+ path->append(uBuf, n);
}
- *p = '\0';
- if (!doc->saveEmbeddedFile(i, path)) {
- error(errIO, -1, "Error saving embedded file as '{0:s}'", p);
+ if (!doc->saveEmbeddedFileU(i, path->getCString())) {
+ error(errIO, -1, "Error saving embedded file as '{0:t}'", path);
+ delete path;
exitCode = 2;
goto err2;
}
+ delete path;
}
// save an embedded file
@@ -183,27 +175,23 @@ int main(int argc, char *argv[]) {
goto err2;
}
if (savePath[0]) {
- p = savePath;
+ path = new GString(savePath);
} else {
name = doc->getEmbeddedFileName(saveNum - 1);
nameLen = doc->getEmbeddedFileNameLength(saveNum - 1);
- p = path;
+ path = new GString();
for (j = 0; j < nameLen; ++j) {
- n = uMap->mapUnicode(name[j], uBuf, sizeof(uBuf));
- if (p + n >= path + sizeof(path)) {
- break;
- }
- memcpy(p, uBuf, n);
- p += n;
+ n = mapUTF8(name[j], uBuf, sizeof(uBuf));
+ path->append(uBuf, n);
}
- *p = '\0';
- p = path;
}
- if (!doc->saveEmbeddedFile(saveNum - 1, p)) {
- error(errIO, -1, "Error saving embedded file as '{0:s}'", p);
+ if (!doc->saveEmbeddedFileU(saveNum - 1, path->getCString())) {
+ error(errIO, -1, "Error saving embedded file as '{0:t}'", path);
+ delete path;
exitCode = 2;
goto err2;
}
+ delete path;
}
exitCode = 0;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc
index 22298b138c5..42c46223d8b 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc
@@ -23,7 +23,7 @@
#include "Dict.h"
#include "GfxFont.h"
#include "Annot.h"
-#include "Form.h"
+#include "AcroForm.h"
#include "PDFDoc.h"
#include "config.h"
@@ -102,7 +102,7 @@ int main(int argc, char *argv[]) {
Page *page;
Dict *resDict;
Annots *annots;
- Form *form;
+ AcroForm *form;
Object obj1, obj2;
int pg, i, j;
int exitCode;
@@ -113,7 +113,7 @@ int main(int argc, char *argv[]) {
fixCommandLine(&argc, &argv);
ok = parseArgs(argDesc, &argc, argv);
if (!ok || argc != 2 || printVersion || printHelp) {
- fprintf(stderr, "pdffonts version %s\n", xpdfVersion);
+ fprintf(stderr, "pdffonts version %s [www.xpdfreader.com]\n", xpdfVersion);
fprintf(stderr, "%s\n", xpdfCopyright);
if (!printVersion) {
printUsage("pdffonts", "<PDF-file>", argDesc);
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc
index 24b7c0d448b..cd5d34014f4 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc
@@ -87,7 +87,7 @@ int main(int argc, char *argv[]) {
fixCommandLine(&argc, &argv);
ok = parseArgs(argDesc, &argc, argv);
if (!ok || argc != 3 || printVersion || printHelp) {
- fprintf(stderr, "pdfimages version %s\n", xpdfVersion);
+ fprintf(stderr, "pdfimages version %s [www.xpdfreader.com]\n", xpdfVersion);
fprintf(stderr, "%s\n", xpdfCopyright);
if (!printVersion) {
printUsage("pdfimages", "<PDF-file> <image-root>", argDesc);
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc
index f5f593b9f13..621fb69ea6f 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc
@@ -102,6 +102,9 @@ int main(int argc, char *argv[]) {
FILE *f;
GString *metadata;
ZxDoc *xmp;
+ int permFlags, keyLength, encVersion;
+ GBool ownerPasswordOk;
+ CryptAlgorithm encAlgorithm;
GBool ok;
int exitCode;
int pg, i;
@@ -113,7 +116,7 @@ int main(int argc, char *argv[]) {
fixCommandLine(&argc, &argv);
ok = parseArgs(argDesc, &argc, argv);
if (!ok || argc != 2 || printVersion || printHelp) {
- fprintf(stderr, "pdfinfo version %s\n", xpdfVersion);
+ fprintf(stderr, "pdfinfo version %s [www.xpdfreader.com]\n", xpdfVersion);
fprintf(stderr, "%s\n", xpdfCopyright);
if (!printVersion) {
printUsage("pdfinfo", "<PDF-file>", argDesc);
@@ -216,15 +219,19 @@ int main(int argc, char *argv[]) {
printf("Pages: %d\n", doc->getNumPages());
// print encryption info
- printf("Encrypted: ");
if (doc->isEncrypted()) {
- printf("yes (print:%s copy:%s change:%s addNotes:%s)\n",
+ doc->getXRef()->getEncryption(&permFlags, &ownerPasswordOk, &keyLength,
+ &encVersion, &encAlgorithm);
+ printf("Encrypted: %s %d-bit\n",
+ encAlgorithm == cryptRC4 ? "RC4" : "AES",
+ keyLength * 8);
+ printf("Permissions: print:%s copy:%s change:%s addNotes:%s\n",
doc->okToPrint(gTrue) ? "yes" : "no",
doc->okToCopy(gTrue) ? "yes" : "no",
doc->okToChange(gTrue) ? "yes" : "no",
doc->okToAddNotes(gTrue) ? "yes" : "no");
} else {
- printf("no\n");
+ printf("Encrypted: no\n");
}
// print page size
@@ -261,18 +268,18 @@ int main(int argc, char *argv[]) {
if (multiPage) {
for (pg = firstPage; pg <= lastPage; ++pg) {
page = doc->getCatalog()->getPage(pg);
- sprintf(buf, "Page %4d MediaBox: ", pg);
+ snprintf(buf, sizeof(buf), "Page %4d MediaBox: ", pg);
printBox(buf, page->getMediaBox());
- sprintf(buf, "Page %4d CropBox: ", pg);
+ snprintf(buf, sizeof(buf), "Page %4d CropBox: ", pg);
printBox(buf, page->getCropBox());
- sprintf(buf, "Page %4d BleedBox: ", pg);
+ snprintf(buf, sizeof(buf), "Page %4d BleedBox: ", pg);
printBox(buf, page->getBleedBox());
- sprintf(buf, "Page %4d TrimBox: ", pg);
+ snprintf(buf, sizeof(buf), "Page %4d TrimBox: ", pg);
printBox(buf, page->getTrimBox());
- sprintf(buf, "Page %4d ArtBox: ", pg);
+ snprintf(buf, sizeof(buf), "Page %4d ArtBox: ", pg);
printBox(buf, page->getArtBox());
}
- } else {
+ } else if (doc->getNumPages() > 0) {
page = doc->getCatalog()->getPage(firstPage);
printBox("MediaBox: ", page->getMediaBox());
printBox("CropBox: ", page->getCropBox());
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc
index 29095509bed..af8fe93fbd5 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc
@@ -31,6 +31,7 @@ static int firstPage = 1;
static int lastPage = 0;
static double zoom = 1;
static int resolution = 150;
+static GBool noFonts = gFalse;
static GBool skipInvisible = gFalse;
static GBool allInvisible = gFalse;
static char ownerPassword[33] = "\001";
@@ -45,10 +46,12 @@ static ArgDesc argDesc[] = {
"first page to convert"},
{"-l", argInt, &lastPage, 0,
"last page to convert"},
- {"-z", argFP, &zoom, 0,
+ {"-z", argFP, &zoom, 0,
"initial zoom level (1.0 means 72dpi)"},
- {"-r", argInt, &resolution, 0,
+ {"-r", argInt, &resolution, 0,
"resolution, in DPI (default is 150)"},
+ {"-nofonts", argFlag, &noFonts, 0,
+ "do not extract embedded fonts"},
{"-skipinvisible", argFlag, &skipInvisible, 0,
"do not draw invisible text"},
{"-allinvisible", argFlag, &allInvisible, 0,
@@ -97,7 +100,7 @@ int main(int argc, char *argv[]) {
fixCommandLine(&argc, &argv);
ok = parseArgs(argDesc, &argc, argv);
if (!ok || argc != 3 || printVersion || printHelp) {
- fprintf(stderr, "pdftohtml version %s\n", xpdfVersion);
+ fprintf(stderr, "pdftohtml version %s [www.xpdfreader.com]\n", xpdfVersion);
fprintf(stderr, "%s\n", xpdfCopyright);
if (!printVersion) {
printUsage("pdftohtml", "<PDF-file> <html-dir>", argDesc);
@@ -155,7 +158,7 @@ int main(int argc, char *argv[]) {
}
// create HTML directory
- if (!createDir(htmlDir, 0755)) {
+ if (makeDir(htmlDir, 0755)) {
error(errIO, -1, "Couldn't create HTML output directory '{0:s}'",
htmlDir);
exitCode = 2;
@@ -171,20 +174,20 @@ int main(int argc, char *argv[]) {
htmlGen->setZoom(zoom);
htmlGen->setDrawInvisibleText(!skipInvisible);
htmlGen->setAllTextInvisible(allInvisible);
- htmlGen->setExtractFontFiles(gTrue);
+ htmlGen->setExtractFontFiles(!noFonts);
htmlGen->startDoc(doc);
// convert the pages
for (pg = firstPage; pg <= lastPage; ++pg) {
htmlFileName = GString::format("{0:s}/page{1:d}.html", htmlDir, pg);
pngFileName = GString::format("{0:s}/page{1:d}.png", htmlDir, pg);
- if (!(htmlFile = fopen(htmlFileName->getCString(), "wb"))) {
+ if (!(htmlFile = openFile(htmlFileName->getCString(), "wb"))) {
error(errIO, -1, "Couldn't open HTML file '{0:t}'", htmlFileName);
delete htmlFileName;
delete pngFileName;
goto err2;
}
- if (!(pngFile = fopen(pngFileName->getCString(), "wb"))) {
+ if (!(pngFile = openFile(pngFileName->getCString(), "wb"))) {
error(errIO, -1, "Couldn't open PNG file '{0:t}'", pngFileName);
fclose(htmlFile);
delete htmlFileName;
@@ -236,7 +239,7 @@ static GBool createIndex(char *htmlDir) {
int pg;
htmlFileName = GString::format("{0:s}/index.html", htmlDir);
- html = fopen(htmlFileName->getCString(), "w");
+ html = openFile(htmlFileName->getCString(), "w");
delete htmlFileName;
if (!html) {
error(errIO, -1, "Couldn't open HTML file '{0:t}'", htmlFileName);
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc
index 64a0087c7d3..f19baccf581 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc
@@ -18,6 +18,7 @@
#include "gmempp.h"
#include "parseargs.h"
#include "GString.h"
+#include "gfile.h"
#include "GlobalParams.h"
#include "Object.h"
#include "PDFDoc.h"
@@ -32,6 +33,7 @@ static double resolution = 150;
static GBool mono = gFalse;
static GBool gray = gFalse;
static GBool pngAlpha = gFalse;
+static int rotate = 0;
static char enableFreeTypeStr[16] = "";
static char antialiasStr[16] = "";
static char vectorAntialiasStr[16] = "";
@@ -55,6 +57,8 @@ static ArgDesc argDesc[] = {
"generate a grayscale PNG file"},
{"-alpha", argFlag, &pngAlpha, 0,
"include an alpha channel in the PNG file"},
+ {"-rot", argInt, &rotate, 0,
+ "set page rotation: 0, 90, 180, or 270"},
#if HAVE_FREETYPE_H
{"-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr),
"enable FreeType font rasterizer: yes, no"},
@@ -119,7 +123,7 @@ int main(int argc, char *argv[]) {
goto err0;
}
if (!ok || argc != 3 || printVersion || printHelp) {
- fprintf(stderr, "pdftopng version %s\n", xpdfVersion);
+ fprintf(stderr, "pdftopng version %s [www.xpdfreader.com]\n", xpdfVersion);
fprintf(stderr, "%s\n", xpdfCopyright);
if (!printVersion) {
printUsage("pdftopng", "<PDF-file> <PNG-root>", argDesc);
@@ -197,7 +201,7 @@ int main(int argc, char *argv[]) {
}
splashOut->startDoc(doc->getXRef());
for (pg = firstPage; pg <= lastPage; ++pg) {
- doc->displayPage(splashOut, pg, resolution, resolution, 0,
+ doc->displayPage(splashOut, pg, resolution, resolution, rotate,
gFalse, gTrue, gFalse);
if (mono) {
if (!strcmp(pngRoot, "-")) {
@@ -207,7 +211,7 @@ int main(int argc, char *argv[]) {
#endif
} else {
pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg);
- if (!(f = fopen(pngFile->getCString(), "wb"))) {
+ if (!(f = openFile(pngFile->getCString(), "wb"))) {
exit(2);
}
delete pngFile;
@@ -225,7 +229,7 @@ int main(int argc, char *argv[]) {
#endif
} else {
pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg);
- if (!(f = fopen(pngFile->getCString(), "wb"))) {
+ if (!(f = openFile(pngFile->getCString(), "wb"))) {
exit(2);
}
delete pngFile;
@@ -244,7 +248,7 @@ int main(int argc, char *argv[]) {
#endif
} else {
pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg);
- if (!(f = fopen(pngFile->getCString(), "wb"))) {
+ if (!(f = openFile(pngFile->getCString(), "wb"))) {
exit(2);
}
delete pngFile;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc
index e01ac5c1304..0b084b014e5 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc
@@ -36,6 +36,7 @@ static GBool gray = gFalse;
#if SPLASH_CMYK
static GBool cmyk = gFalse;
#endif
+static int rotate = 0;
static char enableFreeTypeStr[16] = "";
static char antialiasStr[16] = "";
static char vectorAntialiasStr[16] = "";
@@ -61,6 +62,8 @@ static ArgDesc argDesc[] = {
{"-cmyk", argFlag, &cmyk, 0,
"generate a CMYK PAM file"},
#endif
+ {"-rot", argInt, &rotate, 0,
+ "set page rotation: 0, 90, 180, or 270"},
#if HAVE_FREETYPE_H
{"-freetype", argString, enableFreeTypeStr, sizeof(enableFreeTypeStr),
"enable FreeType font rasterizer: yes, no"},
@@ -133,7 +136,7 @@ int main(int argc, char *argv[]) {
ok = gFalse;
}
if (!ok || argc != 3 || printVersion || printHelp) {
- fprintf(stderr, "pdftoppm version %s\n", xpdfVersion);
+ fprintf(stderr, "pdftoppm version %s [www.xpdfreader.com]\n", xpdfVersion);
fprintf(stderr, "%s\n", xpdfCopyright);
if (!printVersion) {
printUsage("pdftoppm", "<PDF-file> <PPM-root>", argDesc);
@@ -226,7 +229,7 @@ int main(int argc, char *argv[]) {
}
splashOut->startDoc(doc->getXRef());
for (pg = firstPage; pg <= lastPage; ++pg) {
- doc->displayPage(splashOut, pg, resolution, resolution, 0,
+ doc->displayPage(splashOut, pg, resolution, resolution, rotate,
gFalse, gTrue, gFalse);
if (!strcmp(ppmRoot, "-")) {
#ifdef _WIN32
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc
index 8e75dd83f55..36294551a3b 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc
@@ -182,7 +182,7 @@ int main(int argc, char *argv[]) {
fixCommandLine(&argc, &argv);
ok = parseArgs(argDesc, &argc, argv);
if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) {
- fprintf(stderr, "pdftops version %s\n", xpdfVersion);
+ fprintf(stderr, "pdftops version %s [www.xpdfreader.com]\n", xpdfVersion);
fprintf(stderr, "%s\n", xpdfCopyright);
if (!printVersion) {
printUsage("pdftops", "<PDF-file> [<PS-file>]", argDesc);
@@ -355,7 +355,7 @@ int main(int argc, char *argv[]) {
// write PostScript file
psOut = new PSOutputDev(psFileName->getCString(), doc,
firstPage, lastPage, mode,
- 0, 0, 0, 0, gFalse, NULL, NULL, userUnit);
+ 0, 0, 0, 0, gFalse, NULL, NULL, userUnit, gTrue);
if (!psOut->isOk()) {
delete psOut;
exitCode = 2;
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc
index aa74fb68612..975cd6a1959 100644
--- a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc
@@ -19,6 +19,7 @@
#include "gmempp.h"
#include "parseargs.h"
#include "GString.h"
+#include "GList.h"
#include "GlobalParams.h"
#include "Object.h"
#include "Stream.h"
@@ -39,6 +40,7 @@ static int firstPage = 1;
static int lastPage = 0;
static GBool physLayout = gFalse;
static GBool simpleLayout = gFalse;
+static GBool simple2Layout = gFalse;
static GBool tableLayout = gFalse;
static GBool linePrinter = gFalse;
static GBool rawOrder = gFalse;
@@ -58,6 +60,7 @@ static char ownerPassword[33] = "\001";
static char userPassword[33] = "\001";
static GBool quiet = gFalse;
static char cfgFileName[256] = "";
+static GBool listEncodings = gFalse;
static GBool printVersion = gFalse;
static GBool printHelp = gFalse;
@@ -70,6 +73,8 @@ static ArgDesc argDesc[] = {
"maintain original physical layout"},
{"-simple", argFlag, &simpleLayout, 0,
"simple one-column page layout"},
+ {"-simple2", argFlag, &simple2Layout, 0,
+ "simple one-column page layout, version 2"},
{"-table", argFlag, &tableLayout, 0,
"similar to -layout, but optimized for tables"},
{"-lineprinter", argFlag, &linePrinter, 0,
@@ -108,6 +113,8 @@ static ArgDesc argDesc[] = {
"don't print any messages or errors"},
{"-cfg", argString, cfgFileName, sizeof(cfgFileName),
"configuration file to use in place of .xpdfrc"},
+ {"-listencodings", argFlag, &listEncodings, 0,
+ "list all available output text encodings"},
{"-v", argFlag, &printVersion, 0,
"print copyright and version info"},
{"-h", argFlag, &printHelp, 0,
@@ -153,8 +160,19 @@ int main(int argc, char *argv[]) {
// parse args
fixCommandLine(&argc, &argv);
ok = parseArgs(argDesc, &argc, argv);
+ if (ok && listEncodings) {
+ // list available encodings
+ globalParams = new GlobalParams(cfgFileName);
+ GList *encs = globalParams->getAvailableTextEncodings();
+ for (int i = 0; i < encs->getLength(); ++i) {
+ printf("%s\n", ((GString *)encs->get(i))->getCString());
+ }
+ deleteGList(encs, GString);
+ delete globalParams;
+ goto err0;
+ }
if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) {
- fprintf(stderr, "pdftotext version %s\n", xpdfVersion);
+ fprintf(stderr, "pdftotext version %s [www.xpdfreader.com]\n", xpdfVersion);
fprintf(stderr, "%s\n", xpdfCopyright);
if (!printVersion) {
printUsage("pdftotext", "<PDF-file> [<text-file>]", argDesc);
@@ -247,6 +265,8 @@ int main(int argc, char *argv[]) {
textOutControl.fixedPitch = fixedPitch;
} else if (simpleLayout) {
textOutControl.mode = textOutSimpleLayout;
+ } else if (simple2Layout) {
+ textOutControl.mode = textOutSimple2Layout;
} else if (linePrinter) {
textOutControl.mode = textOutLinePrinter;
textOutControl.fixedPitch = fixedPitch;
@@ -264,7 +284,7 @@ int main(int argc, char *argv[]) {
textOutControl.marginTop = marginTop;
textOutControl.marginBottom = marginBottom;
textOut = new TextOutputDev(textFileName->getCString(), &textOutControl,
- gFalse);
+ gFalse, gTrue);
if (textOut->isOk()) {
doc->displayPages(textOut, firstPage, lastPage, 72, 72, 0,
gFalse, gTrue, gFalse);