summaryrefslogtreecommitdiff
path: root/Build/source/libs
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
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')
-rw-r--r--Build/source/libs/README2
-rw-r--r--Build/source/libs/xpdf/ChangeLog5
-rw-r--r--Build/source/libs/xpdf/Makefile.am4
-rw-r--r--Build/source/libs/xpdf/Makefile.in31
-rw-r--r--Build/source/libs/xpdf/TLpatches/ChangeLog4
-rw-r--r--Build/source/libs/xpdf/TLpatches/TL-Changes2
-rw-r--r--Build/source/libs/xpdf/TLpatches/patch-bunched159
-rwxr-xr-xBuild/source/libs/xpdf/configure20
-rw-r--r--Build/source/libs/xpdf/version.ac2
-rw-r--r--Build/source/libs/xpdf/xpdf-src/ANNOUNCE20
-rw-r--r--Build/source/libs/xpdf/xpdf-src/CHANGES96
-rw-r--r--Build/source/libs/xpdf/xpdf-src/INSTALL24
-rw-r--r--Build/source/libs/xpdf/xpdf-src/README12
-rw-r--r--Build/source/libs/xpdf/xpdf-src/cmake-config.txt32
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.18
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat6
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdffonts.18
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat6
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdfimages.18
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat6
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.18
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat6
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.113
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat11
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftopng.111
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat9
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.111
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat9
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftops.114
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat12
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftotext.121
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat21
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/xpdf.156
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat148
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.555
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat106
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc19
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc26
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc2
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc84
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h2
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt1
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h15
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/Trace.cc114
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/Trace.h43
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/gfile.cc141
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/gfile.h13
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/gmem.h8
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/Splash.cc5663
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/Splash.h225
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc20
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h4
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc50
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc36
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h6
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc3
-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
125 files changed, 11783 insertions, 9083 deletions
diff --git a/Build/source/libs/README b/Build/source/libs/README
index 53c71940d8c..b5d36374598 100644
--- a/Build/source/libs/README
+++ b/Build/source/libs/README
@@ -58,7 +58,7 @@ pplib 2.05.0 - checked 21apr20
teckit 2.5.10 - checked 06may20
https://github.com/silnrsi/teckit/archive/2.5.10.tar.gz
-xpdf 4.02 - checked 29sep19
+xpdf 4.03 - checked 01feb21
http://www.xpdfreader.com/download.html
with modifications for pdftex
diff --git a/Build/source/libs/xpdf/ChangeLog b/Build/source/libs/xpdf/ChangeLog
index b619191ae89..153f0b61cdc 100644
--- a/Build/source/libs/xpdf/ChangeLog
+++ b/Build/source/libs/xpdf/ChangeLog
@@ -1,3 +1,8 @@
+2021-02-01 Akira Kakuto <kakuto@w32tex.org>
+
+ * Import xpdf-4.03.
+ * version.ac, Makefile.am: Adjust.
+
2020-05-14 Karl Berry <karl@freefriends.org>
* ac/xpdf.ac: doc change for poppler no longer supported in TL.
diff --git a/Build/source/libs/xpdf/Makefile.am b/Build/source/libs/xpdf/Makefile.am
index be6070e4863..c6141cdefc8 100644
--- a/Build/source/libs/xpdf/Makefile.am
+++ b/Build/source/libs/xpdf/Makefile.am
@@ -62,6 +62,7 @@ goo_libGoo_a_sources = \
@XPDF_TREE@/goo/GHash.cc \
@XPDF_TREE@/goo/GList.cc \
@XPDF_TREE@/goo/GString.cc \
+ @XPDF_TREE@/goo/Trace.cc \
@XPDF_TREE@/goo/gfile.cc \
@XPDF_TREE@/goo/gmem.cc \
@XPDF_TREE@/goo/gmempp.cc
@@ -87,7 +88,6 @@ xpdf_libxpdf_a_sources = \
@XPDF_TREE@/xpdf/Dict.cc \
@XPDF_TREE@/xpdf/Error.cc \
@XPDF_TREE@/xpdf/FontEncodingTables.cc \
- @XPDF_TREE@/xpdf/Form.cc \
@XPDF_TREE@/xpdf/Function.cc \
@XPDF_TREE@/xpdf/Gfx.cc \
@XPDF_TREE@/xpdf/GfxFont.cc \
@@ -115,7 +115,7 @@ xpdf_libxpdf_a_sources = \
@XPDF_TREE@/xpdf/UnicodeMap.cc \
@XPDF_TREE@/xpdf/UnicodeRemapping.cc \
@XPDF_TREE@/xpdf/UTF8.cc \
- @XPDF_TREE@/xpdf/XFAForm.cc \
+ @XPDF_TREE@/xpdf/XFAScanner.cc \
@XPDF_TREE@/xpdf/XRef.cc \
@XPDF_TREE@/xpdf/Zoox.cc
diff --git a/Build/source/libs/xpdf/Makefile.in b/Build/source/libs/xpdf/Makefile.in
index 6fb37b4318c..aaff3513a22 100644
--- a/Build/source/libs/xpdf/Makefile.in
+++ b/Build/source/libs/xpdf/Makefile.in
@@ -115,6 +115,7 @@ am__objects_1 = @XPDF_TREE@/goo/FixedPoint.$(OBJEXT) \
@XPDF_TREE@/goo/GHash.$(OBJEXT) \
@XPDF_TREE@/goo/GList.$(OBJEXT) \
@XPDF_TREE@/goo/GString.$(OBJEXT) \
+ @XPDF_TREE@/goo/Trace.$(OBJEXT) \
@XPDF_TREE@/goo/gfile.$(OBJEXT) @XPDF_TREE@/goo/gmem.$(OBJEXT) \
@XPDF_TREE@/goo/gmempp.$(OBJEXT)
am__objects_2 = @XPDF_TREE@/fofi/FoFiBase.$(OBJEXT) \
@@ -135,7 +136,6 @@ am__objects_3 = @XPDF_TREE@/xpdf/AcroForm.$(OBJEXT) \
@XPDF_TREE@/xpdf/Dict.$(OBJEXT) \
@XPDF_TREE@/xpdf/Error.$(OBJEXT) \
@XPDF_TREE@/xpdf/FontEncodingTables.$(OBJEXT) \
- @XPDF_TREE@/xpdf/Form.$(OBJEXT) \
@XPDF_TREE@/xpdf/Function.$(OBJEXT) \
@XPDF_TREE@/xpdf/Gfx.$(OBJEXT) \
@XPDF_TREE@/xpdf/GfxFont.$(OBJEXT) \
@@ -163,7 +163,7 @@ am__objects_3 = @XPDF_TREE@/xpdf/AcroForm.$(OBJEXT) \
@XPDF_TREE@/xpdf/UnicodeMap.$(OBJEXT) \
@XPDF_TREE@/xpdf/UnicodeRemapping.$(OBJEXT) \
@XPDF_TREE@/xpdf/UTF8.$(OBJEXT) \
- @XPDF_TREE@/xpdf/XFAForm.$(OBJEXT) \
+ @XPDF_TREE@/xpdf/XFAScanner.$(OBJEXT) \
@XPDF_TREE@/xpdf/XRef.$(OBJEXT) \
@XPDF_TREE@/xpdf/Zoox.$(OBJEXT)
nodist_libxpdf_a_OBJECTS = $(am__objects_1) $(am__objects_2) \
@@ -194,6 +194,7 @@ am__depfiles_remade = @XPDF_TREE@/fofi/$(DEPDIR)/FoFiBase.Po \
@XPDF_TREE@/goo/$(DEPDIR)/GHash.Po \
@XPDF_TREE@/goo/$(DEPDIR)/GList.Po \
@XPDF_TREE@/goo/$(DEPDIR)/GString.Po \
+ @XPDF_TREE@/goo/$(DEPDIR)/Trace.Po \
@XPDF_TREE@/goo/$(DEPDIR)/gfile.Po \
@XPDF_TREE@/goo/$(DEPDIR)/gmem.Po \
@XPDF_TREE@/goo/$(DEPDIR)/gmempp.Po \
@@ -209,7 +210,6 @@ am__depfiles_remade = @XPDF_TREE@/fofi/$(DEPDIR)/FoFiBase.Po \
@XPDF_TREE@/xpdf/$(DEPDIR)/Dict.Po \
@XPDF_TREE@/xpdf/$(DEPDIR)/Error.Po \
@XPDF_TREE@/xpdf/$(DEPDIR)/FontEncodingTables.Po \
- @XPDF_TREE@/xpdf/$(DEPDIR)/Form.Po \
@XPDF_TREE@/xpdf/$(DEPDIR)/Function.Po \
@XPDF_TREE@/xpdf/$(DEPDIR)/Gfx.Po \
@XPDF_TREE@/xpdf/$(DEPDIR)/GfxFont.Po \
@@ -237,7 +237,7 @@ am__depfiles_remade = @XPDF_TREE@/fofi/$(DEPDIR)/FoFiBase.Po \
@XPDF_TREE@/xpdf/$(DEPDIR)/UTF8.Po \
@XPDF_TREE@/xpdf/$(DEPDIR)/UnicodeMap.Po \
@XPDF_TREE@/xpdf/$(DEPDIR)/UnicodeRemapping.Po \
- @XPDF_TREE@/xpdf/$(DEPDIR)/XFAForm.Po \
+ @XPDF_TREE@/xpdf/$(DEPDIR)/XFAScanner.Po \
@XPDF_TREE@/xpdf/$(DEPDIR)/XRef.Po \
@XPDF_TREE@/xpdf/$(DEPDIR)/Zoox.Po
am__mv = mv -f
@@ -483,6 +483,7 @@ goo_libGoo_a_sources = \
@XPDF_TREE@/goo/GHash.cc \
@XPDF_TREE@/goo/GList.cc \
@XPDF_TREE@/goo/GString.cc \
+ @XPDF_TREE@/goo/Trace.cc \
@XPDF_TREE@/goo/gfile.cc \
@XPDF_TREE@/goo/gmem.cc \
@XPDF_TREE@/goo/gmempp.cc
@@ -508,7 +509,6 @@ xpdf_libxpdf_a_sources = \
@XPDF_TREE@/xpdf/Dict.cc \
@XPDF_TREE@/xpdf/Error.cc \
@XPDF_TREE@/xpdf/FontEncodingTables.cc \
- @XPDF_TREE@/xpdf/Form.cc \
@XPDF_TREE@/xpdf/Function.cc \
@XPDF_TREE@/xpdf/Gfx.cc \
@XPDF_TREE@/xpdf/GfxFont.cc \
@@ -536,7 +536,7 @@ xpdf_libxpdf_a_sources = \
@XPDF_TREE@/xpdf/UnicodeMap.cc \
@XPDF_TREE@/xpdf/UnicodeRemapping.cc \
@XPDF_TREE@/xpdf/UTF8.cc \
- @XPDF_TREE@/xpdf/XFAForm.cc \
+ @XPDF_TREE@/xpdf/XFAScanner.cc \
@XPDF_TREE@/xpdf/XRef.cc \
@XPDF_TREE@/xpdf/Zoox.cc
@@ -616,6 +616,8 @@ clean-noinstLIBRARIES:
@XPDF_TREE@/goo/$(DEPDIR)/$(am__dirstamp)
@XPDF_TREE@/goo/GString.$(OBJEXT): @XPDF_TREE@/goo/$(am__dirstamp) \
@XPDF_TREE@/goo/$(DEPDIR)/$(am__dirstamp)
+@XPDF_TREE@/goo/Trace.$(OBJEXT): @XPDF_TREE@/goo/$(am__dirstamp) \
+ @XPDF_TREE@/goo/$(DEPDIR)/$(am__dirstamp)
@XPDF_TREE@/goo/gfile.$(OBJEXT): @XPDF_TREE@/goo/$(am__dirstamp) \
@XPDF_TREE@/goo/$(DEPDIR)/$(am__dirstamp)
@XPDF_TREE@/goo/gmem.$(OBJEXT): @XPDF_TREE@/goo/$(am__dirstamp) \
@@ -679,8 +681,6 @@ clean-noinstLIBRARIES:
@XPDF_TREE@/xpdf/FontEncodingTables.$(OBJEXT): \
@XPDF_TREE@/xpdf/$(am__dirstamp) \
@XPDF_TREE@/xpdf/$(DEPDIR)/$(am__dirstamp)
-@XPDF_TREE@/xpdf/Form.$(OBJEXT): @XPDF_TREE@/xpdf/$(am__dirstamp) \
- @XPDF_TREE@/xpdf/$(DEPDIR)/$(am__dirstamp)
@XPDF_TREE@/xpdf/Function.$(OBJEXT): @XPDF_TREE@/xpdf/$(am__dirstamp) \
@XPDF_TREE@/xpdf/$(DEPDIR)/$(am__dirstamp)
@XPDF_TREE@/xpdf/Gfx.$(OBJEXT): @XPDF_TREE@/xpdf/$(am__dirstamp) \
@@ -749,7 +749,8 @@ clean-noinstLIBRARIES:
@XPDF_TREE@/xpdf/$(DEPDIR)/$(am__dirstamp)
@XPDF_TREE@/xpdf/UTF8.$(OBJEXT): @XPDF_TREE@/xpdf/$(am__dirstamp) \
@XPDF_TREE@/xpdf/$(DEPDIR)/$(am__dirstamp)
-@XPDF_TREE@/xpdf/XFAForm.$(OBJEXT): @XPDF_TREE@/xpdf/$(am__dirstamp) \
+@XPDF_TREE@/xpdf/XFAScanner.$(OBJEXT): \
+ @XPDF_TREE@/xpdf/$(am__dirstamp) \
@XPDF_TREE@/xpdf/$(DEPDIR)/$(am__dirstamp)
@XPDF_TREE@/xpdf/XRef.$(OBJEXT): @XPDF_TREE@/xpdf/$(am__dirstamp) \
@XPDF_TREE@/xpdf/$(DEPDIR)/$(am__dirstamp)
@@ -780,6 +781,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/GHash.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/GList.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/GString.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/Trace.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/gfile.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/gmem.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/gmempp.Po@am__quote@ # am--include-marker
@@ -795,7 +797,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/Dict.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/Error.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/FontEncodingTables.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/Form.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/Function.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/Gfx.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/GfxFont.Po@am__quote@ # am--include-marker
@@ -823,7 +824,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/UTF8.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/UnicodeMap.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/UnicodeRemapping.Po@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/XFAForm.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/XFAScanner.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/XRef.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/Zoox.Po@am__quote@ # am--include-marker
@@ -1213,6 +1214,7 @@ distclean: distclean-recursive
-rm -f @XPDF_TREE@/goo/$(DEPDIR)/GHash.Po
-rm -f @XPDF_TREE@/goo/$(DEPDIR)/GList.Po
-rm -f @XPDF_TREE@/goo/$(DEPDIR)/GString.Po
+ -rm -f @XPDF_TREE@/goo/$(DEPDIR)/Trace.Po
-rm -f @XPDF_TREE@/goo/$(DEPDIR)/gfile.Po
-rm -f @XPDF_TREE@/goo/$(DEPDIR)/gmem.Po
-rm -f @XPDF_TREE@/goo/$(DEPDIR)/gmempp.Po
@@ -1228,7 +1230,6 @@ distclean: distclean-recursive
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/Dict.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/Error.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/FontEncodingTables.Po
- -rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/Form.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/Function.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/Gfx.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/GfxFont.Po
@@ -1256,7 +1257,7 @@ distclean: distclean-recursive
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/UTF8.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/UnicodeMap.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/UnicodeRemapping.Po
- -rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/XFAForm.Po
+ -rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/XFAScanner.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/XRef.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/Zoox.Po
-rm -f Makefile
@@ -1316,6 +1317,7 @@ maintainer-clean: maintainer-clean-recursive
-rm -f @XPDF_TREE@/goo/$(DEPDIR)/GHash.Po
-rm -f @XPDF_TREE@/goo/$(DEPDIR)/GList.Po
-rm -f @XPDF_TREE@/goo/$(DEPDIR)/GString.Po
+ -rm -f @XPDF_TREE@/goo/$(DEPDIR)/Trace.Po
-rm -f @XPDF_TREE@/goo/$(DEPDIR)/gfile.Po
-rm -f @XPDF_TREE@/goo/$(DEPDIR)/gmem.Po
-rm -f @XPDF_TREE@/goo/$(DEPDIR)/gmempp.Po
@@ -1331,7 +1333,6 @@ maintainer-clean: maintainer-clean-recursive
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/Dict.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/Error.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/FontEncodingTables.Po
- -rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/Form.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/Function.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/Gfx.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/GfxFont.Po
@@ -1359,7 +1360,7 @@ maintainer-clean: maintainer-clean-recursive
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/UTF8.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/UnicodeMap.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/UnicodeRemapping.Po
- -rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/XFAForm.Po
+ -rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/XFAScanner.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/XRef.Po
-rm -f @XPDF_TREE@/xpdf/$(DEPDIR)/Zoox.Po
-rm -f Makefile
diff --git a/Build/source/libs/xpdf/TLpatches/ChangeLog b/Build/source/libs/xpdf/TLpatches/ChangeLog
index 50d6fb98c82..c0fed972499 100644
--- a/Build/source/libs/xpdf/TLpatches/ChangeLog
+++ b/Build/source/libs/xpdf/TLpatches/ChangeLog
@@ -1,3 +1,7 @@
+2021-02-01 Akira Kakuto <kakuto@w32tex.org>
+
+ * patch-bunched: Adjust.
+
2019-09-29 Akira Kakuto <kakuto@w32tex.org>
* patch-bunched: Adjust.
diff --git a/Build/source/libs/xpdf/TLpatches/TL-Changes b/Build/source/libs/xpdf/TLpatches/TL-Changes
index af5874a4765..c0b5e4aeb54 100644
--- a/Build/source/libs/xpdf/TLpatches/TL-Changes
+++ b/Build/source/libs/xpdf/TLpatches/TL-Changes
@@ -1,4 +1,4 @@
-Changes applied to the xpdf-4.02/ tree as obtained from:
+Changes applied to the xpdf-4.03/ tree as obtained from:
http://www.xpdfreader.com/download.html
Removed:
diff --git a/Build/source/libs/xpdf/TLpatches/patch-bunched b/Build/source/libs/xpdf/TLpatches/patch-bunched
index b842eb4d2ef..f5e182fe9ee 100644
--- a/Build/source/libs/xpdf/TLpatches/patch-bunched
+++ b/Build/source/libs/xpdf/TLpatches/patch-bunched
@@ -1,6 +1,6 @@
-diff -ur xpdf-4.02/goo/gfile.cc xpdf-src/goo/gfile.cc
---- xpdf-4.02/goo/gfile.cc Thu Sep 26 04:54:33 2019
-+++ xpdf-src/goo/gfile.cc Sun Sep 29 11:23:53 2019
+diff -ur xpdf-4.03/goo/gfile.cc xpdf-src/goo/gfile.cc
+--- xpdf-4.03/goo/gfile.cc Thu Jan 28 07:23:51 2021
++++ xpdf-src/goo/gfile.cc Mon Feb 01 13:13:50 2021
@@ -7,6 +7,9 @@
// Copyright 1996-2003 Glyph & Cog, LLC
//
@@ -11,7 +11,7 @@ diff -ur xpdf-4.02/goo/gfile.cc xpdf-src/goo/gfile.cc
#include <aconf.h>
-@@ -54,7 +57,11 @@
+@@ -56,7 +59,11 @@
char *s;
GString *ret;
@@ -23,7 +23,7 @@ diff -ur xpdf-4.02/goo/gfile.cc xpdf-src/goo/gfile.cc
ret = new GString(s);
else
ret = new GString(".");
-@@ -403,6 +410,7 @@
+@@ -405,6 +412,7 @@
#endif
}
@@ -31,7 +31,7 @@ diff -ur xpdf-4.02/goo/gfile.cc xpdf-src/goo/gfile.cc
GBool openTempFile(GString **name, FILE **f,
const char *mode, const char *ext) {
#if defined(_WIN32)
-@@ -517,10 +525,11 @@
+@@ -519,10 +527,11 @@
return gTrue;
#endif
}
@@ -44,24 +44,46 @@ diff -ur xpdf-4.02/goo/gfile.cc xpdf-src/goo/gfile.cc
#else
return !mkdir(path, mode);
#endif
-@@ -574,6 +583,8 @@
+@@ -602,6 +611,8 @@
FILE *openFile(const char *path, const char *mode) {
#if defined(_WIN32)
+ return fopen(path, mode);
+#if 0
- OSVERSIONINFO version;
wchar_t wPath[_MAX_PATH + 1];
- char nPath[_MAX_PATH + 1];
-@@ -630,6 +641,7 @@
- nPath[i] = '\0';
- return fopen(nPath, mode);
- }
+ wchar_t wMode[8];
+ int i;
+@@ -613,6 +624,7 @@
+ wMode[i] = (wchar_t)0;
+ readWindowsShortcut(wPath, _MAX_PATH + 1);
+ return _wfopen(wPath, wMode);
+#endif /* 0 */
#elif defined(VMS)
return fopen(path, mode, "ctx=stm");
#else
-@@ -690,6 +702,7 @@
+@@ -620,6 +632,7 @@
+ #endif
+ }
+
++#if 0
+ #ifdef _WIN32
+ void readWindowsShortcut(wchar_t *wPath, size_t wPathSize) {
+ size_t n = wcslen(wPath);
+@@ -665,11 +678,11 @@
+ wcscpy(wPath, target);
+ }
+ #endif
++#endif /* 0 */
+
+ int makeDir(const char *path, int mode) {
+ #ifdef _WIN32
+- wchar_t wPath[_MAX_PATH + 1];
+- return _wmkdir(fileNameToUCS2(path, wPath, sizeof(wPath) / sizeof(wchar_t)));
++ return _mkdir(path);
+ #else
+ return mkdir(path, (mode_t)mode);
+ #endif
+@@ -728,6 +741,7 @@
#endif
}
@@ -69,15 +91,28 @@ diff -ur xpdf-4.02/goo/gfile.cc xpdf-src/goo/gfile.cc
void fixCommandLine(int *argc, char **argv[]) {
#ifdef _WIN32
int argcw;
-@@ -715,3 +728,4 @@
+@@ -753,3 +767,4 @@
LocalFree(argvw);
#endif
}
+#endif /* !PDF_PARSER_ONLY */
-diff -ur xpdf-4.02/goo/gfile.h xpdf-src/goo/gfile.h
---- xpdf-4.02/goo/gfile.h Thu Sep 26 04:54:33 2019
-+++ xpdf-src/goo/gfile.h Sun Sep 29 11:25:19 2019
-@@ -115,6 +115,8 @@
+diff -ur xpdf-4.03/goo/gfile.h xpdf-src/goo/gfile.h
+--- xpdf-4.03/goo/gfile.h Thu Jan 28 07:23:51 2021
++++ xpdf-src/goo/gfile.h Mon Feb 01 13:14:50 2021
+@@ -91,12 +91,6 @@
+ // UCS-2 and calls _wfopen(). On other OSes, this simply calls fopen().
+ extern FILE *openFile(const char *path, const char *mode);
+
+-#ifdef _WIN32
+-// If [wPath] is a Windows shortcut (.lnk file), read the target path
+-// and store it back into [wPath].
+-extern void readWindowsShortcut(wchar_t *wPath, size_t wPathSize);
+-#endif
+-
+ // Create a directory. On Windows, this converts the path from UTF-8
+ // to UCS-2 and calls _wmkdir(), ignoring the mode argument. On other
+ // OSes, this simply calls mkdir().
+@@ -130,6 +124,8 @@
// On Windows, this gets the Unicode command line and converts it to
// UTF-8. On other systems, this is a nop.
@@ -86,9 +121,9 @@ diff -ur xpdf-4.02/goo/gfile.h xpdf-src/goo/gfile.h
+#endif /* !PDF_PARSER_ONLY */
#endif
-diff -ur xpdf-4.02/xpdf/GlobalParams.cc xpdf-src/xpdf/GlobalParams.cc
---- xpdf-4.02/xpdf/GlobalParams.cc Thu Sep 26 04:54:33 2019
-+++ xpdf-src/xpdf/GlobalParams.cc Sun Sep 29 11:32:58 2019
+diff -ur xpdf-4.03/xpdf/GlobalParams.cc xpdf-src/xpdf/GlobalParams.cc
+--- xpdf-4.03/xpdf/GlobalParams.cc Thu Jan 28 07:23:51 2021
++++ xpdf-src/xpdf/GlobalParams.cc Mon Feb 01 12:20:24 2021
@@ -5,6 +5,9 @@
// Copyright 2001-2003 Glyph & Cog, LLC
//
@@ -114,7 +149,7 @@ diff -ur xpdf-4.02/xpdf/GlobalParams.cc xpdf-src/xpdf/GlobalParams.cc
#endif
#if MULTITHREADED
-@@ -803,6 +810,7 @@
+@@ -787,6 +794,7 @@
f = NULL;
fileName = NULL;
if (cfgFileName && cfgFileName[0]) {
@@ -122,7 +157,7 @@ diff -ur xpdf-4.02/xpdf/GlobalParams.cc xpdf-src/xpdf/GlobalParams.cc
fileName = new GString(cfgFileName);
if (!(f = fopen(fileName->getCString(), "r"))) {
delete fileName;
-@@ -835,6 +843,7 @@
+@@ -819,6 +827,7 @@
parseFile(fileName, f);
delete fileName;
fclose(f);
@@ -130,7 +165,7 @@ diff -ur xpdf-4.02/xpdf/GlobalParams.cc xpdf-src/xpdf/GlobalParams.cc
}
}
-@@ -2265,8 +2274,11 @@
+@@ -2358,8 +2367,11 @@
base14->fontNum,
displayFontTab[i].obliqueFactor));
} else {
@@ -142,9 +177,9 @@ diff -ur xpdf-4.02/xpdf/GlobalParams.cc xpdf-src/xpdf/GlobalParams.cc
}
}
}
-diff -ur xpdf-4.02/xpdf/GlobalParams.h xpdf-src/xpdf/GlobalParams.h
---- xpdf-4.02/xpdf/GlobalParams.h Thu Sep 26 04:54:33 2019
-+++ xpdf-src/xpdf/GlobalParams.h Sun Sep 29 11:34:12 2019
+diff -ur xpdf-4.03/xpdf/GlobalParams.h xpdf-src/xpdf/GlobalParams.h
+--- xpdf-4.03/xpdf/GlobalParams.h Thu Jan 28 07:23:51 2021
++++ xpdf-src/xpdf/GlobalParams.h Mon Feb 01 12:23:07 2021
@@ -5,6 +5,9 @@
// Copyright 2001-2003 Glyph & Cog, LLC
//
@@ -155,7 +190,7 @@ diff -ur xpdf-4.02/xpdf/GlobalParams.h xpdf-src/xpdf/GlobalParams.h
#ifndef GLOBALPARAMS_H
#define GLOBALPARAMS_H
-@@ -219,7 +222,7 @@
+@@ -237,7 +240,7 @@
// Initialize the global parameters by attempting to read a config
// file.
@@ -164,10 +199,22 @@ diff -ur xpdf-4.02/xpdf/GlobalParams.h xpdf-src/xpdf/GlobalParams.h
~GlobalParams();
-diff -ur xpdf-4.02/xpdf/PDFDoc.cc xpdf-src/xpdf/PDFDoc.cc
---- xpdf-4.02/xpdf/PDFDoc.cc Thu Sep 26 04:54:33 2019
-+++ xpdf-src/xpdf/PDFDoc.cc Sun Sep 29 11:41:09 2019
-@@ -156,20 +156,25 @@
+diff -ur xpdf-4.03/xpdf/PDFDoc.cc xpdf-src/xpdf/PDFDoc.cc
+--- xpdf-4.03/xpdf/PDFDoc.cc Thu Jan 28 07:23:51 2021
++++ xpdf-src/xpdf/PDFDoc.cc Mon Feb 01 13:01:20 2021
+@@ -114,6 +114,7 @@
+ ok = setup(ownerPassword, userPassword);
+ }
+
++#if 0
+ #ifdef _WIN32
+ PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
+ GString *userPassword, PDFCore *coreA) {
+@@ -161,23 +162,29 @@
+ ok = setup(ownerPassword, userPassword);
+ }
+ #endif
++#endif /* 0 */
PDFDoc::PDFDoc(char *fileNameA, GString *ownerPassword,
GString *userPassword, PDFCore *coreA) {
@@ -180,7 +227,7 @@ diff -ur xpdf-4.02/xpdf/PDFDoc.cc xpdf-src/xpdf/PDFDoc.cc
+/*
#ifdef _WIN32
Unicode u;
- int n, i, j;
+ int i, j;
#endif
+*/
@@ -190,10 +237,10 @@ diff -ur xpdf-4.02/xpdf/PDFDoc.cc xpdf-src/xpdf/PDFDoc.cc
#if defined(_WIN32)
+#if 0
- n = 0;
+ wchar_t wPath[MAX_PATH + 1];
i = 0;
- while (getUTF8(fileName, &i, &u)) {
-@@ -187,8 +192,11 @@
+ j = 0;
+@@ -197,8 +204,11 @@
if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
file = _wfopen(fileNameU, wfopenReadMode);
} else {
@@ -205,7 +252,7 @@ diff -ur xpdf-4.02/xpdf/PDFDoc.cc xpdf-src/xpdf/PDFDoc.cc
#elif defined(VMS)
file = fopen(fileName->getCString(), fopenReadMode, "ctx=stm");
#else
-@@ -581,6 +589,7 @@
+@@ -603,6 +613,7 @@
GBool ret;
// NB: _wfopen is only available in NT
@@ -213,7 +260,7 @@ diff -ur xpdf-4.02/xpdf/PDFDoc.cc xpdf-src/xpdf/PDFDoc.cc
version.dwOSVersionInfoSize = sizeof(version);
GetVersionEx(&version);
if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
-@@ -590,12 +599,15 @@
+@@ -612,12 +623,15 @@
path2w[i] = 0;
f = _wfopen(path2w, L"wb");
} else {
@@ -229,10 +276,10 @@ diff -ur xpdf-4.02/xpdf/PDFDoc.cc xpdf-src/xpdf/PDFDoc.cc
if (!f) {
return gFalse;
}
-diff -ur xpdf-4.02/xpdf/Page.cc xpdf-src/xpdf/Page.cc
---- xpdf-4.02/xpdf/Page.cc Thu Sep 26 04:54:33 2019
-+++ xpdf-src/xpdf/Page.cc Sun Sep 29 11:43:12 2019
-@@ -480,9 +480,9 @@
+diff -ur xpdf-4.03/xpdf/Page.cc xpdf-src/xpdf/Page.cc
+--- xpdf-4.03/xpdf/Page.cc Thu Jan 28 07:23:51 2021
++++ xpdf-src/xpdf/Page.cc Mon Feb 01 12:31:27 2021
+@@ -485,9 +485,9 @@
delete links;
}
@@ -243,33 +290,17 @@ diff -ur xpdf-4.02/xpdf/Page.cc xpdf-src/xpdf/Page.cc
GfxState *state;
int i;
-@@ -499,5 +499,5 @@
+@@ -504,5 +504,5 @@
ctm[i] = state->getCTM()[i];
}
delete state;
-}
#endif
+}
-diff -ur xpdf-4.02/xpdf/XFAForm.cc xpdf-src/xpdf/XFAForm.cc
---- xpdf-4.02/xpdf/XFAForm.cc Thu Sep 26 04:54:33 2019
-+++ xpdf-src/xpdf/XFAForm.cc Sun Sep 29 11:44:10 2019
-@@ -29,8 +29,10 @@
- #include "XFAForm.h"
-
- #ifdef _WIN32
--# define strcasecmp stricmp
--# define strncasecmp strnicmp
-+# undef strcasecmp
-+# undef strncasecmp
-+# define strcasecmp _stricmp
-+# define strncasecmp _strnicmp
- #endif
-
- //------------------------------------------------------------------------
-diff -ur xpdf-4.02/xpdf/config.h xpdf-src/xpdf/config.h
---- xpdf-4.02/xpdf/config.h Thu Sep 26 04:54:33 2019
-+++ xpdf-src/xpdf/config.h Sun Sep 29 11:45:39 2019
-@@ -78,11 +78,6 @@
+diff -ur xpdf-4.03/xpdf/config.h xpdf-src/xpdf/config.h
+--- xpdf-4.03/xpdf/config.h Thu Jan 28 07:23:52 2021
++++ xpdf-src/xpdf/config.h Mon Feb 01 12:34:28 2021
+@@ -80,11 +80,6 @@
// popen
//------------------------------------------------------------------------
diff --git a/Build/source/libs/xpdf/configure b/Build/source/libs/xpdf/configure
index b028690db96..c616b729103 100755
--- a/Build/source/libs/xpdf/configure
+++ b/Build/source/libs/xpdf/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for xpdf (TeX Live) 4.02.
+# Generated by GNU Autoconf 2.69 for xpdf (TeX Live) 4.03.
#
# Report bugs to <tex-k@tug.org>.
#
@@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='xpdf (TeX Live)'
PACKAGE_TARNAME='xpdf--tex-live-'
-PACKAGE_VERSION='4.02'
-PACKAGE_STRING='xpdf (TeX Live) 4.02'
+PACKAGE_VERSION='4.03'
+PACKAGE_STRING='xpdf (TeX Live) 4.03'
PACKAGE_BUGREPORT='tex-k@tug.org'
PACKAGE_URL=''
@@ -1290,7 +1290,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures xpdf (TeX Live) 4.02 to adapt to many kinds of systems.
+\`configure' configures xpdf (TeX Live) 4.03 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1356,7 +1356,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of xpdf (TeX Live) 4.02:";;
+ short | recursive ) echo "Configuration of xpdf (TeX Live) 4.03:";;
esac
cat <<\_ACEOF
@@ -1460,7 +1460,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-xpdf (TeX Live) configure 4.02
+xpdf (TeX Live) configure 4.03
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1867,7 +1867,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by xpdf (TeX Live) $as_me 4.02, which was
+It was created by xpdf (TeX Live) $as_me 4.03, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3786,7 +3786,7 @@ fi
# Define the identity of the package.
PACKAGE='xpdf--tex-live-'
- VERSION='4.02'
+ VERSION='4.03'
# Some tools Automake needs.
@@ -6699,7 +6699,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by xpdf (TeX Live) $as_me 4.02, which was
+This file was extended by xpdf (TeX Live) $as_me 4.03, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6769,7 +6769,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-xpdf (TeX Live) config.status 4.02
+xpdf (TeX Live) config.status 4.03
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/Build/source/libs/xpdf/version.ac b/Build/source/libs/xpdf/version.ac
index 71f3a1da85d..64e2f3e635e 100644
--- a/Build/source/libs/xpdf/version.ac
+++ b/Build/source/libs/xpdf/version.ac
@@ -8,4 +8,4 @@ dnl
dnl --------------------------------------------------------
dnl
dnl m4-include this file to define the current xpdf version
-m4_define([xpdf_version], [4.02])
+m4_define([xpdf_version], [4.03])
diff --git a/Build/source/libs/xpdf/xpdf-src/ANNOUNCE b/Build/source/libs/xpdf/xpdf-src/ANNOUNCE
index 025303c44de..e070ad95d5b 100644
--- a/Build/source/libs/xpdf/xpdf-src/ANNOUNCE
+++ b/Build/source/libs/xpdf/xpdf-src/ANNOUNCE
@@ -1,4 +1,4 @@
-Subject: ANNOUNCE: Xpdf 4.02 - a PDF viewer and related tools
+Subject: ANNOUNCE: Xpdf 4.03 - a PDF viewer and related tools
Glyph & Cog, LLC is pleased to announce a new version of Xpdf, the
open source Portable Document Format (PDF) viewer. The Xpdf project
@@ -10,18 +10,16 @@ non-graphical components (pdftops, pdftotext, etc.) run on Linux,
Unix, Windows, MacOSX, and pretty much any other system with a decent
C++ compiler.
-4.02 is primarily a bug fix release. There are some new features:
+4.03 is primarily a bug fix release. There are some new features:
-* Pdftohtml now extracts embedded fonts.
+* XpdfReader improvements:
+ - Implemented selection extension via shift-click, and word/line
+ selection via double/triple click.
+ - Added default bindings for ctrl-mousewheel-up/down to zoom in/out.
+ - Added a help menu item that shows all of the key bindings.
-* Various user interface tweaks in XpdfReader: added a button to
- toggle sidebar visibilty, added menu items to toggle the sidebar and
- toolbar, added the 'initialDisplayMode', 'initialToolbarState', and
- 'initialSelectMode' xpdfrc settings.
-
-* If XpdfReader is already running, double-clicking on a PDF file (or
- dragging-and-dropping it on the XpdfReader icon) opens the file in a
- new tab (via the new '-open' command line switch).
+* Various new command line options for pdftotext, pdftohtml, pdftoppm,
+ pdftopng, and xpdf.
See the `CHANGES' file for a complete list of changes.
diff --git a/Build/source/libs/xpdf/xpdf-src/CHANGES b/Build/source/libs/xpdf/xpdf-src/CHANGES
index e707ff01afa..f2dccc095fd 100644
--- a/Build/source/libs/xpdf/xpdf-src/CHANGES
+++ b/Build/source/libs/xpdf/xpdf-src/CHANGES
@@ -2645,4 +2645,98 @@ Increased the width of the font name column in the pdffonts output.
Function objects weren't checking their input/output counts properly.
[Thanks to Pangu Lab for the bug report.]
TextPage::findGaps() wasn't checking the x/y min/max values for int
- overflow. [Thanks to Taolaw for the bugf report.]
+ overflow. [Thanks to Taolaw for the bug report.]
+
+4.03 (2021-jan-28)
+------------------
+Implemented selection extension via shift-click, and word/line
+ selection via double/triple click.
+Added default bindings for ctrl-mousewheel-up/down to zoom in/out.
+Added the "-nofonts" option to pdftohtml.
+Added the "simple2" mode to pdftotext.
+Added the "-rot" flag to xpdf, pdftoppm, and pdftopng.
+Added the "-listencodings" flag to pdftotext.
+Added the 'copyLinkTarget' command.
+Added the 'selectionColor' xpdfrc setting.
+Added the 'initialSidebarWidth' xpdfrc setting.
+Added support for @"..." strings in xpdfrc files. This includes using
+ '%' as an escape character, and also the '${DATADIR}' variable.
+Added a help menu item that shows all of the key bindings.
+Reorganized the gradient shading rasterization code to avoid
+ transparency artifacts between triangles/patches.
+Use interpolation rather than subdivision to rasterize Gouraud
+ triangle shadings.
+Allow escaping (via the \x01 character) in command args, so that "xpdf
+ -open foo(123).pdf" works correctly.
+Extended the damaged file repair code to handle PDF files that use
+ xref streams and object streams.
+Modified pdfinfo to print the encryption algorithm used (if any).
+Handle Windows shortcut (.lnk) files in Xpdf and in the command line
+ tools.
+Display an error dialog when trying to copy text from a protected
+ file.
+Updated all four of the CJK language support packages with the latest
+ Adobe data.
+Modified pdftohtml to draw 'invisible' text over non-horizontal words.
+Catalog.pageLabels wasn't being properly initialized to NULL. [Thanks
+ to Dhiraj for the bug report.]
+Added a sanity check for JBIG2 symbol width. [Reported by Marc
+ Schoenefeld.]
+Fixed a bug in the fontconfig calls that was causing Xpdf to crash.
+The Type 3 font cache code wasn't correctly handling the case where a
+ Type 3 char refers to another char in the same T3 font. [Thanks to
+ Pangu Lab for the bug report.]
+Xpdf was crashing if 'view page labels' was enabled without any
+ document open. [Thanks to TeamSeri0us for the bug report.]
+The Type 1C-to-Type 1 font converter wasn't checking for
+ divide-by-zero with rational numbers. [Thanks to TeamSeri0us for
+ the bug report.]
+Fixed a bug in the TrueType font parser - zero-length loca tables
+ weren't handled correctly. [Thanks to TeamSeri0us for the bug
+ report.]
+Fixed an integer overflow bug in the transparency group setup code.
+ [Thanks to TeamSeri0us for the bug report.]
+Pdfinfo with the "-box" option was crashing on zero-page PDF files.
+ [Thanks to TeamSeri0us for the bug report.]
+The code that caches scaled images wasn't checking the rendering
+ intent. [Thanks to Martin Muskens at Ergosoft for the bug report.]
+The JBIG2 decoder now checks for gibberish values in various segment
+ headers, but keeps any data decoded prior to that.
+Fixed two bugs in the FoFi code that handles CFF fonts. [Thanks to
+ dsmic for the bug report.]
+Redesigned the image scaling code, for performance.
+Cleaned up the code that sanity-checks font sizes to avoid trying to
+ allocate too much memory for the font cache.
+Implemented precincts in the JPEG 2000 decoder.
+The page-up/down snapping code was calling TileMap::getPageTopY() with
+ invalid page numbers. [Thanks to dfandrich for the bug report.]
+Added missing error-checking when parsing an annotation appearance
+ stream's bbox.
+Handled UTF-16 sequences in ToUnicode maps.
+Rewrote the code that draws static XFA forms: combined it with the
+ AcroForm code, to match Adobe's behavior. Removed the enableXFA
+ xpdfrc setting.
+Check for indirect references in content streams and report an error.
+Doing overprint preview in tiling patterns requires tracking the
+ overprint mask at each pixel.
+Check for "decompression bombs" in Flate and LZW streams.
+The nextPageNoScroll and prevPageNoScroll commands weren't working
+ correctly in continuous mode.
+Disable stroke adjustment when drawing Type 3 characters.
+The "xpdf -open" flag now constructs an absolute path, so it's not
+ dependent on the current directory when xpdf was started.
+Check for infinite loops in Type 1C charstring subroutines. [Thanks
+ to blbi for the bug report.]
+Fixed an incomplete test for headless CFF files. [Thanks to Chengbin
+ for the bug report.]
+Tweaked the code that computes segment slope in SplashXPath.cc to
+ avoid problems with extremely small coordinate values. [Thanks to
+ Chengbin for the bug report.]
+Check for an invalid segment length in PSOutputDev's PFB parse.
+ [Thanks to Chengbin for the bug report.]
+Check for JPEG 2000 segments that are only allowed in the first
+ tile-part of a tile. [Thanks to Chengbin for the bug report.]
+Check for invalid AcroForm ListBox field top index. [Thanks to
+ Chengbin for the bug report.]
+Check for invalid mesh shading parameters. [Thanks to Chengbin for
+ the bug report.]
diff --git a/Build/source/libs/xpdf/xpdf-src/INSTALL b/Build/source/libs/xpdf/xpdf-src/INSTALL
index b7e184538c8..5a7eb6423f4 100644
--- a/Build/source/libs/xpdf/xpdf-src/INSTALL
+++ b/Build/source/libs/xpdf/xpdf-src/INSTALL
@@ -1,11 +1,11 @@
Xpdf
====
-version 4.02
-2019-sep-25
+version 4.03
+2021-jan-28
The Xpdf software and documentation are
-copyright 1996-2019 Glyph & Cog, LLC.
+copyright 1996-2021 Glyph & Cog, LLC.
Email: xpdf@xpdfreader.com
WWW: http://www.xpdfreader.com/
@@ -115,25 +115,29 @@ different systems.
-DSYSTEM_XPDFRC="/etc/xpdfrc"
Look for a system-wide xpdfrc config file in this directory.
- -DCMAKE_DISABLE_FIND_PACKAGE_Qt4=1
- -DCMAKE_DISABLE_FIND_PACKAGE_Qt5Widgets=1
+ -DXPDFRC_DATADIR="/usr/share/xpdf"
+ The ${DATADIR} variable in xpdfrc config files will expand to
+ this string.
+
+ -DCMAKE_DISABLE_FIND_PACKAGE_Qt4=1
+ -DCMAKE_DISABLE_FIND_PACKAGE_Qt5Widgets=1
Do not search for the Qt4/Qt5 libraries. This will disable
building the GUI viewer (xpdf). Cmake will look for a "qmake"
binary -- make sure the first qmake binary on your executable
search path matches the desired version of Qt.
- -DCMAKE_C_FLAGS="..."
- -DCMAKE_CXX_FLAGS="..."
+ -DCMAKE_C_FLAGS="..."
+ -DCMAKE_CXX_FLAGS="..."
Set additional options to pass to the C and/or C++ compilers.
- -DCMAKE_EXE_LINKER_FLAGS="..."
+ -DCMAKE_EXE_LINKER_FLAGS="..."
Set additional options to pass to the linker.
- -DCMAKE_INSTALL_BINDIR
+ -DCMAKE_INSTALL_BINDIR
Set the bin directory, relative to CMAKE_INSTALL_PREFIX
(typically "bin").
- -DCMAKE_INSTALL_MANDIR
+ -DCMAKE_INSTALL_MANDIR
Set the man directory, relative to CMAKE_INSTALL_PREFIX
(typically "man" or "share/man").
diff --git a/Build/source/libs/xpdf/xpdf-src/README b/Build/source/libs/xpdf/xpdf-src/README
index ebecfe97c3e..e5a114262f0 100644
--- a/Build/source/libs/xpdf/xpdf-src/README
+++ b/Build/source/libs/xpdf/xpdf-src/README
@@ -1,17 +1,17 @@
Xpdf
====
-version 4.02
-2019-sep-25
+version 4.03
+2021-jan-28
The Xpdf software and documentation are
-copyright 1996-2019 Glyph & Cog, LLC.
+copyright 1996-2021 Glyph & Cog, LLC.
Email: xpdf@xpdfreader.com
WWW: http://www.xpdfreader.com/
The PDF data structures, operators, and specification are
-documented in ISO 32000-2:2017.
+documented in ISO 32000-2:2020.
What is Xpdf?
@@ -326,8 +326,8 @@ were changes made to the bitstream format between that draft and the
published spec.]
ISO, International Standard -- Document Management - Portable document
-format - Part 2: PDF 2.0. ISO 32000-2:2017.
-[The manual for PDF version 2.0.]
+format - Part 2: PDF 2.0. ISO 32000-2:2020.
+[The specification for PDF version 2.0.]
ITU, "Standardization of Group 3 facsimile terminals for document
transmission", ITU-T Recommendation T.4, 1999.
diff --git a/Build/source/libs/xpdf/xpdf-src/cmake-config.txt b/Build/source/libs/xpdf/xpdf-src/cmake-config.txt
index 3fe2faf125c..e4b3650fcb3 100644
--- a/Build/source/libs/xpdf/xpdf-src/cmake-config.txt
+++ b/Build/source/libs/xpdf/xpdf-src/cmake-config.txt
@@ -8,6 +8,17 @@
#
#========================================================================
+if (POLICY CMP0074)
+ cmake_policy(SET CMP0074 NEW)
+endif ()
+
+if (APPLE)
+ if (POLICY CMP0042)
+ cmake_policy(SET CMP0042 NEW)
+ endif ()
+ set(CMAKE_MACOSX_RPATH 1)
+endif ()
+
include(CheckFunctionExists)
include(CheckCXXSourceCompiles)
include(GNUInstallDirs)
@@ -41,12 +52,12 @@ if (CMAKE_GENERATOR STREQUAL "Unix Makefiles")
CMAKE_CXX_FLAGS_PROFILING
CMAKE_EXE_LINKER_FLAGS_PROFILING)
set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING
- "Choose build mode - options are: None Debug Release RelWithDebInfo MinSiz
-eRel Profiling")
+ "Choose build mode - options are: None Debug Release RelWithDebInfo MinSizeRel Profiling")
endif ()
#--- set default C/C++ compiler flags for Windows
if (WIN32)
+ option(USE_MT_IN_DEBUG "use /MT instead of /MTd in debug builds" OFF)
foreach (var CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG
CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_MINSIZEREL
@@ -54,8 +65,13 @@ if (WIN32)
# note: this converts /MD to /MT and /MDd to /MTd
string(REGEX REPLACE "/MD" "/MT" ${var} "${${var}}")
endforeach ()
- set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/NODEFAULTLIB:libcmt ${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
- set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "/NODEFAULTLIB:libcmt ${CMAKE_SHARED_LINKER_FLAGS_DEBUG}")
+ if (USE_MT_IN_DEBUG)
+ string(REPLACE "/MTd" "/MT" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
+ string(REPLACE "/MTd" "/MT" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
+ else ()
+ set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/NODEFAULTLIB:libcmt ${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
+ set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "/NODEFAULTLIB:libcmt ${CMAKE_SHARED_LINKER_FLAGS_DEBUG}")
+ endif ()
foreach (var CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL
@@ -88,6 +104,12 @@ if (SYSTEM_XPDFRC)
else ()
set(SYSTEM_XPDFRC_DEFINE "/* #undef SYSTEM_XPDFRC */")
endif ()
+option(XPDFRC_DATADIR "directory to use for the DATADIR xpdfrc variable" "")
+if (XPDFRC_DATADIR)
+ set(XPDFRC_DATADIR_DEFINE "#define XPDFRC_DATADIR \"${XPDFRC_DATADIR}\"")
+else ()
+ set(XPDFRC_DATADIR_DEFINE "/* #undef SYSTEM_XPDFRC */")
+endif ()
if (WIN32)
option(XPDFWIDGET_PRINTING "include printing support in XpdfWidget" OFF)
else ()
@@ -194,7 +216,7 @@ if (Qt5Widgets_FOUND)
find_package(Qt5Network)
find_package(Qt5PrintSupport)
else ()
- find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED)
+ find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork)
endif ()
if(Qt5Widgets_FOUND)
message(STATUS "Qt5 found")
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.1
index 82497df84fc..7fd3b8a5231 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.1
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.1
@@ -1,8 +1,8 @@
-.\" Copyright 2013-2019 Glyph & Cog, LLC
-.TH pdfdetach 1 "25 Sep 2019"
+.\" Copyright 2013-2021 Glyph & Cog, LLC
+.TH pdfdetach 1 "28 Jan 2021"
.SH NAME
pdfdetach \- Portable Document Format (PDF) document embedded file
-extractor (version 4.02)
+extractor (version 4.03)
.SH SYNOPSIS
.B pdfdetach
[options]
@@ -90,7 +90,7 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdfinfo software and documentation are copyright 1996-2019 Glyph &
+The pdfinfo software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat
index 3d4b026e701..000eb66e5ae 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat
@@ -4,7 +4,7 @@ pdfdetach(1) General Commands Manual pdfdetach(1)
NAME
pdfdetach - Portable Document Format (PDF) document embedded file
- extractor (version 4.02)
+ extractor (version 4.03)
SYNOPSIS
pdfdetach [options] [PDF-file]
@@ -79,7 +79,7 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdfinfo software and documentation are copyright 1996-2019 Glyph &
+ The pdfinfo software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
SEE ALSO
@@ -89,4 +89,4 @@ SEE ALSO
- 25 Sep 2019 pdfdetach(1)
+ 28 Jan 2021 pdfdetach(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.1
index b8ffaa22b2b..0192bd3247e 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.1
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.1
@@ -1,8 +1,8 @@
-.\" Copyright 1999-2019 Glyph & Cog, LLC
-.TH pdffonts 1 "25 Sep 2019"
+.\" Copyright 1999-2021 Glyph & Cog, LLC
+.TH pdffonts 1 "28 Jan 2021"
.SH NAME
pdffonts \- Portable Document Format (PDF) font analyzer (version
-4.02)
+4.03)
.SH SYNOPSIS
.B pdffonts
[options]
@@ -147,7 +147,7 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdffonts software and documentation are copyright 1996-2019 Glyph
+The pdffonts software and documentation are copyright 1996-2021 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat
index 363699cf1c5..59f4dd90ca9 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat
@@ -3,7 +3,7 @@ pdffonts(1) General Commands Manual pdffonts(1)
NAME
- pdffonts - Portable Document Format (PDF) font analyzer (version 4.02)
+ pdffonts - Portable Document Format (PDF) font analyzer (version 4.03)
SYNOPSIS
pdffonts [options] [PDF-file]
@@ -105,7 +105,7 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdffonts software and documentation are copyright 1996-2019 Glyph &
+ The pdffonts software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
SEE ALSO
@@ -115,4 +115,4 @@ SEE ALSO
- 25 Sep 2019 pdffonts(1)
+ 28 Jan 2021 pdffonts(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.1
index bef93eeaba8..2d95ac79b66 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.1
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.1
@@ -1,8 +1,8 @@
-.\" Copyright 1998-2019 Glyph & Cog, LLC
-.TH pdfimages 1 "25 Sep 2019"
+.\" Copyright 1998-2021 Glyph & Cog, LLC
+.TH pdfimages 1 "28 Jan 2021"
.SH NAME
pdfimages \- Portable Document Format (PDF) image extractor
-(version 4.02)
+(version 4.03)
.SH SYNOPSIS
.B pdfimages
[options]
@@ -102,7 +102,7 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdfimages software and documentation are copyright 1998-2019 Glyph
+The pdfimages software and documentation are copyright 1998-2021 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat
index 74b7ace12d0..97a0371573b 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat
@@ -4,7 +4,7 @@ pdfimages(1) General Commands Manual pdfimages(1)
NAME
pdfimages - Portable Document Format (PDF) image extractor (version
- 4.02)
+ 4.03)
SYNOPSIS
pdfimages [options] PDF-file image-root
@@ -85,7 +85,7 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdfimages software and documentation are copyright 1998-2019 Glyph
+ The pdfimages software and documentation are copyright 1998-2021 Glyph
& Cog, LLC.
SEE ALSO
@@ -95,4 +95,4 @@ SEE ALSO
- 25 Sep 2019 pdfimages(1)
+ 28 Jan 2021 pdfimages(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.1
index c095e88bbfa..f84badc9814 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.1
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.1
@@ -1,8 +1,8 @@
-.\" Copyright 1999-2019 Glyph & Cog, LLC
-.TH pdfinfo 1 "25 Sep 2019"
+.\" Copyright 1999-2021 Glyph & Cog, LLC
+.TH pdfinfo 1 "28 Jan 2021"
.SH NAME
pdfinfo \- Portable Document Format (PDF) document information
-extractor (version 4.02)
+extractor (version 4.03)
.SH SYNOPSIS
.B pdfinfo
[options]
@@ -150,7 +150,7 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdfinfo software and documentation are copyright 1996-2019 Glyph &
+The pdfinfo software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat
index 1f2ed6bd637..0478db677c4 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat
@@ -4,7 +4,7 @@ pdfinfo(1) General Commands Manual pdfinfo(1)
NAME
pdfinfo - Portable Document Format (PDF) document information extractor
- (version 4.02)
+ (version 4.03)
SYNOPSIS
pdfinfo [options] [PDF-file]
@@ -104,7 +104,7 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdfinfo software and documentation are copyright 1996-2019 Glyph &
+ The pdfinfo software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
SEE ALSO
@@ -114,4 +114,4 @@ SEE ALSO
- 25 Sep 2019 pdfinfo(1)
+ 28 Jan 2021 pdfinfo(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.1
index f37bf5c4424..4618a7a77a9 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.1
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.1
@@ -1,8 +1,8 @@
-.\" Copyright 1997-2019 Glyph & Cog, LLC
-.TH pdftohtml 1 "25 Sep 2019"
+.\" Copyright 1997-2021 Glyph & Cog, LLC
+.TH pdftohtml 1 "28 Jan 2021"
.SH NAME
pdftohtml \- Portable Document Format (PDF) to HTML converter
-(version 4.02)
+(version 4.03)
.SH SYNOPSIS
.B pdftohtml
[options]
@@ -50,6 +50,11 @@ zoom level is controlled by the \'-z' option. Specifying a larger
\'-r' value will allow the viewer to zoom in farther without upscaling
artifacts in the background.
.TP
+.B \-nofonts
+Disable extraction of embedded fonts. By default, pdftohtml extracts
+TrueType and OpenType fonts. Disabling extraction can work around
+problems with buggy fonts.
+.TP
.B \-skipinvisible
Don't draw invisible text. By default, invisible text (commonly used
in OCR'ed PDF files) is drawn as transparent (alpha=0) HTML text.
@@ -109,7 +114,7 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdftohtml software and documentation are copyright 1996-2019 Glyph
+The pdftohtml software and documentation are copyright 1996-2021 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat
index 5f3a0bb8fad..3eea2e32ec1 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat
@@ -4,7 +4,7 @@ pdftohtml(1) General Commands Manual pdftohtml(1)
NAME
pdftohtml - Portable Document Format (PDF) to HTML converter (version
- 4.02)
+ 4.03)
SYNOPSIS
pdftohtml [options] PDF-file HTML-dir
@@ -48,6 +48,11 @@ OPTIONS
larger '-r' value will allow the viewer to zoom in farther with-
out upscaling artifacts in the background.
+ -nofonts
+ Disable extraction of embedded fonts. By default, pdftohtml
+ extracts TrueType and OpenType fonts. Disabling extraction can
+ work around problems with buggy fonts.
+
-skipinvisible
Don't draw invisible text. By default, invisible text (commonly
used in OCR'ed PDF files) is drawn as transparent (alpha=0) HTML
@@ -97,7 +102,7 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdftohtml software and documentation are copyright 1996-2019 Glyph
+ The pdftohtml software and documentation are copyright 1996-2021 Glyph
& Cog, LLC.
SEE ALSO
@@ -107,4 +112,4 @@ SEE ALSO
- 25 Sep 2019 pdftohtml(1)
+ 28 Jan 2021 pdftohtml(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.1
index c0745ced65b..bd8f517358f 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.1
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.1
@@ -1,8 +1,8 @@
-.\" Copyright 2017-2019 Glyph & Cog, LLC
-.TH pdftopng 1 "25 Sep 2019"
+.\" Copyright 2017-2021 Glyph & Cog, LLC
+.TH pdftopng 1 "28 Jan 2021"
.SH NAME
pdftopng \- Portable Document Format (PDF) to Portable Network Graphics
-(PNG) converter (version 4.02)
+(PNG) converter (version 4.03)
.SH SYNOPSIS
.B pdftopng
[options]
@@ -56,6 +56,9 @@ Generate an alpha channel in the PNG file. This is only useful with
PDF files that have been constructed with a transparent background.
The \-alpha flag cannot be used with \-mono.
.TP
+.BI \-rot " angle"
+Rotate pages by 0 (the default), 90, 180, or 270 degrees.
+.TP
.BI \-freetype " yes | no"
Enable or disable FreeType (a TrueType / Type 1 font rasterizer).
This defaults to "yes".
@@ -107,7 +110,7 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdftopng software and documentation are copyright 1996-2019 Glyph
+The pdftopng software and documentation are copyright 1996-2021 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat
index 0241fee5956..7e17c504b4b 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat
@@ -4,7 +4,7 @@ pdftopng(1) General Commands Manual pdftopng(1)
NAME
pdftopng - Portable Document Format (PDF) to Portable Network Graphics
- (PNG) converter (version 4.02)
+ (PNG) converter (version 4.03)
SYNOPSIS
pdftopng [options] PDF-file PNG-root
@@ -48,6 +48,9 @@ OPTIONS
with PDF files that have been constructed with a transparent
background. The -alpha flag cannot be used with -mono.
+ -rot angle
+ Rotate pages by 0 (the default), 90, 180, or 270 degrees.
+
-freetype yes | no
Enable or disable FreeType (a TrueType / Type 1 font raster-
izer). This defaults to "yes". [config file: enableFreeType]
@@ -87,7 +90,7 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdftopng software and documentation are copyright 1996-2019 Glyph &
+ The pdftopng software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
SEE ALSO
@@ -97,4 +100,4 @@ SEE ALSO
- 25 Sep 2019 pdftopng(1)
+ 28 Jan 2021 pdftopng(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.1
index 5ad6541d5bb..23b5b06980c 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.1
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.1
@@ -1,8 +1,8 @@
-.\" Copyright 2005-2019 Glyph & Cog, LLC
-.TH pdftoppm 1 "25 Sep 2019"
+.\" Copyright 2005-2021 Glyph & Cog, LLC
+.TH pdftoppm 1 "28 Jan 2021"
.SH NAME
pdftoppm \- Portable Document Format (PDF) to Portable Pixmap (PPM)
-converter (version 4.02)
+converter (version 4.03)
.SH SYNOPSIS
.B pdftoppm
[options]
@@ -56,6 +56,9 @@ Generate a grayscale PGM file (instead of an RGB PPM file).
.B \-cmyk
Generate a CMYK PAM file (instead of an RGB PPM file).
.TP
+.BI \-rot " angle"
+Rotate pages by 0 (the default), 90, 180, or 270 degrees.
+.TP
.BI \-freetype " yes | no"
Enable or disable FreeType (a TrueType / Type 1 font rasterizer).
This defaults to "yes".
@@ -107,7 +110,7 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdftoppm software and documentation are copyright 1996-2019 Glyph
+The pdftoppm software and documentation are copyright 1996-2021 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat
index 45e1bf1f255..9621be3c3b0 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat
@@ -4,7 +4,7 @@ pdftoppm(1) General Commands Manual pdftoppm(1)
NAME
pdftoppm - Portable Document Format (PDF) to Portable Pixmap (PPM) con-
- verter (version 4.02)
+ verter (version 4.03)
SYNOPSIS
pdftoppm [options] PDF-file PPM-root
@@ -47,6 +47,9 @@ OPTIONS
-cmyk Generate a CMYK PAM file (instead of an RGB PPM file).
+ -rot angle
+ Rotate pages by 0 (the default), 90, 180, or 270 degrees.
+
-freetype yes | no
Enable or disable FreeType (a TrueType / Type 1 font raster-
izer). This defaults to "yes". [config file: enableFreeType]
@@ -86,7 +89,7 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdftoppm software and documentation are copyright 1996-2019 Glyph &
+ The pdftoppm software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
SEE ALSO
@@ -96,4 +99,4 @@ SEE ALSO
- 25 Sep 2019 pdftoppm(1)
+ 28 Jan 2021 pdftoppm(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftops.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdftops.1
index 360e042121f..e23425d5bad 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdftops.1
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftops.1
@@ -1,8 +1,8 @@
-.\" Copyright 1996-2019 Glyph & Cog, LLC
-.TH pdftops 1 "25 Sep 2019"
+.\" Copyright 1996-2021 Glyph & Cog, LLC
+.TH pdftops 1 "28 Jan 2021"
.SH NAME
pdftops \- Portable Document Format (PDF) to PostScript converter
-(version 4.02)
+(version 4.03)
.SH SYNOPSIS
.B pdftops
[options]
@@ -51,7 +51,7 @@ Generate Level 1 PostScript. The resulting PostScript files will be
significantly larger (if they contain images), but will print on Level
1 printers. This also converts all images to black and white. No
more than one of the PostScript level options (\-level1, \-level1sep,
-\-level2, \-level2sep, \-level3, \-level3Sep) may be given.
+\-level2, \-level2sep, \-level3, \-level3sep) may be given.
.RB "[config file: " psLevel ]
.TP
.B \-level1sep
@@ -86,9 +86,9 @@ Generate grayscale Level 3 PostScript. All colors, including images,
are converted to grayscale.
.RB "[config file: " psLevel ]
.TP
-.B \-level3Sep
+.B \-level3sep
Generate Level 3 separable PostScript. The separation handling is the
-same as for \-level2Sep.
+same as for \-level2sep.
.RB "[config file: " psLevel ]
.TP
.B \-eps
@@ -236,7 +236,7 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdftops software and documentation are copyright 1996-2019 Glyph &
+The pdftops software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat
index 5b2d9d3e0bb..c985d686fea 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat
@@ -4,7 +4,7 @@ pdftops(1) General Commands Manual pdftops(1)
NAME
pdftops - Portable Document Format (PDF) to PostScript converter (ver-
- sion 4.02)
+ sion 4.03)
SYNOPSIS
pdftops [options] [PDF-file [PS-file]]
@@ -42,7 +42,7 @@ OPTIONS
print on Level 1 printers. This also converts all images to
black and white. No more than one of the PostScript level
options (-level1, -level1sep, -level2, -level2sep, -level3,
- -level3Sep) may be given. [config file: psLevel]
+ -level3sep) may be given. [config file: psLevel]
-level1sep
Generate Level 1 separable PostScript. All colors are converted
@@ -72,9 +72,9 @@ OPTIONS
Generate grayscale Level 3 PostScript. All colors, including
images, are converted to grayscale. [config file: psLevel]
- -level3Sep
+ -level3sep
Generate Level 3 separable PostScript. The separation handling
- is the same as for -level2Sep. [config file: psLevel]
+ is the same as for -level2sep. [config file: psLevel]
-eps Generate an Encapsulated PostScript (EPS) file. An EPS file
contains a single image, so if you use this option with a multi-
@@ -201,7 +201,7 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdftops software and documentation are copyright 1996-2019 Glyph &
+ The pdftops software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
SEE ALSO
@@ -211,4 +211,4 @@ SEE ALSO
- 25 Sep 2019 pdftops(1)
+ 28 Jan 2021 pdftops(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.1
index d0190491ac6..d1e81d01f25 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.1
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.1
@@ -1,8 +1,8 @@
-.\" Copyright 1997-2019 Glyph & Cog, LLC
-.TH pdftotext 1 "25 Sep 2019"
+.\" Copyright 1997-2021 Glyph & Cog, LLC
+.TH pdftotext 1 "28 Jan 2021"
.SH NAME
pdftotext \- Portable Document Format (PDF) to text converter
-(version 4.02)
+(version 4.03)
.SH SYNOPSIS
.B pdftotext
[options]
@@ -59,6 +59,12 @@ but optimized for simple one-column pages. This mode will do a better
job of maintaining horizontal spacing, but it will only work properly
with a single column of text.
.TP
+.B \-simple2
+Similar to
+.BR \-simple ,
+but handles slightly rotated text (e.g., OCR output) better. Only works
+for pages with a single column of text.
+.TP
.B \-table
Table mode is similar to physical layout mode, but optimized for
tabular data, with the goal of keeping rows and columns aligned (at
@@ -161,11 +167,14 @@ Read
.I config-file
in place of ~/.xpdfrc or the system-wide config file.
.TP
+.B \-listencodings
+List all available text output encodings, then exit.
+.TP
.B \-v
-Print copyright and version information.
+Print copyright and version information, then exit.
.TP
.B \-h
-Print usage information.
+Print usage information, then exit.
.RB ( \-help
and
.B \-\-help
@@ -192,7 +201,7 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The pdftotext software and documentation are copyright 1996-2019 Glyph
+The pdftotext software and documentation are copyright 1996-2021 Glyph
& Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat
index ec51212f332..d4a001d7c63 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat
@@ -4,7 +4,7 @@ pdftotext(1) General Commands Manual pdftotext(1)
NAME
pdftotext - Portable Document Format (PDF) to text converter (version
- 4.02)
+ 4.03)
SYNOPSIS
pdftotext [options] [PDF-file [text-file]]
@@ -47,6 +47,11 @@ OPTIONS
ing, but it will only work properly with a single column of
text.
+ -simple2
+ Similar to -simple, but handles slightly rotated text (e.g., OCR
+ output) better. Only works for pages with a single column of
+ text.
+
-table Table mode is similar to physical layout mode, but optimized for
tabular data, with the goal of keeping rows and columns aligned
(at the expense of inserting extra whitespace). If the -fixed
@@ -135,12 +140,16 @@ OPTIONS
Read config-file in place of ~/.xpdfrc or the system-wide config
file.
- -v Print copyright and version information.
+ -listencodings
+ List all available text output encodings, then exit.
+
+ -v Print copyright and version information, then exit.
- -h Print usage information. (-help and --help are equivalent.)
+ -h Print usage information, then exit. (-help and --help are
+ equivalent.)
BUGS
- Some PDF files contain fonts whose encodings have been mangled beyond
+ Some PDF files contain fonts whose encodings have been mangled beyond
recognition. There is no way (short of OCR) to extract text from these
files.
@@ -158,7 +167,7 @@ EXIT CODES
99 Other error.
AUTHOR
- The pdftotext software and documentation are copyright 1996-2019 Glyph
+ The pdftotext software and documentation are copyright 1996-2021 Glyph
& Cog, LLC.
SEE ALSO
@@ -168,4 +177,4 @@ SEE ALSO
- 25 Sep 2019 pdftotext(1)
+ 28 Jan 2021 pdftotext(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/xpdf.1 b/Build/source/libs/xpdf/xpdf-src/doc/xpdf.1
index c1aedbd80cc..7cf06315f88 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/xpdf.1
+++ b/Build/source/libs/xpdf/xpdf-src/doc/xpdf.1
@@ -1,7 +1,7 @@
-.\" Copyright 1996-2019 Glyph & Cog, LLC
-.TH xpdf 1 "25 Sep 2019"
+.\" Copyright 1996-2021 Glyph & Cog, LLC
+.TH xpdf 1 "28 Jan 2021"
.SH NAME
-xpdf \- Portable Document Format (PDF) file viewer (version 4.02)
+xpdf \- Portable Document Format (PDF) file viewer (version 4.03)
.SH SYNOPSIS
.B xpdf
[options]
@@ -118,6 +118,9 @@ to the window size, or \'width', to fit the page width to the window
width.
.RB "[config file: " initialZoom ]
.TP
+.BI \-rot " angle"
+Rotate pages by 0 (the default), 90, 180, or 270 degrees.
+.TP
.BI \-aa " yes | no"
Enable or disable font anti-aliasing. This defaults to "yes".
.RB "[config file: " antialias ]
@@ -152,6 +155,10 @@ Set the X display (only available with X11).
Print commands as they're executed (useful for debugging).
.RB "[config file: " printCommands ]
.TP
+.BI \-tabstate " tabstate-file"
+Sets the file used by the loadTabState and saveTabState commands.
+.RB "[config file: " tabStateFile ]
+.TP
.BI \-cfg " config-file"
Read
.I config-file
@@ -240,10 +247,18 @@ a 'save' button for each attached file. Not all PDF files have
attachments.
.PP
.SS Text selection
-Dragging the mouse with the left button held down will highlight an
-arbitrary rectangle. Selected text can be copied to the clipboard
-(with the edit/copy menu item). On X11, selected text will be
-available in the X selection buffer.
+In block selection mode, dragging the mouse with the left button held
+down will highlight an arbitrary rectangle. Shift-clicking will
+extend the selection.
+.PP
+In linear selection mode, dragging with the left button will highlight
+text in reading order. Double-clicking or triple-clicking will select
+a word or a line, respectively. Shift-clicking will extend the
+selection.
+.PP
+Selected text can be copied to the clipboard (with the edit/copy menu
+item). On X11, selected text will be available in the X selection
+buffer.
.PP
.SS Links
When the mouse is over a hyperlink, the link target will be shown in a
@@ -400,6 +415,12 @@ and unbind commands in the config file (see
The bind command allows you to bind a key or mouse button to a
sequence of one or more commands.
.PP
+In commands that take arguments (inside parentheses), special
+characters (namely '(', ')', ',', and '\\x01') can be escaped by
+preceding them with a '\\x01' character. This is mostly useful in
+things like scripts that need to be able to open arbitrary PDF files,
+using the \'openFile' command.
+.PP
The following commands are supported:
.TP
.B about
@@ -452,6 +473,9 @@ Switch to continuous view mode.
.B copy
Copy selected text to the clipboard.
.TP
+.B copyLinkTarget
+Copy the target of the link under the mouse cursor to the clipboard.
+.TP
.B endPan
End a pan operation.
.TP
@@ -626,6 +650,9 @@ Open the \'print' dialog.
.B quit
Quit from xpdf.
.TP
+.B raise
+Bring the xpdf window to the front.
+.TP
.B reload
Reload the current PDF file.
.TP
@@ -751,9 +778,18 @@ Scroll up by
.I n
pixels, moving to the previous page if appropriate.
.TP
+.B selectLine
+Set the selection to the line at the current mouse position.
+.TP
+.B selectWord
+Set the selection to the word at the current mouse position.
+.TP
.BI setSelection( pg , ulx , uly , lrx , lry )
Set the selection to the specified coordinates on the specified page.
.TP
+.B showKeyBindings
+Open the key bindings dialog.
+.TP
.B showToolbar
Show the toolbar.
.TP
@@ -772,6 +808,10 @@ Switch to side-by-side two-page view mode.
.B singlePageMode
Switch to single-page view mode.
.TP
+.B startExtendedSelection
+Extend the selection to the current mouse position, and continue
+extending as the mouse moves.
+.TP
.B startPan
Start a pan operation at the current mouse position, which will scroll
the document as the mouse moves.
@@ -868,7 +908,7 @@ Error related to PDF permissions.
99
Other error.
.SH AUTHOR
-The xpdf software and documentation are copyright 1996-2019 Glyph &
+The xpdf software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR pdftops (1),
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat b/Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat
index 30c841c45ad..3b71667f055 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat
+++ b/Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat
@@ -3,7 +3,7 @@ xpdf(1) General Commands Manual xpdf(1)
NAME
- xpdf - Portable Document Format (PDF) file viewer (version 4.02)
+ xpdf - Portable Document Format (PDF) file viewer (version 4.03)
SYNOPSIS
xpdf [options] [PDF-file [:page | +dest]] ...
@@ -98,6 +98,9 @@ OPTIONS
fit the page to the window size, or 'width', to fit the page
width to the window width. [config file: initialZoom]
+ -rot angle
+ Rotate pages by 0 (the default), 90, 180, or 270 degrees.
+
-aa yes | no
Enable or disable font anti-aliasing. This defaults to "yes".
[config file: antialias]
@@ -130,6 +133,10 @@ OPTIONS
-cmd Print commands as they're executed (useful for debugging).
[config file: printCommands]
+ -tabstate tabstate-file
+ Sets the file used by the loadTabState and saveTabState com-
+ mands. [config file: tabStateFile]
+
-cfg config-file
Read config-file in place of ~/.xpdfrc or the system-wide config
file.
@@ -144,18 +151,18 @@ CONTROLS
Toggles (i.e., shows or hides) the sidebar.
status indicator
- This icon is animated while Xpdf is rendering a page. It turns
- red when an error or warning has been issued. Clicking on it
+ This icon is animated while Xpdf is rendering a page. It turns
+ red when an error or warning has been issued. Clicking on it
opens the error dialog.
selection mode
- This icon is an "I-beam" in linear selection mode, and an arrow
+ This icon is an "I-beam" in linear selection mode, and an arrow
in block selection mode. Clicking on it toggles between the two
selection modes.
page number entry box
- Move to a specific page number. Click in the box to activate
- it, type the page number, then hit return. This will instead
+ Move to a specific page number. Click in the box to activate
+ it, type the page number, then hit return. This will instead
display and accept page labels, if the "view - page labels" menu
item is checked.
@@ -166,18 +173,18 @@ CONTROLS
Zoom out or in (i.e., change magnification) incrementally.
zoom popup menu
- Change the zoom factor (see the description of the -z option
+ Change the zoom factor (see the description of the -z option
above).
fit width button
- Change the zoom factor to fit the page width to the window
+ Change the zoom factor to fit the page width to the window
width.
fit page button
Change the zoom factor to fit the page to the window size.
find entry box
- Find a text string. Click in the box to activate it, type a
+ Find a text string. Click in the box to activate it, type a
search string, then hit return.
find next button
@@ -191,7 +198,7 @@ CONTROLS
whole words (on/off).
Menu bar
- The menu bar is above the toolbar. The menu items should be self-
+ The menu bar is above the toolbar. The menu items should be self-
explanatory.
Tab list
@@ -199,33 +206,41 @@ CONTROLS
tabs.
Outline/layers/attachments pane
- This pane is on the left, below the tab list. The popup allows you to
+ This pane is on the left, below the tab list. The popup allows you to
select from outline, layers, or attachments.
- The outline is a tree-like structure of bookmarks that allows moving
+ The outline is a tree-like structure of bookmarks that allows moving
within the PDF file. Not all PDF files have outlines.
- Layers (a.k.a. optional content) allow parts of the PDF content to be
+ Layers (a.k.a. optional content) allow parts of the PDF content to be
shown or hidden. Not all PDF files have layers.
- Attachments are other files embedded within the PDF file. There is a
- 'save' button for each attached file. Not all PDF files have attach-
+ Attachments are other files embedded within the PDF file. There is a
+ 'save' button for each attached file. Not all PDF files have attach-
ments.
Text selection
- Dragging the mouse with the left button held down will highlight an
- arbitrary rectangle. Selected text can be copied to the clipboard
- (with the edit/copy menu item). On X11, selected text will be avail-
- able in the X selection buffer.
+ In block selection mode, dragging the mouse with the left button held
+ down will highlight an arbitrary rectangle. Shift-clicking will extend
+ the selection.
+
+ In linear selection mode, dragging with the left button will highlight
+ text in reading order. Double-clicking or triple-clicking will select
+ a word or a line, respectively. Shift-clicking will extend the selec-
+ tion.
+
+ Selected text can be copied to the clipboard (with the edit/copy menu
+ item). On X11, selected text will be available in the X selection buf-
+ fer.
Links
- When the mouse is over a hyperlink, the link target will be shown in a
+ When the mouse is over a hyperlink, the link target will be shown in a
popup near the bottom of the window.
Clicking on a hyperlink will jump to the link's destination. A link to
- another PDF document will make xpdf load that document. A 'launch'
- link to an executable program will display a dialog, and if you click
- 'ok', execute the program. URL links are opened in a system-dependent
+ another PDF document will make xpdf load that document. A 'launch'
+ link to an executable program will display a dialog, and if you click
+ 'ok', execute the program. URL links are opened in a system-dependent
way. (On UNIX, Qt uses the $BROWSER environment variable.)
Mouse bindings
@@ -235,19 +250,19 @@ CONTROLS
Dragging the mouse with the middle button held down pans the window.
- The right mouse button opens a popup menu (see popupMenuCmd in
+ The right mouse button opens a popup menu (see popupMenuCmd in
xpdfrc(5)).
Key bindings
- This section lists the default key bindings. Bindings can be changed
+ This section lists the default key bindings. Bindings can be changed
using the config file (see xpdfrc(5)).
control-o
Open a new PDF file via a file requester.
control-r
- Reload the current PDF file. Note that Xpdf will reload the
- file automatically (on a page change or redraw) if it has
+ Reload the current PDF file. Note that Xpdf will reload the
+ file automatically (on a page change or redraw) if it has
changed since it was last loaded.
control-f
@@ -281,8 +296,8 @@ CONTROLS
Open a new window.
control-w
- Close the current tab. Closes the window if this was the last
- open tab. Quits the application if this was the last open win-
+ Close the current tab. Closes the window if this was the last
+ open tab. Quits the application if this was the last open win-
dow.
control-l
@@ -317,11 +332,11 @@ CONTROLS
Go to the last page.
<space> or <PageDown>
- Scroll down on the current page; if already at bottom, move to
+ Scroll down on the current page; if already at bottom, move to
next page.
control-<PageDown> or control-<down-arrow>
- Go to the next page. If <ScrollLock> is active, this maintains
+ Go to the next page. If <ScrollLock> is active, this maintains
the relative position on the page.
<PageUp>
@@ -329,7 +344,7 @@ CONTROLS
ous page.
control-<PageUp> or control-<up-arrow>
- Go to the previous page. If <ScrollLock> is active, this main-
+ Go to the previous page. If <ScrollLock> is active, this main-
tains the relative position on the page.
<esc> Exit full-screen mode.
@@ -343,22 +358,28 @@ CONTROLS
w Set the zoom factor to 'width' (fit page width to window).
Full-screen mode
- Xpdf can be placed into full-screen mode via the -fullscreen command
- line option, the 'full screen' menu item, or a binding to the
+ Xpdf can be placed into full-screen mode via the -fullscreen command
+ line option, the 'full screen' menu item, or a binding to the
fullScreenMode or toggleFullScreenMode command.
- Entering full-screen mode automatically switches to single-page view
+ Entering full-screen mode automatically switches to single-page view
mode and to the fit-page zoom factor.
- Full-screen mode can be exited via the default <esc> key binding, or
+ Full-screen mode can be exited via the default <esc> key binding, or
via a binding to the windowMode or toggleFullScreenModecommand.
COMMANDS
Xpdf's key and mouse bindings are user-configurable, using the bind and
- unbind commands in the config file (see xpdfrc(5)). The bind command
- allows you to bind a key or mouse button to a sequence of one or more
+ unbind commands in the config file (see xpdfrc(5)). The bind command
+ allows you to bind a key or mouse button to a sequence of one or more
commands.
+ In commands that take arguments (inside parentheses), special charac-
+ ters (namely '(', ')', ',', and '\x01') can be escaped by preceding
+ them with a '\x01' character. This is mostly useful in things like
+ scripts that need to be able to open arbitrary PDF files, using the
+ 'openFile' command.
+
The following commands are supported:
about Open the 'about' dialog.
@@ -372,11 +393,11 @@ COMMANDS
Check that file is open in the current tab, and open it if not.
checkOpenFileAtDest(file,dest)
- Check that file is open in the current tab, and open it if not.
+ Check that file is open in the current tab, and open it if not.
In either case go to the specified named destination.
checkOpenFileAtPage(file,page)
- Check that file is open in the current tab, and open it if not.
+ Check that file is open in the current tab, and open it if not.
In either case go to the specified page.
closeSidebar
@@ -403,6 +424,10 @@ COMMANDS
copy Copy selected text to the clipboard.
+ copyLinkTarget
+ Copy the target of the link under the mouse cursor to the clip-
+ board.
+
endPan End a pan operation.
endSelection
@@ -571,6 +596,8 @@ COMMANDS
quit Quit from xpdf.
+ raise Bring the xpdf window to the front.
+
reload Reload the current PDF file.
rotateCCW
@@ -674,10 +701,19 @@ COMMANDS
Scroll up by n pixels, moving to the previous page if appropri-
ate.
+ selectLine
+ Set the selection to the line at the current mouse position.
+
+ selectWord
+ Set the selection to the word at the current mouse position.
+
setSelection(pg,ulx,uly,lrx,lry)
Set the selection to the specified coordinates on the specified
page.
+ showKeyBindings
+ Open the key bindings dialog.
+
showToolbar
Show the toolbar.
@@ -694,12 +730,16 @@ COMMANDS
singlePageMode
Switch to single-page view mode.
+ startExtendedSelection
+ Extend the selection to the current mouse position, and continue
+ extending as the mouse moves.
+
startPan
- Start a pan operation at the current mouse position, which will
+ Start a pan operation at the current mouse position, which will
scroll the document as the mouse moves.
startSelection
- Start a selection at the current mouse position, which will be
+ Start a selection at the current mouse position, which will be
extended as the mouse moves.
toggleContinuousMode
@@ -715,19 +755,19 @@ COMMANDS
Toggle the sidebar between open and closed.
toggleSidebarMoveResizeWin
- Toggle the sidebar between open and closed, resizing the window
- so that the document size doesn't change, and moving the window
+ Toggle the sidebar between open and closed, resizing the window
+ so that the document size doesn't change, and moving the window
so that the document stays in the same place on the screen.
toggleSidebarResizeWin
- Toggle the sidebar between open and closed, resizing the window
+ Toggle the sidebar between open and closed, resizing the window
so that the document size doesn't change.
toggleToolbar
Toggle the toolbar between shown and hidden.
viewPageLabels
- Show page labels (if the PDF file has them), rather than page
+ Show page labels (if the PDF file has them), rather than page
numbers.
viewPageNumbers
@@ -754,19 +794,19 @@ COMMANDS
Zoom to the current selection.
REMOTE SERVER MODE
- Starting xpdf with the "-remote" switch puts it into remote server
- mode. All remaining command line options are commands (see the COM-
+ Starting xpdf with the "-remote" switch puts it into remote server
+ mode. All remaining command line options are commands (see the COM-
MANDS section). Subsequent invocations of "xpdf -remote" with the same
- remote server name will send commands to the already-running instance
- of xpdf. The "checkOpenFile" commands are useful here for things like
+ remote server name will send commands to the already-running instance
+ of xpdf. The "checkOpenFile" commands are useful here for things like
changing pages. For example:
# Start up xpdf, and open something.pdf.
xpdf -remote foo 'openFile(something.pdf)'
- # Switch to page 7 in the already-open something.pdf. If
+ # Switch to page 7 in the already-open something.pdf. If
the user
- # has closed xpdf in the meantime, this will restart it and
+ # has closed xpdf in the meantime, this will restart it and
reopen
# the file.
xpdf -remote foo 'checkOpenFileAtPage(something.pdf, 7)'
@@ -785,7 +825,7 @@ EXIT CODES
99 Other error.
AUTHOR
- The xpdf software and documentation are copyright 1996-2019 Glyph &
+ The xpdf software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
SEE ALSO
@@ -795,4 +835,4 @@ SEE ALSO
- 25 Sep 2019 xpdf(1)
+ 28 Jan 2021 xpdf(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.5 b/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.5
index bde3b5a5973..07639316ec7 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.5
+++ b/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.5
@@ -1,7 +1,7 @@
-.\" Copyright 2002-2019 Glyph & Cog, LLC
-.TH xpdfrc 5 "25 Sep 2019"
+.\" Copyright 2002-2021 Glyph & Cog, LLC
+.TH xpdfrc 5 "28 Jan 2021"
.SH NAME
-xpdfrc \- configuration file for Xpdf tools (version 4.02)
+xpdfrc \- configuration file for Xpdf tools (version 4.03)
.SH DESCRIPTION
All of the Xpdf tools read a single configuration file. If you have a
.I .xpdfrc
@@ -17,8 +17,25 @@ The xpdfrc file consists of a series of configuration options, one
per line. Blank lines and lines starting with a \'#' (comments) are
ignored.
.PP
-Arguments may be quoted, using "double-quote" characters, e.g., for
-file names that contain spaces.
+Arguments can be single-quoted or double-quoted, e.g., for file names
+that contain spaces ("aaa bbb", 'aaa bbb'). This quoting does not
+provide any escaping, so there's no way to include a double quote in a
+double-quoted argument or a single quote in a single-quoted argument.
+.PP
+
+Arguments can also be at-quoted: @"aaa bbb". At-quoted strings allow
+use of the DATADIR variable, which is set to the 'data' subdirectory
+in the xpdf install directory. The percent sign (%) is an escape
+character: a percent sign followed by any other character is replaced
+with that character.
+.PP
+.RS
+.nf
+@"abc %"def%" ghi" --> abc "def" ghi
+@"${DATADIR}/foo" --> ...install-dir.../data/foo
+@"%${DATADIR}/foo" --> ${DATADIR}/foo
+.fi
+.RE
.PP
The following sections list all of the configuration options, sorted
into functional groups. There is an examples section at the end.
@@ -549,6 +566,11 @@ If set to "yes", xpdf opens with the sidebar (tabs, outline, etc.)
visible. If set to "no", xpdf opens with the sidebar collapsed. The
default is "yes".
.TP
+.BI initialSidebarWidth " width"
+Sets the initial sidebar width, in pixels. This is only relevant if
+initialSidebarState is "yes". The default value is zero, which tells
+xpdf to use an internal default size.
+.TP
.BR initialSelectMode " block | linear"
Sets the initial selection mode. The default setting is "linear".
.TP
@@ -567,6 +589,10 @@ color.
Set the matte color for full-screen mode. The color can be #RRGGBB
(hexadecimal) or a named color.
.TP
+.BI selectionColor " color"
+Set the selection color. The color can be #RRGGBB (hexadecimal) or a
+named color.
+.TP
.BI reverseVideoInvertImages " yes | no"
If set to "no", xpdf's reverse-video mode inverts text and vector
graphic content, but not images. If set to "yes", xpdf inverts images
@@ -641,11 +667,13 @@ can be a regular ASCII character, or any one of:
end
pgup
pgdn
- left / right / up / down (arrow keys)
- f1 .. f35 (function keys)
- mousePress1 .. mousePress7 (mouse buttons)
- mouseRelease1 .. mouseRelease7 (mouse buttons)
- mouseClick1 .. mouseClick7 (mouse buttons)
+ left / right / up / down (arrow keys)
+ f1 .. f35 (function keys)
+ mousePress1 .. mousePress7 (mouse buttons)
+ mouseRelease1 .. mouseRelease7 (mouse buttons)
+ mouseClick1 .. mouseClick7 (mouse buttons)
+ mouseDoubleClick1 .. mouseDoubleClick7 (mouse buttons)
+ mouseTripleClick1 .. mouseTripleClick7 (mouse buttons)
.fi
.I Context
@@ -708,11 +736,6 @@ value is "yes".
If set to "no", form fields will not be drawn or printed. The default
value is "yes".
.TP
-.BI enableXFA " yes | no"
-If set to "yes", an XFA form (if present) will be rendered in place of
-an AcroForm. If "no", an XFA form will never be rendered. This
-defaults to "yes".
-.TP
.BI printCommands " yes | no"
If set to "yes", drawing commands are printed as they're executed
(useful for debugging). This defaults to "no".
@@ -783,7 +806,7 @@ Depending on build options, it may be placed elsewhere.
This is the user's configuration file. If it exists, it will be read
in place of the system-wide file.
.SH AUTHOR
-The Xpdf software and documentation are copyright 1996-2019 Glyph &
+The Xpdf software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
.SH "SEE ALSO"
.BR xpdf (1),
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat b/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat
index f2412bd3420..c43e8005e2b 100644
--- a/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat
+++ b/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat
@@ -3,7 +3,7 @@ xpdfrc(5) File Formats Manual xpdfrc(5)
NAME
- xpdfrc - configuration file for Xpdf tools (version 4.02)
+ xpdfrc - configuration file for Xpdf tools (version 4.03)
DESCRIPTION
All of the Xpdf tools read a single configuration file. If you have a
@@ -17,8 +17,20 @@ DESCRIPTION
line. Blank lines and lines starting with a '#' (comments) are
ignored.
- Arguments may be quoted, using "double-quote" characters, e.g., for
- file names that contain spaces.
+ Arguments can be single-quoted or double-quoted, e.g., for file names
+ that contain spaces ("aaa bbb", 'aaa bbb'). This quoting does not pro-
+ vide any escaping, so there's no way to include a double quote in a
+ double-quoted argument or a single quote in a single-quoted argument.
+
+ Arguments can also be at-quoted: @"aaa bbb". At-quoted strings allow
+ use of the DATADIR variable, which is set to the 'data' subdirectory in
+ the xpdf install directory. The percent sign (%) is an escape charac-
+ ter: a percent sign followed by any other character is replaced with
+ that character.
+
+ @"abc %"def%" ghi" --> abc "def" ghi
+ @"${DATADIR}/foo" --> ...install-dir.../data/foo
+ @"%${DATADIR}/foo" --> ${DATADIR}/foo
The following sections list all of the configuration options, sorted
into functional groups. There is an examples section at the end.
@@ -507,6 +519,11 @@ VIEWER SETTINGS
etc.) visible. If set to "no", xpdf opens with the sidebar
collapsed. The default is "yes".
+ initialSidebarWidth width
+ Sets the initial sidebar width, in pixels. This is only rele-
+ vant if initialSidebarState is "yes". The default value is
+ zero, which tells xpdf to use an internal default size.
+
initialSelectMode block | linear
Sets the initial selection mode. The default setting is "lin-
ear".
@@ -526,15 +543,19 @@ VIEWER SETTINGS
Set the matte color for full-screen mode. The color can be
#RRGGBB (hexadecimal) or a named color.
+ selectionColor color
+ Set the selection color. The color can be #RRGGBB (hexadecimal)
+ or a named color.
+
reverseVideoInvertImages yes | no
- If set to "no", xpdf's reverse-video mode inverts text and vec-
- tor graphic content, but not images. If set to "yes", xpdf
+ If set to "no", xpdf's reverse-video mode inverts text and vec-
+ tor graphic content, but not images. If set to "yes", xpdf
inverts images as well. The default is "no".
popupMenuCmd title command ...
- Add a command to the popup menu. Title is the text to be dis-
- played in the menu. Command is an Xpdf command (see the COM-
- MANDS section of the xpdf(1) man page for details). Multiple
+ Add a command to the popup menu. Title is the text to be dis-
+ played in the menu. Command is an Xpdf command (see the COM-
+ MANDS section of the xpdf(1) man page for details). Multiple
commands are separated by whitespace.
maxTileWidth pixels
@@ -542,11 +563,11 @@ VIEWER SETTINGS
ing pages. This defaults to 1500.
maxTileHeight pixels
- Set the maximum height of tiles to be used by xpdf when raster-
+ Set the maximum height of tiles to be used by xpdf when raster-
izing pages. This defaults to 1500.
tileCacheSize tiles
- Set the maximum number of tiles to be cached by xpdf when ras-
+ Set the maximum number of tiles to be cached by xpdf when ras-
terizing pages. This defaults to 10.
workerThreads numThreads
@@ -554,16 +575,16 @@ VIEWER SETTINGS
izing pages. This defaults to 1.
launchCommand command
- Sets the command executed when you click on a "launch"-type
- link. The intent is for the command to be a program/script
- which determines the file type and runs the appropriate viewer.
- The command line will consist of the file to be launched, fol-
- lowed by any parameters specified with the link. Do not use
- "%s" in "command". By default, this is unset, and Xpdf will
+ Sets the command executed when you click on a "launch"-type
+ link. The intent is for the command to be a program/script
+ which determines the file type and runs the appropriate viewer.
+ The command line will consist of the file to be launched, fol-
+ lowed by any parameters specified with the link. Do not use
+ "%s" in "command". By default, this is unset, and Xpdf will
simply try to execute the file (after prompting the user).
movieCommand command
- Sets the command executed when you click on a movie annotation.
+ Sets the command executed when you click on a movie annotation.
The string "%s" will be replaced with the movie file name. This
has no default value.
@@ -571,7 +592,7 @@ VIEWER SETTINGS
Sets the default printer used in the viewer's print dialog.
bind modifiers-key context command ...
- Add a key or mouse button binding. Modifiers can be zero or
+ Add a key or mouse button binding. Modifiers can be zero or
more of:
shift-
@@ -592,11 +613,13 @@ VIEWER SETTINGS
end
pgup
pgdn
- left / right / up / down (arrow keys)
- f1 .. f35 (function keys)
- mousePress1 .. mousePress7 (mouse buttons)
- mouseRelease1 .. mouseRelease7 (mouse buttons)
- mouseClick1 .. mouseClick7 (mouse buttons)
+ left / right / up / down (arrow keys)
+ f1 .. f35 (function keys)
+ mousePress1 .. mousePress7 (mouse buttons)
+ mouseRelease1 .. mouseRelease7 (mouse buttons)
+ mouseClick1 .. mouseClick7 (mouse buttons)
+ mouseDoubleClick1 .. mouseDoubleClick7 (mouse buttons)
+ mouseTripleClick1 .. mouseTripleClick7 (mouse buttons)
Context is either "any" or a comma-separated combination of:
@@ -605,14 +628,14 @@ VIEWER SETTINGS
overLink / offLink (mouse over link or not)
scrLockOn / scrLockOff (scroll lock on/off)
- The context string can include only one of each pair in the
+ The context string can include only one of each pair in the
above list.
- Command is an Xpdf command (see the COMMANDS section of the
- xpdf(1) man page for details). Multiple commands are separated
+ Command is an Xpdf command (see the COMMANDS section of the
+ xpdf(1) man page for details). Multiple commands are separated
by whitespace.
- The bind command replaces any existing binding, but only if it
+ The bind command replaces any existing binding, but only if it
was defined for the exact same modifiers, key, and context. All
tokens (modifiers, key, context, commands) are case-sensitive.
@@ -630,10 +653,10 @@ VIEWER SETTINGS
See the xpdf(1) man page for more examples.
unbind modifiers-key context
- Removes a key binding established with the bind command. This
- is most useful to remove default key bindings before establish-
- ing new ones (e.g., if the default key binding is given for
- "any" context, and you want to create new key bindings for mul-
+ Removes a key binding established with the bind command. This
+ is most useful to remove default key bindings before establish-
+ ing new ones (e.g., if the default key binding is given for
+ "any" context, and you want to create new key bindings for mul-
tiple contexts).
tabStateFile path
@@ -642,24 +665,19 @@ VIEWER SETTINGS
MISCELLANEOUS SETTINGS
drawAnnotations yes | no
- If set to "no", annotations will not be drawn or printed. The
+ If set to "no", annotations will not be drawn or printed. The
default value is "yes".
drawFormFields yes | no
- If set to "no", form fields will not be drawn or printed. The
+ If set to "no", form fields will not be drawn or printed. The
default value is "yes".
- enableXFA yes | no
- If set to "yes", an XFA form (if present) will be rendered in
- place of an AcroForm. If "no", an XFA form will never be ren-
- dered. This defaults to "yes".
-
printCommands yes | no
- If set to "yes", drawing commands are printed as they're exe-
+ If set to "yes", drawing commands are printed as they're exe-
cuted (useful for debugging). This defaults to "no".
errQuiet yes | no
- If set to "yes", this suppresses all error and warning messages
+ If set to "yes", this suppresses all error and warning messages
from all of the Xpdf tools. This defaults to "no".
EXAMPLES
@@ -716,7 +734,7 @@ EXAMPLES
FILES
/usr/local/etc/xpdfrc
- This is the default location for the system-wide configuration
+ This is the default location for the system-wide configuration
file. Depending on build options, it may be placed elsewhere.
$HOME/.xpdfrc
@@ -724,14 +742,14 @@ FILES
read in place of the system-wide file.
AUTHOR
- The Xpdf software and documentation are copyright 1996-2019 Glyph &
+ The Xpdf software and documentation are copyright 1996-2021 Glyph &
Cog, LLC.
SEE ALSO
- xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf-
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf-
fonts(1), pdfdetach(1), pdftoppm(1), pdftopng(1), pdfimages(1)
http://www.xpdfreader.com/
- 25 Sep 2019 xpdfrc(5)
+ 28 Jan 2021 xpdfrc(5)
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc
index 333fe0763b3..4c6797ba9d6 100644
--- a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc
@@ -561,7 +561,7 @@ static FoFiIdentifierType identifyOpenType(Reader *reader) {
static FoFiIdentifierType identifyCFF(Reader *reader, int start) {
Guint offset0, offset1;
- int hdrSize, offSize0, offSize1, pos, endPos, b0, n, i;
+ int hdrSize, offSize0, offSize1, pos, endPos, b0, n;
//----- read the header
if (reader->getByte(start) != 0x01 ||
@@ -622,19 +622,18 @@ static FoFiIdentifierType identifyCFF(Reader *reader, int start) {
//----- parse the top dict, look for ROS as first entry
// for a CID font, the top dict starts with:
// <int> <int> <int> ROS
- for (i = 0; i < 3; ++i) {
- b0 = reader->getByte(pos++);
+ while (pos >= 0 && pos < endPos) {
+ b0 = reader->getByte(pos);
if (b0 == 0x1c) {
- pos += 2;
+ pos += 3;
} else if (b0 == 0x1d) {
- pos += 4;
+ pos += 5;
} else if (b0 >= 0xf7 && b0 <= 0xfe) {
+ pos += 2;
+ } else if (b0 >= 0x20 && b0 <= 0xf6) {
pos += 1;
- } else if (b0 < 0x20 || b0 > 0xf6) {
- return fofiIdCFF8Bit;
- }
- if (pos >= endPos || pos < 0) {
- return fofiIdCFF8Bit;
+ } else {
+ break;
}
}
if (pos + 1 < endPos &&
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc
index 0f5b5bc7230..57b12b14996 100644
--- a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc
@@ -1730,7 +1730,7 @@ void FoFiTrueType::cvtCharStrings(char **encoding,
if (encoding) {
name = encoding[i];
} else {
- sprintf(buf2, "c%02x", i);
+ snprintf(buf2, sizeof(buf2), "c%02x", i);
name = buf2;
}
if (name && strcmp(name, ".notdef")) {
@@ -2143,7 +2143,7 @@ Guint FoFiTrueType::computeTableChecksum(Guchar *data, int length) {
void FoFiTrueType::parse(int fontNum, GBool allowHeadlessCFF) {
Guint topTag;
- int offset, pos, ver, i, j;
+ int offset, pos, ver, i, j, k;
parsedOk = gTrue;
@@ -2206,7 +2206,7 @@ void FoFiTrueType::parse(int fontNum, GBool allowHeadlessCFF) {
// check for the head table; allow for a head-less OpenType CFF font
headlessCFF = gFalse;
if (seekTable("head") < 0) {
- if (openTypeCFF && allowHeadlessCFF) {
+ if (openTypeCFF && allowHeadlessCFF && seekTable("CFF ") >= 0) {
headlessCFF = gTrue;
nGlyphs = 0;
bbox[0] = bbox[1] = bbox[2] = bbox[3] = 0;
@@ -2238,14 +2238,22 @@ void FoFiTrueType::parse(int fontNum, GBool allowHeadlessCFF) {
return;
}
cmaps = (TrueTypeCmap *)gmallocn(nCmaps, sizeof(TrueTypeCmap));
+ k = 0;
for (j = 0; j < nCmaps; ++j) {
- cmaps[j].platform = getU16BE(pos, &parsedOk);
- cmaps[j].encoding = getU16BE(pos + 2, &parsedOk);
- cmaps[j].offset = tables[i].offset + getU32BE(pos + 4, &parsedOk);
+ cmaps[k].platform = getU16BE(pos, &parsedOk);
+ cmaps[k].encoding = getU16BE(pos + 2, &parsedOk);
+ cmaps[k].offset = getU32BE(pos + 4, &parsedOk);
pos += 8;
- cmaps[j].fmt = getU16BE(cmaps[j].offset, &parsedOk);
- cmaps[j].len = getU16BE(cmaps[j].offset + 2, &parsedOk);
+ if (cmaps[k].offset >= tables[i].len) {
+ // skip any invalid subtables
+ continue;
+ }
+ cmaps[k].offset += tables[i].offset;
+ cmaps[k].fmt = getU16BE(cmaps[k].offset, &parsedOk);
+ cmaps[k].len = getU16BE(cmaps[k].offset + 2, &parsedOk);
+ ++k;
}
+ nCmaps = k;
if (!parsedOk) {
return;
}
@@ -2273,7 +2281,7 @@ void FoFiTrueType::parse(int fontNum, GBool allowHeadlessCFF) {
// NB: out-of-bounds entries are handled in writeTTF()
if (!openTypeCFF) {
i = seekTable("loca");
- if (tables[i].len < 0) {
+ if (tables[i].len < (locaFmt ? 4 : 2)) {
parsedOk = gFalse;
return;
}
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc
index 0d067b5c5a9..83ec53ef5e8 100644
--- a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc
@@ -114,7 +114,7 @@ void FoFiType1::writeEncoded(const char **newEncoding,
"0 1 255 {1 index exch /.notdef put} for\n", 40);
for (i = 0; i < 256; ++i) {
if (newEncoding[i]) {
- sprintf(buf, "dup %d /%s put\n", i, newEncoding[i]);
+ snprintf(buf, sizeof(buf), "dup %d /%s put\n", i, newEncoding[i]);
(*outputFunc)(outputStream, buf, (int)strlen(buf));
}
}
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc
index 13c045f8b97..b4f0a061be9 100644
--- a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc
@@ -26,6 +26,8 @@
static char hexChars[17] = "0123456789ABCDEF";
+#define type1cSubrRecursionLimit 20
+
//------------------------------------------------------------------------
GBool Type1COp::isZero() {
@@ -39,28 +41,52 @@ GBool Type1COp::isZero() {
GBool Type1COp::isNegative() {
switch (kind) {
- case type1COpInteger: return intgr < 0;
- case type1COpFloat: return flt < 0;
- case type1COpRational: return (rat.num < 0) != (rat.den < 0);
- default: return gFalse; // shouldn't happen
+ case type1COpInteger:
+ return intgr < 0;
+ case type1COpFloat:
+ return flt < 0;
+ case type1COpRational:
+ return (rat.num < 0) != (rat.den < 0);
+ default:
+ // shouldn't happen
+ return gFalse;
}
}
int Type1COp::toInt() {
switch (kind) {
- case type1COpInteger: return intgr;
- case type1COpFloat: return (int)flt;
- case type1COpRational: return rat.num / rat.den;
- default: return 0; // shouldn't happen
+ case type1COpInteger:
+ return intgr;
+ case type1COpFloat:
+ if (flt < -2e9 || flt > 2e9) {
+ return 0;
+ }
+ return (int)flt;
+ case type1COpRational:
+ if (rat.den == 0) {
+ return 0;
+ }
+ return rat.num / rat.den;
+ default:
+ // shouldn't happen
+ return 0;
}
}
double Type1COp::toFloat() {
switch (kind) {
- case type1COpInteger: return (double)intgr;
- case type1COpFloat: return flt;
- case type1COpRational: return (double)rat.num / (double)rat.den;
- default: return 0.0; // shouldn't happen
+ case type1COpInteger:
+ return (double)intgr;
+ case type1COpFloat:
+ return flt;
+ case type1COpRational:
+ if (rat.den == 0) {
+ return 0;
+ }
+ return (double)rat.num / (double)rat.den;
+ default:
+ // shouldn't happen
+ return 0.0;
}
}
@@ -607,7 +633,8 @@ void FoFiType1C::convertToCIDType0(char *psName, int *codeMap, int nCodes,
subrIdx.pos = -1;
}
cvtGlyph(val.pos, val.len, charStrings,
- &subrIdx, &privateDicts[fdSelect ? fdSelect[gid] : 0], gTrue);
+ &subrIdx, &privateDicts[fdSelect ? fdSelect[gid] : 0],
+ gTrue, 0);
}
}
}
@@ -969,6 +996,10 @@ void FoFiType1C::convertToType0(char *psName, int *codeMap, int nCodes,
}
}
}
+ if (fd < 0 || fd >= nFDs) {
+ // this will only happen in a broken/damaged font
+ fd = 0;
+ }
// font dictionary (unencrypted section)
(*outputFunc)(outputStream, "16 dict begin\n", 14);
@@ -1269,7 +1300,7 @@ void FoFiType1C::eexecCvtGlyph(Type1CEexecBuf *eb, const char *glyphName,
// generate the charstring
charBuf = new GString();
- cvtGlyph(offset, nBytes, charBuf, subrIdx, pDict, gTrue);
+ cvtGlyph(offset, nBytes, charBuf, subrIdx, pDict, gTrue, 0);
buf = GString::format("/{0:s} {1:d} RD ", glyphName, charBuf->getLength());
eexecWrite(eb, buf->getCString());
@@ -1283,7 +1314,7 @@ void FoFiType1C::eexecCvtGlyph(Type1CEexecBuf *eb, const char *glyphName,
void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
Type1CIndex *subrIdx, Type1CPrivateDict *pDict,
- GBool top) {
+ GBool top, int recursion) {
Type1CIndexVal val;
Type1COp zero, tmp;
GBool ok, dInt;
@@ -1292,6 +1323,11 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
Guchar byte;
int pos, subrBias, start, num, den, i, k;
+ if (recursion > type1cSubrRecursionLimit) {
+ //~ error(-1, "Recursive loop in Type1C glyph");
+ return;
+ }
+
start = charBuf->getLength();
if (top) {
charBuf->append((char)73);
@@ -1504,7 +1540,8 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
ok = gTrue;
getIndexVal(subrIdx, k, &val, &ok);
if (ok) {
- cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse);
+ cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse,
+ recursion + 1);
}
} else {
//~ error(-1, "Too few args to Type 2 callsubr");
@@ -1739,7 +1776,8 @@ void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
ok = gTrue;
getIndexVal(&gsubrIdx, k, &val, &ok);
if (ok) {
- cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse);
+ cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse,
+ recursion + 1);
}
} else {
//~ error(-1, "Too few args to Type 2 callgsubr");
@@ -2945,11 +2983,10 @@ void FoFiType1C::readFDSelect() {
int fdSelectFmt, pos, nRanges, gid0, gid1, fd, i, j;
fdSelect = (Guchar *)gmalloc(nGlyphs);
- if (topDict.fdSelectOffset == 0) {
- for (i = 0; i < nGlyphs; ++i) {
- fdSelect[i] = 0;
- }
- } else {
+ for (i = 0; i < nGlyphs; ++i) {
+ fdSelect[i] = 0;
+ }
+ if (topDict.fdSelectOffset != 0) {
pos = topDict.fdSelectOffset;
fdSelectFmt = getU8(pos++, &parsedOk);
if (!parsedOk) {
@@ -2992,9 +3029,6 @@ void FoFiType1C::readFDSelect() {
}
} else {
//~ error(-1, "Unknown FDSelect table format in CID font");
- for (i = 0; i < nGlyphs; ++i) {
- fdSelect[i] = 0;
- }
}
}
}
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h
index eb75dce330d..15a823cffe6 100644
--- a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h
@@ -229,7 +229,7 @@ private:
Type1CPrivateDict *pDict);
void cvtGlyph(int offset, int nBytes, GString *charBuf,
Type1CIndex *subrIdx, Type1CPrivateDict *pDict,
- GBool top);
+ GBool top, int recursion);
void cvtGlyphWidth(GBool useOp, GString *charBuf,
Type1CPrivateDict *pDict);
void cvtNum(Type1COp op, GString *charBuf);
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt b/Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt
index 7302dbbe08c..0a1c56b0ebd 100644
--- a/Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt
+++ b/Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt
@@ -20,6 +20,7 @@ add_library(goo_objs OBJECT
gmem.cc
gmempp.cc
parseargs.c
+ Trace.cc
)
add_library(goo
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h b/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h
index a0baa70b61f..9f0c3240627 100644
--- a/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h
+++ b/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h
@@ -36,6 +36,7 @@ public:
FixedPoint(const FixedPoint &x) { val = x.val; }
FixedPoint(double x) { val = (int)(x * (1 << fixptShift) + 0.5); }
FixedPoint(int x) { val = x << fixptShift; }
+ FixedPoint(Guint x) { val = x << fixptShift; }
FixedPoint(long x) { val = (int)x << fixptShift; }
operator float()
@@ -52,31 +53,37 @@ public:
bool operator ==(FixedPoint x) const { return val == x.val; }
bool operator ==(double x) const { return *this == (FixedPoint)x; }
bool operator ==(int x) const { return *this == (FixedPoint)x; }
+ bool operator ==(Guint x) const { return *this == (FixedPoint)x; }
bool operator ==(long x) const { return *this == (FixedPoint)x; }
bool operator !=(FixedPoint x) const { return val != x.val; }
bool operator !=(double x) const { return *this != (FixedPoint)x; }
bool operator !=(int x) const { return *this != (FixedPoint)x; }
+ bool operator !=(Guint x) const { return *this != (FixedPoint)x; }
bool operator !=(long x) const { return *this != (FixedPoint)x; }
bool operator <(FixedPoint x) const { return val < x.val; }
bool operator <(double x) const { return *this < (FixedPoint)x; }
bool operator <(int x) const { return *this < (FixedPoint)x; }
+ bool operator <(Guint x) const { return *this < (FixedPoint)x; }
bool operator <(long x) const { return *this < (FixedPoint)x; }
bool operator <=(FixedPoint x) const { return val <= x.val; }
bool operator <=(double x) const { return *this <= (FixedPoint)x; }
bool operator <=(int x) const { return *this <= (FixedPoint)x; }
+ bool operator <=(Guint x) const { return *this <= (FixedPoint)x; }
bool operator <=(long x) const { return *this <= (FixedPoint)x; }
bool operator >(FixedPoint x) const { return val > x.val; }
bool operator >(double x) const { return *this > (FixedPoint)x; }
bool operator >(int x) const { return *this > (FixedPoint)x; }
+ bool operator >(Guint x) const { return *this > (FixedPoint)x; }
bool operator >(long x) const { return *this > (FixedPoint)x; }
bool operator >=(FixedPoint x) const { return val >= x.val; }
bool operator >=(double x) const { return *this >= (FixedPoint)x; }
bool operator >=(int x) const { return *this >= (FixedPoint)x; }
+ bool operator >=(Guint x) const { return *this >= (FixedPoint)x; }
bool operator >=(long x) const { return *this >= (FixedPoint)x; }
FixedPoint operator -() { return make(-val); }
@@ -84,41 +91,49 @@ public:
FixedPoint operator +(FixedPoint x) { return make(val + x.val); }
FixedPoint operator +(double x) { return *this + (FixedPoint)x; }
FixedPoint operator +(int x) { return *this + (FixedPoint)x; }
+ FixedPoint operator +(Guint x) { return *this + (FixedPoint)x; }
FixedPoint operator +(long x) { return *this + (FixedPoint)x; }
FixedPoint operator +=(FixedPoint x) { val = val + x.val; return *this; }
FixedPoint operator +=(double x) { return *this += (FixedPoint)x; }
FixedPoint operator +=(int x) { return *this += (FixedPoint)x; }
+ FixedPoint operator +=(Guint x) { return *this += (FixedPoint)x; }
FixedPoint operator +=(long x) { return *this += (FixedPoint)x; }
FixedPoint operator -(FixedPoint x) { return make(val - x.val); }
FixedPoint operator -(double x) { return *this - (FixedPoint)x; }
FixedPoint operator -(int x) { return *this - (FixedPoint)x; }
+ FixedPoint operator -(Guint x) { return *this - (FixedPoint)x; }
FixedPoint operator -(long x) { return *this - (FixedPoint)x; }
FixedPoint operator -=(FixedPoint x) { val = val - x.val; return *this; }
FixedPoint operator -=(double x) { return *this -= (FixedPoint)x; }
FixedPoint operator -=(int x) { return *this -= (FixedPoint)x; }
+ FixedPoint operator -=(Guint x) { return *this -= (FixedPoint)x; }
FixedPoint operator -=(long x) { return *this -= (FixedPoint)x; }
FixedPoint operator *(FixedPoint x) { return make(mul(val, x.val)); }
FixedPoint operator *(double x) { return *this * (FixedPoint)x; }
FixedPoint operator *(int x) { return *this * (FixedPoint)x; }
+ FixedPoint operator *(Guint x) { return *this * (FixedPoint)x; }
FixedPoint operator *(long x) { return *this * (FixedPoint)x; }
FixedPoint operator *=(FixedPoint x) { val = mul(val, x.val); return *this; }
FixedPoint operator *=(double x) { return *this *= (FixedPoint)x; }
FixedPoint operator *=(int x) { return *this *= (FixedPoint)x; }
+ FixedPoint operator *=(Guint x) { return *this *= (FixedPoint)x; }
FixedPoint operator *=(long x) { return *this *= (FixedPoint)x; }
FixedPoint operator /(FixedPoint x) { return make(div(val, x.val)); }
FixedPoint operator /(double x) { return *this / (FixedPoint)x; }
FixedPoint operator /(int x) { return *this / (FixedPoint)x; }
+ FixedPoint operator /(Guint x) { return *this / (FixedPoint)x; }
FixedPoint operator /(long x) { return *this / (FixedPoint)x; }
FixedPoint operator /=(FixedPoint x) { val = div(val, x.val); return *this; }
FixedPoint operator /=(double x) { return *this /= (FixedPoint)x; }
FixedPoint operator /=(int x) { return *this /= (FixedPoint)x; }
+ FixedPoint operator /=(Guint x) { return *this /= (FixedPoint)x; }
FixedPoint operator /=(long x) { return *this /= (FixedPoint)x; }
static FixedPoint abs(FixedPoint x) { return make(::abs(x.val)); }
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/Trace.cc b/Build/source/libs/xpdf/xpdf-src/goo/Trace.cc
new file mode 100644
index 00000000000..4b730e5b97d
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/Trace.cc
@@ -0,0 +1,114 @@
+//========================================================================
+//
+// Trace.cc
+//
+// Nested tracing.
+//
+// Copyright 2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#if ENABLE_TRACING
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include "GString.h"
+#include "Trace.h"
+
+// NB: This module is NOT thread-safe.
+
+static bool traceInitialized = false;
+static FILE *traceOut = NULL;
+
+static void traceInit() {
+ if (traceInitialized) {
+ return;
+ }
+ //~ this could read an env var to set up an output file
+ GString *fileName = GString::format("/tmp/trace.{0:d}", (int)getpid());
+ traceOut = fopen(fileName->getCString(), "w");
+ delete fileName;
+ traceInitialized = true;
+}
+
+static void traceHeader(char flag, void *handle) {
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ if (handle) {
+ fprintf(traceOut, "%c %ld %06ld %p ", flag, tv.tv_sec, tv.tv_usec, handle);
+ } else {
+ fprintf(traceOut, "%c %ld %06ld 0x0 ", flag, tv.tv_sec, tv.tv_usec);
+ }
+}
+
+void traceBegin(void *nestHandle, const char *fmt, ...) {
+ traceInit();
+ if (!traceOut) {
+ return;
+ }
+ traceHeader('B', nestHandle);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(traceOut, fmt, args);
+ va_end(args);
+ fprintf(traceOut, "\n");
+}
+
+void traceEnd(void *nestHandle, const char *fmt, ...) {
+ traceInit();
+ if (!traceOut) {
+ return;
+ }
+ traceHeader('E', nestHandle);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(traceOut, fmt, args);
+ va_end(args);
+ fprintf(traceOut, "\n");
+}
+
+void traceAlloc(void *resourceHandle, const char *fmt, ...) {
+ traceInit();
+ if (!traceOut) {
+ return;
+ }
+ traceHeader('A', resourceHandle);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(traceOut, fmt, args);
+ va_end(args);
+ fprintf(traceOut, "\n");
+}
+
+void traceFree(void *resourceHandle, const char *fmt, ...) {
+ traceInit();
+ if (!traceOut) {
+ return;
+ }
+ traceHeader('F', resourceHandle);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(traceOut, fmt, args);
+ va_end(args);
+ fprintf(traceOut, "\n");
+}
+
+void traceMessage(const char *fmt, ...) {
+ traceInit();
+ if (!traceOut) {
+ return;
+ }
+ traceHeader('M', NULL);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(traceOut, fmt, args);
+ va_end(args);
+ fprintf(traceOut, "\n");
+}
+
+#endif // ENABLE_TRACING
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/Trace.h b/Build/source/libs/xpdf/xpdf-src/goo/Trace.h
new file mode 100644
index 00000000000..9f53ac5ef68
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/Trace.h
@@ -0,0 +1,43 @@
+//========================================================================
+//
+// Trace.h
+//
+// Nested tracing.
+//
+// Copyright 2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef TRACING_H
+#define TRACING_H
+
+#include <aconf.h>
+
+#if ENABLE_TRACING
+
+// Enter a nesting level.
+extern void traceBegin(void *nestHandle, const char *fmt, ...);
+
+// Exit a nesting level.
+extern void traceEnd(void *nestHandle, const char *fmt, ...);
+
+// Mark a resource as allocated.
+extern void traceAlloc(void *resourceHandle, const char *fmt, ...);
+
+// Mark a resource as freed.
+extern void traceFree(void *resourceHandle, const char *fmt, ...);
+
+// Misc message.
+extern void traceMessage(const char *fmt, ...);
+
+#else // ENABLE_TRACING
+
+static inline void traceBegin(void *nestHandle, const char *fmt, ...) {}
+static inline void traceEnd(void *nestHandle, const char *fmt, ...) {}
+static inline void traceAlloc(void *resourceHandle, const char *fmt, ...) {}
+static inline void traceFree(void *nestHandle, const char *fmt, ...) {}
+static inline void traceMessage(const char *fmt, ...) {}
+
+#endif // ENABLE_TRACING
+
+#endif // TRACING_H
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/gfile.cc b/Build/source/libs/xpdf/xpdf-src/goo/gfile.cc
index e7112b2039d..570f4e7ab13 100644
--- a/Build/source/libs/xpdf/xpdf-src/goo/gfile.cc
+++ b/Build/source/libs/xpdf/xpdf-src/goo/gfile.cc
@@ -18,6 +18,8 @@
# include <windows.h>
# include <time.h>
# include <direct.h>
+# include <shobjidl.h>
+# include <shlguid.h>
#else
# if !defined(ACORN)
# include <sys/types.h>
@@ -579,68 +581,49 @@ GString *fileNameToUTF8(wchar_t *path) {
}
return s;
}
+
+wchar_t *fileNameToUCS2(const char *path, wchar_t *out, size_t outSize) {
+ const char *p;
+ size_t i;
+
+ for (p = path, i = 0; *p && i < outSize - 1; ++i) {
+ if ((p[0] & 0xe0) == 0xc0 &&
+ p[1] && (p[1] & 0xc0) == 0x80) {
+ out[i] = (wchar_t)(((p[0] & 0x1f) << 6) |
+ (p[1] & 0x3f));
+ p += 2;
+ } else if ((p[0] & 0xf0) == 0xe0 &&
+ (p[1] & 0xc0) == 0x80 &&
+ (p[2] & 0xc0) == 0x80) {
+ out[i] = (wchar_t)(((p[0] & 0x0f) << 12) |
+ ((p[1] & 0x3f) << 6) |
+ (p[2] & 0x3f));
+ p += 3;
+ } else {
+ out[i] = (wchar_t)(p[0] & 0xff);
+ p += 1;
+ }
+ }
+ out[i] = (wchar_t)0;
+ return out;
+}
#endif
FILE *openFile(const char *path, const char *mode) {
#if defined(_WIN32)
return fopen(path, mode);
#if 0
- OSVERSIONINFO version;
wchar_t wPath[_MAX_PATH + 1];
- char nPath[_MAX_PATH + 1];
wchar_t wMode[8];
- const char *p;
int i;
- // NB: _wfopen is only available in NT
- version.dwOSVersionInfoSize = sizeof(version);
- GetVersionEx(&version);
- if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
- for (p = path, i = 0; *p && i < _MAX_PATH; ++i) {
- if ((p[0] & 0xe0) == 0xc0 &&
- p[1] && (p[1] & 0xc0) == 0x80) {
- wPath[i] = (wchar_t)(((p[0] & 0x1f) << 6) |
- (p[1] & 0x3f));
- p += 2;
- } else if ((p[0] & 0xf0) == 0xe0 &&
- p[1] && (p[1] & 0xc0) == 0x80 &&
- p[2] && (p[2] & 0xc0) == 0x80) {
- wPath[i] = (wchar_t)(((p[0] & 0x0f) << 12) |
- ((p[1] & 0x3f) << 6) |
- (p[2] & 0x3f));
- p += 3;
- } else {
- wPath[i] = (wchar_t)(p[0] & 0xff);
- p += 1;
- }
- }
- wPath[i] = (wchar_t)0;
- for (i = 0; mode[i] && i < sizeof(wMode)/sizeof(wchar_t) - 1; ++i) {
- wMode[i] = (wchar_t)(mode[i] & 0xff);
- }
- wMode[i] = (wchar_t)0;
- return _wfopen(wPath, wMode);
- } else {
- for (p = path, i = 0; *p && i < _MAX_PATH; ++i) {
- if ((p[0] & 0xe0) == 0xc0 &&
- p[1] && (p[1] & 0xc0) == 0x80) {
- nPath[i] = (char)(((p[0] & 0x1f) << 6) |
- (p[1] & 0x3f));
- p += 2;
- } else if ((p[0] & 0xf0) == 0xe0 &&
- p[1] && (p[1] & 0xc0) == 0x80 &&
- p[2] && (p[2] & 0xc0) == 0x80) {
- nPath[i] = (char)(((p[1] & 0x3f) << 6) |
- (p[2] & 0x3f));
- p += 3;
- } else {
- nPath[i] = p[0];
- p += 1;
- }
- }
- nPath[i] = '\0';
- return fopen(nPath, mode);
+ fileNameToUCS2(path, wPath, sizeof(wPath) / sizeof(wchar_t));
+ for (i = 0; mode[i] && i < sizeof(wMode)/sizeof(wchar_t) - 1; ++i) {
+ wMode[i] = (wchar_t)(mode[i] & 0xff);
}
+ wMode[i] = (wchar_t)0;
+ readWindowsShortcut(wPath, _MAX_PATH + 1);
+ return _wfopen(wPath, wMode);
#endif /* 0 */
#elif defined(VMS)
return fopen(path, mode, "ctx=stm");
@@ -649,6 +632,62 @@ FILE *openFile(const char *path, const char *mode) {
#endif
}
+#if 0
+#ifdef _WIN32
+void readWindowsShortcut(wchar_t *wPath, size_t wPathSize) {
+ size_t n = wcslen(wPath);
+ if (n < 4 || wcscmp(wPath + n - 4, L".lnk")) {
+ return;
+ }
+ IShellLinkW *shellLink;
+ HRESULT hres;
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ IID_IShellLinkW, (LPVOID *)&shellLink);
+ bool needCoUninit = false;
+ if (hres == CO_E_NOTINITIALIZED) {
+ CoInitialize(NULL);
+ needCoUninit = true;
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ IID_IShellLinkW, (LPVOID *)&shellLink);
+ }
+ if (FAILED(hres)) {
+ return;
+ }
+ IPersistFile *persistFile;
+ hres = shellLink->QueryInterface(IID_IPersistFile, (LPVOID *)&persistFile);
+ if (FAILED(hres)) {
+ return;
+ }
+ hres = persistFile->Load(wPath, STGM_READ);
+ if (FAILED(hres)) {
+ fprintf(stderr, "IPersistFile.Load failed: 0x%08x\n", hres);
+ exit(1);
+ }
+ wchar_t target[_MAX_PATH + 1];
+ hres = shellLink->GetPath(target, _MAX_PATH + 1, NULL, 0);
+ if (FAILED(hres)) {
+ return;
+ }
+ shellLink->Release();
+ if (needCoUninit) {
+ CoUninitialize();
+ }
+ if (wcslen(target) > wPathSize - 1) {
+ return;
+ }
+ wcscpy(wPath, target);
+}
+#endif
+#endif /* 0 */
+
+int makeDir(const char *path, int mode) {
+#ifdef _WIN32
+ return _mkdir(path);
+#else
+ return mkdir(path, (mode_t)mode);
+#endif
+}
+
char *getLine(char *buf, int size, FILE *f) {
int c, i;
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/gfile.h b/Build/source/libs/xpdf/xpdf-src/goo/gfile.h
index c0e75073ed0..2075d731a6c 100644
--- a/Build/source/libs/xpdf/xpdf-src/goo/gfile.h
+++ b/Build/source/libs/xpdf/xpdf-src/goo/gfile.h
@@ -80,13 +80,22 @@ extern GString *fileNameToUTF8(char *path);
// Convert a file name from UCS-2 to UTF-8.
extern GString *fileNameToUTF8(wchar_t *path);
+
+// Convert a file name from UTF-8 to UCS-2. [out] has space for
+// [outSize] wchar_t elements (including the trailing zero). Returns
+// [out].
+extern wchar_t *fileNameToUCS2(const char *path, wchar_t *out, size_t outSize);
#endif
// Open a file. On Windows, this converts the path from UTF-8 to
-// UCS-2 and calls _wfopen (if available). On other OSes, this simply
-// calls fopen.
+// UCS-2 and calls _wfopen(). On other OSes, this simply calls fopen().
extern FILE *openFile(const char *path, const char *mode);
+// Create a directory. On Windows, this converts the path from UTF-8
+// to UCS-2 and calls _wmkdir(), ignoring the mode argument. On other
+// OSes, this simply calls mkdir().
+extern int makeDir(const char *path, int mode);
+
// Just like fgets, but handles Unix, Mac, and/or DOS end-of-line
// conventions.
extern char *getLine(char *buf, int size, FILE *f);
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/gmem.h b/Build/source/libs/xpdf/xpdf-src/goo/gmem.h
index 87a32f0324c..f131c176f8b 100644
--- a/Build/source/libs/xpdf/xpdf-src/goo/gmem.h
+++ b/Build/source/libs/xpdf/xpdf-src/goo/gmem.h
@@ -32,10 +32,6 @@ public:
#endif // USE_EXCEPTIONS
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
* Same as malloc, but prints error message and exits if malloc()
* returns NULL.
@@ -96,8 +92,4 @@ extern void gMemReport(FILE *f);
*/
extern char *copyString(const char *s);
-#ifdef __cplusplus
-}
-#endif
-
#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/Splash.cc b/Build/source/libs/xpdf/xpdf-src/splash/Splash.cc
index 44f87a9dcd2..343db796fb2 100644
--- a/Build/source/libs/xpdf/xpdf-src/splash/Splash.cc
+++ b/Build/source/libs/xpdf/xpdf-src/splash/Splash.cc
@@ -2,7 +2,7 @@
//
// Splash.cc
//
-// Copyright 2003-2013 Glyph & Cog, LLC
+// Copyright 2003-2020 Glyph & Cog, LLC
//
//========================================================================
@@ -80,6 +80,12 @@ struct SplashPipe {
Guchar aInput;
SplashColor cSrcVal;
+ // source overprint mask
+ //~ this is a kludge - this pointer should be passed as an arg to the
+ //~ pipeRun function, but that would require passing in a lot of
+ //~ null pointers, since it's rarely used
+ Guint *srcOverprintMaskPtr;
+
// special cases and result color
GBool noTransparency;
GBool shapeOnly;
@@ -163,7 +169,7 @@ inline void Splash::updateModY(int y) {
inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern,
Guchar aInput, GBool usesShape,
- GBool nonIsolatedGroup) {
+ GBool nonIsolatedGroup, GBool usesSrcOverprint) {
SplashColorMode mode;
mode = bitmap->mode;
@@ -181,6 +187,9 @@ inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern,
// source alpha
pipe->aInput = aInput;
+ // source overprint mask
+ pipe->srcOverprintMaskPtr = NULL;
+
// special cases
pipe->noTransparency = aInput == 255 &&
!state->softMask &&
@@ -212,7 +221,9 @@ inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern,
// select the 'run' function
pipe->run = &Splash::pipeRun;
- if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) {
+ if (overprintMaskBitmap || usesSrcOverprint) {
+ // use Splash::pipeRun
+ } else if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) {
if (mode == splashModeMono1 && !bitmap->alpha) {
pipe->run = &Splash::pipeRunSimpleMono1;
} else if (mode == splashModeMono8 && bitmap->alpha) {
@@ -239,6 +250,9 @@ inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern,
} else if (mode == splashModeCMYK8 && bitmap->alpha) {
pipe->run = &Splash::pipeRunShapeCMYK8;
#endif
+ } else if (mode == splashModeMono8 && !bitmap->alpha) {
+ // this is used when drawing soft-masked images
+ pipe->run = &Splash::pipeRunShapeNoAlphaMono8;
}
} else if (!pipe->pattern && !pipe->noTransparency && !state->softMask &&
usesShape &&
@@ -258,6 +272,26 @@ inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern,
pipe->run = &Splash::pipeRunAACMYK8;
#endif
}
+ } else if (!pipe->pattern &&
+ aInput == 255 &&
+ state->softMask &&
+ usesShape &&
+ !state->inNonIsolatedGroup &&
+ !state->inKnockoutGroup &&
+ !nonIsolatedGroup &&
+ state->overprintMask == 0xffffffff &&
+ !state->blendFunc) {
+ if (mode == splashModeMono8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSoftMaskMono8;
+ } else if (mode == splashModeRGB8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSoftMaskRGB8;
+ } else if (mode == splashModeBGR8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSoftMaskBGR8;
+#if SPLASH_CMYK
+ } else if (mode == splashModeCMYK8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSoftMaskCMYK8;
+#endif
+ }
} else if (!pipe->pattern && !pipe->noTransparency && !state->softMask &&
usesShape &&
state->inNonIsolatedGroup && groupBackBitmap->alpha &&
@@ -292,6 +326,8 @@ void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y,
Guchar color0Mask;
Guchar *alpha0Ptr;
SplashColorPtr softMaskPtr;
+ Guint overprintMask;
+ Guint *overprintMaskPtr;
#if SPLASH_CMYK
Guchar aPrev;
SplashColor cSrc2, cDest2;
@@ -313,6 +349,9 @@ void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y,
}
cSrcPtr += cSrcStride;
++shapePtr2;
+ if (pipe->srcOverprintMaskPtr) {
+ ++pipe->srcOverprintMaskPtr;
+ }
}
} else {
shapeVal = 0xff;
@@ -326,6 +365,8 @@ void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
if (bitmap->mode == splashModeMono1) {
destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
destColorMask = (Guchar)(0x80 >> (x0 & 7));
@@ -367,6 +408,11 @@ void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y,
} else {
alpha0Ptr = NULL;
}
+ if (overprintMaskBitmap) {
+ overprintMaskPtr = overprintMaskBitmap + y * bitmap->width + x0;
+ } else {
+ overprintMaskPtr = NULL;
+ }
for (x = x0; x <= x1; ++x) {
@@ -399,6 +445,12 @@ void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y,
}
cSrcPtr += cSrcStride;
shapePtr2 += shapeStride;
+ if (pipe->srcOverprintMaskPtr) {
+ ++pipe->srcOverprintMaskPtr;
+ }
+ if (overprintMaskPtr) {
+ ++overprintMaskPtr;
+ }
continue;
}
lastX = x;
@@ -519,6 +571,15 @@ void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y,
//----- read source color; handle overprint
+ if (pipe->srcOverprintMaskPtr) {
+ overprintMask = *pipe->srcOverprintMaskPtr++;
+ } else {
+ overprintMask = state->overprintMask;
+ }
+ if (overprintMaskPtr) {
+ *overprintMaskPtr++ |= overprintMask;
+ }
+
switch (bitmap->mode) {
case splashModeMono1:
case splashModeMono8:
@@ -545,22 +606,22 @@ void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y,
aPrev = aDest;
}
}
- if (state->overprintMask & 0x01) {
+ if (overprintMask & 0x01) {
cSrc[0] = state->cmykTransferC[cSrcPtr[0]];
} else {
cSrc[0] = div255(aPrev * cDest[0]);
}
- if (state->overprintMask & 0x02) {
+ if (overprintMask & 0x02) {
cSrc[1] = state->cmykTransferM[cSrcPtr[1]];
} else {
cSrc[1] = div255(aPrev * cDest[1]);
}
- if (state->overprintMask & 0x04) {
+ if (overprintMask & 0x04) {
cSrc[2] = state->cmykTransferY[cSrcPtr[2]];
} else {
cSrc[2] = div255(aPrev * cDest[2]);
}
- if (state->overprintMask & 0x08) {
+ if (overprintMask & 0x08) {
cSrc[3] = state->cmykTransferK[cSrcPtr[3]];
} else {
cSrc[3] = div255(aPrev * cDest[3]);
@@ -862,6 +923,8 @@ void Splash::pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y,
updateModX(x1);
updateModY(y);
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
destColorMask = (Guchar)(0x80 >> (x0 & 7));
@@ -905,6 +968,8 @@ void Splash::pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y,
updateModX(x1);
updateModY(y);
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -940,6 +1005,8 @@ void Splash::pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y,
updateModX(x1);
updateModY(y);
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -978,6 +1045,8 @@ void Splash::pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y,
updateModX(x1);
updateModY(y);
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -1017,6 +1086,8 @@ void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y,
updateModX(x1);
updateModY(y);
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -1067,6 +1138,8 @@ void Splash::pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
destColorMask = (Guchar)(0x80 >> (x0 & 7));
@@ -1150,6 +1223,8 @@ void Splash::pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -1243,6 +1318,8 @@ void Splash::pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -1349,6 +1426,8 @@ void Splash::pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -1456,6 +1535,8 @@ void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -1480,26 +1561,10 @@ void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y,
aDest = *destAlphaPtr;
//----- overprint
- if (state->overprintMask & 1) {
- cSrc0 = state->cmykTransferC[cSrcPtr[0]];
- } else {
- cSrc0 = div255(aDest * cDest0);
- }
- if (state->overprintMask & 2) {
- cSrc1 = state->cmykTransferM[cSrcPtr[1]];
- } else {
- cSrc1 = div255(aDest * cDest1);
- }
- if (state->overprintMask & 4) {
- cSrc2 = state->cmykTransferY[cSrcPtr[2]];
- } else {
- cSrc2 = div255(aDest * cDest2);
- }
- if (state->overprintMask & 8) {
- cSrc3 = state->cmykTransferK[cSrcPtr[3]];
- } else {
- cSrc3 = div255(aDest * cDest3);
- }
+ cSrc0 = state->cmykTransferC[cSrcPtr[0]];
+ cSrc1 = state->cmykTransferM[cSrcPtr[1]];
+ cSrc2 = state->cmykTransferY[cSrcPtr[2]];
+ cSrc3 = state->cmykTransferK[cSrcPtr[3]];
//----- source alpha
aSrc = shape;
@@ -1552,6 +1617,80 @@ void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y,
// special case:
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeMono8 && !bitmap->alpha
+void Splash::pipeRunShapeNoAlphaMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr,
+ SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ ++destColorPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- source color
+ cSrc0 = state->grayTransfer[cSrcPtr[0]];
+
+ //----- source alpha
+ aSrc = shape;
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ cResult0 = cSrc0;
+ } else {
+
+ //----- read destination pixel
+ cDest0 = *destColorPtr;
+
+ //----- result color
+ cResult0 = div255((255 - aSrc) * cDest0 + aSrc * cSrc0);
+ }
+
+ //----- write destination pixel
+ *destColorPtr++ = cResult0;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
// !pipe->nonIsolatedGroup &&
@@ -1584,6 +1723,8 @@ void Splash::pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
destColorMask = (Guchar)(0x80 >> (x0 & 7));
@@ -1663,6 +1804,8 @@ void Splash::pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -1746,6 +1889,8 @@ void Splash::pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -1840,6 +1985,8 @@ void Splash::pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -1935,6 +2082,8 @@ void Splash::pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -2017,6 +2166,457 @@ void Splash::pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y,
#endif
+// special case:
+// !pipe->pattern && aInput == 255 && state->softMask && usesShape &&
+// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
+// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
+// !state->blendFunc &&
+// bitmap->mode == splashModeMono8 && bitmap->alpha
+void Splash::pipeRunSoftMaskMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ SplashColorPtr softMaskPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ ++destColorPtr;
+ ++destAlphaPtr;
+ ++softMaskPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read source color
+ cSrc0 = state->grayTransfer[cSrcPtr[0]];
+
+ //----- source alpha
+ aSrc = div255(*softMaskPtr++ * shape);
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ aResult = 255;
+ cResult0 = cSrc0;
+ } else {
+
+ //----- read destination alpha
+ aDest = *destAlphaPtr;
+
+ //----- special case for aDest = 0
+ if (aDest == 0) {
+ aResult = aSrc;
+ cResult0 = cSrc0;
+ } else {
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ }
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ ++destColorPtr;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && aInput == 255 && state->softMask && usesShape &&
+// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
+// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
+// !state->blendFunc &&
+// bitmap->mode == splashModeRGB8 && bitmap->alpha
+void Splash::pipeRunSoftMaskRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ SplashColorPtr softMaskPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ ++softMaskPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = div255(*softMaskPtr++ * shape);
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ aResult = 255;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ } else {
+
+ //----- read destination alpha
+ aDest = *destAlphaPtr;
+
+ //----- special case for aDest = 0
+ if (aDest == 0) {
+ aResult = aSrc;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ } else {
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ }
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && aInput == 255 && state->softMask && usesShape &&
+// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
+// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
+// !state->blendFunc &&
+// bitmap->mode == splashModeBGR8 && bitmap->alpha
+void Splash::pipeRunSoftMaskBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ SplashColorPtr softMaskPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ ++softMaskPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = div255(*softMaskPtr++ * shape);
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ aResult = 255;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ } else {
+
+ //----- read destination alpha
+ aDest = *destAlphaPtr;
+
+ //----- special case for aDest = 0
+ if (aDest == 0) {
+ aResult = aSrc;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ } else {
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[2];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[0];
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ }
+ }
+
+ //----- write destination pixel
+ destColorPtr[2] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[0] = cResult2;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+#if SPLASH_CMYK
+// special case:
+// !pipe->pattern && aInput == 255 && state->softMask && usesShape &&
+// !state->inNonIsolatedGroup && !state->inKnockoutGroup &&
+// !nonIsolatedGroup && state->overprintMask == 0xffffffff &&
+// !state->blendFunc &&
+// bitmap->mode == splashModeCMYK8 && bitmap->alpha
+void Splash::pipeRunSoftMaskCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult;
+ Guchar cSrc0, cSrc1, cSrc2, cSrc3;
+ Guchar cDest0, cDest1, cDest2, cDest3;
+ Guchar cResult0, cResult1, cResult2, cResult3;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ SplashColorPtr softMaskPtr;
+ int cSrcStride, x, lastX;
+
+ if (cSrcPtr) {
+ cSrcStride = 4;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 4];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 4;
+ ++destAlphaPtr;
+ ++softMaskPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ cDest3 = destColorPtr[3];
+
+ //----- read destination alpha
+ aDest = *destAlphaPtr;
+
+ //----- overprint
+ cSrc0 = state->cmykTransferC[cSrcPtr[0]];
+ cSrc1 = state->cmykTransferM[cSrcPtr[1]];
+ cSrc2 = state->cmykTransferY[cSrcPtr[2]];
+ cSrc3 = state->cmykTransferK[cSrcPtr[3]];
+
+ //----- source alpha
+ aSrc = div255(*softMaskPtr++ * shape);
+
+ //----- special case for aSrc = 255
+ if (aSrc == 255) {
+ aResult = 255;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ cResult3 = cSrc3;
+ } else {
+
+ //----- special case for aDest = 0
+ if (aDest == 0) {
+ aResult = aSrc;
+ cResult0 = cSrc0;
+ cResult1 = cSrc1;
+ cResult2 = cSrc2;
+ cResult3 = cSrc3;
+ } else {
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI);
+ cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI);
+ }
+ }
+
+ //----- write destination pixel
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr[3] = cResult3;
+ destColorPtr += 4;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+#endif
+
+
void Splash::pipeRunNonIsoMono8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr) {
Guchar shape, aSrc, aDest, alphaI, alpha0, aResult;
@@ -2046,6 +2646,8 @@ void Splash::pipeRunNonIsoMono8(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
@@ -2130,6 +2732,8 @@ void Splash::pipeRunNonIsoRGB8(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
@@ -2225,6 +2829,8 @@ void Splash::pipeRunNonIsoBGR8(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 3];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
@@ -2321,6 +2927,8 @@ void Splash::pipeRunNonIsoCMYK8(SplashPipe *pipe, int x0, int x1, int y,
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * 4];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
@@ -2409,6 +3017,84 @@ void Splash::pipeRunNonIsoCMYK8(SplashPipe *pipe, int x0, int x1, int y,
#endif
+void Splash::useDestRow(int y) {
+ int y0, y1, yy;
+
+ if (groupDestInitMode == splashGroupDestPreInit) {
+ return;
+ }
+ if (groupDestInitYMin > groupDestInitYMax) {
+ y0 = y1 = y;
+ groupDestInitYMin = groupDestInitYMax = y;
+ } else if (y < groupDestInitYMin) {
+ y0 = y;
+ y1 = groupDestInitYMin - 1;
+ groupDestInitYMin = y;
+ } else if (y > groupDestInitYMax) {
+ y0 = groupDestInitYMax + 1;
+ y1 = y;
+ groupDestInitYMax = y;
+ } else {
+ return;
+ }
+ for (yy = y0; yy <= y1; ++yy) {
+ if (groupDestInitMode == splashGroupDestInitZero) {
+ // same as clear(color=0, alpha=0)
+ memset(bitmap->data + bitmap->rowSize * yy, 0,
+ bitmap->rowSize < 0 ? -bitmap->rowSize : bitmap->rowSize);
+ if (bitmap->alpha) {
+ memset(bitmap->alpha + bitmap->alphaRowSize * yy, 0,
+ bitmap->alphaRowSize);
+ }
+ } else { // (groupDestInitMode == splashGroupDestInitCopy)
+ // same as blitTransparent
+ copyGroupBackdropRow(yy);
+ }
+ }
+}
+
+void Splash::copyGroupBackdropRow(int y) {
+ SplashColorPtr p, q;
+ Guchar mask, srcMask;
+ int x;
+
+ if (groupBackBitmap->mode != bitmap->mode) {
+ return;
+ }
+
+ if (bitmap->mode == splashModeMono1) {
+ p = &bitmap->data[y * bitmap->rowSize];
+ mask = (Guchar)0x80;
+ q = &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize
+ + (groupBackX >> 3)];
+ srcMask = (Guchar)(0x80 >> (groupBackX & 7));
+ for (x = 0; x < bitmap->width; ++x) {
+ if (*q & srcMask) {
+ *p |= mask;
+ } else {
+ *p &= (Guchar)~mask;
+ }
+ if (!(mask = (Guchar)(mask >> 1))) {
+ mask = 0x80;
+ ++p;
+ }
+ if (!(srcMask = (Guchar)(srcMask >> 1))) {
+ srcMask = 0x80;
+ ++q;
+ }
+ }
+ } else {
+ p = &bitmap->data[y * bitmap->rowSize];
+ q = &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize
+ + bitmapComps * groupBackX];
+ memcpy(p, q, bitmapComps * bitmap->width);
+ }
+
+ if (bitmap->alpha) {
+ memset(&bitmap->alpha[y * bitmap->alphaRowSize], 0, bitmap->width);
+ }
+}
+
//------------------------------------------------------------------------
// Transform a point from user space to device space.
@@ -2428,7 +3114,13 @@ inline void Splash::transform(SplashCoord *matrix,
SplashImageCache::SplashImageCache() {
tag = NULL;
- image = NULL;
+ width = 0;
+ height = 0;
+ mode = splashModeRGB8;
+ alpha = gFalse;
+ interpolate = gFalse;
+ colorData = NULL;
+ alphaData = NULL;
refCount = 1;
}
@@ -2436,9 +3128,39 @@ SplashImageCache::~SplashImageCache() {
if (tag) {
delete tag;
}
- if (image) {
- delete image;
+ gfree(colorData);
+ gfree(alphaData);
+}
+
+GBool SplashImageCache::match(GString *aTag, int aWidth, int aHeight,
+ SplashColorMode aMode, GBool aAlpha,
+ GBool aInterpolate) {
+ return aTag && tag && !aTag->cmp(tag) &&
+ aWidth == width && aHeight == height &&
+ aMode == mode && aAlpha == alpha &&
+ aInterpolate == interpolate;
+}
+
+void SplashImageCache::reset(GString *aTag, int aWidth, int aHeight,
+ SplashColorMode aMode, GBool aAlpha,
+ GBool aInterpolate) {
+ if (tag) {
+ delete tag;
}
+ if (aTag) {
+ tag = aTag->copy();
+ } else {
+ tag = NULL;
+ }
+ width = aWidth;
+ height = aHeight;
+ mode = aMode;
+ alpha = aAlpha;
+ interpolate = aInterpolate;
+ gfree(colorData);
+ colorData = NULL;
+ gfree(alphaData);
+ alphaData = NULL;
}
void SplashImageCache::incRefCount() {
@@ -2453,6 +3175,1172 @@ void SplashImageCache::decRefCount() {
}
//------------------------------------------------------------------------
+// ImageScaler
+//------------------------------------------------------------------------
+
+// Abstract base class.
+class ImageScaler {
+public:
+
+ ImageScaler() {}
+ virtual ~ImageScaler() {}
+
+ // Compute the next line of the scaled image. This can be called up
+ // to [scaledHeight] times.
+ virtual void nextLine() = 0;
+
+ // Retrieve the color and alpha data generated by the most recent
+ // call to nextLine().
+ virtual Guchar *colorData() = 0;
+ virtual Guchar *alphaData() = 0;
+};
+
+//------------------------------------------------------------------------
+// BasicImageScaler
+//------------------------------------------------------------------------
+
+// The actual image scaler.
+class BasicImageScaler: public ImageScaler {
+public:
+
+ BasicImageScaler(SplashImageSource aSrc, void *aSrcData,
+ int aSrcWidth, int aSrcHeight, int aNComps, GBool aHasAlpha,
+ int aScaledWidth, int aScaledHeight, GBool aInterpolate);
+ virtual ~BasicImageScaler();
+ virtual void nextLine();
+ virtual Guchar *colorData() { return colorLine; }
+ virtual Guchar *alphaData() { return alphaLine; }
+
+protected:
+
+ void vertDownscaleHorizDownscale();
+ void vertDownscaleHorizUpscaleNoInterp();
+ void vertDownscaleHorizUpscaleInterp();
+ void vertUpscaleHorizDownscaleNoInterp();
+ void vertUpscaleHorizDownscaleInterp();
+ void vertUpscaleHorizUpscaleNoInterp();
+ void vertUpscaleHorizUpscaleInterp();
+
+ // source image data function
+ SplashImageSource src;
+ void *srcData;
+
+ // source image size
+ int srcWidth;
+ int srcHeight;
+
+ // scaled image size
+ int scaledWidth;
+ int scaledHeight;
+
+ // number of color and alpha components
+ int nComps;
+ GBool hasAlpha;
+
+ // params/state for vertical scaling
+ int yp, yq;
+ int yt, yn;
+ int ySrcCur, yScaledCur;
+ SplashCoord yInvScale;
+
+ // params for horizontal scaling
+ int xp, xq;
+ SplashCoord xInvScale;
+
+ // scaling function
+ void (BasicImageScaler::*scalingFunc)();
+
+ // temporary buffers for vertical scaling
+ Guchar *colorTmpBuf0;
+ Guchar *colorTmpBuf1;
+ Guchar *colorTmpBuf2;
+ Guchar *alphaTmpBuf0;
+ Guchar *alphaTmpBuf1;
+ Guchar *alphaTmpBuf2;
+ Guint *colorAccBuf;
+ Guint *alphaAccBuf;
+
+ // output of horizontal scaling
+ Guchar *colorLine;
+ Guchar *alphaLine;
+};
+
+BasicImageScaler::BasicImageScaler(SplashImageSource aSrc, void *aSrcData,
+ int aSrcWidth, int aSrcHeight,
+ int aNComps, GBool aHasAlpha,
+ int aScaledWidth, int aScaledHeight,
+ GBool aInterpolate) {
+ colorTmpBuf0 = NULL;
+ colorTmpBuf1 = NULL;
+ colorTmpBuf2 = NULL;
+ alphaTmpBuf0 = NULL;
+ alphaTmpBuf1 = NULL;
+ alphaTmpBuf2 = NULL;
+ colorAccBuf = NULL;
+ alphaAccBuf = NULL;
+ colorLine = NULL;
+ alphaLine = NULL;
+
+ src = aSrc;
+ srcData = aSrcData;
+ srcWidth = aSrcWidth;
+ srcHeight = aSrcHeight;
+ scaledWidth = aScaledWidth;
+ scaledHeight = aScaledHeight;
+ nComps = aNComps;
+ hasAlpha = aHasAlpha;
+
+ // select scaling function; allocate buffers
+ if (scaledHeight <= srcHeight) {
+ // vertical downscaling
+ yp = srcHeight / scaledHeight;
+ yq = srcHeight % scaledHeight;
+ yt = 0;
+ colorTmpBuf0 = (Guchar *)gmallocn(srcWidth, nComps);
+ colorAccBuf = (Guint *)gmallocn(srcWidth, nComps * (int)sizeof(Guint));
+ if (hasAlpha) {
+ alphaTmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ alphaAccBuf = (Guint *)gmallocn(srcWidth, sizeof(Guint));
+ }
+ if (scaledWidth <= srcWidth) {
+ scalingFunc = &BasicImageScaler::vertDownscaleHorizDownscale;
+ } else {
+ if (aInterpolate) {
+ scalingFunc = &BasicImageScaler::vertDownscaleHorizUpscaleInterp;
+ } else {
+ scalingFunc = &BasicImageScaler::vertDownscaleHorizUpscaleNoInterp;
+ }
+ }
+ } else {
+ // vertical upscaling
+ yp = scaledHeight / srcHeight;
+ yq = scaledHeight % srcHeight;
+ yt = 0;
+ yn = 0;
+ if (aInterpolate) {
+ yInvScale = (SplashCoord)srcHeight / (SplashCoord)scaledHeight;
+ colorTmpBuf0 = (Guchar *)gmallocn(srcWidth, nComps);
+ colorTmpBuf1 = (Guchar *)gmallocn(srcWidth, nComps);
+ if (hasAlpha) {
+ alphaTmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ alphaTmpBuf1 = (Guchar *)gmalloc(srcWidth);
+ }
+ ySrcCur = 0;
+ yScaledCur = 0;
+ if (scaledWidth <= srcWidth) {
+ scalingFunc = &BasicImageScaler::vertUpscaleHorizDownscaleInterp;
+ } else {
+ colorTmpBuf2 = (Guchar *)gmallocn(srcWidth, nComps);
+ if (hasAlpha) {
+ alphaTmpBuf2 = (Guchar *)gmalloc(srcWidth);
+ }
+ scalingFunc = &BasicImageScaler::vertUpscaleHorizUpscaleInterp;
+ }
+ } else {
+ colorTmpBuf0 = (Guchar *)gmallocn(srcWidth, nComps);
+ if (hasAlpha) {
+ alphaTmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ }
+ if (scaledWidth <= srcWidth) {
+ scalingFunc = &BasicImageScaler::vertUpscaleHorizDownscaleNoInterp;
+ } else {
+ scalingFunc = &BasicImageScaler::vertUpscaleHorizUpscaleNoInterp;
+ }
+ }
+ }
+ if (scaledWidth <= srcWidth) {
+ xp = srcWidth / scaledWidth;
+ xq = srcWidth % scaledWidth;
+ } else {
+ xp = scaledWidth / srcWidth;
+ xq = scaledWidth % srcWidth;
+ if (aInterpolate) {
+ xInvScale = (SplashCoord)srcWidth / (SplashCoord)scaledWidth;
+ }
+ }
+ colorLine = (Guchar *)gmallocn(scaledWidth, nComps);
+ if (hasAlpha) {
+ alphaLine = (Guchar *)gmalloc(scaledWidth);
+ }
+}
+
+BasicImageScaler::~BasicImageScaler() {
+ gfree(colorTmpBuf0);
+ gfree(colorTmpBuf1);
+ gfree(colorTmpBuf2);
+ gfree(alphaTmpBuf0);
+ gfree(alphaTmpBuf1);
+ gfree(alphaTmpBuf2);
+ gfree(colorAccBuf);
+ gfree(alphaAccBuf);
+ gfree(colorLine);
+ gfree(alphaLine);
+}
+
+void BasicImageScaler::nextLine() {
+ (this->*scalingFunc)();
+}
+
+void BasicImageScaler::vertDownscaleHorizDownscale() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(colorAccBuf, 0, (srcWidth * nComps) * sizeof(Guint));
+ if (hasAlpha) {
+ memset(alphaAccBuf, 0, srcWidth * sizeof(Guint));
+ }
+ int nRowComps = srcWidth * nComps;
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ for (int j = 0; j < nRowComps; ++j) {
+ colorAccBuf[j] += colorTmpBuf0[j];
+ }
+ if (hasAlpha) {
+ for (int j = 0; j < srcWidth; ++j) {
+ alphaAccBuf[j] += alphaTmpBuf0[j];
+ }
+ }
+ }
+
+ //--- horiz downscale
+ int colorAcc[splashMaxColorComps];
+ int xt = 0;
+ int unscaledColorIdx = 0;
+ int unscaledAlphaIdx = 0;
+ int scaledColorIdx = 0;
+
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ for (int j = 0; j < nComps; ++j) {
+ colorAcc[j] = 0;
+ }
+ for (int i = 0; i < xStep; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ colorAcc[j] += colorAccBuf[unscaledColorIdx + j];
+ }
+ unscaledColorIdx += nComps;
+ }
+ int nPixels = yStep * xStep;
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] = (Guchar)(colorAcc[j] / nPixels);
+ }
+ scaledColorIdx += nComps;
+
+ if (hasAlpha) {
+ int alphaAcc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ alphaAcc += alphaAccBuf[unscaledAlphaIdx];
+ ++unscaledAlphaIdx;
+ }
+ alphaLine[scaledIdx] = (Guchar)(alphaAcc / nPixels);
+ }
+ }
+}
+
+void BasicImageScaler::vertDownscaleHorizUpscaleNoInterp() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(colorAccBuf, 0, (srcWidth * nComps) * sizeof(Guint));
+ if (hasAlpha) {
+ memset(alphaAccBuf, 0, srcWidth * sizeof(Guint));
+ }
+ int nRowComps = srcWidth * nComps;
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ for (int j = 0; j < nRowComps; ++j) {
+ colorAccBuf[j] += colorTmpBuf0[j];
+ }
+ if (hasAlpha) {
+ for (int j = 0; j < srcWidth; ++j) {
+ alphaAccBuf[j] += alphaTmpBuf0[j];
+ }
+ }
+ }
+
+ //--- horiz upscale
+ Guchar colorBuf[splashMaxColorComps];
+ int xt = 0;
+ int scaledColorIdx = 0;
+ int srcColorIdx = 0;
+ int scaledAlphaIdx = 0;
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= srcWidth) {
+ xt -= srcWidth;
+ ++xStep;
+ }
+ for (int j = 0; j < nComps; ++j) {
+ colorBuf[j] = (Guchar)(colorAccBuf[srcColorIdx + j] / yStep);
+ }
+ srcColorIdx += nComps;
+ for (int i = 0; i < xStep; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] = colorBuf[j];
+ }
+ scaledColorIdx += nComps;
+ }
+ if (hasAlpha) {
+ Guchar alphaBuf = (Guchar)(alphaAccBuf[srcIdx] / yStep);
+ for (int i = 0; i < xStep; ++i) {
+ alphaLine[scaledAlphaIdx] = alphaBuf;
+ ++scaledAlphaIdx;
+ }
+ }
+ }
+}
+
+void BasicImageScaler::vertDownscaleHorizUpscaleInterp() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(colorAccBuf, 0, (srcWidth * nComps) * sizeof(Guint));
+ if (hasAlpha) {
+ memset(alphaAccBuf, 0, srcWidth * sizeof(Guint));
+ }
+ int nRowComps = srcWidth * nComps;
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ for (int j = 0; j < nRowComps; ++j) {
+ colorAccBuf[j] += colorTmpBuf0[j];
+ }
+ if (hasAlpha) {
+ for (int j = 0; j < srcWidth; ++j) {
+ alphaAccBuf[j] += alphaTmpBuf0[j];
+ }
+ }
+ }
+ for (int j = 0; j < srcWidth * nComps; ++j) {
+ colorAccBuf[j] /= yStep;
+ }
+ if (hasAlpha) {
+ for (int j = 0; j < srcWidth; ++j) {
+ alphaAccBuf[j] /= yStep;
+ }
+ }
+
+ //--- horiz upscale
+ int scaledColorIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ SplashCoord s0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord s1 = (SplashCoord)1 - s0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= srcWidth) {
+ x1 = srcWidth - 1;
+ }
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] =
+ (Guchar)(int)(s0 * colorAccBuf[x0 * nComps + j] +
+ s1 * colorAccBuf[x1 * nComps + j]);
+ }
+ scaledColorIdx += nComps;
+ if (hasAlpha) {
+ alphaLine[scaledIdx] = (Guchar)(int)(s0 * alphaAccBuf[x0] +
+ s1 * alphaAccBuf[x1]);
+ }
+ }
+}
+
+void BasicImageScaler::vertUpscaleHorizDownscaleNoInterp() {
+ //--- vert upscale
+ if (yn == 0) {
+ yn = yp;
+ yt += yq;
+ if (yt >= srcHeight) {
+ yt -= srcHeight;
+ ++yn;
+ }
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ }
+ --yn;
+
+ //--- horiz downscale
+ int colorAcc[splashMaxColorComps];
+ int xt = 0;
+ int unscaledColorIdx = 0;
+ int unscaledAlphaIdx = 0;
+ int scaledColorIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ for (int j = 0; j < nComps; ++j) {
+ colorAcc[j] = 0;
+ }
+ for (int i = 0; i < xStep; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ colorAcc[j] += colorTmpBuf0[unscaledColorIdx + j];
+ }
+ unscaledColorIdx += nComps;
+ }
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] = (Guchar)(colorAcc[j] / xStep);
+ }
+ scaledColorIdx += nComps;
+
+ if (hasAlpha) {
+ int alphaAcc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ alphaAcc += alphaTmpBuf0[unscaledAlphaIdx];
+ ++unscaledAlphaIdx;
+ }
+ alphaLine[scaledIdx] = (Guchar)(alphaAcc / xStep);
+ }
+ }
+}
+
+void BasicImageScaler::vertUpscaleHorizDownscaleInterp() {
+ //--- vert upscale
+ if (ySrcCur == 0) {
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ (*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
+ ySrcCur = 1;
+ }
+ SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ SplashCoord vs0 = (SplashCoord)y1 + 0.5 - ys;
+ SplashCoord vs1 = (SplashCoord)1 - vs0;
+ if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
+ Guchar *t = colorTmpBuf0;
+ colorTmpBuf0 = colorTmpBuf1;
+ colorTmpBuf1 = t;
+ if (hasAlpha) {
+ t = alphaTmpBuf0;
+ alphaTmpBuf0 = alphaTmpBuf1;
+ alphaTmpBuf1 = t;
+ }
+ (*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
+ ++ySrcCur;
+ }
+ Guchar *color0 = colorTmpBuf0;
+ Guchar *color1 = colorTmpBuf1;
+ Guchar *alpha0 = alphaTmpBuf0;
+ Guchar *alpha1 = alphaTmpBuf1;
+ if (y0 < 0) {
+ y0 = 0;
+ color1 = color0;
+ alpha1 = alpha0;
+ }
+ if (y1 >= srcHeight) {
+ y1 = srcHeight - 1;
+ color0 = color1;
+ alpha0 = alpha1;
+ }
+ ++yScaledCur;
+
+ //--- horiz downscale
+ int colorAcc[splashMaxColorComps];
+ int xt = 0;
+ int unscaledColorIdx = 0;
+ int unscaledAlphaIdx = 0;
+ int scaledColorIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ for (int j = 0; j < nComps; ++j) {
+ colorAcc[j] = 0;
+ }
+ for (int i = 0; i < xStep; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ colorAcc[j] += (int)(vs0 * (int)color0[unscaledColorIdx + j] +
+ vs1 * (int)color1[unscaledColorIdx + j]);
+ }
+ unscaledColorIdx += nComps;
+ }
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] = (Guchar)(colorAcc[j] / xStep);
+ }
+ scaledColorIdx += nComps;
+
+ if (hasAlpha) {
+ int alphaAcc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ alphaAcc += (int)(vs0 * (int)alpha0[unscaledAlphaIdx] +
+ vs1 * (int)alpha1[unscaledAlphaIdx]);
+ ++unscaledAlphaIdx;
+ }
+ alphaLine[scaledIdx] = (Guchar)(alphaAcc / xStep);
+ }
+ }
+}
+
+void BasicImageScaler::vertUpscaleHorizUpscaleNoInterp() {
+ //--- vert upscale
+ if (yn == 0) {
+ yn = yp;
+ yt += yq;
+ if (yt >= srcHeight) {
+ yt -= srcHeight;
+ ++yn;
+ }
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ }
+ --yn;
+
+ //--- horiz upscale
+ int xt = 0;
+ int scaledColorIdx = 0;
+ int srcColorIdx = 0;
+ int scaledAlphaIdx = 0;
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= srcWidth) {
+ xt -= srcWidth;
+ ++xStep;
+ }
+ for (int i = 0; i < xStep; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] = colorTmpBuf0[srcColorIdx + j];
+ }
+ scaledColorIdx += nComps;
+ }
+ srcColorIdx += nComps;
+ if (hasAlpha) {
+ Guchar alphaBuf = alphaTmpBuf0[srcIdx];
+ for (int i = 0; i < xStep; ++i) {
+ alphaLine[scaledAlphaIdx] = alphaBuf;
+ ++scaledAlphaIdx;
+ }
+ }
+ }
+}
+
+void BasicImageScaler::vertUpscaleHorizUpscaleInterp() {
+ //--- vert upscale
+ if (ySrcCur == 0) {
+ (*src)(srcData, colorTmpBuf0, alphaTmpBuf0);
+ (*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
+ ySrcCur = 1;
+ }
+ SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ SplashCoord vs0 = (SplashCoord)y1 + 0.5 - ys;
+ SplashCoord vs1 = (SplashCoord)1 - vs0;
+ if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
+ Guchar *t = colorTmpBuf0;
+ colorTmpBuf0 = colorTmpBuf1;
+ colorTmpBuf1 = t;
+ if (hasAlpha) {
+ t = alphaTmpBuf0;
+ alphaTmpBuf0 = alphaTmpBuf1;
+ alphaTmpBuf1 = t;
+ }
+ (*src)(srcData, colorTmpBuf1, alphaTmpBuf1);
+ ++ySrcCur;
+ }
+ Guchar *color0 = colorTmpBuf0;
+ Guchar *color1 = colorTmpBuf1;
+ Guchar *alpha0 = alphaTmpBuf0;
+ Guchar *alpha1 = alphaTmpBuf1;
+ if (y0 < 0) {
+ y0 = 0;
+ color1 = color0;
+ alpha1 = alpha0;
+ }
+ if (y1 >= srcHeight) {
+ y1 = srcHeight - 1;
+ color0 = color1;
+ alpha0 = alpha1;
+ }
+ ++yScaledCur;
+ for (int srcIdx = 0; srcIdx < srcWidth * nComps; ++srcIdx) {
+ colorTmpBuf2[srcIdx] = (Guchar)(int)(vs0 * (int)color0[srcIdx] +
+ vs1 * (int)color1[srcIdx]);
+ }
+ if (hasAlpha) {
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ alphaTmpBuf2[srcIdx] = (Guchar)(int)(vs0 * (int)alpha0[srcIdx] +
+ vs1 * (int)alpha1[srcIdx]);
+ }
+ }
+
+ //--- horiz upscale
+ int scaledColorIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ SplashCoord hs0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord hs1 = (SplashCoord)1 - hs0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= srcWidth) {
+ x1 = srcWidth - 1;
+ }
+ for (int j = 0; j < nComps; ++j) {
+ colorLine[scaledColorIdx + j] =
+ (Guchar)(int)(hs0 * (int)colorTmpBuf2[x0 * nComps + j] +
+ hs1 * (int)colorTmpBuf2[x1 * nComps + j]);
+ }
+ scaledColorIdx += nComps;
+ if (hasAlpha) {
+ alphaLine[scaledIdx] = (Guchar)(int)(hs0 * (int)alphaTmpBuf2[x0] +
+ hs1 * (int)alphaTmpBuf2[x1]);
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// SavingImageScaler
+//------------------------------------------------------------------------
+
+// Wrapper around BasicImageScaler that saves the scaled image for use
+// by ReplayImageScaler.
+class SavingImageScaler: public BasicImageScaler {
+public:
+
+ SavingImageScaler(SplashImageSource aSrc, void *aSrcData,
+ int aSrcWidth, int aSrcHeight, int aNComps, GBool aHasAlpha,
+ int aScaledWidth, int aScaledHeight, GBool aInterpolate,
+ Guchar *aColorCache, Guchar *aAlphaCache);
+ virtual void nextLine();
+
+private:
+
+ Guchar *colorPtr;
+ Guchar *alphaPtr;
+};
+
+SavingImageScaler::SavingImageScaler(SplashImageSource aSrc, void *aSrcData,
+ int aSrcWidth, int aSrcHeight,
+ int aNComps, GBool aHasAlpha,
+ int aScaledWidth, int aScaledHeight,
+ GBool aInterpolate,
+ Guchar *aColorCache, Guchar *aAlphaCache):
+ BasicImageScaler(aSrc, aSrcData, aSrcWidth, aSrcHeight, aNComps, aHasAlpha,
+ aScaledWidth, aScaledHeight, aInterpolate)
+{
+ colorPtr = aColorCache;
+ alphaPtr = aAlphaCache;
+}
+
+void SavingImageScaler::nextLine() {
+ BasicImageScaler::nextLine();
+ memcpy(colorPtr, colorData(), scaledWidth * nComps);
+ colorPtr += scaledWidth * nComps;
+ if (hasAlpha) {
+ memcpy(alphaPtr, alphaData(), scaledWidth);
+ alphaPtr += scaledWidth;
+ }
+}
+
+//------------------------------------------------------------------------
+// ReplayImageScaler
+//------------------------------------------------------------------------
+
+// "Replay" a scaled image saved by SavingImageScaler.
+class ReplayImageScaler: public ImageScaler {
+public:
+
+ ReplayImageScaler(int aNComps, GBool aHasAlpha,
+ int aScaledWidth,
+ Guchar *aColorCache, Guchar *aAlphaCache);
+ virtual void nextLine();
+ virtual Guchar *colorData() { return colorLine; }
+ virtual Guchar *alphaData() { return alphaLine; }
+
+private:
+
+ int nComps;
+ GBool hasAlpha;
+ int scaledWidth;
+ Guchar *colorPtr;
+ Guchar *alphaPtr;
+ Guchar *colorLine;
+ Guchar *alphaLine;
+};
+
+ReplayImageScaler::ReplayImageScaler(int aNComps, GBool aHasAlpha,
+ int aScaledWidth,
+ Guchar *aColorCache, Guchar *aAlphaCache) {
+ nComps = aNComps;
+ hasAlpha = aHasAlpha;
+ scaledWidth = aScaledWidth;
+ colorPtr = aColorCache;
+ alphaPtr = aAlphaCache;
+ colorLine = NULL;
+ alphaLine = NULL;
+}
+
+void ReplayImageScaler::nextLine() {
+ colorLine = colorPtr;
+ alphaLine = alphaPtr;
+ colorPtr += scaledWidth * nComps;
+ if (hasAlpha) {
+ alphaPtr += scaledWidth;
+ }
+}
+
+//------------------------------------------------------------------------
+// ImageMaskScaler
+//------------------------------------------------------------------------
+
+class ImageMaskScaler {
+public:
+
+ // Set up a MaskScaler to scale from [srcWidth]x[srcHeight] to
+ // [scaledWidth]x[scaledHeight].
+ ImageMaskScaler(SplashImageMaskSource aSrc, void *aSrcData,
+ int aSrcWidth, int aSrcHeight,
+ int aScaledWidth, int aScaledHeight, GBool aInterpolate);
+
+ ~ImageMaskScaler();
+
+ // Compute the next line of the scaled image mask. This can be
+ // called up to [scaledHeight] times.
+ void nextLine();
+
+ // Retrieve the data generated by the most recent call to
+ // nextLine().
+ Guchar *data() { return line; }
+
+private:
+
+ void vertDownscaleHorizDownscale();
+ void vertDownscaleHorizUpscaleNoInterp();
+ void vertDownscaleHorizUpscaleInterp();
+ void vertUpscaleHorizDownscaleNoInterp();
+ void vertUpscaleHorizDownscaleInterp();
+ void vertUpscaleHorizUpscaleNoInterp();
+ void vertUpscaleHorizUpscaleInterp();
+
+ // source image data function
+ SplashImageMaskSource src;
+ void *srcData;
+
+ // source image size
+ int srcWidth;
+ int srcHeight;
+
+ // scaled image size
+ int scaledWidth;
+ int scaledHeight;
+
+ // params/state for vertical scaling
+ int yp, yq;
+ int yt, yn;
+ int ySrcCur, yScaledCur;
+ SplashCoord yInvScale;
+
+ // params for horizontal scaling
+ int xp, xq;
+ SplashCoord xInvScale;
+
+ // vertical and horizontal scaling functions
+ void (ImageMaskScaler::*scalingFunc)();
+
+ // temporary buffers for vertical scaling
+ Guchar *tmpBuf0;
+ Guchar *tmpBuf1;
+ Guchar *tmpBuf2;
+ Guint *accBuf;
+
+ // output of horizontal scaling
+ Guchar *line;
+};
+
+ImageMaskScaler::ImageMaskScaler(SplashImageMaskSource aSrc, void *aSrcData,
+ int aSrcWidth, int aSrcHeight,
+ int aScaledWidth, int aScaledHeight,
+ GBool aInterpolate) {
+ tmpBuf0 = NULL;
+ tmpBuf1 = NULL;
+ tmpBuf2 = NULL;
+ accBuf = NULL;
+ line = NULL;
+
+ src = aSrc;
+ srcData = aSrcData;
+ srcWidth = aSrcWidth;
+ srcHeight = aSrcHeight;
+ scaledWidth = aScaledWidth;
+ scaledHeight = aScaledHeight;
+
+ // select scaling function; allocate buffers
+ if (scaledHeight <= srcHeight) {
+ // vertical downscaling
+ yp = srcHeight / scaledHeight;
+ yq = srcHeight % scaledHeight;
+ yt = 0;
+ tmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ accBuf = (Guint *)gmallocn(srcWidth, sizeof(Guint));
+ if (scaledWidth <= srcWidth) {
+ scalingFunc = &ImageMaskScaler::vertDownscaleHorizDownscale;
+ } else {
+ if (aInterpolate) {
+ scalingFunc = &ImageMaskScaler::vertDownscaleHorizUpscaleInterp;
+ } else {
+ scalingFunc = &ImageMaskScaler::vertDownscaleHorizUpscaleNoInterp;
+ }
+ }
+ } else {
+ // vertical upscaling
+ yp = scaledHeight / srcHeight;
+ yq = scaledHeight % srcHeight;
+ yt = 0;
+ yn = 0;
+ if (aInterpolate) {
+ yInvScale = (SplashCoord)srcHeight / (SplashCoord)scaledHeight;
+ tmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ tmpBuf1 = (Guchar *)gmalloc(srcWidth);
+ ySrcCur = 0;
+ yScaledCur = 0;
+ if (scaledWidth <= srcWidth) {
+ scalingFunc = &ImageMaskScaler::vertUpscaleHorizDownscaleInterp;
+ } else {
+ tmpBuf2 = (Guchar *)gmalloc(srcWidth);
+ scalingFunc = &ImageMaskScaler::vertUpscaleHorizUpscaleInterp;
+ }
+ } else {
+ tmpBuf0 = (Guchar *)gmalloc(srcWidth);
+ if (scaledWidth <= srcWidth) {
+ scalingFunc = &ImageMaskScaler::vertUpscaleHorizDownscaleNoInterp;
+ } else {
+ scalingFunc = &ImageMaskScaler::vertUpscaleHorizUpscaleNoInterp;
+ }
+ }
+ }
+ if (scaledWidth <= srcWidth) {
+ xp = srcWidth / scaledWidth;
+ xq = srcWidth % scaledWidth;
+ } else {
+ xp = scaledWidth / srcWidth;
+ xq = scaledWidth % srcWidth;
+ if (aInterpolate) {
+ xInvScale = (SplashCoord)srcWidth / (SplashCoord)scaledWidth;
+ }
+ }
+ line = (Guchar *)gmalloc(scaledWidth);
+}
+
+ImageMaskScaler::~ImageMaskScaler() {
+ gfree(tmpBuf0);
+ gfree(tmpBuf1);
+ gfree(tmpBuf2);
+ gfree(accBuf);
+ gfree(line);
+}
+
+void ImageMaskScaler::nextLine() {
+ (this->*scalingFunc)();
+}
+
+void ImageMaskScaler::vertDownscaleHorizDownscale() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(accBuf, 0, srcWidth * sizeof(Guint));
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, tmpBuf0);
+ for (int j = 0; j < srcWidth; ++j) {
+ accBuf[j] += tmpBuf0[j];
+ }
+ }
+
+ //--- horiz downscale
+ int acc;
+ int xt = 0;
+ int unscaledIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ acc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ acc += accBuf[unscaledIdx];
+ ++unscaledIdx;
+ }
+ line[scaledIdx] = (Guchar)((255 * acc) / (xStep * yStep));
+ }
+}
+
+void ImageMaskScaler::vertDownscaleHorizUpscaleNoInterp() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(accBuf, 0, srcWidth * sizeof(Guint));
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, tmpBuf0);
+ for (int j = 0; j < srcWidth; ++j) {
+ accBuf[j] += tmpBuf0[j];
+ }
+ }
+
+ //--- horiz upscale
+ int xt = 0;
+ int scaledIdx = 0;
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= srcWidth) {
+ xt -= srcWidth;
+ ++xStep;
+ }
+ Guchar buf = (Guchar)((255 * accBuf[srcIdx]) / yStep);
+ for (int i = 0; i < xStep; ++i) {
+ line[scaledIdx] = buf;
+ ++scaledIdx;
+ }
+ }
+}
+
+void ImageMaskScaler::vertDownscaleHorizUpscaleInterp() {
+ //--- vert downscale
+ int yStep = yp;
+ yt += yq;
+ if (yt >= scaledHeight) {
+ yt -= scaledHeight;
+ ++yStep;
+ }
+ memset(accBuf, 0, srcWidth * sizeof(Guint));
+ for (int i = 0; i < yStep; ++i) {
+ (*src)(srcData, tmpBuf0);
+ for (int j = 0; j < srcWidth; ++j) {
+ accBuf[j] += tmpBuf0[j];
+ }
+ }
+ for (int j = 0; j < srcWidth; ++j) {
+ accBuf[j] = (255 * accBuf[j]) / yStep;
+ }
+
+ //--- horiz upscale
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ SplashCoord s0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord s1 = (SplashCoord)1 - s0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= srcWidth) {
+ x1 = srcWidth - 1;
+ }
+ line[scaledIdx] = (Guchar)(int)(s0 * accBuf[x0] + s1 * accBuf[x1]);
+ }
+}
+
+void ImageMaskScaler::vertUpscaleHorizDownscaleNoInterp() {
+ //--- vert upscale
+ if (yn == 0) {
+ yn = yp;
+ yt += yq;
+ if (yt >= srcHeight) {
+ yt -= srcHeight;
+ ++yn;
+ }
+ (*src)(srcData, tmpBuf0);
+ }
+ --yn;
+
+ //--- horiz downscale
+ int acc;
+ int xt = 0;
+ int unscaledIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ acc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ acc += tmpBuf0[unscaledIdx];
+ ++unscaledIdx;
+ }
+ line[scaledIdx] = (Guchar)((255 * acc) / xStep);
+ }
+}
+
+void ImageMaskScaler::vertUpscaleHorizDownscaleInterp() {
+ //--- vert upscale
+ if (ySrcCur == 0) {
+ (*src)(srcData, tmpBuf0);
+ (*src)(srcData, tmpBuf1);
+ ySrcCur = 1;
+ }
+ SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ SplashCoord vs0 = (SplashCoord)y1 + 0.5 - ys;
+ SplashCoord vs1 = (SplashCoord)1 - vs0;
+ if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
+ Guchar *t = tmpBuf0;
+ tmpBuf0 = tmpBuf1;
+ tmpBuf1 = t;
+ (*src)(srcData, tmpBuf1);
+ ++ySrcCur;
+ }
+ Guchar *mask0 = tmpBuf0;
+ Guchar *mask1 = tmpBuf1;
+ if (y0 < 0) {
+ y0 = 0;
+ mask1 = mask0;
+ }
+ if (y1 >= srcHeight) {
+ y1 = srcHeight - 1;
+ mask0 = mask1;
+ }
+ ++yScaledCur;
+
+ //--- horiz downscale
+ int acc;
+ int xt = 0;
+ int unscaledIdx = 0;
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= scaledWidth) {
+ xt -= scaledWidth;
+ ++xStep;
+ }
+
+ acc = 0;
+ for (int i = 0; i < xStep; ++i) {
+ acc += (int)(vs0 * (int)mask0[unscaledIdx] +
+ vs1 * (int)mask1[unscaledIdx]);
+ ++unscaledIdx;
+ }
+ line[scaledIdx] = (Guchar)((255 * acc) / xStep);
+ }
+}
+
+void ImageMaskScaler::vertUpscaleHorizUpscaleNoInterp() {
+ //--- vert upscale
+ if (yn == 0) {
+ yn = yp;
+ yt += yq;
+ if (yt >= srcHeight) {
+ yt -= srcHeight;
+ ++yn;
+ }
+ (*src)(srcData, tmpBuf0);
+ }
+ --yn;
+
+ //--- horiz upscale
+ int xt = 0;
+ int scaledIdx = 0;
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ int xStep = xp;
+ xt += xq;
+ if (xt >= srcWidth) {
+ xt -= srcWidth;
+ ++xStep;
+ }
+ Guchar buf = (Guchar)(255 * tmpBuf0[srcIdx]);
+ for (int i = 0; i < xStep; ++i) {
+ line[scaledIdx] = buf;
+ ++scaledIdx;
+ }
+ }
+}
+
+void ImageMaskScaler::vertUpscaleHorizUpscaleInterp() {
+ //--- vert upscale
+ if (ySrcCur == 0) {
+ (*src)(srcData, tmpBuf0);
+ (*src)(srcData, tmpBuf1);
+ ySrcCur = 1;
+ }
+ SplashCoord ys = ((SplashCoord)yScaledCur + 0.5) * yInvScale;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ SplashCoord vs0 = (SplashCoord)255 * ((SplashCoord)y1 + 0.5 - ys);
+ SplashCoord vs1 = (SplashCoord)255 - vs0;
+ if (y1 > ySrcCur && ySrcCur < srcHeight - 1) {
+ Guchar *t = tmpBuf0;
+ tmpBuf0 = tmpBuf1;
+ tmpBuf1 = t;
+ (*src)(srcData, tmpBuf1);
+ ++ySrcCur;
+ }
+ Guchar *mask0 = tmpBuf0;
+ Guchar *mask1 = tmpBuf1;
+ if (y0 < 0) {
+ y0 = 0;
+ mask1 = mask0;
+ }
+ if (y1 >= srcHeight) {
+ y1 = srcHeight - 1;
+ mask0 = mask1;
+ }
+ ++yScaledCur;
+ for (int srcIdx = 0; srcIdx < srcWidth; ++srcIdx) {
+ tmpBuf2[srcIdx] = (Guchar)(int)(vs0 * (int)mask0[srcIdx] +
+ vs1 * (int)mask1[srcIdx]);
+ }
+
+ //--- horiz upscale
+ for (int scaledIdx = 0; scaledIdx < scaledWidth; ++scaledIdx) {
+ SplashCoord xs = ((SplashCoord)scaledIdx + 0.5) * xInvScale;
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ SplashCoord hs0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord hs1 = (SplashCoord)1 - hs0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= srcWidth) {
+ x1 = srcWidth - 1;
+ }
+ line[scaledIdx] = (Guchar)(int)(hs0 * (int)tmpBuf2[x0] +
+ hs1 * (int)tmpBuf2[x1]);
+ }
+}
+
+//------------------------------------------------------------------------
// Splash
//------------------------------------------------------------------------
@@ -2472,6 +4360,8 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
scanBuf2 = NULL;
}
groupBackBitmap = NULL;
+ groupDestInitMode = splashGroupDestPreInit;
+ overprintMaskBitmap = NULL;
minLineWidth = 0;
clearModRegion();
debugMode = gFalse;
@@ -2499,6 +4389,8 @@ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
scanBuf2 = NULL;
}
groupBackBitmap = NULL;
+ groupDestInitMode = splashGroupDestPreInit;
+ overprintMaskBitmap = NULL;
minLineWidth = 0;
clearModRegion();
debugMode = gFalse;
@@ -2699,14 +4591,23 @@ void Splash::setSoftMask(SplashBitmap *softMask) {
void Splash::setInTransparencyGroup(SplashBitmap *groupBackBitmapA,
int groupBackXA, int groupBackYA,
+ SplashGroupDestInitMode groupDestInitModeA,
GBool nonIsolated, GBool knockout) {
groupBackBitmap = groupBackBitmapA;
groupBackX = groupBackXA;
groupBackY = groupBackYA;
+ groupDestInitMode = groupDestInitModeA;
+ groupDestInitYMin = 1;
+ groupDestInitYMax = 0;
state->inNonIsolatedGroup = nonIsolated;
state->inKnockoutGroup = knockout;
}
+void Splash::forceDeferredInit(int y, int h) {
+ useDestRow(y);
+ useDestRow(y + h - 1);
+}
+
void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue,
Guchar *gray) {
state->setTransfer(red, green, blue, gray);
@@ -3839,1140 +5740,493 @@ void Splash::getImageBounds(SplashCoord xyMin, SplashCoord xyMax,
}
}
+struct SplashDrawImageMaskRowData {
+ SplashPipe pipe;
+};
+
// The glyphMode flag is not currently used, but may be useful if the
// stroke adjustment behavior is changed.
SplashError Splash::fillImageMask(GString *imageTag,
SplashImageMaskSource src, void *srcData,
int w, int h, SplashCoord *mat,
GBool glyphMode, GBool interpolate) {
- SplashBitmap *scaledMask;
- SplashClipResult clipRes;
- GBool minorAxisZero;
- SplashCoord wSize, hSize, t0, t1;
- int x0, y0, x1, y1, scaledWidth, scaledHeight;
-
if (debugMode) {
printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
w, h, (double)mat[0], (double)mat[1], (double)mat[2],
(double)mat[3], (double)mat[4], (double)mat[5]);
}
- // check for singular matrix
+ //--- check for singular matrix
if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
return splashErrSingularMatrix;
}
- minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001;
-
- // rough estimate of size of scaled mask
- t0 = splashAbs(mat[0]);
- t1 = splashAbs(mat[1]);
- wSize = t0 > t1 ? t0 : t1;
- t0 = splashAbs(mat[2]);
- t1 = splashAbs(mat[3]);
- hSize = t0 > t1 ? t0 : t1;
-
- // stream-mode upscaling -- this is slower, so we only use it if the
- // upscaled mask is large (in which case clipping should remove many
- // pixels)
-#if USE_FIXEDPOINT
- if ((wSize > 2 * w && hSize > 2 * h && (int)wSize > 1000000 / (int)hSize) ||
- (wSize > w && hSize > h && (int)wSize > 10000000 / (int)hSize) ||
- ((wSize > w || hSize > h) && (int)wSize > 25000000 / (int)hSize)) {
-#else
- if ((wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) ||
- (wSize > w && hSize > h && wSize * hSize > 10000000) ||
- ((wSize > w || hSize > h) && wSize * hSize > 25000000)) {
- upscaleMask(src, srcData, w, h, mat, glyphMode, interpolate);
-#endif
-
- // scaling only
- } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
- getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1);
- getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1);
- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
- if (clipRes != splashClipAllOutside) {
- scaledWidth = x1 - x0;
- scaledHeight = y1 - y0;
- scaledMask = scaleMask(imageTag, src, srcData, w, h,
- scaledWidth, scaledHeight, interpolate);
- if (imageCache->vertFlip) {
- vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->vertFlip = gFalse;
- }
- if (imageCache->horizFlip) {
- horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->horizFlip = gFalse;
- }
- blitMask(scaledMask, x0, y0, clipRes);
- }
-
- // scaling plus vertical flip
- } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
- getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1);
- getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1);
- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
- if (clipRes != splashClipAllOutside) {
- scaledWidth = x1 - x0;
- scaledHeight = y1 - y0;
- scaledMask = scaleMask(imageTag, src, srcData, w, h,
- scaledWidth, scaledHeight, interpolate);
- if (!imageCache->vertFlip) {
- vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->vertFlip = gTrue;
+ //--- compute image bbox, check clipping
+ GBool flipsOnly = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001;
+ GBool horizFlip = gFalse;
+ GBool vertFlip = gFalse;
+ int xMin, yMin, xMax, yMax;
+ if (flipsOnly) {
+ horizFlip = mat[0] < 0;
+ vertFlip = mat[3] < 0;
+ if (vertFlip) {
+ if (horizFlip) { // bottom-up, mirrored
+ getImageBounds(mat[0] + mat[4], mat[4], &xMin, &xMax);
+ getImageBounds(mat[3] + mat[5], mat[5], &yMin, &yMax);
+ } else { // bottom-up
+ getImageBounds(mat[4], mat[0] + mat[4], &xMin, &xMax);
+ getImageBounds(mat[3] + mat[5], mat[5], &yMin, &yMax);
}
- if (imageCache->horizFlip) {
- horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->horizFlip = gFalse;
+ } else {
+ if (horizFlip) { // top-down, mirrored
+ getImageBounds(mat[0] + mat[4], mat[4], &xMin, &xMax);
+ getImageBounds(mat[5], mat[3] + mat[5], &yMin, &yMax);
+ } else { // top-down
+ getImageBounds(mat[4], mat[0] + mat[4], &xMin, &xMax);
+ getImageBounds(mat[5], mat[3] + mat[5], &yMin, &yMax);
}
- blitMask(scaledMask, x0, y0, clipRes);
}
+ } else {
+ int xx = splashRound(mat[4]); // (0,0)
+ int yy = splashRound(mat[5]);
+ xMin = xMax = xx;
+ yMin = yMax = yy;
+ xx = splashRound(mat[0] + mat[4]); // (1,0)
+ yy = splashRound(mat[1] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ xx = splashRound(mat[2] + mat[4]); // (0,1)
+ yy = splashRound(mat[3] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ xx = splashRound(mat[0] + mat[2] + mat[4]); // (1,1)
+ yy = splashRound(mat[1] + mat[3] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ if (xMax <= xMin) {
+ xMax = xMin + 1;
+ }
+ if (yMax <= yMin) {
+ yMax = yMin + 1;
+ }
+ }
+ SplashClipResult clipRes =
+ state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
+ state->strokeAdjust);
+ // If the scaled mask is much wider and/or taller than the clip
+ // region, we use the "arbitrary" scaling path, to avoid a
+ // potentially very slow loop in the flips-only path (which scans
+ // the full width and height of the scaled mask, regardless of the
+ // clip region).
+ int clipW = state->clip->getXMaxI(state->strokeAdjust)
+ - state->clip->getXMinI(state->strokeAdjust);
+ int clipH = state->clip->getYMaxI(state->strokeAdjust)
+ - state->clip->getYMinI(state->strokeAdjust);
+ GBool veryLarge = ((xMax - xMin) / 8 > clipW && xMax - xMin > 1000) ||
+ ((yMax - yMin) / 8 > clipH && yMax - yMin > 1000);
+
+ //--- set up the SplashDrawImageMaskRowData object and the pipes
+ SplashDrawImageMaskRowData dd;
+ pipeInit(&dd.pipe, state->fillPattern,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ gTrue, gFalse);
+
+ //--- choose the drawRow function
+ SplashDrawImageMaskRowFunc drawRowFunc;
+ if (clipRes == splashClipAllInside) {
+ drawRowFunc = &Splash::drawImageMaskRowNoClip;
+ } else {
+ if (vectorAntialias) {
+ drawRowFunc = &Splash::drawImageMaskRowClipAA;
+ } else {
+ drawRowFunc = &Splash::drawImageMaskRowClipNoAA;
+ }
+ }
- // scaling plus horizontal flip
- } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) {
- getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1);
- getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1);
- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
+ //--- horizontal/vertical flips only
+ if (flipsOnly && !veryLarge) {
if (clipRes != splashClipAllOutside) {
- scaledWidth = x1 - x0;
- scaledHeight = y1 - y0;
- scaledMask = scaleMask(imageTag, src, srcData, w, h,
+ int scaledWidth = xMax - xMin;
+ int scaledHeight = yMax - yMin;
+ ImageMaskScaler scaler(src, srcData, w, h,
scaledWidth, scaledHeight, interpolate);
- if (imageCache->vertFlip) {
- vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->vertFlip = gFalse;
- }
- if (!imageCache->horizFlip) {
- horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->horizFlip = gTrue;
+ Guchar *tmpLine = NULL;
+ if (horizFlip) {
+ tmpLine = (Guchar *)gmalloc(scaledWidth);
+ }
+ if (vertFlip) {
+ if (horizFlip) { // bottom-up, mirrored
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ mirrorImageMaskRow(scaler.data(), tmpLine, scaledWidth);
+ (this->*drawRowFunc)(&dd, tmpLine,
+ xMin, yMax - 1 - y, scaledWidth);
+ }
+ } else { // bottom-up
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ (this->*drawRowFunc)(&dd, scaler.data(),
+ xMin, yMax - 1 - y, scaledWidth);
+ }
+ }
+ } else {
+ if (horizFlip) { // top-down, mirrored
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ mirrorImageMaskRow(scaler.data(), tmpLine, scaledWidth);
+ (this->*drawRowFunc)(&dd, tmpLine,
+ xMin, yMin + y, scaledWidth);
+ }
+ } else { // top-down
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ (this->*drawRowFunc)(&dd, scaler.data(),
+ xMin, yMin + y, scaledWidth);
+ }
+ }
}
- blitMask(scaledMask, x0, y0, clipRes);
+ gfree(tmpLine);
}
- // scaling plus horizontal and vertical flips
- } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) {
- getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1);
- getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1);
- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
- if (clipRes != splashClipAllOutside) {
- scaledWidth = x1 - x0;
- scaledHeight = y1 - y0;
- scaledMask = scaleMask(imageTag, src, srcData, w, h,
+ //--- arbitrary transform
+ } else {
+ // estimate of size of scaled image
+ int scaledWidth = splashRound(splashSqrt(mat[0] * mat[0]
+ + mat[1] * mat[1]));
+ int scaledHeight = splashRound(splashSqrt(mat[2] * mat[2]
+ + mat[3] * mat[3]));
+ if (scaledWidth < 1) {
+ scaledWidth = 1;
+ }
+ if (scaledHeight < 1) {
+ scaledHeight = 1;
+ }
+ GBool downscaling = gTrue;
+ if (veryLarge || (scaledWidth >= w && scaledHeight >= h)) {
+ downscaling = gFalse;
+ scaledWidth = w;
+ scaledHeight = h;
+ }
+
+ // compute mapping from device space to scaled image space
+ SplashCoord mat1[6];
+ mat1[0] = mat[0] / scaledWidth;
+ mat1[1] = mat[1] / scaledWidth;
+ mat1[2] = mat[2] / scaledHeight;
+ mat1[3] = mat[3] / scaledHeight;
+ mat1[4] = mat[4];
+ mat1[5] = mat[5];
+ SplashCoord det = mat1[0] * mat1[3] - mat1[1] * mat1[2];
+ if (splashAbs(det) < 1e-6) {
+ // this should be caught by the singular matrix check in drawImage
+ return splashErrSingularMatrix;
+ }
+ SplashCoord invMat[6];
+ invMat[0] = mat1[3] / det;
+ invMat[1] = -mat1[1] / det;
+ invMat[2] = -mat1[2] / det;
+ invMat[3] = mat1[0] / det;
+ // the extra "+ 0.5 * (...)" terms are here because the
+ // drawImageArbitrary(No)Interp functions multiply by pixel
+ // centers, (x + 0.5, y + 0.5)
+ invMat[4] = (mat1[2] * mat1[5] - mat1[3] * mat1[4]) / det
+ + (invMat[0] + invMat[2]) * 0.5;
+ invMat[5] = (mat1[1] * mat1[4] - mat1[0] * mat1[5]) / det
+ + (invMat[1] + invMat[3]) * 0.5;
+
+ // if downscaling: store the downscaled image mask
+ // if upscaling: store the unscaled image mask
+ Guchar *scaledMask = (Guchar *)gmallocn(scaledHeight, scaledWidth);
+ if (downscaling) {
+ ImageMaskScaler scaler(src, srcData, w, h,
scaledWidth, scaledHeight, interpolate);
- if (!imageCache->vertFlip) {
- vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->vertFlip = gTrue;
+ Guchar *ptr = scaledMask;
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ memcpy(ptr, scaler.data(), scaledWidth);
+ ptr += scaledWidth;
}
- if (!imageCache->horizFlip) {
- horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->horizFlip = gTrue;
+ } else {
+ Guchar *ptr = scaledMask;
+ for (int y = 0; y < scaledHeight; ++y) {
+ (*src)(srcData, ptr);
+ for (int x = 0; x < scaledWidth; ++x) {
+ *ptr = (Guchar)(*ptr * 255);
+ ++ptr;
+ }
}
- blitMask(scaledMask, x0, y0, clipRes);
}
- // all other cases
- } else {
- arbitraryTransformMask(imageTag, src, srcData, w, h,
- mat, glyphMode, interpolate);
+ // draw it
+ if (interpolate) {
+ drawImageMaskArbitraryInterp(scaledMask,
+ &dd, drawRowFunc, invMat,
+ scaledWidth, scaledHeight,
+ xMin, yMin, xMax, yMax);
+ } else {
+ drawImageMaskArbitraryNoInterp(scaledMask,
+ &dd, drawRowFunc, invMat,
+ scaledWidth, scaledHeight,
+ xMin, yMin, xMax, yMax);
+ }
+
+ // free the downscaled/unscaled image
+ gfree(scaledMask);
}
return splashOk;
}
-// The glyphMode flag is not currently used, but may be useful if the
-// stroke adjustment behavior is changed.
-void Splash::upscaleMask(SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- SplashCoord *mat, GBool glyphMode,
- GBool interpolate) {
- SplashClipResult clipRes;
- SplashPipe pipe;
- Guchar *unscaledImage, *p;
- SplashCoord xMin, yMin, xMax, yMax, t;
- SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det;
- SplashCoord ix, iy, sx, sy, pix0, pix1;
- int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt;
-
- // compute the bbox of the target quadrilateral
- xMin = xMax = mat[4];
- t = mat[2] + mat[4];
- if (t < xMin) {
- xMin = t;
- } else if (t > xMax) {
- xMax = t;
- }
- t = mat[0] + mat[2] + mat[4];
- if (t < xMin) {
- xMin = t;
- } else if (t > xMax) {
- xMax = t;
- }
- t = mat[0] + mat[4];
- if (t < xMin) {
- xMin = t;
- } else if (t > xMax) {
- xMax = t;
- }
- getImageBounds(xMin, xMax, &xMinI, &xMaxI);
- yMin = yMax = mat[5];
- t = mat[3] + mat[5];
- if (t < yMin) {
- yMin = t;
- } else if (t > yMax) {
- yMax = t;
- }
- t = mat[1] + mat[3] + mat[5];
- if (t < yMin) {
- yMin = t;
- } else if (t > yMax) {
- yMax = t;
- }
- t = mat[1] + mat[5];
- if (t < yMin) {
- yMin = t;
- } else if (t > yMax) {
- yMax = t;
- }
- getImageBounds(yMin, yMax, &yMinI, &yMaxI);
-
- // clipping
- clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
- if (clipRes == splashClipAllOutside) {
- return;
- }
- if (clipRes != splashClipAllInside) {
- if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) {
- xMinI = tt;
- }
- if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) {
- xMaxI = tt;
- }
- if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) {
- yMinI = tt;
- }
- if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) {
- yMaxI = tt;
- }
- }
-
- // invert the matrix
- det = mat[0] * mat[3] - mat[1] * mat[2];
- if (splashAbs(det) < 1e-6) {
- // this should be caught by the singular matrix check in fillImageMask
+void Splash::drawImageMaskArbitraryNoInterp(
+ Guchar *scaledMask,
+ SplashDrawImageMaskRowData *dd,
+ SplashDrawImageMaskRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax) {
+ int tt = state->clip->getXMinI(state->strokeAdjust);
+ if (tt > xMin) {
+ xMin = tt;
+ }
+ tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
+ if (tt < xMax) {
+ xMax = tt;
+ }
+ tt = state->clip->getYMinI(state->strokeAdjust);
+ if (tt > yMin) {
+ yMin = tt;
+ }
+ tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
+ if (tt < yMax) {
+ yMax = tt;
+ }
+ if (xMax <= xMin || yMax <= yMin) {
return;
}
- det = (SplashCoord)1 / det;
- mi0 = det * mat[3] * srcWidth;
- mi1 = -det * mat[1] * srcHeight;
- mi2 = -det * mat[2] * srcWidth;
- mi3 = det * mat[0] * srcHeight;
- mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth;
- mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight;
- // grab the image
- unscaledImage = (Guchar *)gmallocn(srcWidth, srcHeight);
- for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += srcWidth) {
- (*src)(srcData, p);
- for (x = 0; x < srcWidth; ++x) {
- p[x] = (Guchar)(p[x] * 255);
- }
- }
-
- // draw it
- pipeInit(&pipe, state->fillPattern,
- (Guchar)splashRound(state->fillAlpha * 255),
- gTrue, gFalse);
- for (y = yMinI; y < yMaxI; ++y) {
- for (x = xMinI; x < xMaxI; ++x) {
- ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4;
- iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5;
- if (interpolate) {
- if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) {
- x0 = splashFloor(ix - 0.5);
- x1 = x0 + 1;
- sx = (ix - 0.5) - x0;
- y0 = splashFloor(iy - 0.5);
- y1 = y0 + 1;
- sy = (iy - 0.5) - y0;
- if (x0 < 0) {
- x0 = 0;
- }
- if (x1 >= srcWidth) {
- x1 = srcWidth - 1;
- }
- if (y0 < 0) {
- y0 = 0;
- }
- if (y1 >= srcHeight) {
- y1 = srcHeight - 1;
- }
- pix0 = ((SplashCoord)1 - sx)
- * (SplashCoord)unscaledImage[y0 * srcWidth + x0]
- + sx * (SplashCoord)unscaledImage[y0 * srcWidth + x1];
- pix1 = ((SplashCoord)1 - sx)
- * (SplashCoord)unscaledImage[y1 * srcWidth + x0]
- + sx * (SplashCoord)unscaledImage[y1 * srcWidth + x1];
- scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0
- + sy * pix1);
- } else {
- scanBuf[x] = 0;
- }
- } else {
- x0 = splashFloor(ix);
- y0 = splashFloor(iy);
- if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) {
- scanBuf[x] = unscaledImage[y0 * srcWidth + x0];
- } else {
- scanBuf[x] = 0;
+ Guchar *buf = (Guchar *)gmalloc(xMax - xMin);
+
+ for (int y = yMin; y < yMax; ++y) {
+ int rowMin = xMax;
+ int rowMax = 0;
+ for (int x = xMin; x < xMax; ++x) {
+ // note: invMat includes a "+0.5" factor so that this is really
+ // a multiply by (x+0.5, y+0.5)
+ int xx = splashFloor((SplashCoord)x * invMat[0]
+ + (SplashCoord)y * invMat[2] + invMat[4]);
+ int yy = splashFloor((SplashCoord)x * invMat[1]
+ + (SplashCoord)y * invMat[3] + invMat[5]);
+ if (xx >= 0 && xx < scaledWidth &&
+ yy >= 0 && yy < scaledHeight) {
+ Guchar *p = scaledMask + (yy * scaledWidth + xx);
+ Guchar *q = buf + (x - xMin);
+ *q = *p;
+ if (x < rowMin) {
+ rowMin = x;
}
+ rowMax = x + 1;
}
}
- if (clipRes != splashClipAllInside) {
- if (vectorAntialias) {
- state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1,
- state->strokeAdjust);
- } else {
- state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1,
- state->strokeAdjust);
- }
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd, buf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
}
- (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, NULL);
}
- gfree(unscaledImage);
+ gfree(buf);
}
-// The glyphMode flag is not currently used, but may be useful if the
-// stroke adjustment behavior is changed.
-void Splash::arbitraryTransformMask(GString *imageTag,
- SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- SplashCoord *mat, GBool glyphMode,
- GBool interpolate) {
- SplashBitmap *scaledMask;
- SplashClipResult clipRes;
- SplashPipe pipe;
- int scaledWidth, scaledHeight, t0, t1;
- SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
- SplashCoord vx[4], vy[4];
- int xMin, yMin, xMax, yMax;
- ImageSection section[3];
- int nSections;
- int bw, y, xa, xb, x, i, xx, yy;
-
- // compute the four vertices of the target quadrilateral
- vx[0] = mat[4]; vy[0] = mat[5];
- vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5];
- vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5];
- vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5];
-
- // clipping
- xMin = splashRound(vx[0]);
- xMax = splashRound(vx[0]);
- yMin = splashRound(vy[0]);
- yMax = splashRound(vy[0]);
- for (i = 1; i < 4; ++i) {
- t0 = splashRound(vx[i]);
- if (t0 < xMin) {
- xMin = t0;
- } else if (t0 > xMax) {
- xMax = t0;
- }
- t1 = splashRound(vy[i]);
- if (t1 < yMin) {
- yMin = t1;
- } else if (t1 > yMax) {
- yMax = t1;
- }
- }
- clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
- if (clipRes == splashClipAllOutside) {
- return;
- }
-
- // compute the scale factors
- if (mat[0] >= 0) {
- t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]);
- } else {
- t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]);
- }
- if (mat[1] >= 0) {
- t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]);
- } else {
- t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]);
- }
- scaledWidth = t0 > t1 ? t0 : t1;
- if (mat[2] >= 0) {
- t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]);
- } else {
- t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]);
- }
- if (mat[3] >= 0) {
- t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]);
- } else {
- t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]);
- }
- scaledHeight = t0 > t1 ? t0 : t1;
- if (scaledWidth == 0) {
- scaledWidth = 1;
- }
- if (scaledHeight == 0) {
- scaledHeight = 1;
- }
-
- // compute the inverse transform (after scaling) matrix
- r00 = mat[0] / scaledWidth;
- r01 = mat[1] / scaledWidth;
- r10 = mat[2] / scaledHeight;
- r11 = mat[3] / scaledHeight;
- det = r00 * r11 - r01 * r10;
- if (splashAbs(det) < 1e-6) {
- // this should be caught by the singular matrix check in fillImageMask
+void Splash::drawImageMaskArbitraryInterp(
+ Guchar *scaledMask,
+ SplashDrawImageMaskRowData *dd,
+ SplashDrawImageMaskRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax) {
+ int tt = state->clip->getXMinI(state->strokeAdjust);
+ if (tt > xMin) {
+ xMin = tt;
+ }
+ tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
+ if (tt < xMax) {
+ xMax = tt;
+ }
+ tt = state->clip->getYMinI(state->strokeAdjust);
+ if (tt > yMin) {
+ yMin = tt;
+ }
+ tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
+ if (tt < yMax) {
+ yMax = tt;
+ }
+ if (xMax <= xMin || yMax <= yMin) {
return;
}
- ir00 = r11 / det;
- ir01 = -r01 / det;
- ir10 = -r10 / det;
- ir11 = r00 / det;
-
- // scale the input image
- scaledMask = scaleMask(imageTag, src, srcData, srcWidth, srcHeight,
- scaledWidth, scaledHeight, interpolate);
- if (imageCache->vertFlip) {
- vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->vertFlip = gFalse;
- }
- if (imageCache->horizFlip) {
- horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->horizFlip = gFalse;
- }
-
- // construct the three sections
- i = 0;
- if (vy[1] < vy[i]) {
- i = 1;
- }
- if (vy[2] < vy[i]) {
- i = 2;
- }
- if (vy[3] < vy[i]) {
- i = 3;
- }
- // NB: if using fixed point, 0.000001 will be truncated to zero,
- // so these two comparisons must be <=, not <
- if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 &&
- vy[(i-1) & 3] < vy[(i+1) & 3]) {
- i = (i-1) & 3;
- }
- if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) {
- section[0].y0 = splashRound(vy[i]);
- section[0].y1 = splashRound(vy[(i+2) & 3]) - 1;
- if (vx[i] < vx[(i+1) & 3]) {
- section[0].ia0 = i;
- section[0].ia1 = (i+3) & 3;
- section[0].ib0 = (i+1) & 3;
- section[0].ib1 = (i+2) & 3;
- } else {
- section[0].ia0 = (i+1) & 3;
- section[0].ia1 = (i+2) & 3;
- section[0].ib0 = i;
- section[0].ib1 = (i+3) & 3;
- }
- nSections = 1;
- } else {
- section[0].y0 = splashRound(vy[i]);
- section[2].y1 = splashRound(vy[(i+2) & 3]) - 1;
- section[0].ia0 = section[0].ib0 = i;
- section[2].ia1 = section[2].ib1 = (i+2) & 3;
- if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
- section[0].ia1 = section[2].ia0 = (i+1) & 3;
- section[0].ib1 = section[2].ib0 = (i+3) & 3;
- } else {
- section[0].ia1 = section[2].ia0 = (i+3) & 3;
- section[0].ib1 = section[2].ib0 = (i+1) & 3;
- }
- if (vy[(i+1) & 3] < vy[(i+3) & 3]) {
- section[1].y0 = splashRound(vy[(i+1) & 3]);
- section[2].y0 = splashRound(vy[(i+3) & 3]);
- if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
- section[1].ia0 = (i+1) & 3;
- section[1].ia1 = (i+2) & 3;
- section[1].ib0 = i;
- section[1].ib1 = (i+3) & 3;
- } else {
- section[1].ia0 = i;
- section[1].ia1 = (i+3) & 3;
- section[1].ib0 = (i+1) & 3;
- section[1].ib1 = (i+2) & 3;
- }
- } else {
- section[1].y0 = splashRound(vy[(i+3) & 3]);
- section[2].y0 = splashRound(vy[(i+1) & 3]);
- if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
- section[1].ia0 = i;
- section[1].ia1 = (i+1) & 3;
- section[1].ib0 = (i+3) & 3;
- section[1].ib1 = (i+2) & 3;
- } else {
- section[1].ia0 = (i+3) & 3;
- section[1].ia1 = (i+2) & 3;
- section[1].ib0 = i;
- section[1].ib1 = (i+1) & 3;
- }
- }
- section[0].y1 = section[1].y0 - 1;
- section[1].y1 = section[2].y0 - 1;
- nSections = 3;
- }
- for (i = 0; i < nSections; ++i) {
- section[i].xa0 = vx[section[i].ia0];
- section[i].ya0 = vy[section[i].ia0];
- section[i].xa1 = vx[section[i].ia1];
- section[i].ya1 = vy[section[i].ia1];
- section[i].xb0 = vx[section[i].ib0];
- section[i].yb0 = vy[section[i].ib0];
- section[i].xb1 = vx[section[i].ib1];
- section[i].yb1 = vy[section[i].ib1];
- section[i].dxdya = (section[i].xa1 - section[i].xa0) /
- (section[i].ya1 - section[i].ya0);
- section[i].dxdyb = (section[i].xb1 - section[i].xb0) /
- (section[i].yb1 - section[i].yb0);
- }
-
- // initialize the pixel pipe
- pipeInit(&pipe, state->fillPattern,
- (Guchar)splashRound(state->fillAlpha * 255),
- gTrue, gFalse);
- // make sure narrow images cover at least one pixel
- if (nSections == 1) {
- if (section[0].y0 == section[0].y1) {
- ++section[0].y1;
- clipRes = opClipRes = splashClipPartial;
- }
- } else {
- if (section[0].y0 == section[2].y1) {
- ++section[1].y1;
- clipRes = opClipRes = splashClipPartial;
- }
- }
-
- // scan all pixels inside the target region
- bw = bitmap->width;
- for (i = 0; i < nSections; ++i) {
- for (y = section[i].y0; y <= section[i].y1; ++y) {
- xa = splashRound(section[i].xa0 +
- ((SplashCoord)y + 0.5 - section[i].ya0) *
- section[i].dxdya);
- xb = splashRound(section[i].xb0 +
- ((SplashCoord)y + 0.5 - section[i].yb0) *
- section[i].dxdyb);
- if (xa > xb) {
- continue;
- }
- // make sure narrow images cover at least one pixel
- if (xa == xb) {
- ++xb;
- }
- // check the scanBuf bounds
- if (xa >= bw || xb < 0) {
- continue;
- }
- if (xa < 0) {
- xa = 0;
- }
- if (xb > bw) {
- xb = bw;
- }
- // get the scan line
- for (x = xa; x < xb; ++x) {
- // map (x+0.5, y+0.5) back to the scaled image
- xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 +
- ((SplashCoord)y + 0.5 - mat[5]) * ir10);
- yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 +
- ((SplashCoord)y + 0.5 - mat[5]) * ir11);
- // xx should always be within bounds, but floating point
- // inaccuracy can cause problems
- if (xx < 0) {
- xx = 0;
- } else if (xx >= scaledWidth) {
- xx = scaledWidth - 1;
+ Guchar *buf = (Guchar *)gmalloc(xMax - xMin);
+
+ for (int y = yMin; y < yMax; ++y) {
+ int rowMin = xMax;
+ int rowMax = 0;
+ for (int x = xMin; x < xMax; ++x) {
+ // note: invMat includes a "+0.5" factor so that this is really
+ // a multiply by (x+0.5, y+0.5)
+ SplashCoord xs = (SplashCoord)x * invMat[0]
+ + (SplashCoord)y * invMat[2] + invMat[4];
+ SplashCoord ys = (SplashCoord)x * invMat[1]
+ + (SplashCoord)y * invMat[3] + invMat[5];
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ if (x1 >= 0 && x0 < scaledWidth && y1 >= 0 && y0 < scaledHeight) {
+ SplashCoord sx0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord sx1 = (SplashCoord)1 - sx0;
+ SplashCoord sy0 = (SplashCoord)y1 + 0.5 - ys;
+ SplashCoord sy1 = (SplashCoord)1 - sy0;
+ if (x0 < 0) {
+ x0 = 0;
}
- if (yy < 0) {
- yy = 0;
- } else if (yy >= scaledHeight) {
- yy = scaledHeight - 1;
+ if (x1 >= scaledWidth) {
+ x1 = scaledWidth - 1;
}
- scanBuf[x] = scaledMask->data[yy * scaledWidth + xx];
- }
- // clip the scan line
- if (clipRes != splashClipAllInside) {
- if (vectorAntialias) {
- state->clip->clipSpan(scanBuf, y, xa, xb - 1, state->strokeAdjust);
- } else {
- state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1,
- state->strokeAdjust);
+ if (y0 < 0) {
+ y0 = 0;
}
- }
- // draw the scan line
- (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, NULL);
- }
- }
-}
-
-// Scale an image mask into a SplashBitmap.
-SplashBitmap *Splash::scaleMask(GString *imageTag,
- SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- GBool interpolate) {
- if (imageCache->tag && imageTag &&
- !imageCache->tag->cmp(imageTag) &&
- imageCache->isMask &&
- imageCache->width == scaledWidth &&
- imageCache->height == scaledHeight &&
- imageCache->interpolate == interpolate) {
- return imageCache->image;
- }
- if (imageCache->tag) {
- delete imageCache->tag;
- }
- if (imageCache->image) {
- delete imageCache->image;
- }
- imageCache->tag = imageTag ? imageTag->copy() : (GString *)NULL;
- imageCache->isMask = gTrue;
- imageCache->width = scaledWidth;
- imageCache->height = scaledHeight;
- imageCache->interpolate = interpolate;
- imageCache->vertFlip = gFalse;
- imageCache->horizFlip = gFalse;
- imageCache->image = new SplashBitmap(scaledWidth, scaledHeight,
- 1, splashModeMono8, gFalse,
- gTrue, NULL);
- if (scaledHeight < srcHeight) {
- if (scaledWidth < srcWidth) {
- scaleMaskYdXd(src, srcData, srcWidth, srcHeight,
- scaledWidth, scaledHeight, imageCache->image);
- } else {
- scaleMaskYdXu(src, srcData, srcWidth, srcHeight,
- scaledWidth, scaledHeight, imageCache->image);
- }
- } else {
- if (scaledWidth < srcWidth) {
- scaleMaskYuXd(src, srcData, srcWidth, srcHeight,
- scaledWidth, scaledHeight, imageCache->image);
- } else {
- if (interpolate) {
- scaleMaskYuXuI(src, srcData, srcWidth, srcHeight,
- scaledWidth, scaledHeight, imageCache->image);
- } else {
- scaleMaskYuXu(src, srcData, srcWidth, srcHeight,
- scaledWidth, scaledHeight, imageCache->image);
- }
- }
- }
- return imageCache->image;
-}
-
-void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest) {
- Guchar *lineBuf;
- Guint *pixBuf;
- Guint pix;
- Guchar *destPtr;
- int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1;
- int i, j;
-
- // Bresenham parameters for y scale
- yp = srcHeight / scaledHeight;
- yq = srcHeight % scaledHeight;
-
- // Bresenham parameters for x scale
- xp = srcWidth / scaledWidth;
- xq = srcWidth % scaledWidth;
-
- // allocate buffers
- lineBuf = (Guchar *)gmalloc(srcWidth);
- pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
-
- // init y scale Bresenham
- yt = 0;
-
- destPtr = dest->data;
- for (y = 0; y < scaledHeight; ++y) {
-
- // y scale Bresenham
- if ((yt += yq) >= scaledHeight) {
- yt -= scaledHeight;
- yStep = yp + 1;
- } else {
- yStep = yp;
- }
-
- // read rows from image
- memset(pixBuf, 0, srcWidth * sizeof(int));
- for (i = 0; i < yStep; ++i) {
- (*src)(srcData, lineBuf);
- for (j = 0; j < srcWidth; ++j) {
- pixBuf[j] += lineBuf[j];
+ if (y1 >= scaledHeight) {
+ y1 = scaledHeight - 1;
+ }
+ Guchar *p00 = scaledMask + (y0 * scaledWidth + x0);
+ Guchar *p10 = scaledMask + (y0 * scaledWidth + x1);
+ Guchar *p01 = scaledMask + (y1 * scaledWidth + x0);
+ Guchar *p11 = scaledMask + (y1 * scaledWidth + x1);
+ Guchar *q = buf + (x - xMin);
+ *q = (Guchar)(int)(sx0 * (sy0 * (int)*p00 + sy1 * (int)*p01) +
+ sx1 * (sy0 * (int)*p10 + sy1 * (int)*p11));
+ if (x < rowMin) {
+ rowMin = x;
+ }
+ rowMax = x + 1;
}
}
-
- // init x scale Bresenham
- xt = 0;
- d0 = (255 << 23) / (yStep * xp);
- d1 = (255 << 23) / (yStep * (xp + 1));
-
- xx = 0;
- for (x = 0; x < scaledWidth; ++x) {
-
- // x scale Bresenham
- if ((xt += xq) >= scaledWidth) {
- xt -= scaledWidth;
- xStep = xp + 1;
- d = d1;
- } else {
- xStep = xp;
- d = d0;
- }
-
- // compute the final pixel
- pix = 0;
- for (i = 0; i < xStep; ++i) {
- pix += pixBuf[xx++];
- }
- // (255 * pix) / xStep * yStep
- pix = (pix * d + (1 << 22)) >> 23;
-
- // store the pixel
- *destPtr++ = (Guchar)pix;
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd, buf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
}
}
- gfree(pixBuf);
- gfree(lineBuf);
+ gfree(buf);
}
-void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest) {
- Guchar *lineBuf;
- Guint *pixBuf;
- Guint pix;
- Guchar *destPtr;
- int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
- int i, j;
-
- // Bresenham parameters for y scale
- yp = srcHeight / scaledHeight;
- yq = srcHeight % scaledHeight;
-
- // Bresenham parameters for x scale
- xp = scaledWidth / srcWidth;
- xq = scaledWidth % srcWidth;
-
- // allocate buffers
- lineBuf = (Guchar *)gmalloc(srcWidth);
- pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
+void Splash::mirrorImageMaskRow(Guchar *maskIn, Guchar *maskOut, int width) {
+ Guchar *p, *q;
- // init y scale Bresenham
- yt = 0;
-
- destPtr = dest->data;
- for (y = 0; y < scaledHeight; ++y) {
-
- // y scale Bresenham
- if ((yt += yq) >= scaledHeight) {
- yt -= scaledHeight;
- yStep = yp + 1;
- } else {
- yStep = yp;
- }
-
- // read rows from image
- memset(pixBuf, 0, srcWidth * sizeof(int));
- for (i = 0; i < yStep; ++i) {
- (*src)(srcData, lineBuf);
- for (j = 0; j < srcWidth; ++j) {
- pixBuf[j] += lineBuf[j];
- }
- }
-
- // init x scale Bresenham
- xt = 0;
- d = (255 << 23) / yStep;
-
- for (x = 0; x < srcWidth; ++x) {
-
- // x scale Bresenham
- if ((xt += xq) >= srcWidth) {
- xt -= srcWidth;
- xStep = xp + 1;
- } else {
- xStep = xp;
- }
-
- // compute the final pixel
- pix = pixBuf[x];
- // (255 * pix) / yStep
- pix = (pix * d + (1 << 22)) >> 23;
-
- // store the pixel
- for (i = 0; i < xStep; ++i) {
- *destPtr++ = (Guchar)pix;
- }
- }
+ p = maskIn;
+ q = maskOut + (width - 1);
+ for (int i = 0; i < width; ++i) {
+ *q = *p;
+ ++p;
+ --q;
}
-
- gfree(pixBuf);
- gfree(lineBuf);
}
-void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest) {
- Guchar *lineBuf;
- Guint pix;
- Guchar *destPtr0, *destPtr;
- int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1;
- int i;
-
- // Bresenham parameters for y scale
- yp = scaledHeight / srcHeight;
- yq = scaledHeight % srcHeight;
-
- // Bresenham parameters for x scale
- xp = srcWidth / scaledWidth;
- xq = srcWidth % scaledWidth;
-
- // allocate buffers
- lineBuf = (Guchar *)gmalloc(srcWidth);
-
- // init y scale Bresenham
- yt = 0;
-
- destPtr0 = dest->data;
- for (y = 0; y < srcHeight; ++y) {
-
- // y scale Bresenham
- if ((yt += yq) >= srcHeight) {
- yt -= srcHeight;
- yStep = yp + 1;
- } else {
- yStep = yp;
- }
-
- // read row from image
- (*src)(srcData, lineBuf);
-
- // init x scale Bresenham
- xt = 0;
- d0 = (255 << 23) / xp;
- d1 = (255 << 23) / (xp + 1);
-
- xx = 0;
- for (x = 0; x < scaledWidth; ++x) {
-
- // x scale Bresenham
- if ((xt += xq) >= scaledWidth) {
- xt -= scaledWidth;
- xStep = xp + 1;
- d = d1;
- } else {
- xStep = xp;
- d = d0;
- }
-
- // compute the final pixel
- pix = 0;
- for (i = 0; i < xStep; ++i) {
- pix += lineBuf[xx++];
- }
- // (255 * pix) / xStep
- pix = (pix * d + (1 << 22)) >> 23;
-
- // store the pixel
- for (i = 0; i < yStep; ++i) {
- destPtr = destPtr0 + i * scaledWidth + x;
- *destPtr = (Guchar)pix;
- }
- }
-
- destPtr0 += yStep * scaledWidth;
- }
-
- gfree(lineBuf);
+void Splash::drawImageMaskRowNoClip(SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width) {
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y, maskData, NULL);
}
-void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest) {
- Guchar *lineBuf;
- Guchar pix;
- Guchar *srcPtr, *destPtr;
- int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep;
- int i;
-
- // Bresenham parameters for y scale
- yp = scaledHeight / srcHeight;
- yq = scaledHeight % srcHeight;
-
- // Bresenham parameters for x scale
- xp = scaledWidth / srcWidth;
- xq = scaledWidth % srcWidth;
-
- // allocate buffers
- lineBuf = (Guchar *)gmalloc(srcWidth);
-
- // init y scale Bresenham
- yt = 0;
-
- destPtr = dest->data;
- for (y = 0; y < srcHeight; ++y) {
-
- // y scale Bresenham
- if ((yt += yq) >= srcHeight) {
- yt -= srcHeight;
- yStep = yp + 1;
- } else {
- yStep = yp;
- }
-
- // read row from image
- (*src)(srcData, lineBuf);
-
- // init x scale Bresenham
- xt = 0;
-
- // generate one row
- srcPtr = lineBuf;
- for (x = 0; x < srcWidth; ++x) {
-
- // x scale Bresenham
- if ((xt += xq) >= srcWidth) {
- xt -= srcWidth;
- xStep = xp + 1;
- } else {
- xStep = xp;
- }
-
- // compute the final pixel
- pix = *srcPtr ? 255 : 0;
- ++srcPtr;
-
- // duplicate the pixel horizontally
- for (i = 0; i < xStep; ++i) {
- *destPtr++ = pix;
- }
- }
-
- // duplicate the row vertically
- for (i = 1 ; i < yStep; ++i) {
- memcpy(destPtr, destPtr - scaledWidth, scaledWidth);
- destPtr += scaledWidth;
- }
+void Splash::drawImageMaskRowClipNoAA(SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
}
-
- gfree(lineBuf);
+ if (x < 0) {
+ maskData -= x;
+ width += x;
+ x = 0;
+ }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
+ }
+ if (width <= 0) {
+ return;
+ }
+ memcpy(scanBuf + x, maskData, width);
+ state->clip->clipSpanBinary(scanBuf, y, x, x + width - 1,
+ state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, NULL);
}
-void Splash::scaleMaskYuXuI(SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest) {
- Guchar *lineBuf0, *lineBuf1, *tBuf;
- Guchar pix;
- SplashCoord yr, xr, ys, xs, ySrc, xSrc;
- int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x;
- Guchar *destPtr;
-
- // ratios
- yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight;
- xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth;
-
- // allocate buffers
- lineBuf0 = (Guchar *)gmalloc(scaledWidth);
- lineBuf1 = (Guchar *)gmalloc(scaledWidth);
-
- // read first two rows
- (*src)(srcData, lineBuf0);
- if (srcHeight > 1) {
- (*src)(srcData, lineBuf1);
- yBuf = 1;
- } else {
- memcpy(lineBuf1, lineBuf0, srcWidth);
- yBuf = 0;
- }
-
- // interpolate first two rows
- for (x = scaledWidth - 1; x >= 0; --x) {
- xSrc = xr * x;
- xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5);
- xSrc1 = xSrc0 + 1;
- xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5);
- if (xSrc0 < 0) {
- xSrc0 = 0;
- }
- if (xSrc1 >= srcWidth) {
- xSrc1 = srcWidth - 1;
- }
- lineBuf0[x] = (Guchar)(int)
- ((xs * (int)lineBuf0[xSrc0] +
- ((SplashCoord)1 - xs) * (int)lineBuf0[xSrc1]) * 255);
- lineBuf1[x] = (Guchar)(int)
- ((xs * (int)lineBuf1[xSrc0] +
- ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1]) * 255);
- }
-
- destPtr = dest->data;
- for (y = 0; y < scaledHeight; ++y) {
-
- // compute vertical interpolation parameters
- ySrc = yr * y;
- ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5);
- ySrc1 = ySrc0 + 1;
- ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5);
- if (ySrc0 < 0) {
- ySrc0 = 0;
- ys = 1;
- }
- if (ySrc1 >= srcHeight) {
- ySrc1 = srcHeight - 1;
- ys = 0;
- }
-
- // read another row (if necessary)
- if (ySrc1 > yBuf) {
- tBuf = lineBuf0;
- lineBuf0 = lineBuf1;
- lineBuf1 = tBuf;
- (*src)(srcData, lineBuf1);
-
- // interpolate the row
- for (x = scaledWidth - 1; x >= 0; --x) {
- xSrc = xr * x;
- xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5);
- xSrc1 = xSrc0 + 1;
- xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5);
- if (xSrc0 < 0) {
- xSrc0 = 0;
- }
- if (xSrc1 >= srcWidth) {
- xSrc1 = srcWidth - 1;
- }
- lineBuf1[x] = (Guchar)(int)
- ((xs * (int)lineBuf1[xSrc0] +
- ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1]) * 255);
- }
-
- ++yBuf;
- }
-
- // do the vertical interpolation
- for (x = 0; x < scaledWidth; ++x) {
-
- pix = (Guchar)(int)(ys * (int)lineBuf0[x] +
- ((SplashCoord)1 - ys) * (int)lineBuf1[x]);
-
- // store the pixel
- *destPtr++ = pix;
- }
+void Splash::drawImageMaskRowClipAA(SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
}
-
- gfree(lineBuf1);
- gfree(lineBuf0);
+ if (x < 0) {
+ maskData -= x;
+ width += x;
+ x = 0;
+ }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
+ }
+ if (width <= 0) {
+ return;
+ }
+ memcpy(scanBuf + x, maskData, width);
+ state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, NULL);
}
-void Splash::blitMask(SplashBitmap *src, int xDest, int yDest,
- SplashClipResult clipRes) {
+struct SplashDrawImageRowData {
+ int nComps;
+ GBool srcAlpha;
SplashPipe pipe;
- int w, h, x0, x1, y0, y1, y, t;
-
- w = src->width;
- h = src->height;
- pipeInit(&pipe, state->fillPattern,
- (Guchar)splashRound(state->fillAlpha * 255),
- gTrue, gFalse);
- if (clipRes == splashClipAllInside) {
- for (y = 0; y < h; ++y) {
- (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
- src->data + y * (size_t)w, NULL);
- }
- } else {
- x0 = xDest;
- if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
- x0 = t;
- }
- x1 = xDest + w;
- if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
- x1 = t;
- }
- y0 = yDest;
- if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
- y0 = t;
- }
- y1 = yDest + h;
- if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
- y1 = t;
- }
- if (x0 < x1 && y0 < y1) {
- for (y = y0; y < y1; ++y) {
- memcpy(scanBuf + x0,
- src->data + (y - yDest) * (size_t)w + (x0 - xDest),
- x1 - x0);
- if (vectorAntialias) {
- state->clip->clipSpan(scanBuf, y, x0, x1 - 1,
- state->strokeAdjust);
- } else {
- state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1,
- state->strokeAdjust);
- }
- (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, NULL);
- }
- }
- }
-}
+};
SplashError Splash::drawImage(GString *imageTag,
SplashImageSource src, void *srcData,
SplashColorMode srcMode, GBool srcAlpha,
int w, int h, SplashCoord *mat,
GBool interpolate) {
- GBool ok;
- SplashBitmap *scaledImg;
- SplashClipResult clipRes;
- GBool minorAxisZero;
- SplashCoord wSize, hSize, t0, t1;
- int x0, y0, x1, y1, scaledWidth, scaledHeight;
- int nComps;
-
if (debugMode) {
printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],
(double)mat[3], (double)mat[4], (double)mat[5]);
}
- // check color modes
- ok = gFalse; // make gcc happy
- nComps = 0; // make gcc happy
+ //--- check color modes
+ GBool ok = gFalse;
+ int nComps = 0;
switch (bitmap->mode) {
case splashModeMono1:
case splashModeMono8:
@@ -4998,1665 +6252,727 @@ SplashError Splash::drawImage(GString *imageTag,
return splashErrModeMismatch;
}
- // check for singular matrix
+ //--- check for singular matrix
if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
return splashErrSingularMatrix;
}
- minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001;
-
- // rough estimate of size of scaled image
- t0 = splashAbs(mat[0]);
- t1 = splashAbs(mat[1]);
- wSize = t0 > t1 ? t0 : t1;
- t0 = splashAbs(mat[2]);
- t1 = splashAbs(mat[3]);
- hSize = t0 > t1 ? t0 : t1;
-
- // stream-mode upscaling -- this is slower, so we only use it if the
- // upscaled image is large (in which case clipping should remove
- // many pixels)
-#if USE_FIXEDPOINT
- if ((wSize > 2 * w && hSize > 2 * h && (int)wSize > 1000000 / (int)hSize) ||
- (wSize > w && hSize > h && (int)wSize > 10000000 / (int)hSize) ||
- ((wSize > w || hSize > h) && (int)wSize > 25000000 / (int)hSize)) {
-#else
- if ((wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) ||
- (wSize > w && hSize > h && wSize * hSize > 10000000) ||
- ((wSize > w || hSize > h) && wSize * hSize > 25000000)) {
-#endif
- upscaleImage(src, srcData, srcMode, nComps, srcAlpha,
- w, h, mat, interpolate);
-
- // scaling only
- } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
- getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1);
- getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1);
- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
- if (clipRes != splashClipAllOutside) {
- scaledWidth = x1 - x0;
- scaledHeight = y1 - y0;
- scaledImg = scaleImage(imageTag,
- src, srcData, srcMode, nComps, srcAlpha, w, h,
- scaledWidth, scaledHeight, interpolate);
- if (imageCache->vertFlip) {
- vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
- imageCache->vertFlip = gFalse;
- }
- if (imageCache->horizFlip) {
- horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
- imageCache->horizFlip = gFalse;
- }
- blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
- }
-
- // scaling plus vertical flip
- } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
- getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1);
- getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1);
- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
- if (clipRes != splashClipAllOutside) {
- scaledWidth = x1 - x0;
- scaledHeight = y1 - y0;
- scaledImg = scaleImage(imageTag,
- src, srcData, srcMode, nComps, srcAlpha, w, h,
- scaledWidth, scaledHeight, interpolate);
- if (!imageCache->vertFlip) {
- vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
- imageCache->vertFlip = gTrue;
- }
- if (imageCache->horizFlip) {
- horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
- imageCache->horizFlip = gFalse;
- }
- blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
- }
-
- // scaling plus horizontal flip
- } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) {
- getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1);
- getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1);
- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
- if (clipRes != splashClipAllOutside) {
- scaledWidth = x1 - x0;
- scaledHeight = y1 - y0;
- scaledImg = scaleImage(imageTag,
- src, srcData, srcMode, nComps, srcAlpha, w, h,
- scaledWidth, scaledHeight, interpolate);
- if (imageCache->vertFlip) {
- vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
- imageCache->vertFlip = gFalse;
- }
- if (!imageCache->horizFlip) {
- horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
- imageCache->horizFlip = gTrue;
- }
- blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
- }
-
- // scaling plus horizontal and vertical flips
- } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) {
- getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1);
- getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1);
- clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
- if (clipRes != splashClipAllOutside) {
- scaledWidth = x1 - x0;
- scaledHeight = y1 - y0;
- scaledImg = scaleImage(imageTag,
- src, srcData, srcMode, nComps, srcAlpha, w, h,
- scaledWidth, scaledHeight, interpolate);
- if (!imageCache->vertFlip) {
- vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
- imageCache->vertFlip = gTrue;
+ //--- compute image bbox, check clipping
+ GBool flipsOnly = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001;
+ GBool horizFlip = gFalse;
+ GBool vertFlip = gFalse;
+ int xMin, yMin, xMax, yMax;
+ if (flipsOnly) {
+ horizFlip = mat[0] < 0;
+ vertFlip = mat[3] < 0;
+ if (vertFlip) {
+ if (horizFlip) { // bottom-up, mirrored
+ getImageBounds(mat[0] + mat[4], mat[4], &xMin, &xMax);
+ getImageBounds(mat[3] + mat[5], mat[5], &yMin, &yMax);
+ } else { // bottom-up
+ getImageBounds(mat[4], mat[0] + mat[4], &xMin, &xMax);
+ getImageBounds(mat[3] + mat[5], mat[5], &yMin, &yMax);
}
- if (!imageCache->horizFlip) {
- horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
- imageCache->horizFlip = gTrue;
+ } else {
+ if (horizFlip) { // top-down, mirrored
+ getImageBounds(mat[0] + mat[4], mat[4], &xMin, &xMax);
+ getImageBounds(mat[5], mat[3] + mat[5], &yMin, &yMax);
+ } else { // top-down
+ getImageBounds(mat[4], mat[0] + mat[4], &xMin, &xMax);
+ getImageBounds(mat[5], mat[3] + mat[5], &yMin, &yMax);
}
- blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
}
-
- // all other cases
} else {
- arbitraryTransformImage(imageTag, src, srcData, srcMode, nComps, srcAlpha,
- w, h, mat, interpolate);
- }
-
- return splashOk;
-}
-
-void Splash::upscaleImage(SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
- SplashCoord *mat, GBool interpolate) {
- SplashClipResult clipRes;
- SplashPipe pipe;
- SplashColorPtr unscaledImage, pixelBuf, p, q, q00, q01, q10, q11;
- Guchar *unscaledAlpha, *alphaPtr;
- SplashCoord xMin, yMin, xMax, yMax, t;
- SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det;
- SplashCoord ix, iy, sx, sy, pix0, pix1;
- SplashBitmapRowSize rowSize;
- int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt, i;
-
- // compute the bbox of the target quadrilateral
- xMin = xMax = mat[4];
- t = mat[2] + mat[4];
- if (t < xMin) {
- xMin = t;
- } else if (t > xMax) {
- xMax = t;
- }
- t = mat[0] + mat[2] + mat[4];
- if (t < xMin) {
- xMin = t;
- } else if (t > xMax) {
- xMax = t;
- }
- t = mat[0] + mat[4];
- if (t < xMin) {
- xMin = t;
- } else if (t > xMax) {
- xMax = t;
- }
- getImageBounds(xMin, xMax, &xMinI, &xMaxI);
- yMin = yMax = mat[5];
- t = mat[3] + mat[5];
- if (t < yMin) {
- yMin = t;
- } else if (t > yMax) {
- yMax = t;
- }
- t = mat[1] + mat[3] + mat[5];
- if (t < yMin) {
- yMin = t;
- } else if (t > yMax) {
- yMax = t;
- }
- t = mat[1] + mat[5];
- if (t < yMin) {
- yMin = t;
- } else if (t > yMax) {
- yMax = t;
- }
- getImageBounds(yMin, yMax, &yMinI, &yMaxI);
-
- // clipping
- clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
- if (clipRes == splashClipAllOutside) {
- return;
- }
- if (clipRes != splashClipAllInside) {
- if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) {
- xMinI = tt;
- }
- if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) {
- xMaxI = tt;
- }
- if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) {
- yMinI = tt;
- }
- if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) {
- yMaxI = tt;
- }
- }
+ int xx = splashRound(mat[4]); // (0,0)
+ int yy = splashRound(mat[5]);
+ xMin = xMax = xx;
+ yMin = yMax = yy;
+ xx = splashRound(mat[0] + mat[4]); // (1,0)
+ yy = splashRound(mat[1] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ xx = splashRound(mat[2] + mat[4]); // (0,1)
+ yy = splashRound(mat[3] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ xx = splashRound(mat[0] + mat[2] + mat[4]); // (1,1)
+ yy = splashRound(mat[1] + mat[3] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ if (xMax <= xMin) {
+ xMax = xMin + 1;
+ }
+ if (yMax <= yMin) {
+ yMax = yMin + 1;
+ }
+ }
+ SplashClipResult clipRes =
+ state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
+ state->strokeAdjust);
+ // If the scaled image is much wider and/or taller than the clip
+ // region, we use the arbitrary transform path, to avoid a
+ // potentially very slow loop in the flips-only path (which scans
+ // the full width and height of the scaled image, regardless of the
+ // clip region).
+ int clipW = state->clip->getXMaxI(state->strokeAdjust)
+ - state->clip->getXMinI(state->strokeAdjust);
+ int clipH = state->clip->getYMaxI(state->strokeAdjust)
+ - state->clip->getYMinI(state->strokeAdjust);
+ GBool veryLarge = ((xMax - xMin) / 8 > clipW && xMax - xMin > 1000) ||
+ ((yMax - yMin) / 8 > clipH && yMax - yMin > 1000);
+
+ //--- set up the SplashDrawImageRowData object and the pipes
+ SplashDrawImageRowData dd;
+ dd.nComps = nComps;
+ dd.srcAlpha = srcAlpha;
+ pipeInit(&dd.pipe, NULL,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ clipRes != splashClipAllInside || srcAlpha,
+ gFalse);
- // invert the matrix
- det = mat[0] * mat[3] - mat[1] * mat[2];
- if (splashAbs(det) < 1e-6) {
- // this should be caught by the singular matrix check in fillImageMask
- return;
- }
- det = (SplashCoord)1 / det;
- mi0 = det * mat[3] * srcWidth;
- mi1 = -det * mat[1] * srcHeight;
- mi2 = -det * mat[2] * srcWidth;
- mi3 = det * mat[0] * srcHeight;
- mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth;
- mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight;
-
- // grab the image
- if (srcWidth > INT_MAX / nComps) {
- rowSize = -1;
- } else {
- rowSize = srcWidth * nComps;
- }
- unscaledImage = (SplashColorPtr)gmallocn64(srcHeight, rowSize);
- if (srcAlpha) {
- unscaledAlpha = (Guchar *)gmallocn(srcHeight, srcWidth);
- for (y = 0, p = unscaledImage, alphaPtr = unscaledAlpha;
- y < srcHeight;
- ++y, p += rowSize, alphaPtr += srcWidth) {
- (*src)(srcData, p, alphaPtr);
+ //--- choose the drawRow function
+ SplashDrawImageRowFunc drawRowFunc;
+ if (clipRes == splashClipAllInside) {
+ if (srcAlpha) {
+ drawRowFunc = &Splash::drawImageRowNoClipAlpha;
+ } else {
+ drawRowFunc = &Splash::drawImageRowNoClipNoAlpha;
}
} else {
- unscaledAlpha = NULL;
- for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += rowSize) {
- (*src)(srcData, p, NULL);
- }
- }
-
- // draw it
- pixelBuf = (SplashColorPtr)gmallocn(xMaxI - xMinI, nComps);
- pipeInit(&pipe, NULL,
- (Guchar)splashRound(state->fillAlpha * 255),
- gTrue, gFalse);
- for (y = yMinI; y < yMaxI; ++y) {
- p = pixelBuf;
- for (x = xMinI; x < xMaxI; ++x) {
- ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4;
- iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5;
- if (interpolate) {
- if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) {
- x0 = splashFloor(ix - 0.5);
- x1 = x0 + 1;
- sx = (ix - 0.5) - x0;
- y0 = splashFloor(iy - 0.5);
- y1 = y0 + 1;
- sy = (iy - 0.5) - y0;
- if (x0 < 0) {
- x0 = 0;
- }
- if (x1 >= srcWidth) {
- x1 = srcWidth - 1;
- }
- if (y0 < 0) {
- y0 = 0;
- }
- if (y1 >= srcHeight) {
- y1 = srcHeight - 1;
- }
- q00 = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x0 * nComps];
- q01 = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x1 * nComps];
- q10 = &unscaledImage[y1 * rowSize + (SplashBitmapRowSize)x0 * nComps];
- q11 = &unscaledImage[y1 * rowSize + (SplashBitmapRowSize)x1 * nComps];
- for (i = 0; i < nComps; ++i) {
- pix0 = ((SplashCoord)1 - sx) * (int)*q00++ + sx * (int)*q01++;
- pix1 = ((SplashCoord)1 - sx) * (int)*q10++ + sx * (int)*q11++;
- *p++ = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0
- + sy * pix1);
- }
- if (srcAlpha) {
- pix0 = ((SplashCoord)1 - sx)
- * (SplashCoord)unscaledAlpha[y0 * srcWidth + x0]
- + sx * (SplashCoord)unscaledAlpha[y0 * srcWidth + x1];
- pix1 = ((SplashCoord)1 - sx)
- * (SplashCoord)unscaledAlpha[y1 * srcWidth + x0]
- + sx * (SplashCoord)unscaledAlpha[y1 * srcWidth + x1];
- scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0
- + sy * pix1);
- } else {
- scanBuf[x] = 0xff;
- }
- } else {
- for (i = 0; i < nComps; ++i) {
- *p++ = 0;
- }
- scanBuf[x] = 0;
- }
+ if (srcAlpha) {
+ if (vectorAntialias) {
+ drawRowFunc = &Splash::drawImageRowClipAlphaAA;
} else {
- x0 = splashFloor(ix);
- y0 = splashFloor(iy);
- if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) {
- q = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x0 * nComps];
- for (i = 0; i < nComps; ++i) {
- *p++ = *q++;
- }
- if (srcAlpha) {
- scanBuf[x] = unscaledAlpha[y0 * srcWidth + x0];
- } else {
- scanBuf[x] = 0xff;
- }
- } else {
- for (i = 0; i < nComps; ++i) {
- *p++ = 0;
- }
- scanBuf[x] = 0;
- }
+ drawRowFunc = &Splash::drawImageRowClipAlphaNoAA;
}
- }
- if (clipRes != splashClipAllInside) {
+ } else {
if (vectorAntialias) {
- state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1,
- state->strokeAdjust);
+ drawRowFunc = &Splash::drawImageRowClipNoAlphaAA;
} else {
- state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1,
- state->strokeAdjust);
+ drawRowFunc = &Splash::drawImageRowClipNoAlphaNoAA;
}
}
- (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, pixelBuf);
- }
-
- gfree(pixelBuf);
- gfree(unscaledImage);
- gfree(unscaledAlpha);
-}
-
-void Splash::arbitraryTransformImage(GString *imageTag,
- SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha,
- int srcWidth, int srcHeight,
- SplashCoord *mat, GBool interpolate) {
- SplashBitmap *scaledImg;
- SplashClipResult clipRes;
- SplashPipe pipe;
- SplashColorPtr pixelBuf;
- int scaledWidth, scaledHeight, t0, t1;
- SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
- SplashCoord vx[4], vy[4];
- int xMin, yMin, xMax, yMax;
- ImageSection section[3];
- int nSections;
- int y, xa, xb, x, i, xx, yy;
-
- // compute the four vertices of the target quadrilateral
- vx[0] = mat[4]; vy[0] = mat[5];
- vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5];
- vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5];
- vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5];
-
- // clipping
- xMin = splashRound(vx[0]);
- xMax = splashRound(vx[0]);
- yMin = splashRound(vy[0]);
- yMax = splashRound(vy[0]);
- for (i = 1; i < 4; ++i) {
- t0 = splashRound(vx[i]);
- if (t0 < xMin) {
- xMin = t0;
- } else if (t0 > xMax) {
- xMax = t0;
- }
- t1 = splashRound(vy[i]);
- if (t1 < yMin) {
- yMin = t1;
- } else if (t1 > yMax) {
- yMax = t1;
- }
- }
- clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
- if (clipRes == splashClipAllOutside) {
- return;
- }
-
- // compute the scale factors
- if (mat[0] >= 0) {
- t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]);
- } else {
- t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]);
- }
- if (mat[1] >= 0) {
- t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]);
- } else {
- t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]);
- }
- scaledWidth = t0 > t1 ? t0 : t1;
- if (mat[2] >= 0) {
- t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]);
- } else {
- t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]);
- }
- if (mat[3] >= 0) {
- t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]);
- } else {
- t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]);
}
- scaledHeight = t0 > t1 ? t0 : t1;
- if (scaledWidth == 0) {
- scaledWidth = 1;
- }
- if (scaledHeight == 0) {
- scaledHeight = 1;
- }
-
- // compute the inverse transform (after scaling) matrix
- r00 = mat[0] / scaledWidth;
- r01 = mat[1] / scaledWidth;
- r10 = mat[2] / scaledHeight;
- r11 = mat[3] / scaledHeight;
- det = r00 * r11 - r01 * r10;
- if (splashAbs(det) < 1e-6) {
- // this should be caught by the singular matrix check in drawImage
- return;
- }
- ir00 = r11 / det;
- ir01 = -r01 / det;
- ir10 = -r10 / det;
- ir11 = r00 / det;
-
- // scale the input image
- scaledImg = scaleImage(imageTag, src, srcData, srcMode, nComps, srcAlpha,
- srcWidth, srcHeight, scaledWidth, scaledHeight,
- interpolate);
- if (imageCache->vertFlip) {
- vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
- imageCache->vertFlip = gFalse;
- }
- if (imageCache->horizFlip) {
- horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
- imageCache->horizFlip = gFalse;
- }
-
- // construct the three sections
- i = 0;
- if (vy[1] < vy[i]) {
- i = 1;
- }
- if (vy[2] < vy[i]) {
- i = 2;
- }
- if (vy[3] < vy[i]) {
- i = 3;
- }
- // NB: if using fixed point, 0.000001 will be truncated to zero,
- // so these two comparisons must be <=, not <
- if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 &&
- vy[(i-1) & 3] < vy[(i+1) & 3]) {
- i = (i-1) & 3;
- }
- if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) {
- section[0].y0 = splashRound(vy[i]);
- section[0].y1 = splashRound(vy[(i+2) & 3]) - 1;
- if (vx[i] < vx[(i+1) & 3]) {
- section[0].ia0 = i;
- section[0].ia1 = (i+3) & 3;
- section[0].ib0 = (i+1) & 3;
- section[0].ib1 = (i+2) & 3;
- } else {
- section[0].ia0 = (i+1) & 3;
- section[0].ia1 = (i+2) & 3;
- section[0].ib0 = i;
- section[0].ib1 = (i+3) & 3;
- }
- nSections = 1;
- } else {
- section[0].y0 = splashRound(vy[i]);
- section[2].y1 = splashRound(vy[(i+2) & 3]) - 1;
- section[0].ia0 = section[0].ib0 = i;
- section[2].ia1 = section[2].ib1 = (i+2) & 3;
- if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
- section[0].ia1 = section[2].ia0 = (i+1) & 3;
- section[0].ib1 = section[2].ib0 = (i+3) & 3;
- } else {
- section[0].ia1 = section[2].ia0 = (i+3) & 3;
- section[0].ib1 = section[2].ib0 = (i+1) & 3;
- }
- if (vy[(i+1) & 3] < vy[(i+3) & 3]) {
- section[1].y0 = splashRound(vy[(i+1) & 3]);
- section[2].y0 = splashRound(vy[(i+3) & 3]);
- if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
- section[1].ia0 = (i+1) & 3;
- section[1].ia1 = (i+2) & 3;
- section[1].ib0 = i;
- section[1].ib1 = (i+3) & 3;
- } else {
- section[1].ia0 = i;
- section[1].ia1 = (i+3) & 3;
- section[1].ib0 = (i+1) & 3;
- section[1].ib1 = (i+2) & 3;
- }
- } else {
- section[1].y0 = splashRound(vy[(i+3) & 3]);
- section[2].y0 = splashRound(vy[(i+1) & 3]);
- if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
- section[1].ia0 = i;
- section[1].ia1 = (i+1) & 3;
- section[1].ib0 = (i+3) & 3;
- section[1].ib1 = (i+2) & 3;
- } else {
- section[1].ia0 = (i+3) & 3;
- section[1].ia1 = (i+2) & 3;
- section[1].ib0 = i;
- section[1].ib1 = (i+1) & 3;
- }
- }
- section[0].y1 = section[1].y0 - 1;
- section[1].y1 = section[2].y0 - 1;
- nSections = 3;
- }
- for (i = 0; i < nSections; ++i) {
- section[i].xa0 = vx[section[i].ia0];
- section[i].ya0 = vy[section[i].ia0];
- section[i].xa1 = vx[section[i].ia1];
- section[i].ya1 = vy[section[i].ia1];
- section[i].xb0 = vx[section[i].ib0];
- section[i].yb0 = vy[section[i].ib0];
- section[i].xb1 = vx[section[i].ib1];
- section[i].yb1 = vy[section[i].ib1];
- section[i].dxdya = (section[i].xa1 - section[i].xa0) /
- (section[i].ya1 - section[i].ya0);
- section[i].dxdyb = (section[i].xb1 - section[i].xb0) /
- (section[i].yb1 - section[i].yb0);
- }
-
- // initialize the pixel pipe
- pipeInit(&pipe, NULL,
- (Guchar)splashRound(state->fillAlpha * 255),
- gTrue, gFalse);
- // make sure narrow images cover at least one pixel
- if (nSections == 1) {
- if (section[0].y0 == section[0].y1) {
- ++section[0].y1;
- clipRes = opClipRes = splashClipPartial;
- }
- } else {
- if (section[0].y0 == section[2].y1) {
- ++section[1].y1;
- clipRes = opClipRes = splashClipPartial;
- }
- }
-
- pixelBuf = (SplashColorPtr)gmallocn(xMax - xMin + 1, bitmapComps);
-
- // scan all pixels inside the target region
- for (i = 0; i < nSections; ++i) {
- for (y = section[i].y0; y <= section[i].y1; ++y) {
- xa = splashRound(section[i].xa0 +
- ((SplashCoord)y + 0.5 - section[i].ya0) *
- section[i].dxdya);
- xb = splashRound(section[i].xb0 +
- ((SplashCoord)y + 0.5 - section[i].yb0) *
- section[i].dxdyb);
- if (xa > xb) {
- continue;
- }
- // make sure narrow images cover at least one pixel
- if (xa == xb) {
- ++xb;
- }
- // check the scanBuf bounds
- if (xa >= bitmap->width || xb < 0) {
- continue;
- }
- if (xa < 0) {
- xa = 0;
- }
- if (xb > bitmap->width) {
- xb = bitmap->width;
- }
- // clip the scan line
- memset(scanBuf + xa, 0xff, xb - xa);
- if (clipRes != splashClipAllInside) {
- if (vectorAntialias) {
- state->clip->clipSpan(scanBuf, y, xa, xb - 1,
- state->strokeAdjust);
- } else {
- state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1,
- state->strokeAdjust);
- }
- }
- // draw the scan line
- for (x = xa; x < xb; ++x) {
- // map (x+0.5, y+0.5) back to the scaled image
- xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 +
- ((SplashCoord)y + 0.5 - mat[5]) * ir10);
- yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 +
- ((SplashCoord)y + 0.5 - mat[5]) * ir11);
- // xx should always be within bounds, but floating point
- // inaccuracy can cause problems
- if (xx < 0) {
- xx = 0;
- } else if (xx >= scaledWidth) {
- xx = scaledWidth - 1;
- }
- if (yy < 0) {
- yy = 0;
- } else if (yy >= scaledHeight) {
- yy = scaledHeight - 1;
- }
- // get the color
- scaledImg->getPixel(xx, yy, pixelBuf + (x - xa) * bitmapComps);
- // apply alpha
+ //--- horizontal/vertical flips only
+ if (flipsOnly && !veryLarge) {
+ if (clipRes != splashClipAllOutside) {
+ int scaledWidth = xMax - xMin;
+ int scaledHeight = yMax - yMin;
+ ImageScaler *scaler = getImageScaler(imageTag, src, srcData,
+ w, h, nComps,
+ scaledWidth, scaledHeight,
+ srcMode, srcAlpha, interpolate);
+ Guchar *tmpLine = NULL;
+ Guchar *tmpAlphaLine = NULL;
+ if (horizFlip) {
+ tmpLine = (Guchar *)gmallocn(scaledWidth, nComps);
if (srcAlpha) {
- scanBuf[x] = div255(scanBuf[x] *
- scaledImg->alpha[yy * scaledWidth + xx]);
+ tmpAlphaLine = (Guchar *)gmalloc(scaledWidth);
}
}
- (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, pixelBuf);
- }
- }
-
- gfree(pixelBuf);
-}
-
-// Scale an image into a SplashBitmap.
-SplashBitmap *Splash::scaleImage(GString *imageTag,
- SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- GBool interpolate) {
- if (imageCache->tag && imageTag &&
- !imageCache->tag->cmp(imageTag) &&
- !imageCache->isMask &&
- imageCache->width == scaledWidth &&
- imageCache->height == scaledHeight &&
- imageCache->mode == srcMode &&
- imageCache->alpha == srcAlpha &&
- imageCache->interpolate == interpolate) {
- return imageCache->image;
- }
- if (imageCache->tag) {
- delete imageCache->tag;
- }
- if (imageCache->image) {
- delete imageCache->image;
- }
- imageCache->tag = imageTag ? imageTag->copy() : (GString *)NULL;
- imageCache->isMask = gFalse;
- imageCache->width = scaledWidth;
- imageCache->height = scaledHeight;
- imageCache->mode = srcMode;
- imageCache->alpha = srcAlpha;
- imageCache->interpolate = interpolate;
- imageCache->vertFlip = gFalse;
- imageCache->horizFlip = gFalse;
- imageCache->image = new SplashBitmap(scaledWidth, scaledHeight, 1,
- srcMode, srcAlpha, gTrue, NULL);
- if (scaledHeight < srcHeight) {
- if (scaledWidth < srcWidth) {
- scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha,
- srcWidth, srcHeight, scaledWidth, scaledHeight,
- imageCache->image);
- } else {
- scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha,
- srcWidth, srcHeight, scaledWidth, scaledHeight,
- imageCache->image);
- }
- } else {
- if (scaledWidth < srcWidth) {
- scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha,
- srcWidth, srcHeight, scaledWidth, scaledHeight,
- imageCache->image);
- } else {
- if (interpolate) {
- scaleImageYuXuI(src, srcData, srcMode, nComps, srcAlpha,
- srcWidth, srcHeight, scaledWidth, scaledHeight,
- imageCache->image);
+ if (vertFlip) {
+ if (horizFlip) { // bottom-up, mirrored
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler->nextLine();
+ mirrorImageRow(scaler->colorData(), scaler->alphaData(),
+ tmpLine, tmpAlphaLine,
+ scaledWidth, nComps, srcAlpha);
+ (this->*drawRowFunc)(&dd, tmpLine, tmpAlphaLine,
+ xMin, yMax - 1 - y, scaledWidth);
+ }
+ } else { // bottom-up
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler->nextLine();
+ (this->*drawRowFunc)(&dd, scaler->colorData(), scaler->alphaData(),
+ xMin, yMax - 1 - y, scaledWidth);
+ }
+ }
} else {
- scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha,
- srcWidth, srcHeight, scaledWidth, scaledHeight,
- imageCache->image);
+ if (horizFlip) { // top-down, mirrored
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler->nextLine();
+ mirrorImageRow(scaler->colorData(), scaler->alphaData(),
+ tmpLine, tmpAlphaLine,
+ scaledWidth, nComps, srcAlpha);
+ (this->*drawRowFunc)(&dd, tmpLine, tmpAlphaLine,
+ xMin, yMin + y, scaledWidth);
+ }
+ } else { // top-down
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler->nextLine();
+ (this->*drawRowFunc)(&dd, scaler->colorData(), scaler->alphaData(),
+ xMin, yMin + y, scaledWidth);
+ }
+ }
}
+ gfree(tmpLine);
+ gfree(tmpAlphaLine);
+ delete scaler;
}
- }
- return imageCache->image;
-}
-void Splash::scaleImageYdXd(SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest) {
- Guchar *lineBuf, *alphaLineBuf;
- Guint *pixBuf, *alphaPixBuf;
- Guint pix0, pix1, pix2;
-#if SPLASH_CMYK
- Guint pix3;
-#endif
- Guint alpha;
- Guchar *destPtr, *destAlphaPtr;
- int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
- int i, j;
-
- // Bresenham parameters for y scale
- yp = srcHeight / scaledHeight;
- yq = srcHeight % scaledHeight;
-
- // Bresenham parameters for x scale
- xp = srcWidth / scaledWidth;
- xq = srcWidth % scaledWidth;
-
- // allocate buffers
- lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
- pixBuf = (Guint *)gmallocn(srcWidth, (int)(nComps * sizeof(int)));
- if (srcAlpha) {
- alphaLineBuf = (Guchar *)gmalloc(srcWidth);
- alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
+ //--- arbitrary transform
} else {
- alphaLineBuf = NULL;
- alphaPixBuf = NULL;
- }
-
- // init y scale Bresenham
- yt = 0;
-
- destPtr = dest->data;
- destAlphaPtr = dest->alpha;
- for (y = 0; y < scaledHeight; ++y) {
-
- // y scale Bresenham
- if ((yt += yq) >= scaledHeight) {
- yt -= scaledHeight;
- yStep = yp + 1;
+ // estimate of size of scaled image
+ int scaledWidth = splashRound(splashSqrt(mat[0] * mat[0]
+ + mat[1] * mat[1]));
+ int scaledHeight = splashRound(splashSqrt(mat[2] * mat[2]
+ + mat[3] * mat[3]));
+ if (scaledWidth < 1) {
+ scaledWidth = 1;
+ }
+ if (scaledHeight < 1) {
+ scaledHeight = 1;
+ }
+ if (veryLarge || (scaledWidth >= w && scaledHeight >= h)) {
+ scaledWidth = w;
+ scaledHeight = h;
+ }
+
+ // compute mapping from device space to scaled image space
+ SplashCoord mat1[6];
+ mat1[0] = mat[0] / scaledWidth;
+ mat1[1] = mat[1] / scaledWidth;
+ mat1[2] = mat[2] / scaledHeight;
+ mat1[3] = mat[3] / scaledHeight;
+ mat1[4] = mat[4];
+ mat1[5] = mat[5];
+ SplashCoord det = mat1[0] * mat1[3] - mat1[1] * mat1[2];
+ if (splashAbs(det) < 1e-6) {
+ // this should be caught by the singular matrix check in drawImage
+ return splashErrSingularMatrix;
+ }
+ SplashCoord invMat[6];
+ invMat[0] = mat1[3] / det;
+ invMat[1] = -mat1[1] / det;
+ invMat[2] = -mat1[2] / det;
+ invMat[3] = mat1[0] / det;
+ // the extra "+ 0.5 * (...)" terms are here because the
+ // drawImageArbitrary(No)Interp functions multiply by pixel
+ // centers, (x + 0.5, y + 0.5)
+ invMat[4] = (mat1[2] * mat1[5] - mat1[3] * mat1[4]) / det
+ + (invMat[0] + invMat[2]) * 0.5;
+ invMat[5] = (mat1[1] * mat1[4] - mat1[0] * mat1[5]) / det
+ + (invMat[1] + invMat[3]) * 0.5;
+
+ Guchar *scaledColor, *scaledAlpha;
+ GBool freeScaledImage;
+ getScaledImage(imageTag, src, srcData, w, h, nComps,
+ scaledWidth, scaledHeight, srcMode, srcAlpha, interpolate,
+ &scaledColor, &scaledAlpha, &freeScaledImage);
+
+ // draw it
+ if (interpolate) {
+ drawImageArbitraryInterp(scaledColor, scaledAlpha,
+ &dd, drawRowFunc, invMat,
+ scaledWidth, scaledHeight,
+ xMin, yMin, xMax, yMax,
+ nComps, srcAlpha);
} else {
- yStep = yp;
+ drawImageArbitraryNoInterp(scaledColor, scaledAlpha,
+ &dd, drawRowFunc, invMat,
+ scaledWidth, scaledHeight,
+ xMin, yMin, xMax, yMax,
+ nComps, srcAlpha);
}
- // read rows from image
- memset(pixBuf, 0, srcWidth * nComps * sizeof(int));
- if (srcAlpha) {
- memset(alphaPixBuf, 0, srcWidth * sizeof(int));
- }
- for (i = 0; i < yStep; ++i) {
- (*src)(srcData, lineBuf, alphaLineBuf);
- for (j = 0; j < srcWidth * nComps; ++j) {
- pixBuf[j] += lineBuf[j];
- }
- if (srcAlpha) {
- for (j = 0; j < srcWidth; ++j) {
- alphaPixBuf[j] += alphaLineBuf[j];
- }
- }
+ // free the downscaled/unscaled image
+ if (freeScaledImage) {
+ gfree(scaledColor);
+ gfree(scaledAlpha);
}
+ }
- // init x scale Bresenham
- xt = 0;
- d0 = (1 << 23) / (yStep * xp);
- d1 = (1 << 23) / (yStep * (xp + 1));
-
- xx = xxa = 0;
- for (x = 0; x < scaledWidth; ++x) {
+ return splashOk;
+}
- // x scale Bresenham
- if ((xt += xq) >= scaledWidth) {
- xt -= scaledWidth;
- xStep = xp + 1;
- d = d1;
+ImageScaler *Splash::getImageScaler(GString *imageTag,
+ SplashImageSource src, void *srcData,
+ int w, int h, int nComps,
+ int scaledWidth, int scaledHeight,
+ SplashColorMode srcMode,
+ GBool srcAlpha, GBool interpolate) {
+ // Notes:
+ //
+ // * If the scaled image width or height is greater than 2000, we
+ // don't cache it.
+ //
+ // * Caching is done on the third consecutive use (second
+ // consecutive reuse) of an image; this avoids overhead on the
+ // common case of single-use images.
+
+ if (scaledWidth < 2000 && scaledHeight < 2000 &&
+ imageCache->match(imageTag, scaledWidth, scaledHeight,
+ srcMode, srcAlpha, interpolate)) {
+ if (imageCache->colorData) {
+ return new ReplayImageScaler(nComps, srcAlpha, scaledWidth,
+ imageCache->colorData,
+ imageCache->alphaData);
+ } else {
+ int lineSize;
+ if (scaledWidth < INT_MAX / nComps) {
+ lineSize = scaledWidth * nComps;
} else {
- xStep = xp;
- d = d0;
+ lineSize = -1;
}
-
- switch (srcMode) {
-
- case splashModeMono8:
-
- // compute the final pixel
- pix0 = 0;
- for (i = 0; i < xStep; ++i) {
- pix0 += pixBuf[xx++];
- }
- // pix / xStep * yStep
- pix0 = (pix0 * d + (1 << 22)) >> 23;
-
- // store the pixel
- *destPtr++ = (Guchar)pix0;
- break;
-
- case splashModeRGB8:
-
- // compute the final pixel
- pix0 = pix1 = pix2 = 0;
- for (i = 0; i < xStep; ++i) {
- pix0 += pixBuf[xx];
- pix1 += pixBuf[xx+1];
- pix2 += pixBuf[xx+2];
- xx += 3;
- }
- // pix / xStep * yStep
- pix0 = (pix0 * d + (1 << 22)) >> 23;
- pix1 = (pix1 * d + (1 << 22)) >> 23;
- pix2 = (pix2 * d + (1 << 22)) >> 23;
-
- // store the pixel
- *destPtr++ = (Guchar)pix0;
- *destPtr++ = (Guchar)pix1;
- *destPtr++ = (Guchar)pix2;
- break;
-
-#if SPLASH_CMYK
- case splashModeCMYK8:
-
- // compute the final pixel
- pix0 = pix1 = pix2 = pix3 = 0;
- for (i = 0; i < xStep; ++i) {
- pix0 += pixBuf[xx];
- pix1 += pixBuf[xx+1];
- pix2 += pixBuf[xx+2];
- pix3 += pixBuf[xx+3];
- xx += 4;
- }
- // pix / xStep * yStep
- pix0 = (pix0 * d + (1 << 22)) >> 23;
- pix1 = (pix1 * d + (1 << 22)) >> 23;
- pix2 = (pix2 * d + (1 << 22)) >> 23;
- pix3 = (pix3 * d + (1 << 22)) >> 23;
-
- // store the pixel
- *destPtr++ = (Guchar)pix0;
- *destPtr++ = (Guchar)pix1;
- *destPtr++ = (Guchar)pix2;
- *destPtr++ = (Guchar)pix3;
- break;
-#endif
-
-
- case splashModeMono1: // mono1 is not allowed
- case splashModeBGR8: // bgr8 is not allowed
- default:
- break;
- }
-
- // process alpha
+ imageCache->colorData = (Guchar *)gmallocn(scaledHeight, lineSize);
if (srcAlpha) {
- alpha = 0;
- for (i = 0; i < xStep; ++i, ++xxa) {
- alpha += alphaPixBuf[xxa];
- }
- // alpha / xStep * yStep
- alpha = (alpha * d + (1 << 22)) >> 23;
- *destAlphaPtr++ = (Guchar)alpha;
+ imageCache->alphaData = (Guchar *)gmallocn(scaledHeight, scaledWidth);
}
+ return new SavingImageScaler(src, srcData,
+ w, h, nComps, srcAlpha,
+ scaledWidth, scaledHeight,
+ interpolate,
+ imageCache->colorData,
+ imageCache->alphaData);
}
+ } else {
+ imageCache->reset(imageTag, scaledWidth, scaledHeight,
+ srcMode, srcAlpha, interpolate);
+ return new BasicImageScaler(src, srcData,
+ w, h, nComps, srcAlpha,
+ scaledWidth, scaledHeight,
+ interpolate);
}
-
- gfree(alphaPixBuf);
- gfree(alphaLineBuf);
- gfree(pixBuf);
- gfree(lineBuf);
}
-void Splash::scaleImageYdXu(SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
+void Splash::getScaledImage(GString *imageTag,
+ SplashImageSource src, void *srcData,
+ int w, int h, int nComps,
int scaledWidth, int scaledHeight,
- SplashBitmap *dest) {
- Guchar *lineBuf, *alphaLineBuf;
- Guint *pixBuf, *alphaPixBuf;
- Guint pix[splashMaxColorComps];
- Guint alpha;
- Guchar *destPtr, *destAlphaPtr;
- int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
- int i, j;
-
- // Bresenham parameters for y scale
- yp = srcHeight / scaledHeight;
- yq = srcHeight % scaledHeight;
-
- // Bresenham parameters for x scale
- xp = scaledWidth / srcWidth;
- xq = scaledWidth % srcWidth;
-
- // allocate buffers
- lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
- pixBuf = (Guint *)gmallocn(srcWidth, (int)(nComps * sizeof(int)));
- if (srcAlpha) {
- alphaLineBuf = (Guchar *)gmalloc(srcWidth);
- alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
- } else {
- alphaLineBuf = NULL;
- alphaPixBuf = NULL;
- }
-
- // make gcc happy
- pix[0] = pix[1] = pix[2] = 0;
-#if SPLASH_CMYK
- pix[3] = 0;
-#endif
-
- // init y scale Bresenham
- yt = 0;
-
- destPtr = dest->data;
- destAlphaPtr = dest->alpha;
- for (y = 0; y < scaledHeight; ++y) {
-
- // y scale Bresenham
- if ((yt += yq) >= scaledHeight) {
- yt -= scaledHeight;
- yStep = yp + 1;
+ SplashColorMode srcMode,
+ GBool srcAlpha, GBool interpolate,
+ Guchar **scaledColor, Guchar **scaledAlpha,
+ GBool *freeScaledImage) {
+ // Notes:
+ //
+ // * If the scaled image width or height is greater than 2000, we
+ // don't cache it.
+ //
+ // * This buffers the whole image anyway, so there's no reason to
+ // skip caching on the first reuse.
+
+ if (scaledWidth >= 2000 || scaledHeight >= 2000) {
+ int lineSize;
+ if (scaledWidth < INT_MAX / nComps) {
+ lineSize = scaledWidth * nComps;
} else {
- yStep = yp;
+ lineSize = -1;
}
-
- // read rows from image
- memset(pixBuf, 0, srcWidth * nComps * sizeof(int));
+ *scaledColor = (Guchar *)gmallocn(scaledHeight, lineSize);
if (srcAlpha) {
- memset(alphaPixBuf, 0, srcWidth * sizeof(int));
- }
- for (i = 0; i < yStep; ++i) {
- (*src)(srcData, lineBuf, alphaLineBuf);
- for (j = 0; j < srcWidth * nComps; ++j) {
- pixBuf[j] += lineBuf[j];
+ *scaledAlpha = (Guchar *)gmallocn(scaledHeight, scaledWidth);
+ } else {
+ *scaledAlpha = NULL;
+ }
+ *freeScaledImage = gTrue;
+ if (scaledWidth == w && scaledHeight == h) {
+ Guchar *colorPtr = *scaledColor;
+ Guchar *alphaPtr = *scaledAlpha;
+ for (int y = 0; y < scaledHeight; ++y) {
+ (*src)(srcData, colorPtr, alphaPtr);
+ colorPtr += scaledWidth * nComps;
+ if (srcAlpha) {
+ alphaPtr += scaledWidth;
+ }
}
- if (srcAlpha) {
- for (j = 0; j < srcWidth; ++j) {
- alphaPixBuf[j] += alphaLineBuf[j];
+ } else {
+ BasicImageScaler scaler(src, srcData, w, h, nComps, srcAlpha,
+ scaledWidth, scaledHeight, interpolate);
+ Guchar *colorPtr = *scaledColor;
+ Guchar *alphaPtr = *scaledAlpha;
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ memcpy(colorPtr, scaler.colorData(), scaledWidth * nComps);
+ colorPtr += scaledWidth * nComps;
+ if (srcAlpha) {
+ memcpy(alphaPtr, scaler.alphaData(), scaledWidth);
+ alphaPtr += scaledWidth;
}
}
}
-
- // init x scale Bresenham
- xt = 0;
- d = (1 << 23) / yStep;
-
- for (x = 0; x < srcWidth; ++x) {
-
- // x scale Bresenham
- if ((xt += xq) >= srcWidth) {
- xt -= srcWidth;
- xStep = xp + 1;
+ } else {
+ if (!imageCache->match(imageTag, scaledWidth, scaledHeight,
+ srcMode, srcAlpha, interpolate) ||
+ !imageCache->colorData) {
+ imageCache->reset(imageTag, scaledWidth, scaledHeight,
+ srcMode, srcAlpha, interpolate);
+ int lineSize;
+ if (scaledWidth < INT_MAX / nComps) {
+ lineSize = scaledWidth * nComps;
} else {
- xStep = xp;
+ lineSize = -1;
}
-
- // compute the final pixel
- for (i = 0; i < nComps; ++i) {
- // pixBuf[] / yStep
- pix[i] = (pixBuf[x * nComps + i] * d + (1 << 22)) >> 23;
- }
-
- // store the pixel
- switch (srcMode) {
- case splashModeMono8:
- for (i = 0; i < xStep; ++i) {
- *destPtr++ = (Guchar)pix[0];
- }
- break;
- case splashModeRGB8:
- for (i = 0; i < xStep; ++i) {
- *destPtr++ = (Guchar)pix[0];
- *destPtr++ = (Guchar)pix[1];
- *destPtr++ = (Guchar)pix[2];
- }
- break;
-#if SPLASH_CMYK
- case splashModeCMYK8:
- for (i = 0; i < xStep; ++i) {
- *destPtr++ = (Guchar)pix[0];
- *destPtr++ = (Guchar)pix[1];
- *destPtr++ = (Guchar)pix[2];
- *destPtr++ = (Guchar)pix[3];
- }
- break;
-#endif
- case splashModeMono1: // mono1 is not allowed
- case splashModeBGR8: // BGR8 is not allowed
- default:
- break;
- }
-
- // process alpha
+ imageCache->colorData = (Guchar *)gmallocn(scaledHeight, lineSize);
if (srcAlpha) {
- // alphaPixBuf[] / yStep
- alpha = (alphaPixBuf[x] * d + (1 << 22)) >> 23;
- for (i = 0; i < xStep; ++i) {
- *destAlphaPtr++ = (Guchar)alpha;
+ imageCache->alphaData = (Guchar *)gmallocn(scaledHeight, scaledWidth);
+ }
+ if (scaledWidth == w && scaledHeight == h) {
+ Guchar *colorPtr = imageCache->colorData;
+ Guchar *alphaPtr = imageCache->alphaData;
+ for (int y = 0; y < scaledHeight; ++y) {
+ (*src)(srcData, colorPtr, alphaPtr);
+ colorPtr += scaledWidth * nComps;
+ if (srcAlpha) {
+ alphaPtr += scaledWidth;
+ }
+ }
+ } else {
+ SavingImageScaler scaler(src, srcData, w, h, nComps, srcAlpha,
+ scaledWidth, scaledHeight, interpolate,
+ imageCache->colorData, imageCache->alphaData);
+ Guchar *colorPtr = imageCache->colorData;
+ Guchar *alphaPtr = imageCache->alphaData;
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ memcpy(colorPtr, scaler.colorData(), scaledWidth * nComps);
+ colorPtr += scaledWidth * nComps;
+ if (srcAlpha) {
+ memcpy(alphaPtr, scaler.alphaData(), scaledWidth);
+ alphaPtr += scaledWidth;
+ }
}
}
}
+ *scaledColor = imageCache->colorData;
+ *scaledAlpha = imageCache->alphaData;
+ *freeScaledImage = gFalse;
}
-
- gfree(alphaPixBuf);
- gfree(alphaLineBuf);
- gfree(pixBuf);
- gfree(lineBuf);
}
-void Splash::scaleImageYuXd(SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest) {
- Guchar *lineBuf, *alphaLineBuf;
- Guint pix[splashMaxColorComps];
- Guint alpha;
- Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
- int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
- int i, j;
-
- // Bresenham parameters for y scale
- yp = scaledHeight / srcHeight;
- yq = scaledHeight % srcHeight;
-
- // Bresenham parameters for x scale
- xp = srcWidth / scaledWidth;
- xq = srcWidth % scaledWidth;
-
- // allocate buffers
- lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
- if (srcAlpha) {
- alphaLineBuf = (Guchar *)gmalloc(srcWidth);
- } else {
- alphaLineBuf = NULL;
+void Splash::drawImageArbitraryNoInterp(Guchar *scaledColor,
+ Guchar *scaledAlpha,
+ SplashDrawImageRowData *dd,
+ SplashDrawImageRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax,
+ int nComps, GBool srcAlpha) {
+ int tt = state->clip->getXMinI(state->strokeAdjust);
+ if (tt > xMin) {
+ xMin = tt;
+ }
+ tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
+ if (tt < xMax) {
+ xMax = tt;
+ }
+ tt = state->clip->getYMinI(state->strokeAdjust);
+ if (tt > yMin) {
+ yMin = tt;
+ }
+ tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
+ if (tt < yMax) {
+ yMax = tt;
+ }
+ if (xMax <= xMin || yMax <= yMin) {
+ return;
}
- // make gcc happy
- pix[0] = pix[1] = pix[2] = 0;
-#if SPLASH_CMYK
- pix[3] = 0;
-#endif
-
- // init y scale Bresenham
- yt = 0;
-
- destPtr0 = dest->data;
- destAlphaPtr0 = dest->alpha;
- for (y = 0; y < srcHeight; ++y) {
-
- // y scale Bresenham
- if ((yt += yq) >= srcHeight) {
- yt -= srcHeight;
- yStep = yp + 1;
- } else {
- yStep = yp;
- }
-
- // read row from image
- (*src)(srcData, lineBuf, alphaLineBuf);
-
- // init x scale Bresenham
- xt = 0;
- d0 = (1 << 23) / xp;
- d1 = (1 << 23) / (xp + 1);
-
- xx = xxa = 0;
- for (x = 0; x < scaledWidth; ++x) {
-
- // x scale Bresenham
- if ((xt += xq) >= scaledWidth) {
- xt -= scaledWidth;
- xStep = xp + 1;
- d = d1;
- } else {
- xStep = xp;
- d = d0;
- }
-
- // compute the final pixel
- for (i = 0; i < nComps; ++i) {
- pix[i] = 0;
- }
- for (i = 0; i < xStep; ++i) {
- for (j = 0; j < nComps; ++j, ++xx) {
- pix[j] += lineBuf[xx];
- }
- }
- for (i = 0; i < nComps; ++i) {
- // pix[] / xStep
- pix[i] = (pix[i] * d + (1 << 22)) >> 23;
- }
-
- // store the pixel
- switch (srcMode) {
- case splashModeMono8:
- for (i = 0; i < yStep; ++i) {
- destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
- *destPtr++ = (Guchar)pix[0];
- }
- break;
- case splashModeRGB8:
- for (i = 0; i < yStep; ++i) {
- destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
- *destPtr++ = (Guchar)pix[0];
- *destPtr++ = (Guchar)pix[1];
- *destPtr++ = (Guchar)pix[2];
- }
- break;
-#if SPLASH_CMYK
- case splashModeCMYK8:
- for (i = 0; i < yStep; ++i) {
- destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
- *destPtr++ = (Guchar)pix[0];
- *destPtr++ = (Guchar)pix[1];
- *destPtr++ = (Guchar)pix[2];
- *destPtr++ = (Guchar)pix[3];
+ Guchar *colorBuf = (Guchar *)gmallocn(xMax - xMin, nComps);
+ Guchar *alphaBuf = NULL;
+ if (srcAlpha) {
+ alphaBuf = (Guchar *)gmalloc(xMax - xMin);
+ }
+
+ for (int y = yMin; y < yMax; ++y) {
+ int rowMin = xMax;
+ int rowMax = 0;
+ for (int x = xMin; x < xMax; ++x) {
+ // note: invMat includes a "+0.5" factor so that this is really
+ // a multiply by (x+0.5, y+0.5)
+ int xx = splashFloor((SplashCoord)x * invMat[0]
+ + (SplashCoord)y * invMat[2] + invMat[4]);
+ int yy = splashFloor((SplashCoord)x * invMat[1]
+ + (SplashCoord)y * invMat[3] + invMat[5]);
+ if (xx >= 0 && xx < scaledWidth &&
+ yy >= 0 && yy < scaledHeight) {
+ Guchar *p = scaledColor + (yy * scaledWidth + xx) * nComps;
+ Guchar *q = colorBuf + (x - xMin) * nComps;
+ for (int i = 0; i < nComps; ++i) {
+ *q++ = *p++;
}
- break;
-#endif
- case splashModeMono1: // mono1 is not allowed
- case splashModeBGR8: // BGR8 is not allowed
- default:
- break;
- }
-
- // process alpha
- if (srcAlpha) {
- alpha = 0;
- for (i = 0; i < xStep; ++i, ++xxa) {
- alpha += alphaLineBuf[xxa];
+ if (srcAlpha) {
+ alphaBuf[x - xMin] = scaledAlpha[yy * scaledWidth + xx];
}
- // alpha / xStep
- alpha = (alpha * d + (1 << 22)) >> 23;
- for (i = 0; i < yStep; ++i) {
- destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x;
- *destAlphaPtr = (Guchar)alpha;
+ if (x < rowMin) {
+ rowMin = x;
}
+ rowMax = x + 1;
}
}
-
- destPtr0 += yStep * scaledWidth * nComps;
- if (srcAlpha) {
- destAlphaPtr0 += yStep * scaledWidth;
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd,
+ colorBuf + (rowMin - xMin) * nComps,
+ alphaBuf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
}
}
- gfree(alphaLineBuf);
- gfree(lineBuf);
+ gfree(colorBuf);
+ gfree(alphaBuf);
}
-void Splash::scaleImageYuXu(SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest) {
- Guchar *lineBuf, *alphaLineBuf;
- Guchar pix0, pix1, pix2;
-#if SPLASH_CMYK
- Guchar pix3;
-#endif
- Guchar alpha;
- Guchar *srcPtr, *srcAlphaPtr;
- Guchar *destPtr, *destAlphaPtr;
- int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep;
- int i;
-
- // Bresenham parameters for y scale
- yp = scaledHeight / srcHeight;
- yq = scaledHeight % srcHeight;
-
- // Bresenham parameters for x scale
- xp = scaledWidth / srcWidth;
- xq = scaledWidth % srcWidth;
-
- // allocate buffers
- lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
- if (srcAlpha) {
- alphaLineBuf = (Guchar *)gmalloc(srcWidth);
- } else {
- alphaLineBuf = NULL;
+void Splash::drawImageArbitraryInterp(Guchar *scaledColor, Guchar *scaledAlpha,
+ SplashDrawImageRowData *dd,
+ SplashDrawImageRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax,
+ int nComps, GBool srcAlpha) {
+ int tt = state->clip->getXMinI(state->strokeAdjust);
+ if (tt > xMin) {
+ xMin = tt;
+ }
+ tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
+ if (tt < xMax) {
+ xMax = tt;
+ }
+ tt = state->clip->getYMinI(state->strokeAdjust);
+ if (tt > yMin) {
+ yMin = tt;
+ }
+ tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
+ if (tt < yMax) {
+ yMax = tt;
+ }
+ if (xMax <= xMin || yMax <= yMin) {
+ return;
}
- // init y scale Bresenham
- yt = 0;
-
- destPtr = dest->data;
- destAlphaPtr = dest->alpha;
- for (y = 0; y < srcHeight; ++y) {
-
- // y scale Bresenham
- if ((yt += yq) >= srcHeight) {
- yt -= srcHeight;
- yStep = yp + 1;
- } else {
- yStep = yp;
- }
-
- // read row from image
- (*src)(srcData, lineBuf, alphaLineBuf);
-
- // init x scale Bresenham
- xt = 0;
-
- // generate one row
- srcPtr = lineBuf;
- srcAlphaPtr = alphaLineBuf;
- for (x = 0; x < srcWidth; ++x) {
-
- // x scale Bresenham
- if ((xt += xq) >= srcWidth) {
- xt -= srcWidth;
- xStep = xp + 1;
- } else {
- xStep = xp;
- }
-
- // duplicate the pixel horizontally
- switch (srcMode) {
- case splashModeMono8:
- pix0 = *srcPtr++;
- for (i = 0; i < xStep; ++i) {
- *destPtr++ = pix0;
+ Guchar *colorBuf = (Guchar *)gmallocn(xMax - xMin, nComps);
+ Guchar *alphaBuf = NULL;
+ if (srcAlpha) {
+ alphaBuf = (Guchar *)gmalloc(xMax - xMin);
+ }
+
+ for (int y = yMin; y < yMax; ++y) {
+ int rowMin = xMax;
+ int rowMax = 0;
+ for (int x = xMin; x < xMax; ++x) {
+ // note: invMat includes a "+0.5" factor so that this is really
+ // a multiply by (x+0.5, y+0.5)
+ SplashCoord xs = (SplashCoord)x * invMat[0]
+ + (SplashCoord)y * invMat[2] + invMat[4];
+ SplashCoord ys = (SplashCoord)x * invMat[1]
+ + (SplashCoord)y * invMat[3] + invMat[5];
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ if (x1 >= 0 && x0 < scaledWidth && y1 >= 0 && y0 < scaledHeight) {
+ SplashCoord sx0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord sx1 = (SplashCoord)1 - sx0;
+ SplashCoord sy0 = (SplashCoord)y1 + 0.5 - ys;
+ SplashCoord sy1 = (SplashCoord)1 - sy0;
+ if (x0 < 0) {
+ x0 = 0;
}
- break;
- case splashModeRGB8:
- pix0 = *srcPtr++;
- pix1 = *srcPtr++;
- pix2 = *srcPtr++;
- for (i = 0; i < xStep; ++i) {
- *destPtr++ = pix0;
- *destPtr++ = pix1;
- *destPtr++ = pix2;
+ if (x1 >= scaledWidth) {
+ x1 = scaledWidth - 1;
}
- break;
-#if SPLASH_CMYK
- case splashModeCMYK8:
- pix0 = *srcPtr++;
- pix1 = *srcPtr++;
- pix2 = *srcPtr++;
- pix3 = *srcPtr++;
- for (i = 0; i < xStep; ++i) {
- *destPtr++ = pix0;
- *destPtr++ = pix1;
- *destPtr++ = pix2;
- *destPtr++ = pix3;
+ if (y0 < 0) {
+ y0 = 0;
}
- break;
-#endif
- case splashModeMono1: // mono1 is not allowed
- case splashModeBGR8: // BGR8 is not allowed
- default:
- break;
- }
-
- // duplicate the alpha value horizontally
- if (srcAlpha) {
- alpha = *srcAlphaPtr++;
- for (i = 0; i < xStep; ++i) {
- *destAlphaPtr++ = alpha;
+ if (y1 >= scaledHeight) {
+ y1 = scaledHeight - 1;
}
+ Guchar *p00 = scaledColor + (y0 * scaledWidth + x0) * nComps;
+ Guchar *p10 = scaledColor + (y0 * scaledWidth + x1) * nComps;
+ Guchar *p01 = scaledColor + (y1 * scaledWidth + x0) * nComps;
+ Guchar *p11 = scaledColor + (y1 * scaledWidth + x1) * nComps;
+ Guchar *q = colorBuf + (x - xMin) * nComps;
+ for (int i = 0; i < nComps; ++i) {
+ *q++ = (Guchar)(int)(sx0 * (sy0 * (int)*p00++ + sy1 * (int)*p01++) +
+ sx1 * (sy0 * (int)*p10++ + sy1 * (int)*p11++));
+ }
+ if (srcAlpha) {
+ p00 = scaledAlpha + (y0 * scaledWidth + x0);
+ p10 = scaledAlpha + (y0 * scaledWidth + x1);
+ p01 = scaledAlpha + (y1 * scaledWidth + x0);
+ p11 = scaledAlpha + (y1 * scaledWidth + x1);
+ q = alphaBuf + (x - xMin);
+ *q = (Guchar)(int)(sx0 * (sy0 * (int)*p00 + sy1 * (int)*p01) +
+ sx1 * (sy0 * (int)*p10 + sy1 * (int)*p11));
+ }
+ if (x < rowMin) {
+ rowMin = x;
+ }
+ rowMax = x + 1;
}
}
-
- // duplicate the row vertically
- for (i = 1; i < yStep; ++i) {
- memcpy(destPtr, destPtr - scaledWidth * nComps,
- scaledWidth * nComps);
- destPtr += scaledWidth * nComps;
- }
- if (srcAlpha) {
- for (i = 1; i < yStep; ++i) {
- memcpy(destAlphaPtr, destAlphaPtr - scaledWidth, scaledWidth);
- destAlphaPtr += scaledWidth;
- }
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd,
+ colorBuf + (rowMin - xMin) * nComps,
+ alphaBuf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
}
}
- gfree(alphaLineBuf);
- gfree(lineBuf);
+ gfree(colorBuf);
+ gfree(alphaBuf);
}
-void Splash::scaleImageYuXuI(SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest) {
- Guchar *lineBuf0, *lineBuf1, *alphaLineBuf0, *alphaLineBuf1, *tBuf;
- Guchar pix[splashMaxColorComps];
- SplashCoord yr, xr, ys, xs, ySrc, xSrc;
- int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x, i;
- Guchar *destPtr, *destAlphaPtr;
-
- // ratios
- yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight;
- xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth;
-
- // allocate buffers
- lineBuf0 = (Guchar *)gmallocn(scaledWidth, nComps);
- lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps);
- if (srcAlpha) {
- alphaLineBuf0 = (Guchar *)gmalloc(scaledWidth);
- alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth);
- } else {
- alphaLineBuf0 = NULL;
- alphaLineBuf1 = NULL;
- }
-
- // read first two rows
- (*src)(srcData, lineBuf0, alphaLineBuf0);
- if (srcHeight > 1) {
- (*src)(srcData, lineBuf1, alphaLineBuf1);
- yBuf = 1;
- } else {
- memcpy(lineBuf1, lineBuf0, srcWidth * nComps);
- if (srcAlpha) {
- memcpy(alphaLineBuf1, alphaLineBuf0, srcWidth);
- }
- yBuf = 0;
- }
+void Splash::mirrorImageRow(Guchar *colorIn, Guchar *alphaIn,
+ Guchar *colorOut, Guchar *alphaOut,
+ int width, int nComps, GBool srcAlpha) {
+ Guchar *p, *q;
- // interpolate first two rows
- for (x = scaledWidth - 1; x >= 0; --x) {
- xSrc = xr * x;
- xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5);
- xSrc1 = xSrc0 + 1;
- xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5);
- if (xSrc0 < 0) {
- xSrc0 = 0;
- }
- if (xSrc1 >= srcWidth) {
- xSrc1 = srcWidth - 1;
- }
- for (i = 0; i < nComps; ++i) {
- lineBuf0[x*nComps+i] = (Guchar)(int)
- (xs * (int)lineBuf0[xSrc0*nComps+i] +
- ((SplashCoord)1 - xs) * (int)lineBuf0[xSrc1*nComps+i]);
- lineBuf1[x*nComps+i] = (Guchar)(int)
- (xs * (int)lineBuf1[xSrc0*nComps+i] +
- ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1*nComps+i]);
- }
- if (srcAlpha) {
- alphaLineBuf0[x] = (Guchar)(int)
- (xs * (int)alphaLineBuf0[xSrc0] +
- ((SplashCoord)1 - xs) * (int)alphaLineBuf0[xSrc1]);
- alphaLineBuf1[x] = (Guchar)(int)
- (xs * (int)alphaLineBuf1[xSrc0] +
- ((SplashCoord)1 - xs) * (int)alphaLineBuf1[xSrc1]);
+ p = colorIn;
+ q = colorOut + (width - 1) * nComps;
+ for (int i = 0; i < width; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ q[j] = p[j];
}
+ p += nComps;
+ q -= nComps;
}
- // make gcc happy
- pix[0] = pix[1] = pix[2] = 0;
-#if SPLASH_CMYK
- pix[3] = 0;
-#endif
-
- destPtr = dest->data;
- destAlphaPtr = dest->alpha;
- for (y = 0; y < scaledHeight; ++y) {
-
- // compute vertical interpolation parameters
- ySrc = yr * y;
- ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5);
- ySrc1 = ySrc0 + 1;
- ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5);
- if (ySrc0 < 0) {
- ySrc0 = 0;
- ys = 1;
- }
- if (ySrc1 >= srcHeight) {
- ySrc1 = srcHeight - 1;
- ys = 0;
- }
-
- // read another row (if necessary)
- if (ySrc1 > yBuf) {
- tBuf = lineBuf0;
- lineBuf0 = lineBuf1;
- lineBuf1 = tBuf;
- tBuf = alphaLineBuf0;
- alphaLineBuf0 = alphaLineBuf1;
- alphaLineBuf1 = tBuf;
- (*src)(srcData, lineBuf1, alphaLineBuf1);
-
- // interpolate the row
- for (x = scaledWidth - 1; x >= 0; --x) {
- xSrc = xr * x;
- xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5);
- xSrc1 = xSrc0 + 1;
- xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5);
- if (xSrc0 < 0) {
- xSrc0 = 0;
- }
- if (xSrc1 >= srcWidth) {
- xSrc1 = srcWidth - 1;
- }
- for (i = 0; i < nComps; ++i) {
- lineBuf1[x*nComps+i] = (Guchar)(int)
- (xs * (int)lineBuf1[xSrc0*nComps+i] +
- ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1*nComps+i]);
- }
- if (srcAlpha) {
- alphaLineBuf1[x] = (Guchar)(int)
- (xs * (int)alphaLineBuf1[xSrc0] +
- ((SplashCoord)1 - xs) * (int)alphaLineBuf1[xSrc1]);
- }
- }
-
- ++yBuf;
- }
-
- // do the vertical interpolation
- for (x = 0; x < scaledWidth; ++x) {
-
- for (i = 0; i < nComps; ++i) {
- pix[i] = (Guchar)(int)
- (ys * (int)lineBuf0[x*nComps+i] +
- ((SplashCoord)1 - ys) * (int)lineBuf1[x*nComps+i]);
- }
-
- // store the pixel
- switch (srcMode) {
- case splashModeMono8:
- *destPtr++ = pix[0];
- break;
- case splashModeRGB8:
- *destPtr++ = pix[0];
- *destPtr++ = pix[1];
- *destPtr++ = pix[2];
- break;
-#if SPLASH_CMYK
- case splashModeCMYK8:
- *destPtr++ = pix[0];
- *destPtr++ = pix[1];
- *destPtr++ = pix[2];
- *destPtr++ = pix[3];
- break;
-#endif
- case splashModeMono1: // mono1 is not allowed
- case splashModeBGR8: // BGR8 is not allowed
- default:
- break;
- }
-
- // process alpha
- if (srcAlpha) {
- *destAlphaPtr++ = (Guchar)(int)
- (ys * (int)alphaLineBuf0[x] +
- ((SplashCoord)1 - ys) * (int)alphaLineBuf1[x]);
- }
+ if (srcAlpha) {
+ p = alphaIn;
+ q = alphaOut + (width - 1);
+ for (int i = 0; i < width; ++i) {
+ *q = *p;
+ ++p;
+ --q;
}
}
+}
- gfree(alphaLineBuf1);
- gfree(alphaLineBuf0);
- gfree(lineBuf1);
- gfree(lineBuf0);
+void Splash::drawImageRowNoClipNoAlpha(SplashDrawImageRowData *data,
+ Guchar *colorData, Guchar *alphaData,
+ int x, int y, int width) {
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y, NULL, colorData);
}
-void Splash::vertFlipImage(SplashBitmap *img, int width, int height,
- int nComps) {
- Guchar *lineBuf;
- Guchar *p0, *p1;
- int w;
+void Splash::drawImageRowNoClipAlpha(SplashDrawImageRowData *data,
+ Guchar *colorData, Guchar *alphaData,
+ int x, int y, int width) {
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ alphaData, colorData);
+}
- w = width * nComps;
- lineBuf = (Guchar *)gmalloc(w);
- for (p0 = img->data, p1 = img->data + (height - 1) * (size_t)w;
- p0 < p1;
- p0 += w, p1 -= w) {
- memcpy(lineBuf, p0, w);
- memcpy(p0, p1, w);
- memcpy(p1, lineBuf, w);
+void Splash::drawImageRowClipNoAlphaNoAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
}
- if (img->alpha) {
- for (p0 = img->alpha, p1 = img->alpha + (height - 1) * (size_t)width;
- p0 < p1;
- p0 += width, p1 -= width) {
- memcpy(lineBuf, p0, width);
- memcpy(p0, p1, width);
- memcpy(p1, lineBuf, width);
- }
+ if (x < 0) {
+ colorData -= x * data->nComps;
+ width += x;
+ x = 0;
}
- gfree(lineBuf);
-}
-
-void Splash::horizFlipImage(SplashBitmap *img, int width, int height,
- int nComps) {
- Guchar *lineBuf;
- SplashColorPtr p0, p1, p2;
- int w, x, y, i;
-
- w = width * nComps;
- lineBuf = (Guchar *)gmalloc(w);
- for (y = 0, p0 = img->data; y < height; ++y, p0 += img->rowSize) {
- memcpy(lineBuf, p0, w);
- p1 = p0;
- p2 = lineBuf + (w - nComps);
- for (x = 0; x < width; ++x) {
- for (i = 0; i < nComps; ++i) {
- p1[i] = p2[i];
- }
- p1 += nComps;
- p2 -= nComps;
- }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
}
- if (img->alpha) {
- for (y = 0, p0 = img->alpha; y < height; ++y, p0 += width) {
- memcpy(lineBuf, p0, width);
- p1 = p0;
- p2 = lineBuf + (width - 1);
- for (x = 0; x < width; ++x) {
- *p1++ = *p2--;
- }
- }
+ if (width <= 0) {
+ return;
}
- gfree(lineBuf);
+ memset(scanBuf + x, 0xff, width);
+ state->clip->clipSpanBinary(scanBuf, y, x, x + width - 1,
+ state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, colorData);
}
-void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
- SplashClipResult clipRes) {
- SplashPipe pipe;
- int w, h, x0, y0, x1, y1, y;
-
- // split the image into clipped and unclipped regions
- w = src->width;
- h = src->height;
- if (clipRes == splashClipAllInside) {
- x0 = 0;
- y0 = 0;
- x1 = w;
- y1 = h;
- } else {
- if (state->clip->getNumPaths()) {
- x0 = x1 = w;
- y0 = y1 = h;
- } else {
- if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) {
- x0 = 0;
- }
- if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) {
- y0 = 0;
- }
- if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) {
- x1 = w;
- }
- if (x1 < x0) {
- x1 = x0;
- }
- if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) {
- y1 = h;
- }
- if (y1 < y0) {
- y1 = y0;
- }
- }
+void Splash::drawImageRowClipNoAlphaAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
}
-
- // draw the unclipped region
- if (x0 < w && y0 < h && x0 < x1 && y0 < y1) {
- pipeInit(&pipe, NULL,
- (Guchar)splashRound(state->fillAlpha * 255),
- srcAlpha, gFalse);
- if (srcAlpha) {
- for (y = y0; y < y1; ++y) {
- (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y,
- src->alpha + y * src->alphaRowSize + x0,
- src->data + y * src->rowSize + x0 * bitmapComps);
- }
- } else {
- for (y = y0; y < y1; ++y) {
- (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y,
- NULL,
- src->data + y * src->getRowSize() +
- x0 * bitmapComps);
- }
- }
+ if (x < 0) {
+ colorData -= x * data->nComps;
+ width += x;
+ x = 0;
+ }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
+ }
+ if (width <= 0) {
+ return;
}
+ memset(scanBuf + x, 0xff, width);
+ state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, colorData);
+}
- // draw the clipped regions
- if (y0 > 0) {
- blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0);
+void Splash::drawImageRowClipAlphaNoAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
}
- if (y1 < h) {
- blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1);
+ if (x < 0) {
+ colorData -= x * data->nComps;
+ alphaData -= x;
+ width += x;
+ x = 0;
}
- if (x0 > 0 && y0 < y1) {
- blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0);
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
}
- if (x1 < w && y0 < y1) {
- blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0,
- w - x1, y1 - y0);
+ if (width <= 0) {
+ return;
}
+ memcpy(scanBuf + x, alphaData, width);
+ state->clip->clipSpanBinary(scanBuf, y, x, x + width - 1,
+ state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, colorData);
}
-void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha,
- int xSrc, int ySrc, int xDest, int yDest,
- int w, int h) {
- SplashPipe pipe;
- int y;
-
- if (xDest < 0) {
- xSrc -= xDest;
- w += xDest;
- xDest = 0;
- }
- if (xDest + w > bitmap->width) {
- w = bitmap->width - xDest;
+void Splash::drawImageRowClipAlphaAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
}
- if (yDest < 0) {
- ySrc -= yDest;
- h += yDest;
- yDest = 0;
+ if (x < 0) {
+ colorData -= x * data->nComps;
+ alphaData -= x;
+ width += x;
+ x = 0;
}
- if (yDest + h > bitmap->height) {
- h = bitmap->height - yDest;
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
}
- if (w <= 0 || h <= 0) {
+ if (width <= 0) {
return;
}
-
- pipeInit(&pipe, NULL,
- (Guchar)splashRound(state->fillAlpha * 255),
- gTrue, gFalse);
- if (srcAlpha) {
- for (y = 0; y < h; ++y) {
- memcpy(scanBuf + xDest,
- src->alpha + (ySrc + y) * src->alphaRowSize + xSrc,
- w);
- if (vectorAntialias) {
- state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1,
- state->strokeAdjust);
- } else {
- state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1,
- state->strokeAdjust);
- }
- (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
- scanBuf + xDest,
- src->data + (ySrc + y) * src->rowSize +
- xSrc * bitmapComps);
- }
- } else {
- for (y = 0; y < h; ++y) {
- memset(scanBuf + xDest, 0xff, w);
- if (vectorAntialias) {
- state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1,
- state->strokeAdjust);
- } else {
- state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1,
- state->strokeAdjust);
- }
- (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
- scanBuf + xDest,
- src->data + (ySrc + y) * src->rowSize +
- xSrc * bitmapComps);
- }
- }
+ memcpy(scanBuf + x, alphaData, width);
+ state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, colorData);
}
SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
@@ -6950,6 +7266,101 @@ SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
return splashOk;
}
+SplashError Splash::compositeWithOverprint(SplashBitmap *src,
+ Guint *srcOverprintMaskBitmap,
+ int xSrc, int ySrc,
+ int xDest, int yDest, int w, int h,
+ GBool noClip, GBool nonIsolated) {
+ SplashPipe pipe;
+ int x0, x1, y0, y1, y, t;
+
+ if (!(src->mode == bitmap->mode ||
+ (src->mode == splashModeMono8 && bitmap->mode == splashModeMono1) ||
+ (src->mode == splashModeRGB8 && bitmap->mode == splashModeBGR8))) {
+ return splashErrModeMismatch;
+ }
+
+ pipeInit(&pipe, NULL,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ !noClip || src->alpha != NULL, nonIsolated, gTrue);
+
+ if (noClip) {
+ if (src->alpha) {
+ for (y = 0; y < h; ++y) {
+ pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap + y * w + xSrc;
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ src->alpha +
+ (ySrc + y) * src->alphaRowSize + xSrc,
+ src->data + (ySrc + y) * src->rowSize +
+ xSrc * bitmapComps);
+ }
+ } else {
+ for (y = 0; y < h; ++y) {
+ pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap + y * w + xSrc;
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ NULL,
+ src->data + (ySrc + y) * src->rowSize +
+ xSrc * bitmapComps);
+ }
+ }
+ } else {
+ x0 = xDest;
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) {
+ x0 = t;
+ }
+ x1 = xDest + w;
+ if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) {
+ x1 = t;
+ }
+ y0 = yDest;
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) {
+ y0 = t;
+ }
+ y1 = yDest + h;
+ if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) {
+ y1 = t;
+ }
+ if (x0 < x1 && y0 < y1) {
+ if (src->alpha) {
+ for (y = y0; y < y1; ++y) {
+ memcpy(scanBuf + x0,
+ src->alpha + (ySrc + y - yDest) * src->alphaRowSize +
+ (xSrc + x0 - xDest),
+ x1 - x0);
+ state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
+ pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap
+ + (ySrc + y - yDest) * w
+ + (xSrc + x0 - xDest);
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, x0, x1 - 1, y,
+ scanBuf + x0,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * bitmapComps);
+ }
+ } else {
+ for (y = y0; y < y1; ++y) {
+ memset(scanBuf + x0, 0xff, x1 - x0);
+ state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
+ pipe.srcOverprintMaskPtr = srcOverprintMaskBitmap
+ + (ySrc + y - yDest) * w
+ + (xSrc + x0 - xDest);
+ (this->*pipe.run)(&pipe, x0, x1 - 1, y,
+ scanBuf + x0,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * bitmapComps);
+ }
+ }
+ }
+ }
+
+ return splashOk;
+}
+
void Splash::compositeBackground(SplashColorPtr color) {
SplashColorPtr p;
Guchar *q;
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/Splash.h b/Build/source/libs/xpdf/xpdf-src/splash/Splash.h
index f35aa7eafd9..d6cc33a5fad 100644
--- a/Build/source/libs/xpdf/xpdf-src/splash/Splash.h
+++ b/Build/source/libs/xpdf/xpdf-src/splash/Splash.h
@@ -29,6 +29,17 @@ class SplashPath;
class SplashXPath;
class SplashFont;
struct SplashPipe;
+struct SplashDrawImageMaskRowData;
+class ImageScaler;
+typedef void (Splash::*SplashDrawImageMaskRowFunc)(
+ SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width);
+struct SplashDrawImageRowData;
+typedef void (Splash::*SplashDrawImageRowFunc)(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width);
//------------------------------------------------------------------------
@@ -66,6 +77,15 @@ enum SplashPipeResultColorCtrl {
};
//------------------------------------------------------------------------
+
+// Transparency group destination bitmap initialization control.
+enum SplashGroupDestInitMode {
+ splashGroupDestPreInit, // dest is already initialized
+ splashGroupDestInitZero, // initialize to zero (isolated group)
+ splashGroupDestInitCopy // copy backdrop (non-isolated group)
+};
+
+//------------------------------------------------------------------------
// SplashImageCache
//------------------------------------------------------------------------
@@ -76,19 +96,23 @@ public:
SplashImageCache();
~SplashImageCache();
+ GBool match(GString *aTag, int aWidth, int aHeight,
+ SplashColorMode aMode, GBool aAlpha,
+ GBool aInterpolate);
+ void reset(GString *aTag, int aWidth, int aHeight,
+ SplashColorMode aMode, GBool aAlpha,
+ GBool aInterpolate);
void incRefCount();
void decRefCount();
GString *tag;
- GBool isMask;
int width;
int height;
SplashColorMode mode;
GBool alpha;
GBool interpolate;
- GBool vertFlip;
- GBool horizFlip;
- SplashBitmap *image;
+ Guchar *colorData;
+ Guchar *alphaData;
int refCount;
};
@@ -161,7 +185,9 @@ public:
void setSoftMask(SplashBitmap *softMask);
void setInTransparencyGroup(SplashBitmap *groupBackBitmapA,
int groupBackXA, int groupBackYA,
+ SplashGroupDestInitMode groupDestInitModeA,
GBool nonIsolated, GBool knockout);
+ void forceDeferredInit(int y, int h);
void setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray);
void setOverprintMask(Guint overprintMask);
void setEnablePathSimplification(GBool en);
@@ -234,6 +260,15 @@ public:
int xDest, int yDest, int w, int h,
GBool noClip, GBool nonIsolated);
+ // Composite a rectangular region from <src> onto this Splash
+ // object, using <srcOverprintMaskBitmap> as the overprint mask per
+ // pixel. This is only supported for CMYK and DeviceN bitmaps.
+ SplashError compositeWithOverprint(SplashBitmap *src,
+ Guint *srcOverprintMaskBitmap,
+ int xSrc, int ySrc,
+ int xDest, int yDest, int w, int h,
+ GBool noClip, GBool nonIsolated);
+
// Composite this Splash object onto a background color. The
// background alpha is assumed to be 1.
void compositeBackground(SplashColorPtr color);
@@ -269,6 +304,10 @@ public:
// Return the associated bitmap.
SplashBitmap *getBitmap() { return bitmap; }
+ // Enable writing the per-pixel overprint mask to a separate bitmap.
+ void setOverprintMaskBitmap(Guint *overprintMaskBitmapA)
+ { overprintMaskBitmap = overprintMaskBitmapA; }
+
// Set the minimum line width.
void setMinLineWidth(SplashCoord w) { minLineWidth = w; }
@@ -298,7 +337,7 @@ private:
void pipeInit(SplashPipe *pipe, SplashPattern *pattern,
Guchar aInput, GBool usesShape,
- GBool nonIsolatedGroup);
+ GBool nonIsolatedGroup, GBool usesSrcOverprint = gFalse);
void pipeRun(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr);
void pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y,
@@ -325,6 +364,8 @@ private:
void pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr);
#endif
+ void pipeRunShapeNoAlphaMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
void pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr);
void pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y,
@@ -337,6 +378,16 @@ private:
void pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr);
#endif
+ void pipeRunSoftMaskMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunSoftMaskRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunSoftMaskBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+#if SPLASH_CMYK
+ void pipeRunSoftMaskCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+#endif
void pipeRunNonIsoMono8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr);
void pipeRunNonIsoRGB8(SplashPipe *pipe, int x0, int x1, int y,
@@ -347,6 +398,8 @@ private:
void pipeRunNonIsoCMYK8(SplashPipe *pipe, int x0, int x1, int y,
Guchar *shapePtr, SplashColorPtr cSrcPtr);
#endif
+ void useDestRow(int y);
+ void copyGroupBackdropRow(int y);
void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi,
SplashCoord *xo, SplashCoord *yo);
void updateModX(int x);
@@ -371,92 +424,81 @@ private:
SplashError fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph);
void getImageBounds(SplashCoord xyMin, SplashCoord xyMax,
int *xyMinI, int *xyMaxI);
- void upscaleMask(SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- SplashCoord *mat, GBool glyphMode,
- GBool interpolate);
- void arbitraryTransformMask(GString *imageTag,
- SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- SplashCoord *mat, GBool glyphMode,
- GBool interpolate);
- SplashBitmap *scaleMask(GString *imageTag,
- SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- GBool interpolate);
- void scaleMaskYdXd(SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest);
- void scaleMaskYdXu(SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest);
- void scaleMaskYuXd(SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest);
- void scaleMaskYuXu(SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest);
- void scaleMaskYuXuI(SplashImageMaskSource src, void *srcData,
- int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest);
- void blitMask(SplashBitmap *src, int xDest, int yDest,
- SplashClipResult clipRes);
- void upscaleImage(SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
- SplashCoord *mat, GBool interpolate);
- void arbitraryTransformImage(GString *imageTag,
- SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha,
- int srcWidth, int srcHeight,
- SplashCoord *mat, GBool interpolate);
- SplashBitmap *scaleImage(GString *imageTag,
- SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- GBool interpolate);
- void scaleImageYdXd(SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest);
- void scaleImageYdXu(SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest);
- void scaleImageYuXd(SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest);
- void scaleImageYuXu(SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
+ void drawImageMaskArbitraryNoInterp(Guchar *scaledMask,
+ SplashDrawImageMaskRowData *dd,
+ SplashDrawImageMaskRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax);
+ void drawImageMaskArbitraryInterp(Guchar *scaledMask,
+ SplashDrawImageMaskRowData *dd,
+ SplashDrawImageMaskRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax);
+ void mirrorImageMaskRow(Guchar *maskIn, Guchar *maskOut, int width);
+ void drawImageMaskRowNoClip(SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width);
+ void drawImageMaskRowClipNoAA(SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width);
+ void drawImageMaskRowClipAA(SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width);
+ ImageScaler *getImageScaler(GString *imageTag,
+ SplashImageSource src, void *srcData,
+ int w, int h, int nComps,
+ int scaledWidth, int scaledHeight,
+ SplashColorMode srcMode,
+ GBool srcAlpha, GBool interpolate);
+ void getScaledImage(GString *imageTag,
+ SplashImageSource src, void *srcData,
+ int w, int h, int nComps,
int scaledWidth, int scaledHeight,
- SplashBitmap *dest);
- void scaleImageYuXuI(SplashImageSource src, void *srcData,
- SplashColorMode srcMode, int nComps,
- GBool srcAlpha, int srcWidth, int srcHeight,
- int scaledWidth, int scaledHeight,
- SplashBitmap *dest);
- void vertFlipImage(SplashBitmap *img, int width, int height,
- int nComps);
- void horizFlipImage(SplashBitmap *img, int width, int height,
- int nComps);
- void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
- SplashClipResult clipRes);
- void blitImageClipped(SplashBitmap *src, GBool srcAlpha,
- int xSrc, int ySrc, int xDest, int yDest,
- int w, int h);
+ SplashColorMode srcMode,
+ GBool srcAlpha, GBool interpolate,
+ Guchar **scaledColor, Guchar **scaledAlpha,
+ GBool *freeScaledImage);
+ void drawImageArbitraryNoInterp(Guchar *scaledColor, Guchar *scaledAlpha,
+ SplashDrawImageRowData *dd,
+ SplashDrawImageRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax,
+ int nComps, GBool srcAlpha);
+ void drawImageArbitraryInterp(Guchar *scaledColor, Guchar *scaledAlpha,
+ SplashDrawImageRowData *dd,
+ SplashDrawImageRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax,
+ int nComps, GBool srcAlpha);
+ void mirrorImageRow(Guchar *colorIn, Guchar *alphaIn,
+ Guchar *colorOut, Guchar *alphaOut,
+ int width, int nComps, GBool srcAlpha);
+ void drawImageRowNoClipNoAlpha(SplashDrawImageRowData *data,
+ Guchar *colorData, Guchar *alphaData,
+ int x, int y, int width);
+ void drawImageRowNoClipAlpha(SplashDrawImageRowData *data,
+ Guchar *colorData, Guchar *alphaData,
+ int x, int y, int width);
+ void drawImageRowClipNoAlphaNoAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width);
+ void drawImageRowClipNoAlphaAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width);
+ void drawImageRowClipAlphaNoAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width);
+ void drawImageRowClipAlphaAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width);
void dumpPath(SplashPath *path);
void dumpXPath(SplashXPath *path);
@@ -474,6 +516,9 @@ private:
SplashBitmap // for transparency groups, this is the bitmap
*groupBackBitmap; // containing the alpha0/color0 values
int groupBackX, groupBackY; // offset within groupBackBitmap
+ SplashGroupDestInitMode groupDestInitMode;
+ int groupDestInitYMin, groupDestInitYMax;
+ Guint *overprintMaskBitmap;
SplashCoord minLineWidth;
int modXMin, modYMin, modXMax, modYMax;
SplashClipResult opClipRes;
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc
index 88ce49ca3b4..db341d7c733 100644
--- a/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc
@@ -16,6 +16,8 @@
#include <limits.h>
#include "gmem.h"
#include "gmempp.h"
+#include "gfile.h"
+#include "Trace.h"
#include "SplashErrorCodes.h"
#include "SplashBitmap.h"
@@ -64,6 +66,11 @@ SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPad,
rowSize += rowPad - 1;
rowSize -= rowSize % rowPad;
+ traceAlloc(this, "alloc bitmap: %d x %d x %d %s -> %lld bytes",
+ width, height, splashColorModeNComps[mode],
+ alphaA ? "with alpha" : "without alpha",
+ height * rowSize + (alphaA ? height * width : 0));
+
parent = parentA;
oldData = NULL;
oldAlpha = NULL;
@@ -75,8 +82,14 @@ SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPad,
parent->oldHeight == height) {
data = parent->oldData;
parent->oldData = NULL;
+ traceMessage("reusing bitmap memory");
} else {
data = (SplashColorPtr)gmallocn64(height, rowSize);
+ traceMessage("not reusing bitmap memory"
+ " (parent=%p parent->oldData=%p same-size=%d)",
+ parent, parent ? parent->oldData : NULL,
+ parent ? (parent->oldRowSize == rowSize &&
+ parent->oldHeight == height) : 0);
}
if (!topDown) {
data += (height - 1) * rowSize;
@@ -99,11 +112,12 @@ SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPad,
}
SplashBitmap::~SplashBitmap() {
+ traceFree(this, "free bitmap");
if (data && rowSize < 0) {
rowSize = -rowSize;
data -= (height - 1) * rowSize;
}
- if (parent && rowSize > 10000000 / height) {
+ if (parent && rowSize > 4000000 / height) {
gfree(parent->oldData);
gfree(parent->oldAlpha);
parent->oldData = data;
@@ -123,7 +137,7 @@ SplashError SplashBitmap::writePNMFile(char *fileName) {
FILE *f;
SplashError err;
- if (!(f = fopen(fileName, "wb"))) {
+ if (!(f = openFile(fileName, "wb"))) {
return splashErrOpenFile;
}
err = writePNMFile(f);
@@ -211,7 +225,7 @@ SplashError SplashBitmap::writeAlphaPGMFile(char *fileName) {
if (!alpha) {
return splashErrModeMismatch;
}
- if (!(f = fopen(fileName, "wb"))) {
+ if (!(f = openFile(fileName, "wb"))) {
return splashErrOpenFile;
}
fprintf(f, "P5\n%d %d\n255\n", width, height);
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h
index 44980833668..54975a377a2 100644
--- a/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h
@@ -17,6 +17,10 @@
#include <stdio.h>
#include <limits.h>
+// older compilers won't define SIZE_MAX in stdint.h without this
+#ifndef __STDC_LIMIT_MACROS
+# define __STDC_LIMIT_MACROS 1
+#endif
#include <stdint.h>
#include "SplashTypes.h"
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc
index add34311756..2fcd83d1266 100644
--- a/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc
@@ -71,7 +71,11 @@ void SplashFont::initCache() {
// if the glyphs are too large, don't cache them -- setting the
// cache bitmap size to something tiny will cause getGlyph() to
// fall back to the uncached case
- glyphW = glyphH = 1;
+ glyphW = glyphH = 0;
+ glyphSize = 0;
+ cacheSets = 0;
+ cacheAssoc = 0;
+ return;
}
if (aa) {
glyphSize = glyphW * glyphH;
@@ -117,29 +121,33 @@ GBool SplashFont::getGlyph(int c, int xFrac, int yFrac,
}
// check the cache
- i = (c & (cacheSets - 1)) * cacheAssoc;
- for (j = 0; j < cacheAssoc; ++j) {
- if ((cacheTags[i+j].mru & 0x80000000) &&
- cacheTags[i+j].c == c &&
- (int)cacheTags[i+j].xFrac == xFrac &&
- (int)cacheTags[i+j].yFrac == yFrac) {
- bitmap->x = cacheTags[i+j].x;
- bitmap->y = cacheTags[i+j].y;
- bitmap->w = cacheTags[i+j].w;
- bitmap->h = cacheTags[i+j].h;
- for (k = 0; k < cacheAssoc; ++k) {
- if (k != j &&
- (cacheTags[i+k].mru & 0x7fffffff) <
+ if (cache) {
+ i = (c & (cacheSets - 1)) * cacheAssoc;
+ for (j = 0; j < cacheAssoc; ++j) {
+ if ((cacheTags[i+j].mru & 0x80000000) &&
+ cacheTags[i+j].c == c &&
+ (int)cacheTags[i+j].xFrac == xFrac &&
+ (int)cacheTags[i+j].yFrac == yFrac) {
+ bitmap->x = cacheTags[i+j].x;
+ bitmap->y = cacheTags[i+j].y;
+ bitmap->w = cacheTags[i+j].w;
+ bitmap->h = cacheTags[i+j].h;
+ for (k = 0; k < cacheAssoc; ++k) {
+ if (k != j &&
+ (cacheTags[i+k].mru & 0x7fffffff) <
(cacheTags[i+j].mru & 0x7fffffff)) {
- ++cacheTags[i+k].mru;
+ ++cacheTags[i+k].mru;
+ }
}
+ cacheTags[i+j].mru = 0x80000000;
+ bitmap->aa = aa;
+ bitmap->data = cache + (i+j) * glyphSize;
+ bitmap->freeData = gFalse;
+ return gTrue;
}
- cacheTags[i+j].mru = 0x80000000;
- bitmap->aa = aa;
- bitmap->data = cache + (i+j) * glyphSize;
- bitmap->freeData = gFalse;
- return gTrue;
}
+ } else {
+ i = 0; // make gcc happy
}
// generate the glyph bitmap
@@ -149,7 +157,7 @@ GBool SplashFont::getGlyph(int c, int xFrac, int yFrac,
// if the glyph doesn't fit in the bounding box, return a temporary
// uncached bitmap
- if (bitmap2.w > glyphW || bitmap2.h > glyphH) {
+ if (!cache || bitmap2.w > glyphW || bitmap2.h > glyphH) {
*bitmap = bitmap2;
return gTrue;
}
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc
index fe377fe25f4..81afe0cd914 100644
--- a/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc
@@ -20,6 +20,7 @@
#include "gmem.h"
#include "gmempp.h"
#include "GString.h"
+#include "GList.h"
#include "SplashMath.h"
#include "SplashFTFontEngine.h"
#include "SplashFontFile.h"
@@ -48,6 +49,7 @@ SplashFontEngine::SplashFontEngine(
for (i = 0; i < splashFontCacheSize; ++i) {
fontCache[i] = NULL;
}
+ badFontFiles = new GList();
#if HAVE_FREETYPE_H
if (enableFreeType) {
@@ -66,6 +68,7 @@ SplashFontEngine::~SplashFontEngine() {
delete fontCache[i];
}
}
+ deleteGList(badFontFiles, SplashFontFileID);
#if HAVE_FREETYPE_H
if (ftEngine) {
@@ -89,6 +92,15 @@ SplashFontFile *SplashFontEngine::getFontFile(SplashFontFileID *id) {
return NULL;
}
+GBool SplashFontEngine::checkForBadFontFile(SplashFontFileID *id) {
+ for (int i = 0; i < badFontFiles->getLength(); ++i) {
+ if (((SplashFontFileID *)badFontFiles->get(i))->matches(id)) {
+ return gTrue;
+ }
+ }
+ return gFalse;
+}
+
SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA,
#if LOAD_FONTS_FROM_MEM
GString *fontBuf,
@@ -122,6 +134,10 @@ SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA,
}
#endif
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
return fontFile;
}
@@ -162,6 +178,10 @@ SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA,
}
#endif
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
return fontFile;
}
@@ -202,6 +222,10 @@ SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA,
}
#endif
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
return fontFile;
}
@@ -243,6 +267,10 @@ SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA,
}
#endif
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
return fontFile;
}
@@ -284,6 +312,10 @@ SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA,
}
#endif
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
return fontFile;
}
@@ -327,6 +359,10 @@ SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA,
}
#endif
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
return fontFile;
}
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h
index f9eab4d00f4..59adb95858a 100644
--- a/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h
@@ -17,6 +17,7 @@
#include "gtypes.h"
class GString;
+class GList;
class SplashFTFontEngine;
class SplashDTFontEngine;
@@ -54,6 +55,10 @@ public:
// matching entry in the cache.
SplashFontFile *getFontFile(SplashFontFileID *id);
+ // Returns true if [id] refers to a bad font file, i.e., if one of
+ // the loadXXXFont functions has returned NULL for that ID.
+ GBool checkForBadFontFile(SplashFontFileID *id);
+
// Load fonts - these create new SplashFontFile objects.
SplashFontFile *loadType1Font(SplashFontFileID *idA,
#if LOAD_FONTS_FROM_MEM
@@ -114,6 +119,7 @@ public:
private:
SplashFont *fontCache[splashFontCacheSize];
+ GList *badFontFiles; // [SplashFontFileID]
#if HAVE_FREETYPE_H
SplashFTFontEngine *ftEngine;
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc
index 01a94987637..4c77765723e 100644
--- a/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc
@@ -593,7 +593,8 @@ void SplashXPath::finishSegments() {
seg->dydx = 0;
}
#else
- if (seg->y0 == seg->y1 || seg->x0 == seg->x1) {
+ if (splashAbs(seg->y1 - seg->y0) < 1e-200 ||
+ splashAbs(seg->x1 - seg->x0) < 1e-200) {
seg->dxdy = 0;
seg->dydx = 0;
} else {
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);