texlive[57588] Build/source/libs: xpdf 4.03
commits+kakuto at tug.org
commits+kakuto at tug.org
Mon Feb 1 07:11:19 CET 2021
Revision: 57588
http://tug.org/svn/texlive?view=revision&revision=57588
Author: kakuto
Date: 2021-02-01 07:11:18 +0100 (Mon, 01 Feb 2021)
Log Message:
-----------
xpdf 4.03
Modified Paths:
--------------
trunk/Build/source/libs/README
trunk/Build/source/libs/xpdf/ChangeLog
trunk/Build/source/libs/xpdf/Makefile.am
trunk/Build/source/libs/xpdf/Makefile.in
trunk/Build/source/libs/xpdf/TLpatches/ChangeLog
trunk/Build/source/libs/xpdf/TLpatches/TL-Changes
trunk/Build/source/libs/xpdf/TLpatches/patch-bunched
trunk/Build/source/libs/xpdf/configure
trunk/Build/source/libs/xpdf/version.ac
trunk/Build/source/libs/xpdf/xpdf-src/ANNOUNCE
trunk/Build/source/libs/xpdf/xpdf-src/CHANGES
trunk/Build/source/libs/xpdf/xpdf-src/INSTALL
trunk/Build/source/libs/xpdf/xpdf-src/README
trunk/Build/source/libs/xpdf/xpdf-src/cmake-config.txt
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.1
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.1
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.1
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.1
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.1
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.1
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.1
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftops.1
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.1
trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat
trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdf.1
trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat
trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.5
trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat
trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc
trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc
trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc
trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc
trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h
trunk/Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt
trunk/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h
trunk/Build/source/libs/xpdf/xpdf-src/goo/gfile.cc
trunk/Build/source/libs/xpdf/xpdf-src/goo/gfile.h
trunk/Build/source/libs/xpdf/xpdf-src/goo/gmem.h
trunk/Build/source/libs/xpdf/xpdf-src/splash/Splash.cc
trunk/Build/source/libs/xpdf/xpdf-src/splash/Splash.h
trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc
trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h
trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc
trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc
trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h
trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/config.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc
Added Paths:
-----------
trunk/Build/source/libs/xpdf/xpdf-src/goo/Trace.cc
trunk/Build/source/libs/xpdf/xpdf-src/goo/Trace.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h
Removed Paths:
-------------
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Form.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Form.h
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc
trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h
Modified: trunk/Build/source/libs/README
===================================================================
--- trunk/Build/source/libs/README 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/README 2021-02-01 06:11:18 UTC (rev 57588)
@@ -58,7 +58,7 @@
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
Modified: trunk/Build/source/libs/xpdf/ChangeLog
===================================================================
--- trunk/Build/source/libs/xpdf/ChangeLog 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/ChangeLog 2021-02-01 06:11:18 UTC (rev 57588)
@@ -1,3 +1,8 @@
+2021-02-01 Akira Kakuto <kakuto at w32tex.org>
+
+ * Import xpdf-4.03.
+ * version.ac, Makefile.am: Adjust.
+
2020-05-14 Karl Berry <karl at freefriends.org>
* ac/xpdf.ac: doc change for poppler no longer supported in TL.
Modified: trunk/Build/source/libs/xpdf/Makefile.am
===================================================================
--- trunk/Build/source/libs/xpdf/Makefile.am 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/Makefile.am 2021-02-01 06:11:18 UTC (rev 57588)
@@ -62,6 +62,7 @@
@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_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_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
Modified: trunk/Build/source/libs/xpdf/Makefile.in
===================================================================
--- trunk/Build/source/libs/xpdf/Makefile.in 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/Makefile.in 2021-02-01 06:11:18 UTC (rev 57588)
@@ -115,6 +115,7 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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_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_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 @@
@XPDF_TREE@/goo/$(DEPDIR)/$(am__dirstamp)
@XPDF_TREE@/goo/GString.$(OBJEXT): @XPDF_TREE@/goo/$(am__dirstamp) \
@XPDF_TREE@/goo/$(DEPDIR)/$(am__dirstamp)
+ at 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 @@
@XPDF_TREE@/xpdf/FontEncodingTables.$(OBJEXT): \
@XPDF_TREE@/xpdf/$(am__dirstamp) \
@XPDF_TREE@/xpdf/$(DEPDIR)/$(am__dirstamp)
- at 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 @@
@XPDF_TREE@/xpdf/$(DEPDIR)/$(am__dirstamp)
@XPDF_TREE@/xpdf/UTF8.$(OBJEXT): @XPDF_TREE@/xpdf/$(am__dirstamp) \
@XPDF_TREE@/xpdf/$(DEPDIR)/$(am__dirstamp)
- at XPDF_TREE@/xpdf/XFAForm.$(OBJEXT): @XPDF_TREE@/xpdf/$(am__dirstamp) \
+ at 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 @@
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/GHash.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/GList.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/GString.Po at am__quote@ # am--include-marker
+ at AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/Trace.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/gfile.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/gmem.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/goo/$(DEPDIR)/gmempp.Po at am__quote@ # am--include-marker
@@ -795,7 +797,6 @@
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/Dict.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/Error.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/FontEncodingTables.Po at am__quote@ # am--include-marker
- at AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/Form.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/Function.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/Gfx.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/GfxFont.Po at am__quote@ # am--include-marker
@@ -823,7 +824,7 @@
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/UTF8.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/UnicodeMap.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/UnicodeRemapping.Po at am__quote@ # am--include-marker
- at AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/XFAForm.Po at am__quote@ # am--include-marker
+ at AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/XFAScanner.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/XRef.Po at am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@@XPDF_TREE@/xpdf/$(DEPDIR)/Zoox.Po at am__quote@ # am--include-marker
@@ -1213,6 +1214,7 @@
-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 @@
-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 @@
-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 @@
-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 @@
-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 @@
-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
Modified: trunk/Build/source/libs/xpdf/TLpatches/ChangeLog
===================================================================
--- trunk/Build/source/libs/xpdf/TLpatches/ChangeLog 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/TLpatches/ChangeLog 2021-02-01 06:11:18 UTC (rev 57588)
@@ -1,3 +1,7 @@
+2021-02-01 Akira Kakuto <kakuto at w32tex.org>
+
+ * patch-bunched: Adjust.
+
2019-09-29 Akira Kakuto <kakuto at w32tex.org>
* patch-bunched: Adjust.
Modified: trunk/Build/source/libs/xpdf/TLpatches/TL-Changes
===================================================================
--- trunk/Build/source/libs/xpdf/TLpatches/TL-Changes 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/TLpatches/TL-Changes 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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:
Modified: trunk/Build/source/libs/xpdf/TLpatches/patch-bunched
===================================================================
--- trunk/Build/source/libs/xpdf/TLpatches/patch-bunched 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/TLpatches/patch-bunched 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
#include <aconf.h>
-@@ -54,7 +57,11 @@
+@@ -56,7 +59,11 @@
char *s;
GString *ret;
@@ -23,7 +23,7 @@
ret = new GString(s);
else
ret = new GString(".");
-@@ -403,6 +410,7 @@
+@@ -405,6 +412,7 @@
#endif
}
@@ -31,7 +31,7 @@
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,41 +44,76 @@
#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
+ }
+
+#ifndef PDF_PARSER_ONLY
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.
+#ifndef PDF_PARSER_ONLY
@@ -86,9 +121,9 @@
+#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 @@
#endif
#if MULTITHREADED
-@@ -803,6 +810,7 @@
+@@ -787,6 +794,7 @@
f = NULL;
fileName = NULL;
if (cfgFileName && cfgFileName[0]) {
@@ -122,7 +157,7 @@
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 @@
}
}
-@@ -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.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 @@
#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,11 +199,23 @@
~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 @@
+/*
#ifdef _WIN32
Unicode u;
- int n, i, j;
+ int i, j;
#endif
+*/
@@ -190,10 +237,10 @@
#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 @@
#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 @@
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 @@
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,7 +290,7 @@
GfxState *state;
int i;
-@@ -499,5 +499,5 @@
+@@ -504,5 +504,5 @@
ctm[i] = state->getCTM()[i];
}
delete state;
@@ -250,26 +297,10 @@
-}
#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
//------------------------------------------------------------------------
Modified: trunk/Build/source/libs/xpdf/configure
===================================================================
--- trunk/Build/source/libs/xpdf/configure 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/configure 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 at tug.org>.
#
@@ -580,8 +580,8 @@
# 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 at tug.org'
PACKAGE_URL=''
@@ -1290,7 +1290,7 @@
# 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 @@
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 @@
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 @@
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 @@
# Define the identity of the package.
PACKAGE='xpdf--tex-live-'
- VERSION='4.02'
+ VERSION='4.03'
# Some tools Automake needs.
@@ -6699,7 +6699,7 @@
# 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 @@
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\\"
Modified: trunk/Build/source/libs/xpdf/version.ac
===================================================================
--- trunk/Build/source/libs/xpdf/version.ac 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/version.ac 2021-02-01 06:11:18 UTC (rev 57588)
@@ -8,4 +8,4 @@
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])
Modified: trunk/Build/source/libs/xpdf/xpdf-src/ANNOUNCE
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/ANNOUNCE 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/ANNOUNCE 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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,19 +10,17 @@
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.
+* Various new command line options for pdftotext, pdftohtml, pdftoppm,
+ pdftopng, and xpdf.
-* 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).
-
See the `CHANGES' file for a complete list of changes.
Source (C++ and C) is available, and it should be fairly easy to
Modified: trunk/Build/source/libs/xpdf/xpdf-src/CHANGES
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/CHANGES 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/CHANGES 2021-02-01 06:11:18 UTC (rev 57588)
@@ -2645,4 +2645,98 @@
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.]
Modified: trunk/Build/source/libs/xpdf/xpdf-src/INSTALL
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/INSTALL 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/INSTALL 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 at xpdfreader.com
WWW: http://www.xpdfreader.com/
@@ -115,25 +115,29 @@
-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").
Modified: trunk/Build/source/libs/xpdf/xpdf-src/README
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/README 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/README 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 at 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 @@
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.
Modified: trunk/Build/source/libs/xpdf/xpdf-src/cmake-config.txt
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/cmake-config.txt 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/cmake-config.txt 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
# 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 @@
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 @@
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")
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.1
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.1 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.1 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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),
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat 2021-02-01 06:11:18 UTC (rev 57588)
@@ -4,7 +4,7 @@
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 @@
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 @@
- 25 Sep 2019 pdfdetach(1)
+ 28 Jan 2021 pdfdetach(1)
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.1
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.1 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.1 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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),
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat 2021-02-01 06:11:18 UTC (rev 57588)
@@ -3,7 +3,7 @@
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 @@
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 @@
- 25 Sep 2019 pdffonts(1)
+ 28 Jan 2021 pdffonts(1)
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.1
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.1 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.1 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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),
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat 2021-02-01 06:11:18 UTC (rev 57588)
@@ -4,7 +4,7 @@
NAME
pdfimages - Portable Document Format (PDF) image extractor (version
- 4.02)
+ 4.03)
SYNOPSIS
pdfimages [options] PDF-file image-root
@@ -85,7 +85,7 @@
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 @@
- 25 Sep 2019 pdfimages(1)
+ 28 Jan 2021 pdfimages(1)
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.1
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.1 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.1 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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),
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat 2021-02-01 06:11:18 UTC (rev 57588)
@@ -4,7 +4,7 @@
NAME
pdfinfo - Portable Document Format (PDF) document information extractor
- (version 4.02)
+ (version 4.03)
SYNOPSIS
pdfinfo [options] [PDF-file]
@@ -104,7 +104,7 @@
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 @@
- 25 Sep 2019 pdfinfo(1)
+ 28 Jan 2021 pdfinfo(1)
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.1
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.1 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.1 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
\'-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 @@
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),
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat 2021-02-01 06:11:18 UTC (rev 57588)
@@ -4,7 +4,7 @@
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 @@
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 @@
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 @@
- 25 Sep 2019 pdftohtml(1)
+ 28 Jan 2021 pdftohtml(1)
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.1
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.1 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.1 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
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),
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat 2021-02-01 06:11:18 UTC (rev 57588)
@@ -4,7 +4,7 @@
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 @@
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 @@
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 @@
- 25 Sep 2019 pdftopng(1)
+ 28 Jan 2021 pdftopng(1)
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.1
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.1 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.1 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
.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 @@
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),
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat 2021-02-01 06:11:18 UTC (rev 57588)
@@ -4,7 +4,7 @@
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 @@
-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 @@
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 @@
- 25 Sep 2019 pdftoppm(1)
+ 28 Jan 2021 pdftoppm(1)
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftops.1
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftops.1 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftops.1 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
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 @@
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),
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat 2021-02-01 06:11:18 UTC (rev 57588)
@@ -4,7 +4,7 @@
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 @@
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 @@
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 @@
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 @@
- 25 Sep 2019 pdftops(1)
+ 28 Jan 2021 pdftops(1)
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.1
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.1 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.1 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
.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 @@
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),
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat 2021-02-01 06:11:18 UTC (rev 57588)
@@ -4,7 +4,7 @@
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 @@
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 @@
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.
- -h Print usage information. (-help and --help are equivalent.)
+ -v Print copyright and version information, then exit.
+ -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 @@
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 @@
- 25 Sep 2019 pdftotext(1)
+ 28 Jan 2021 pdftotext(1)
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdf.1
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdf.1 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdf.1 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
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,11 +247,19 @@
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
popup near the bottom of the window.
@@ -400,6 +415,12 @@
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 @@
.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 @@
.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 @@
.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 @@
.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 @@
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),
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat 2021-02-01 06:11:18 UTC (rev 57588)
@@ -3,7 +3,7 @@
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 @@
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 @@
-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 @@
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,11 +173,11 @@
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
@@ -177,7 +184,7 @@
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 @@
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 @@
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,11 +250,11 @@
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
@@ -246,8 +261,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
quit Quit from xpdf.
+ raise Bring the xpdf window to the front.
+
reload Reload the current PDF file.
rotateCCW
@@ -674,10 +701,19 @@
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 @@
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,12 +755,12 @@
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
@@ -727,7 +767,7 @@
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 @@
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 @@
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 @@
- 25 Sep 2019 xpdf(1)
+ 28 Jan 2021 xpdf(1)
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.5
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.5 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.5 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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,9 +17,26 @@
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.
.SH INCLUDE FILES
@@ -549,6 +566,11 @@
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 @@
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 @@
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 @@
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 @@
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),
Modified: trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat 2021-02-01 06:11:18 UTC (rev 57588)
@@ -3,7 +3,7 @@
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,9 +17,21 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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)
Modified: trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -561,7 +561,7 @@
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,20 +622,19 @@
//----- 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;
+ } else {
+ break;
}
- if (pos >= endPos || pos < 0) {
- return fofiIdCFF8Bit;
- }
}
if (pos + 1 < endPos &&
reader->getByte(pos) == 12 &&
Modified: trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -1730,7 +1730,7 @@
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 @@
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 @@
// 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 @@
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 @@
// 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;
}
Modified: trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -114,7 +114,7 @@
"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));
}
}
Modified: trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -26,6 +26,8 @@
static char hexChars[17] = "0123456789ABCDEF";
+#define type1cSubrRecursionLimit 20
+
//------------------------------------------------------------------------
GBool Type1COp::isZero() {
@@ -39,28 +41,52 @@
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 @@
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 @@
}
}
}
+ 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 @@
// 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::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 @@
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 @@
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 @@
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 @@
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 @@
}
} else {
//~ error(-1, "Unknown FDSelect table format in CID font");
- for (i = 0; i < nGlyphs; ++i) {
- fdSelect[i] = 0;
- }
}
}
}
Modified: trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -229,7 +229,7 @@
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);
Modified: trunk/Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt 2021-02-01 06:11:18 UTC (rev 57588)
@@ -20,6 +20,7 @@
gmem.cc
gmempp.cc
parseargs.c
+ Trace.cc
)
add_library(goo
Modified: trunk/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -36,6 +36,7 @@
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 @@
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 @@
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)); }
Added: trunk/Build/source/libs/xpdf/xpdf-src/goo/Trace.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/goo/Trace.cc (rev 0)
+++ trunk/Build/source/libs/xpdf/xpdf-src/goo/Trace.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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
Added: trunk/Build/source/libs/xpdf/xpdf-src/goo/Trace.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/goo/Trace.h (rev 0)
+++ trunk/Build/source/libs/xpdf/xpdf-src/goo/Trace.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/goo/gfile.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/goo/gfile.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/goo/gfile.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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,6 +581,32 @@
}
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) {
@@ -585,62 +613,17 @@
#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 @@
#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;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/goo/gfile.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/goo/gfile.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/goo/gfile.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -80,13 +80,22 @@
// 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);
Modified: trunk/Build/source/libs/xpdf/xpdf-src/goo/gmem.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/goo/gmem.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/goo/gmem.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -32,10 +32,6 @@
#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 char *copyString(const char *s);
-#ifdef __cplusplus
-}
#endif
-
-#endif
Modified: trunk/Build/source/libs/xpdf/xpdf-src/splash/Splash.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/splash/Splash.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/splash/Splash.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -2,7 +2,7 @@
//
// Splash.cc
//
-// Copyright 2003-2013 Glyph & Cog, LLC
+// Copyright 2003-2020 Glyph & Cog, LLC
//
//========================================================================
@@ -80,6 +80,12 @@
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::pipeInit(SplashPipe *pipe, SplashPattern *pattern,
Guchar aInput, GBool usesShape,
- GBool nonIsolatedGroup) {
+ GBool nonIsolatedGroup, GBool usesSrcOverprint) {
SplashColorMode mode;
mode = bitmap->mode;
@@ -181,6 +187,9 @@
// source alpha
pipe->aInput = aInput;
+ // source overprint mask
+ pipe->srcOverprintMaskPtr = NULL;
+
// special cases
pipe->noTransparency = aInput == 255 &&
!state->softMask &&
@@ -212,7 +221,9 @@
// 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 @@
} 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 @@
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 @@
Guchar color0Mask;
Guchar *alpha0Ptr;
SplashColorPtr softMaskPtr;
+ Guint overprintMask;
+ Guint *overprintMaskPtr;
#if SPLASH_CMYK
Guchar aPrev;
SplashColor cSrc2, cDest2;
@@ -313,6 +349,9 @@
}
cSrcPtr += cSrcStride;
++shapePtr2;
+ if (pipe->srcOverprintMaskPtr) {
+ ++pipe->srcOverprintMaskPtr;
+ }
}
} else {
shapeVal = 0xff;
@@ -326,6 +365,8 @@
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 @@
} else {
alpha0Ptr = NULL;
}
+ if (overprintMaskBitmap) {
+ overprintMaskPtr = overprintMaskBitmap + y * bitmap->width + x0;
+ } else {
+ overprintMaskPtr = NULL;
+ }
for (x = x0; x <= x1; ++x) {
@@ -399,6 +445,12 @@
}
cSrcPtr += cSrcStride;
shapePtr2 += shapeStride;
+ if (pipe->srcOverprintMaskPtr) {
+ ++pipe->srcOverprintMaskPtr;
+ }
+ if (overprintMaskPtr) {
+ ++overprintMaskPtr;
+ }
continue;
}
lastX = x;
@@ -519,6 +571,15 @@
//----- 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 @@
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 @@
updateModX(x1);
updateModY(y);
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
destColorMask = (Guchar)(0x80 >> (x0 & 7));
@@ -905,6 +968,8 @@
updateModX(x1);
updateModY(y);
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -940,6 +1005,8 @@
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 @@
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 @@
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 @@
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
destColorMask = (Guchar)(0x80 >> (x0 & 7));
@@ -1150,6 +1223,8 @@
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -1243,6 +1318,8 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
destColorMask = (Guchar)(0x80 >> (x0 & 7));
@@ -1663,6 +1804,8 @@
updateModY(y);
lastX = x0;
+ useDestRow(y);
+
destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
@@ -1746,6 +1889,8 @@
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 @@
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 @@
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 @@
#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 @@
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 @@
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 @@
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 @@
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 @@
#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 @@
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 @@
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 @@
}
//------------------------------------------------------------------------
+// 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 @@
scanBuf2 = NULL;
}
groupBackBitmap = NULL;
+ groupDestInitMode = splashGroupDestPreInit;
+ overprintMaskBitmap = NULL;
minLineWidth = 0;
clearModRegion();
debugMode = gFalse;
@@ -2499,6 +4389,8 @@
scanBuf2 = NULL;
}
groupBackBitmap = NULL;
+ groupDestInitMode = splashGroupDestPreInit;
+ overprintMaskBitmap = NULL;
minLineWidth = 0;
clearModRegion();
debugMode = gFalse;
@@ -2699,14 +4591,23 @@
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,6 +5740,10 @@
}
}
+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,
@@ -3845,12 +5750,6 @@
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],
@@ -3857,1113 +5756,468 @@
(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;
+ //--- 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);
}
-
- // 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;
- }
- if (imageCache->horizFlip) {
- horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->horizFlip = gFalse;
- }
- 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;
}
-
- // 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;
- 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 = gTrue;
- }
- blitMask(scaledMask, x0, y0, clipRes);
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
}
-
- // 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,
- scaledWidth, scaledHeight, interpolate);
- if (!imageCache->vertFlip) {
- vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->vertFlip = gTrue;
- }
- if (!imageCache->horizFlip) {
- horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
- imageCache->horizFlip = gTrue;
- }
- blitMask(scaledMask, x0, y0, clipRes);
+ 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;
}
-
- // all other cases
- } else {
- arbitraryTransformMask(imageTag, src, srcData, w, h,
- mat, glyphMode, interpolate);
- }
-
- 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 (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
}
- if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) {
- xMaxI = tt;
+ 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 ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) {
- yMinI = tt;
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
}
- if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) {
- yMaxI = tt;
+ 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);
- // 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;
+ //--- set up the SplashDrawImageMaskRowData object and the pipes
+ SplashDrawImageMaskRowData dd;
+ pipeInit(&dd.pipe, state->fillPattern,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ gTrue, gFalse);
- // 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);
+ //--- choose the drawRow function
+ SplashDrawImageMaskRowFunc drawRowFunc;
+ if (clipRes == splashClipAllInside) {
+ drawRowFunc = &Splash::drawImageMaskRowNoClip;
+ } else {
+ if (vectorAntialias) {
+ drawRowFunc = &Splash::drawImageMaskRowClipAA;
+ } else {
+ drawRowFunc = &Splash::drawImageMaskRowClipNoAA;
}
}
- // 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;
+ //--- horizontal/vertical flips only
+ if (flipsOnly && !veryLarge) {
+ if (clipRes != splashClipAllOutside) {
+ int scaledWidth = xMax - xMin;
+ int scaledHeight = yMax - yMin;
+ ImageMaskScaler scaler(src, srcData, w, h,
+ scaledWidth, scaledHeight, interpolate);
+ 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);
}
- if (x1 >= srcWidth) {
- x1 = srcWidth - 1;
+ } else { // bottom-up
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ (this->*drawRowFunc)(&dd, scaler.data(),
+ xMin, yMax - 1 - y, scaledWidth);
}
- if (y0 < 0) {
- y0 = 0;
+ }
+ } 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);
}
- if (y1 >= srcHeight) {
- y1 = srcHeight - 1;
+ } else { // top-down
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ (this->*drawRowFunc)(&dd, scaler.data(),
+ xMin, yMin + y, scaledWidth);
}
- 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;
- }
}
+ gfree(tmpLine);
}
- 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);
- }
- }
- (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, NULL);
- }
- gfree(unscaledImage);
-}
-
-// 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;
+ //--- 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;
}
- t1 = splashRound(vy[i]);
- if (t1 < yMin) {
- yMin = t1;
- } else if (t1 > yMax) {
- yMax = t1;
+ if (scaledHeight < 1) {
+ scaledHeight = 1;
}
- }
- clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
- state->strokeAdjust);
- opClipRes = clipRes;
- if (clipRes == splashClipAllOutside) {
- return;
- }
+ GBool downscaling = gTrue;
+ if (veryLarge || (scaledWidth >= w && scaledHeight >= h)) {
+ downscaling = gFalse;
+ scaledWidth = w;
+ scaledHeight = h;
+ }
- // 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 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;
- // 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
- 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;
+ // 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);
+ Guchar *ptr = scaledMask;
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ memcpy(ptr, scaler.data(), scaledWidth);
+ ptr += scaledWidth;
}
} 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;
+ 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;
+ }
}
}
- 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);
+ // 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);
+ }
- // 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;
- }
+ // free the downscaled/unscaled image
+ gfree(scaledMask);
}
- // 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;
- }
- if (yy < 0) {
- yy = 0;
- } else if (yy >= scaledHeight) {
- yy = scaledHeight - 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);
- }
- }
- // draw the scan line
- (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, NULL);
- }
- }
+ return splashOk;
}
-// 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;
+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;
}
- if (imageCache->tag) {
- delete imageCache->tag;
+ tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
+ if (tt < xMax) {
+ xMax = tt;
}
- if (imageCache->image) {
- delete imageCache->image;
+ tt = state->clip->getYMinI(state->strokeAdjust);
+ if (tt > yMin) {
+ yMin = tt;
}
- 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);
- }
- }
+ tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
+ if (tt < yMax) {
+ yMax = tt;
}
- return imageCache->image;
-}
+ if (xMax <= xMin || yMax <= yMin) {
+ return;
+ }
-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;
+ Guchar *buf = (Guchar *)gmalloc(xMax - xMin);
- // 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];
+ 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;
}
}
-
- // 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;
+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;
+ }
- // Bresenham parameters for y scale
- yp = srcHeight / scaledHeight;
- yq = srcHeight % scaledHeight;
+ Guchar *buf = (Guchar *)gmalloc(xMax - xMin);
- // Bresenham parameters for x scale
- xp = scaledWidth / srcWidth;
- xq = scaledWidth % srcWidth;
-
- // 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];
+ 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 (x1 >= scaledWidth) {
+ x1 = scaledWidth - 1;
+ }
+ if (y0 < 0) {
+ y0 = 0;
+ }
+ 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;
- 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;
- }
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd, buf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
}
}
- gfree(pixBuf);
- gfree(lineBuf);
+ gfree(buf);
}
-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;
+void Splash::mirrorImageMaskRow(Guchar *maskIn, Guchar *maskOut, int width) {
+ Guchar *p, *q;
- // 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;
+ p = maskIn;
+ q = maskOut + (width - 1);
+ for (int i = 0; i < width; ++i) {
+ *q = *p;
+ ++p;
+ --q;
}
+}
- 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;
+void Splash::drawImageMaskRowClipAA(SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
}
-
- // 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);
+ if (x < 0) {
+ maskData -= x;
+ width += x;
+ x = 0;
}
-
- 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;
- }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
}
-
- gfree(lineBuf1);
- gfree(lineBuf0);
+ 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],
@@ -4970,9 +6224,9 @@
(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 @@
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;
+ //--- 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 = 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);
}
- 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);
+ } 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;
}
-
- // 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);
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
}
-
- // 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;
- }
- if (!imageCache->horizFlip) {
- horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
- imageCache->horizFlip = gTrue;
- }
- blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
+ 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;
}
-
- // 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 (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
}
- if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) {
- xMaxI = tt;
+ 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 ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) {
- yMinI = tt;
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
}
- if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) {
- yMaxI = tt;
+ if (xMax <= xMin) {
+ xMax = xMin + 1;
}
- }
-
- // 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);
+ if (yMax <= yMin) {
+ yMax = yMin + 1;
}
- } else {
- unscaledAlpha = NULL;
- for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += rowSize) {
- (*src)(srcData, p, NULL);
- }
}
+ 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);
- // draw it
- pixelBuf = (SplashColorPtr)gmallocn(xMaxI - xMinI, nComps);
- pipeInit(&pipe, NULL,
+ //--- 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),
- 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;
- }
- } 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;
- }
- }
- }
- 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);
- }
- }
- (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, pixelBuf);
- }
+ clipRes != splashClipAllInside || srcAlpha,
+ gFalse);
- 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;
+ //--- choose the drawRow function
+ SplashDrawImageRowFunc drawRowFunc;
+ if (clipRes == splashClipAllInside) {
+ if (srcAlpha) {
+ drawRowFunc = &Splash::drawImageRowNoClipAlpha;
} else {
- section[0].ia0 = (i+1) & 3;
- section[0].ia1 = (i+2) & 3;
- section[0].ib0 = i;
- section[0].ib1 = (i+3) & 3;
+ drawRowFunc = &Splash::drawImageRowNoClipNoAlpha;
}
- 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;
+ if (srcAlpha) {
+ if (vectorAntialias) {
+ drawRowFunc = &Splash::drawImageRowClipAlphaAA;
} else {
- section[1].ia0 = i;
- section[1].ia1 = (i+3) & 3;
- section[1].ib0 = (i+1) & 3;
- section[1].ib1 = (i+2) & 3;
+ drawRowFunc = &Splash::drawImageRowClipAlphaNoAA;
}
} 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;
+ if (vectorAntialias) {
+ drawRowFunc = &Splash::drawImageRowClipNoAlphaAA;
} else {
- section[1].ia0 = (i+3) & 3;
- section[1].ia1 = (i+2) & 3;
- section[1].ib0 = i;
- section[1].ib1 = (i+1) & 3;
+ drawRowFunc = &Splash::drawImageRowClipNoAlphaNoAA;
}
}
- 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);
+ //--- 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) {
+ tmpAlphaLine = (Guchar *)gmalloc(scaledWidth);
}
}
- // 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 (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);
+ }
}
- if (yy < 0) {
- yy = 0;
- } else if (yy >= scaledHeight) {
- yy = scaledHeight - 1;
+ } else {
+ 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);
+ }
}
- // get the color
- scaledImg->getPixel(xx, yy, pixelBuf + (x - xa) * bitmapComps);
- // apply alpha
- if (srcAlpha) {
- scanBuf[x] = div255(scanBuf[x] *
- scaledImg->alpha[yy * scaledWidth + xx]);
- }
}
- (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, pixelBuf);
+ gfree(tmpLine);
+ gfree(tmpAlphaLine);
+ delete scaler;
}
- }
- 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);
- }
+ //--- arbitrary transform
} 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);
- } else {
- scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha,
- srcWidth, srcHeight, scaledWidth, scaledHeight,
- imageCache->image);
- }
+ // 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;
}
- }
- return imageCache->image;
-}
+ if (scaledHeight < 1) {
+ scaledHeight = 1;
+ }
+ if (veryLarge || (scaledWidth >= w && scaledHeight >= h)) {
+ scaledWidth = w;
+ scaledHeight = h;
+ }
-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;
+ // 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;
- // Bresenham parameters for y scale
- yp = srcHeight / scaledHeight;
- yq = srcHeight % scaledHeight;
+ Guchar *scaledColor, *scaledAlpha;
+ GBool freeScaledImage;
+ getScaledImage(imageTag, src, srcData, w, h, nComps,
+ scaledWidth, scaledHeight, srcMode, srcAlpha, interpolate,
+ &scaledColor, &scaledAlpha, &freeScaledImage);
- // 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));
- } 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;
+ // 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));
+ // free the downscaled/unscaled image
+ if (freeScaledImage) {
+ gfree(scaledColor);
+ gfree(scaledAlpha);
}
- 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];
- }
- }
- }
+ }
- // init x scale Bresenham
- xt = 0;
- d0 = (1 << 23) / (yStep * xp);
- d1 = (1 << 23) / (yStep * (xp + 1));
+ return splashOk;
+}
- xx = xxa = 0;
- for (x = 0; x < scaledWidth; ++x) {
+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.
- // x scale Bresenham
- if ((xt += xq) >= scaledWidth) {
- xt -= scaledWidth;
- xStep = xp + 1;
- d = d1;
+ 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;
+ 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.
- // 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;
+ 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));
+ *scaledAlpha = (Guchar *)gmallocn(scaledHeight, scaledWidth);
+ } else {
+ *scaledAlpha = NULL;
}
- for (i = 0; i < yStep; ++i) {
- (*src)(srcData, lineBuf, alphaLineBuf);
- for (j = 0; j < srcWidth * nComps; ++j) {
- pixBuf[j] += lineBuf[j];
+ *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;
+ imageCache->colorData = (Guchar *)gmallocn(scaledHeight, lineSize);
+ if (srcAlpha) {
+ imageCache->alphaData = (Guchar *)gmallocn(scaledHeight, scaledWidth);
}
-
- // store the pixel
- switch (srcMode) {
- case splashModeMono8:
- for (i = 0; i < xStep; ++i) {
- *destPtr++ = (Guchar)pix[0];
+ 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;
+ }
}
- break;
- case splashModeRGB8:
- for (i = 0; i < xStep; ++i) {
- *destPtr++ = (Guchar)pix[0];
- *destPtr++ = (Guchar)pix[1];
- *destPtr++ = (Guchar)pix[2];
+ } 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;
+ }
}
- 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
- if (srcAlpha) {
- // alphaPixBuf[] / yStep
- alpha = (alphaPixBuf[x] * d + (1 << 22)) >> 23;
- for (i = 0; i < xStep; ++i) {
- *destAlphaPtr++ = (Guchar)alpha;
- }
- }
}
+ *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;
+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;
+ }
- // 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);
+ Guchar *colorBuf = (Guchar *)gmallocn(xMax - xMin, nComps);
+ Guchar *alphaBuf = NULL;
if (srcAlpha) {
- alphaLineBuf = (Guchar *)gmalloc(srcWidth);
- } else {
- alphaLineBuf = NULL;
+ alphaBuf = (Guchar *)gmalloc(xMax - xMin);
}
- // 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 (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++;
}
- }
- 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];
+ if (srcAlpha) {
+ alphaBuf[x - xMin] = scaledAlpha[yy * scaledWidth + xx];
}
- 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];
+ if (x < rowMin) {
+ rowMin = x;
}
- 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];
- }
- break;
-#endif
- case splashModeMono1: // mono1 is not allowed
- case splashModeBGR8: // BGR8 is not allowed
- default:
- break;
+ rowMax = x + 1;
}
-
- // process alpha
- if (srcAlpha) {
- alpha = 0;
- for (i = 0; i < xStep; ++i, ++xxa) {
- alpha += alphaLineBuf[xxa];
- }
- // alpha / xStep
- alpha = (alpha * d + (1 << 22)) >> 23;
- for (i = 0; i < yStep; ++i) {
- destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x;
- *destAlphaPtr = (Guchar)alpha;
- }
- }
}
-
- 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;
+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;
+ }
- // 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);
+ Guchar *colorBuf = (Guchar *)gmallocn(xMax - xMin, nComps);
+ Guchar *alphaBuf = NULL;
if (srcAlpha) {
- alphaLineBuf = (Guchar *)gmalloc(srcWidth);
- } else {
- alphaLineBuf = NULL;
+ alphaBuf = (Guchar *)gmalloc(xMax - xMin);
}
- // 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;
+ 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 (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd,
+ colorBuf + (rowMin - xMin) * nComps,
+ alphaBuf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
}
- if (srcAlpha) {
- for (i = 1; i < yStep; ++i) {
- memcpy(destAlphaPtr, destAlphaPtr - scaledWidth, scaledWidth);
- destAlphaPtr += scaledWidth;
- }
- }
}
- 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;
+void Splash::mirrorImageRow(Guchar *colorIn, Guchar *alphaIn,
+ Guchar *colorOut, Guchar *alphaOut,
+ int width, int nComps, GBool srcAlpha) {
+ Guchar *p, *q;
- // 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);
+ 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];
}
- yBuf = 0;
+ p += nComps;
+ q -= nComps;
}
- // 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 (srcAlpha) {
+ p = alphaIn;
+ q = alphaOut + (width - 1);
+ for (int i = 0; i < width; ++i) {
+ *q = *p;
+ ++p;
+ --q;
}
- 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]);
- }
}
+}
- // make gcc happy
- pix[0] = pix[1] = pix[2] = 0;
-#if SPLASH_CMYK
- pix[3] = 0;
-#endif
+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);
+}
- 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]);
- }
- }
- }
-
- gfree(alphaLineBuf1);
- gfree(alphaLineBuf0);
- gfree(lineBuf1);
- gfree(lineBuf0);
+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);
}
-void Splash::vertFlipImage(SplashBitmap *img, int width, int height,
- int nComps) {
- Guchar *lineBuf;
- Guchar *p0, *p1;
- int w;
-
- 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;
+void Splash::drawImageRowClipAlphaAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
}
- if (xDest + w > bitmap->width) {
- w = bitmap->width - xDest;
+ if (x < 0) {
+ colorData -= x * data->nComps;
+ alphaData -= x;
+ width += x;
+ x = 0;
}
- if (yDest < 0) {
- ySrc -= yDest;
- h += yDest;
- yDest = 0;
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
}
- if (yDest + h > bitmap->height) {
- h = bitmap->height - yDest;
- }
- 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 @@
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;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/splash/Splash.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/splash/Splash.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/splash/Splash.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -29,6 +29,17 @@
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 @@
};
//------------------------------------------------------------------------
+
+// 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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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,
+ 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 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,
- 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 @@
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;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
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() {
+ 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 @@
FILE *f;
SplashError err;
- if (!(f = fopen(fileName, "wb"))) {
+ if (!(f = openFile(fileName, "wb"))) {
return splashErrOpenFile;
}
err = writePNMFile(f);
@@ -211,7 +225,7 @@
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);
Modified: trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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"
Modified: trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -71,7 +71,11 @@
// 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 @@
}
// 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 @@
// 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;
}
Modified: trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
for (i = 0; i < splashFontCacheSize; ++i) {
fontCache[i] = NULL;
}
+ badFontFiles = new GList();
#if HAVE_FREETYPE_H
if (enableFreeType) {
@@ -66,6 +68,7 @@
delete fontCache[i];
}
}
+ deleteGList(badFontFiles, SplashFontFileID);
#if HAVE_FREETYPE_H
if (ftEngine) {
@@ -89,6 +92,15 @@
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 @@
}
#endif
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
return fontFile;
}
@@ -162,6 +178,10 @@
}
#endif
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
return fontFile;
}
@@ -202,6 +222,10 @@
}
#endif
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
return fontFile;
}
@@ -243,6 +267,10 @@
}
#endif
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
return fontFile;
}
@@ -284,6 +312,10 @@
}
#endif
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
return fontFile;
}
@@ -327,6 +359,10 @@
}
#endif
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
return fontFile;
}
Modified: trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -17,6 +17,7 @@
#include "gtypes.h"
class GString;
+class GList;
class SplashFTFontEngine;
class SplashDTFontEngine;
@@ -54,6 +55,10 @@
// 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 @@
private:
SplashFont *fontCache[splashFontCacheSize];
+ GList *badFontFiles; // [SplashFontFileID]
#if HAVE_FREETYPE_H
SplashFTFontEngine *ftEngine;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -593,7 +593,8 @@
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 {
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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,8 +71,258 @@
// = (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
class AcroFormAnnotPage {
public:
@@ -83,7 +341,7 @@
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
@@ -90,6 +348,14 @@
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,11 +449,14 @@
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() {
@@ -194,8 +463,13 @@
acroFormObj.free();
deleteGList(annotPages, AcroFormAnnotPage);
deleteGList(fields, AcroFormField);
+ delete xfaScanner;
}
+const char *AcroForm::getType() {
+ return isStaticXFA ? "static XFA" : "AcroForm";
+}
+
void AcroForm::buildAnnotPageList(Catalog *catalog) {
Object annotsObj, annotObj;
int pageNum, i;
@@ -296,10 +570,44 @@
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 @@
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 @@
//----- 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 @@
parentObj.dictLookup("Parent", &parentObj2);
parentObj.free();
parentObj = parentObj2;
+
+ ++depth;
}
parentObj.free();
@@ -381,6 +695,33 @@
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 @@
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 @@
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(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 @@
name = nameA;
flags = flagsA;
typeFromParent = typeFromParentA;
+ xfaField = xfaFieldA;
}
AcroFormField::~AcroFormField() {
@@ -478,6 +823,7 @@
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 @@
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;
+ // 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);
}
- *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();
+
+ // 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 @@
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 @@
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 @@
asObj.free();
apObj.free();
+ int valueLength;
+ Unicode *value = getValue(&valueLength);
+
// draw the field contents
if (ftObj.isName("Btn")) {
caption = NULL;
@@ -1153,12 +1523,13 @@
// 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 @@
}
}
}
- 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 @@
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 @@
}
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
}
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 @@
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 @@
}
// 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 @@
// 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 @@
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 @@
// 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 @@
if (comb > 0) {
// compute comb spacing
- w = (dx - 2 * border) / comb;
+ w = dx / comb;
// compute font autosize
if (fontSize == 0) {
@@ -1712,25 +2201,48 @@
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;
}
- 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;
+ }
+ 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 @@
// 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 @@
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 @@
// 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 @@
}
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 @@
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(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;
+}
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
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 @@
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 @@
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,6 +135,9 @@
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);
@@ -132,6 +144,15 @@
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 @@
TextString *name;
Guint flags;
GBool typeFromParent;
+ XFAField *xfaField;
friend class AcroForm;
};
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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"
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt 2021-02-01 06:11:18 UTC (rev 57588)
@@ -38,7 +38,6 @@
Dict.cc
Error.cc
FontEncodingTables.cc
- Form.cc
Function.cc
Gfx.cc
GfxFont.cc
@@ -67,7 +66,7 @@
UnicodeRemapping.cc
UnicodeTypeTable.cc
UTF8.cc
- XFAForm.cc
+ XFAScanner.cc
XRef.cc
Zoox.cc
)
@@ -88,6 +87,7 @@
PDFCore.cc
PreScanOutputDev.cc
PSOutputDev.cc
+ ShadingImage.cc
SplashOutputDev.cc
TextOutputDev.cc
TileCache.cc
@@ -104,6 +104,7 @@
$<TARGET_OBJECTS:xpdf_objs>
PreScanOutputDev.cc
PSOutputDev.cc
+ ShadingImage.cc
SplashOutputDev.cc
pdftops.cc
)
@@ -151,6 +152,7 @@
add_executable(pdftohtml
$<TARGET_OBJECTS:xpdf_objs>
HTMLGen.cc
+ ShadingImage.cc
SplashOutputDev.cc
TextOutputDev.cc
WebFont.cc
@@ -215,6 +217,7 @@
if (HAVE_SPLASH)
add_executable(pdftoppm
$<TARGET_OBJECTS:xpdf_objs>
+ ShadingImage.cc
SplashOutputDev.cc
pdftoppm.cc
)
@@ -234,6 +237,7 @@
if (HAVE_SPLASH AND PNG_FOUND)
add_executable(pdftopng
$<TARGET_OBJECTS:xpdf_objs>
+ ShadingImage.cc
SplashOutputDev.cc
pdftopng.cc
)
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
baseURI = NULL;
form = NULL;
embeddedFiles = NULL;
+ pageLabels = NULL;
#if MULTITHREADED
gInitMutex(&pageMutex);
#endif
@@ -222,7 +223,7 @@
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 @@
// 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 @@
// get the ViewerPreferences object
catDict.dictLookupNF("ViewerPreferences", &viewerPrefs);
- pageLabels = NULL;
if (catDict.dictLookup("PageLabels", &obj)->isDict()) {
readPageLabelTree(&obj);
}
@@ -1190,7 +1190,7 @@
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;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -30,7 +30,7 @@
class LinkDest;
class PageTreeNode;
class PageLabelNode;
-class Form;
+class AcroForm;
class TextString;
//------------------------------------------------------------------------
@@ -88,7 +88,7 @@
Object *getAcroForm() { return &acroForm; }
- Form *getForm() { return form; }
+ AcroForm *getForm() { return form; }
GBool getNeedsRendering() { return needsRendering; }
@@ -137,7 +137,7 @@
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]
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -470,8 +470,13 @@
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.
@@ -478,6 +483,11 @@
// (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 @@
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,8 +544,38 @@
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) {
CharCode oldLen, i;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -80,6 +80,7 @@
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);
Deleted: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Form.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Form.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Form.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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() {
-}
Deleted: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Form.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Form.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Form.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
&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 @@
&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 @@
Ref r;
if (resDict) {
+ valid = gTrue;
// build font dictionary
fonts = NULL;
@@ -312,6 +330,7 @@
resDict->lookup("Properties", &propsDict);
} else {
+ valid = gFalse;
fonts = NULL;
xObjDict.initNull();
colorSpaceDict.initNull();
@@ -394,7 +413,8 @@
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 @@
}
obj->free();
}
+ if (!inherit && valid) {
+ break;
+ }
}
obj->initNull();
}
@@ -692,7 +715,7 @@
aborted = gFalse;
errCount = 0;
numArgs = 0;
- parser->getObj(&obj);
+ getContentObj(&obj);
while (!obj.isEOF()) {
// check for an abort
@@ -748,7 +771,7 @@
}
// grab the next object
- parser->getObj(&obj);
+ getContentObj(&obj);
}
obj.free();
@@ -772,6 +795,15 @@
}
}
+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 @@
}
} 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::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 @@
}
}
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 @@
"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::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 @@
// 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 @@
}
}
+// 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 @@
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 @@
state->lineTo(0, 1);
state->closePath();
doPatternFill(gTrue);
+ state->clearPath();
restoreState();
}
@@ -2318,35 +2323,9 @@
state->clearPath();
}
-#if 1 //~tmp: turn off anti-aliasing temporarily
- out->setInShading(gTrue);
-#endif
+ // perform the fill
+ doShFill(shading);
- // 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
-
// restore graphics state
restoreStateStack(savedState);
}
@@ -2395,10 +2374,20 @@
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()) {
case 1:
@@ -2419,15 +2408,6 @@
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) {
@@ -2434,11 +2414,6 @@
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 @@
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 @@
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 @@
}
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 @@
//~ 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 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 @@
// 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 @@
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 @@
// 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,13 +4705,9 @@
// 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();
}
@@ -4742,12 +4714,14 @@
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 @@
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,11 +4821,34 @@
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 @@
// 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 @@
} 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 @@
// 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 @@
}
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();
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -76,7 +76,7 @@
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 @@
private:
+ GBool valid;
GfxFontDict *fonts;
Object xObjDict;
Object colorSpaceDict;
@@ -164,10 +165,9 @@
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 @@
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 @@
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 @@
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,
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -467,6 +467,9 @@
} 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 @@
}
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 @@
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 @@
missingWidth = 0;
ascent = 0.75;
descent = -0.25;
+ declaredAscent = ascent;
fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
}
@@ -966,6 +974,7 @@
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 @@
}
// 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 @@
missingWidth = 0;
ascent = 0.95;
descent = -0.35;
+ declaredAscent = ascent;
fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
collection = NULL;
cMap = NULL;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -185,6 +185,7 @@
// 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 @@
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;
};
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -101,6 +101,7 @@
GfxColorSpace::GfxColorSpace() {
overprintMask = 0x0f;
+ defaultColorSpace = gFalse;
}
GfxColorSpace::~GfxColorSpace() {
@@ -2683,6 +2684,11 @@
"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 @@
"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 @@
"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 @@
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::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 @@
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 @@
"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 @@
"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 @@
"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 @@
}
obj1.free();
- nPatchesA = 0;
- patchesA = NULL;
- patchesSize = 0;
bitBuf = new GfxShadingBitBuf(str);
while (1) {
if (!bitBuf->getBits(flagBits, &flag)) {
@@ -3506,6 +3569,9 @@
err2:
obj1.free();
err1:
+ if (patchesA) {
+ gfree(patchesA);
+ }
return NULL;
}
@@ -3513,6 +3579,38 @@
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;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -207,6 +207,11 @@
// 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 @@
protected:
Guint overprintMask;
+ GBool defaultColorSpace;
};
//------------------------------------------------------------------------
@@ -877,6 +883,7 @@
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 @@
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:
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -178,24 +178,21 @@
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,16 +203,152 @@
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;
}
-GBool SysFontInfo::match(GString *nameA, GBool boldA, GBool italicA) {
- return !strcasecmp(name->getCString(), nameA->getCString()) &&
- bold == boldA && italic == italicA;
+// 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;
}
+// 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;
+}
+
//------------------------------------------------------------------------
// SysFontList
//------------------------------------------------------------------------
@@ -254,102 +387,17 @@
}
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 @@
SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum,
char *path) {
- int n;
- GBool bold, italic;
- GString *s;
- char c;
- int i;
- SysFontType type;
+ int n = (int)strlen(name);
- n = (int)strlen(name);
- bold = italic = gFalse;
-
- // 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 @@
} 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 @@
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 @@
#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 @@
initialDisplayMode = new GString("continuous");
initialToolbarState = gTrue;
initialSidebarState = gTrue;
+ initialSidebarWidth = 0;
initialSelectMode = new GString("linear");
maxTileWidth = 1500;
maxTileHeight = 1500;
@@ -761,6 +745,7 @@
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 @@
mapUnknownCharNames = gFalse;
mapExtTrueTypeFontsViaUnicode = gTrue;
droppedFonts = new GHash(gTrue);
- enableXFA = gTrue;
createDefaultKeyBindings();
popupMenuCmds = new GList();
tabStateFile = appendToPath(getHomeDir(), ".xpdf.tab-state");
@@ -847,6 +831,31 @@
}
}
+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();
@@ -853,8 +862,20 @@
//----- 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 @@
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::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 @@
} 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 @@
} 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 @@
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 @@
} 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 @@
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 @@
*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 @@
*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 @@
(!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 @@
delete macRomanReverseMap;
delete baseDir;
+ deleteGHash(configFileVars, GString);
delete nameToUnicode;
deleteGHash(cidToUnicodes, GString);
deleteGHash(unicodeToUnicodes, GString);
@@ -2009,6 +2099,9 @@
if (fullScreenMatteColor) {
delete fullScreenMatteColor;
}
+ if (selectionColor) {
+ delete selectionColor;
+ }
if (launchCommand) {
delete launchCommand;
}
@@ -2784,6 +2877,26 @@
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 @@
return state;
}
+int GlobalParams::getInitialSidebarWidth() {
+ int w;
+
+ lockGlobalParams;
+ w = initialSidebarWidth;
+ unlockGlobalParams;
+ return w;
+}
+
GString *GlobalParams::getInitialSelectMode() {
GString *s;
@@ -3075,6 +3197,15 @@
return s;
}
+GString *GlobalParams::getSelectionColor() {
+ GString *s;
+
+ lockGlobalParams;
+ s = selectionColor->copy();
+ unlockGlobalParams;
+ return s;
+}
+
GBool GlobalParams::getReverseVideoInvertImages() {
GBool invert;
@@ -3129,15 +3260,6 @@
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 @@
return cmds;
}
+GList *GlobalParams::getAllKeyBindings() {
+ return keyBindings;
+}
+
int GlobalParams::getNumPopupMenuCmds() {
int n;
@@ -3214,7 +3340,7 @@
return debugLogFile;
}
-void GlobalParams::debugLogPrintf(char *fmt, ...) {
+void GlobalParams::debugLogPrintf(const char *fmt, ...) {
GString *path;
FILE *f;
GBool needClose;
@@ -3620,12 +3746,6 @@
unlockGlobalParams;
}
-void GlobalParams::setEnableXFA(GBool enable) {
- lockGlobalParams;
- enableXFA = enable;
- unlockGlobalParams;
-}
-
void GlobalParams::setTabStateFile(char *tabStateFileA) {
lockGlobalParams;
delete tabStateFile;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -134,50 +134,68 @@
~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 @@
GBool getPSAlwaysRasterize();
GBool getPSNeverRasterize();
GString *getTextEncodingName();
+ GList *getAvailableTextEncodings();
EndOfLineKind getTextEOL();
GBool getTextPageBreaks();
GBool getTextKeepTinyChars();
@@ -287,6 +306,7 @@
GString *getInitialDisplayMode();
GBool getInitialToolbarState();
GBool getInitialSidebarState();
+ int getInitialSidebarWidth();
GString *getInitialSelectMode();
int getMaxTileWidth();
int getMaxTileHeight();
@@ -312,6 +332,7 @@
GString *getPaperColor();
GString *getMatteColor();
GString *getFullScreenMatteColor();
+ GString *getSelectionColor();
GBool getReverseVideoInvertImages();
GString *getLaunchCommand() { return launchCommand; }
GString *getMovieCommand() { return movieCommand; }
@@ -320,8 +341,8 @@
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();
@@ -328,7 +349,7 @@
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 @@
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 @@
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 @@
NameToCharCode * // mapping from char name to
macRomanReverseMap; // MacRomanEncoding index
+ //----- meta settings
+
+ GString *baseDir; // base directory - for plugins, etc.
+ GHash *configFileVars; // variables for use in the config file
+ // [GString]
+
//----- user-modifiable settings
- GString *baseDir; // base directory - for plugins, etc.
NameToCharCode * // mapping from char name to Unicode
nameToUnicode;
GHash *cidToUnicodes; // files for mappings from char collections
@@ -522,6 +549,7 @@
// 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 @@
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 @@
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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -214,6 +214,7 @@
// 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 @@
(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 @@
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 @@
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;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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() {
- gfree(fileName);
gfree(fileRoot);
}
@@ -60,6 +58,7 @@
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 @@
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 @@
} 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 @@
} 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 @@
}
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 @@
int *maskColors, GBool inlineImg,
GBool interpolate) {
GfxColorSpaceMode csMode;
+ GString *fileName;
FILE *f;
ImageStream *imgStr;
Guchar *p;
@@ -173,11 +178,12 @@
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 @@
!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 @@
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 @@
(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 @@
} 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 @@
}
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 @@
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 @@
}
}
-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 @@
}
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);
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -85,12 +85,13 @@
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 @@
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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -1304,7 +1304,6 @@
Guint *refSegs;
int c1, c2, c3;
Guint i;
- GBool done;
done = gFalse;
while (!done && readULong(&segNum)) {
@@ -1723,17 +1722,18 @@
"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 @@
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 @@
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 @@
!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 @@
"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 @@
"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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -128,6 +128,7 @@
Guchar *dataPtr;
Guchar *dataEnd;
Guint byteCounter;
+ GBool done;
JArithmeticDecoder *arithDecoder;
JArithmeticDecoderStats *genericRegionStats;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
//----- 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 @@
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 @@
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 @@
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 @@
(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 @@
(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,13 +2425,17 @@
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;
+ }
}
}
}
@@ -2397,34 +2442,42 @@
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) {
@@ -2431,21 +2484,28 @@
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 @@
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 @@
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,7 +2711,7 @@
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) {
@@ -2658,9 +2718,9 @@
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 @@
}
}
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,7 +2874,7 @@
int shift2;
double mu;
int val;
- Guint r, cbX, cbY, x, y;
+ Guint r, pre, cbX, cbY, x, y;
cover(68);
@@ -2821,8 +2881,6 @@
//----- (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 @@
}
// 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 @@
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 @@
// | 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 @@
}
// 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 @@
//----- 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 @@
}
// 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;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -135,7 +135,6 @@
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 JPXPrecinct {
- //----- computed
- Guint x0, y0, x1, y1; // bounds of the precinct
-
//----- children
JPXSubband *subbands; // the subbands
};
@@ -165,12 +161,18 @@
//----- 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 @@
//----- 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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -237,11 +237,6 @@
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 @@
}
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':
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -45,11 +45,12 @@
#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 @@
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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -172,6 +172,7 @@
int policy;
Ref ref;
Object obj2, obj3, obj4, obj5;
+ GBool gotOCG;
int i;
if (obj->isNull()) {
@@ -224,34 +225,40 @@
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;
+ 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;
+ }
}
- 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();
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -113,7 +113,7 @@
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::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) {
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -24,9 +24,7 @@
struct GfxColor;
class GfxColorSpace;
class GfxImageColorMap;
-class GfxFunctionShading;
-class GfxAxialShading;
-class GfxRadialShading;
+class GfxShading;
class Stream;
class Links;
class Link;
@@ -61,11 +59,6 @@
// 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,13 +159,8 @@
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 shadedFill(GfxState *state, GfxShading *shading)
{ return gFalse; }
- virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading)
- { return gFalse; }
- virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading)
- { return gFalse; }
//----- path clipping
virtual void clip(GfxState *state) {}
@@ -212,12 +200,13 @@
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 @@
//----- 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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
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 @@
// 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,9 +1118,9 @@
// 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);
@@ -1117,42 +1127,42 @@
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,10 +1274,33 @@
}
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,
double *lrx, double *lry) {
SelectRect *rect;
@@ -1550,7 +1583,46 @@
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) {
return tileMap->cvtWindowToUser(xw, yw, pg, xu, yu);
@@ -1683,7 +1755,7 @@
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 @@
return doc->getCatalog()->getForm()->findFieldIdx(pg, x, y);
}
-FormField *PDFCore::getFormField(int idx) {
+AcroFormField *PDFCore::getFormField(int idx) {
if (!doc->getCatalog()->getForm()) {
return NULL;
}
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -32,7 +32,7 @@
class LinkAction;
class Annot;
class Annots;
-class FormField;
+class AcroFormField;
class TextPage;
class HighlightFile;
class OptionalContentGroup;
@@ -66,6 +66,17 @@
};
//------------------------------------------------------------------------
+// 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 @@
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 @@
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 @@
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);
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
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 @@
ok = setup(ownerPassword, userPassword);
}
#endif
+#endif /* 0 */
PDFDoc::PDFDoc(char *fileNameA, GString *ownerPassword,
GString *userPassword, PDFCore *coreA) {
@@ -165,7 +175,7 @@
/*
#ifdef _WIN32
Unicode u;
- int n, i, j;
+ int i, j;
#endif
*/
@@ -175,17 +185,19 @@
#if defined(_WIN32)
#if 0
- n = 0;
+ wchar_t wPath[MAX_PATH + 1];
i = 0;
- while (getUTF8(fileName, &i, &u)) {
- ++n;
+ j = 0;
+ while (j < MAX_PATH && getUTF8(fileName, &i, &u)) {
+ wPath[j++] = (wchar_t)u;
}
- fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t));
- i = j = 0;
- while (j < n && getUTF8(fileName, &i, &u)) {
- fileNameU[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 @@
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;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -185,6 +185,7 @@
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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
GBool manualCtrlA,
PSOutCustomCodeCbk customCodeCbkA,
void *customCodeCbkDataA,
- GBool honorUserUnitA) {
+ GBool honorUserUnitA,
+ GBool fileNameIsUTF8) {
FILE *f;
PSFileType fileTypeA;
@@ -1333,7 +1334,12 @@
#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 @@
Page *page;
Dict *resDict;
Annots *annots;
- Form *form;
+ AcroForm *form;
Object obj1, obj2, obj3;
GString *s;
GBool needDefaultFont;
@@ -1994,6 +2000,7 @@
}
patDict.free();
+
//----- recursively scan SMask transparency groups in ExtGState dicts
resDict->lookup("ExtGState", &gsDict);
if (gsDict.isDict()) {
@@ -2211,13 +2218,15 @@
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 @@
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 @@
// 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 @@
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 @@
resObj.free();
}
+
GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
int rotateA, GBool useMediaBox, GBool crop,
int sliceX, int sliceY,
@@ -4441,9 +4452,17 @@
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 {
@@ -4450,8 +4469,10 @@
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 @@
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::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 @@
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 @@
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);
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -38,6 +38,9 @@
class GfxIndexedColorSpace;
class GfxSeparationColorSpace;
class GfxDeviceNColorSpace;
+class GfxFunctionShading;
+class GfxAxialShading;
+class GfxRadialShading;
class PDFRectangle;
class PSOutCustomColor;
class PSOutputDev;
@@ -82,7 +85,8 @@
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 @@
// 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 @@
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 @@
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 @@
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,
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
return;
}
+ traceBegin(this, "begin page");
+
rotate += getRotate();
if (rotate >= 360) {
rotate -= 360;
@@ -404,7 +407,9 @@
}
delete gfx;
-#endif
+#endif // PDF_PARSER_ONLY
+
+ traceEnd(this, "end page");
}
void Page::makeBox(double hDPI, double vDPI, int rotate,
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -127,6 +127,7 @@
// 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(); }
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -76,8 +76,7 @@
}
}
-GBool PreScanOutputDev::functionShadedFill(GfxState *state,
- GfxFunctionShading *shading) {
+GBool PreScanOutputDev::shadedFill(GfxState *state, GfxShading *shading) {
if (shading->getColorSpace()->getMode() != csDeviceGray &&
shading->getColorSpace()->getMode() != csCalGray) {
gray = gFalse;
@@ -90,34 +89,6 @@
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) {
- if (shading->getColorSpace()->getMode() != csDeviceGray &&
- shading->getColorSpace()->getMode() != csCalGray) {
- gray = gFalse;
- }
- mono = gFalse;
- if (state->getFillOpacity() != 1 ||
- state->getBlendMode() != gfxBlendNormal) {
- transparency = gTrue;
- }
- return gTrue;
-}
-
void PreScanOutputDev::clip(GfxState *state) {
//~ check for a rectangle "near" the edge of the page;
//~ else set gdi to false
@@ -229,7 +200,7 @@
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 @@
Stream *str,
int width, int height,
GfxImageColorMap *colorMap,
- Stream *maskStr,
+ Object *maskRef, Stream *maskStr,
int maskWidth, int maskHeight,
GfxImageColorMap *maskColorMap,
double *matte, GBool interpolate) {
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -46,11 +46,6 @@
// 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 @@
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 @@
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);
Added: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc (rev 0)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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;
+ }
+}
+
Added: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h (rev 0)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
//------------------------------------------------------------------------
+// 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 @@
//------------------------------------------------------------------------
+// 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 @@
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 @@
for (i = 0; i < cacheSets * cacheAssoc; ++i) {
cacheTags[i].mru = (Gushort)(i & (cacheAssoc - 1));
}
- inUse = gFalse;
+ refCount = 0;
}
T3FontCache::~T3FontCache() {
@@ -565,6 +584,7 @@
SplashBitmap *origBitmap;
Splash *origSplash;
double origCTM4, origCTM5;
+ SplashStrokeAdjustMode savedStrokeAdjust;
T3GlyphStack *next; // next object on stack
};
@@ -711,6 +731,9 @@
if (bitmap) {
delete bitmap;
}
+ if (textClipPath) {
+ delete textClipPath;
+ }
}
void SplashOutputDev::startDoc(XRef *xrefA) {
@@ -763,6 +786,7 @@
delete bitmap;
bitmap = NULL;
}
+ traceMessage("page bitmap");
bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,
colorMode != splashModeMono1, bitmapTopDown,
NULL);
@@ -1194,18 +1218,21 @@
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 @@
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
case fontType1C:
@@ -1337,7 +1364,7 @@
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
case fontType1COT:
@@ -1370,7 +1397,7 @@
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
case fontTrueType:
@@ -1415,7 +1442,7 @@
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
case fontCIDType0:
@@ -1443,7 +1470,7 @@
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
case fontCIDType0COT:
@@ -1513,7 +1540,7 @@
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
case fontCIDType2:
@@ -1588,7 +1615,7 @@
gfxFont->getName() ? gfxFont->getName()->getCString()
: "(unnamed)");
delete fontLoc;
- goto err2;
+ goto err1;
}
break;
default:
@@ -1734,6 +1761,7 @@
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 @@
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 @@
// 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 @@
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 @@
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;
+ 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: 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;
- }
- }
-
- // 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;
+ splash->composite(tileBitmap, 0, 0, x, y, tileW, tileH,
+ gFalse, gFalse);
}
- 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;
}
}
}
- // 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(overprintMaskBitmap);
+ delete tileBitmap;
+}
- gfree(sColors);
- delete tBitmap;
+GBool SplashOutputDev::shadedFill(GfxState *state, GfxShading *shading) {
- return gTrue;
-}
-
-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;
+ // generate the bitmap
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;
- }
-
- // 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
if (colorMode == splashModeMono1) {
srcMode = splashModeMono8;
} else if (colorMode == splashModeBGR8) {
@@ -2440,287 +2097,36 @@
} 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;
+ 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;
}
- 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;
+ // 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);
}
- // 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;
- }
- }
-
- // 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;
}
-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 @@
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 @@
}
if (!(gfxFont = state->getFont())) {
- return gFalse;
+ return gTrue;
}
fontID = gfxFont->getID();
ctm = state->getCTM();
@@ -2954,7 +2361,7 @@
}
} else {
for (j = nT3Fonts - 1; j >= 0; --j) {
- if (!t3FontCache[j]->inUse) {
+ if (t3FontCache[j]->refCount == 0) {
break;
}
}
@@ -3038,7 +2445,11 @@
}
}
- 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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
#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,7 +3605,12 @@
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;
@@ -4193,6 +3617,7 @@
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 @@
}
}
- 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 @@
}
}
-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,7 +3900,8 @@
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) {
@@ -4478,7 +3908,8 @@
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 @@
}
// 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 @@
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,7 +4102,8 @@
Splash *tSplash;
SplashTransparencyGroup *transpGroup;
SplashColor color;
- SplashColorPtr p;
+ SplashColorPtr p, colorPtr, colorPtr2;
+ Guchar *alphaPtr;
GfxGray gray;
GfxRGB rgb;
#if SPLASH_CMYK
@@ -4639,7 +4110,9 @@
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,6 +4182,7 @@
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),
@@ -4715,45 +4189,65 @@
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 @@
}
#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);
}
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -69,11 +69,6 @@
// 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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -1317,6 +1317,7 @@
eof = gFalse;
inputBits = 0;
clearTable();
+ totalIn = totalOut = 0;
}
GBool LZWStream::processNextCode() {
@@ -1382,7 +1383,15 @@
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 @@
return EOF;
inputBuf = (inputBuf << 8) | (c & 0xff);
inputBits += 8;
+ ++totalIn;
}
code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1);
inputBits -= nextBits;
@@ -2268,7 +2278,6 @@
GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent,
GBool okToReadStream) {
GString *s;
- char s1[50];
if (psLevel < 2) {
return NULL;
@@ -2278,8 +2287,7 @@
}
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 @@
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 @@
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 @@
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::getBlock(char *blk, int size) {
- int n;
+ int n, k;
if (pred) {
return pred->getBlock(blk, size);
@@ -5031,11 +5039,17 @@
}
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 @@
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 @@
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,8 +5193,17 @@
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;
err:
@@ -5164,6 +5253,7 @@
"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 @@
}
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 @@
return EOF;
codeBuf |= (c & 0xff) << codeSize;
codeSize += 8;
+ ++totalIn;
}
c = codeBuf & ((1 << bits) - 1);
codeBuf >>= bits;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -501,6 +501,8 @@
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 @@
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];
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
yMax = 1e8;
}
rot = (Guchar)rotA;
+ rotated = (char)rotatedA;
clipped = (char)clippedA;
invisible = (char)invisibleA;
spaceAfter = (char)gFalse;
@@ -236,6 +267,7 @@
colorR = colorRA;
colorG = colorGA;
colorB = colorBA;
+ overlap = gFalse;
}
int TextChar::cmpX(const void *p1, const void *p2) {
@@ -264,6 +296,12 @@
}
}
+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 @@
}
//------------------------------------------------------------------------
+// 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 @@
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 @@
}
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 @@
// 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 @@
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 @@
curFontSize = 0;
curRot = 0;
diagonal = gFalse;
+ rotated = gFalse;
nTinyChars = 0;
actualText = NULL;
actualTextLen = 0;
@@ -1108,6 +1188,7 @@
curFontSize = 0;
curRot = 0;
diagonal = gFalse;
+ rotated = gFalse;
nTinyChars = 0;
gfree(actualText);
actualText = NULL;
@@ -1219,7 +1300,12 @@
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 @@
}
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 @@
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 @@
}
// 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 @@
}
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 @@
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 @@
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 @@
rotateUnderlinesAndLinks(rot);
generateUnderlinesAndLinks(columns);
}
+ if (overlappingChars) {
+ if (overlappingChars->getLength() > 0) {
+ columns->append(buildOverlappingTextColumn(overlappingChars));
+ }
+ deleteGList(overlappingChars, TextChar);
+ }
#if 0 //~debug
dumpColumns(columns);
#endif
@@ -1585,24 +1700,44 @@
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();
+ 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);
+ }
}
- unrotateChars(chars, rot);
- unrotateColumns(columns, rot);
- if (control.html) {
- generateUnderlinesAndLinks(columns);
- }
return columns;
}
@@ -1618,6 +1753,7 @@
TextColumn *col;
TextParagraph *par;
TextLine *line;
+ GList *overlappingChars;
GList *columns;
GBool primaryLR;
int ph, colIdx, parIdx, lineIdx, rot, y, i;
@@ -1628,6 +1764,11 @@
#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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+// 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 @@
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 @@
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 @@
++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 @@
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 @@
// 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 @@
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 @@
int i;
lines = new GList();
- buildLines(blk, lines);
+ buildLines(blk, lines, gFalse);
spaceThresh = paragraphSpacingThreshold * getAverageLineSpacing(lines);
@@ -3712,12 +4435,13 @@
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);
@@ -3724,32 +4448,219 @@
} 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, rot);
- wordSp = computeWordSpacingThreshold(charsA, blk->rot);
-
words = new GList();
lineFontSize = 0;
spaceBefore = gFalse;
@@ -3758,10 +4669,11 @@
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 @@
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 @@
}
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 @@
}
}
- 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 @@
// 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 @@
// (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 @@
// 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 @@
} 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,16 +4988,10 @@
GList *lines;
int i;
- if (blk->tag == blkTagLine) {
+ if (blk->tag == blkTagLine || blk->tag == blkTagSuperLine) {
lines = new GList();
- buildLines(blk, lines);
+ buildLines(blk, lines, gTrue);
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);
- }
- superLines->append(new TextSuperLine(lines));
} else {
for (i = 0; i < blk->children->getLength(); ++i) {
buildSuperLines((TextBlock *)blk->children->get(i), superLines);
@@ -4681,6 +5594,59 @@
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 @@
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 @@
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 @@
switch (control.mode) {
case textOutReadingOrder:
+ case textOutSimple2Layout:
// already in reading order
break;
case textOutPhysLayout:
@@ -4958,7 +5952,8 @@
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 @@
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 @@
}
TextOutputDev::TextOutputDev(char *fileName, TextOutputControl *controlA,
- GBool append) {
+ GBool append, GBool fileNameIsUTF8) {
text = NULL;
control = *controlA;
ok = gTrue;
@@ -5055,15 +6050,22 @@
// keep DOS from munging the end-of-line characters
setmode(fileno(stdout), O_BINARY);
#endif
- } else if ((outputStream = fopen(fileName, append ? "ab" : "wb"))) {
+ } else {
+ 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;
- } else {
- error(errIO, -1, "Couldn't open text file '{0:s}'", fileName);
- ok = gFalse;
- return;
}
outputFunc = &outputToFile;
} else {
+ outputFunc = NULL;
outputStream = NULL;
}
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -42,6 +42,7 @@
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
@@ -48,6 +49,12 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
GList *getWords() { return words; }
int getLength() { return len; }
double getEdge(int idx) { return edge[idx]; }
+ GBool getHyphenated() { return hyphenated; }
private:
@@ -407,6 +426,16 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
// 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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -201,3 +201,13 @@
}
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;
+}
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -56,6 +56,9 @@
// Create a PDF text string from a TextString.
GString *toPDFTextString();
+ // Convert a TextString to UTF-8.
+ GString *toUTF8();
+
private:
void expand(int delta);
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -22,6 +22,7 @@
WebFont::WebFont(GfxFont *gfxFontA, XRef *xref) {
GfxFontType type;
+ Ref id;
gfxFont = gfxFontA;
fontBuf = NULL;
@@ -29,24 +30,26 @@
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);
+ 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;
+ }
}
- } 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;
- }
}
}
Deleted: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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;
-}
Deleted: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAForm.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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
Added: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc (rev 0)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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");
+}
Added: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h (rev 0)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -350,7 +350,7 @@
// 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 @@
}
// 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,13 +463,14 @@
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,
@@ -474,34 +477,30 @@
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 @@
//~ 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 @@
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 @@
// 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();
+ }
- // got trailer dictionary
- if (!strncmp(p, "trailer", 7)) {
- obj.initNull();
- parser = new Parser(NULL,
+ gfree(streamObjNums);
+
+ 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 @@
gLockMutex(&objStrsMutex);
#endif
if (!(objStr = getObjectStream(objStrNum))) {
+#if MULTITHREADED
+ gUnlockMutex(&objStrsMutex);
+#endif
return gFalse;
}
cleanObjectStreamCache();
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -166,11 +166,17 @@
#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);
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -430,7 +430,8 @@
GString *name, *value;
const char *start;
char quote, c;
- int x, n;
+ unsigned int x;
+ int n;
name = parseName();
parseSpace();
@@ -559,7 +560,8 @@
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 @@
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) {
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -103,7 +103,7 @@
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);
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/config.h
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/config.h 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/config.h 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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
//------------------------------------------------------------------------
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
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 @@
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 @@
} 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 @@
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;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 @@
Page *page;
Dict *resDict;
Annots *annots;
- Form *form;
+ AcroForm *form;
Object obj1, obj2;
int pg, i, j;
int exitCode;
@@ -113,7 +113,7 @@
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);
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -87,7 +87,7 @@
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);
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -102,6 +102,9 @@
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 @@
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 @@
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 @@
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());
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -31,6 +31,7 @@
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 @@
"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 @@
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 @@
}
// 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,7 +174,7 @@
htmlGen->setZoom(zoom);
htmlGen->setDrawInvisibleText(!skipInvisible);
htmlGen->setAllTextInvisible(allInvisible);
- htmlGen->setExtractFontFiles(gTrue);
+ htmlGen->setExtractFontFiles(!noFonts);
htmlGen->startDoc(doc);
// convert the pages
@@ -178,13 +181,13 @@
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 @@
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);
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 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 @@
"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 @@
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 @@
}
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 @@
#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 @@
#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 @@
#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;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -36,6 +36,7 @@
#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 @@
{"-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 @@
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 @@
}
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
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -182,7 +182,7 @@
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 @@
// 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;
Modified: trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc
===================================================================
--- trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc 2021-02-01 02:15:42 UTC (rev 57587)
+++ trunk/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc 2021-02-01 06:11:18 UTC (rev 57588)
@@ -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 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 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 @@
"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 @@
"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 @@
// 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 @@
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 @@
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);
More information about the tex-live-commits
mailing list.