summaryrefslogtreecommitdiff
path: root/Build/source/libs/xpdf/xpdf-src
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
committerKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
commitad547a6b5986815fda458221149728d9d9ab1d87 (patch)
tree16296910eb3eca724371474ea9aea3994dc69614 /Build/source/libs/xpdf/xpdf-src
parent947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff)
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/libs/xpdf/xpdf-src')
-rw-r--r--Build/source/libs/xpdf/xpdf-src/ANNOUNCE37
-rw-r--r--Build/source/libs/xpdf/xpdf-src/CHANGES2742
-rw-r--r--Build/source/libs/xpdf/xpdf-src/CMakeLists.txt41
-rw-r--r--Build/source/libs/xpdf/xpdf-src/COPYING339
-rw-r--r--Build/source/libs/xpdf/xpdf-src/COPYING3674
-rw-r--r--Build/source/libs/xpdf/xpdf-src/INSTALL196
-rw-r--r--Build/source/libs/xpdf/xpdf-src/README391
-rw-r--r--Build/source/libs/xpdf/xpdf-src/aconf2.h41
-rw-r--r--Build/source/libs/xpdf/xpdf-src/cmake-config.txt306
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.1107
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat92
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdffonts.1164
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat118
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdfimages.1119
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat98
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.1167
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat117
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.1131
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat115
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftopng.1127
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat103
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.1127
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat102
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftops.1253
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat214
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftotext.1218
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat180
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/sample-xpdfrc86
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/xpdf.1925
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat838
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.5823
-rw-r--r--Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat755
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/CMakeLists.txt26
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiBase.cc177
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiBase.h58
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiEncodings.cc995
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiEncodings.h36
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc887
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.h55
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc2461
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.h216
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc359
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.h64
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc3397
-rw-r--r--Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h281
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt28
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.cc134
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h180
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/GHash.cc385
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/GHash.h78
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/GList.cc123
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/GList.h106
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/GMutex.h101
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/GString.cc853
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/GString.h151
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/Trace.cc114
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/Trace.h43
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/gfile.cc770
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/gfile.h131
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/gmem.cc396
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/gmem.h95
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/gmempp.cc51
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/gmempp.h48
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/gtypes.h29
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/parseargs.c195
-rw-r--r--Build/source/libs/xpdf/xpdf-src/goo/parseargs.h72
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/CMakeLists.txt47
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/Splash.cc8210
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/Splash.h532
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc288
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h98
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashClip.cc521
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashClip.h130
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashErrorCodes.h34
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFTFont.cc460
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFTFont.h60
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontEngine.cc392
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontEngine.h99
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontFile.cc171
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontFile.h95
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc195
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFont.h111
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc409
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h129
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFontFile.cc84
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFontFile.h95
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFontFileID.cc26
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashFontFileID.h32
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashGlyphBitmap.h28
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashMath.h378
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashPath.cc213
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashPath.h133
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashPattern.cc44
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashPattern.h69
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashScreen.cc383
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashScreen.h81
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashState.cc295
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashState.h135
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashTypes.h145
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc645
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.h143
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashXPathScanner.cc620
-rw-r--r--Build/source/libs/xpdf/xpdf-src/splash/SplashXPathScanner.h84
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc3886
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h169
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc1443
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Annot.h185
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Array.cc74
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Array.h70
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFont.cc67
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFont.h58
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFontTables.cc4285
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFontTables.h23
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt273
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/CMap.cc440
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/CMap.h115
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc1197
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h166
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc787
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h126
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/CharTypes.h24
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/CompactFontTables.h464
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Decrypt.cc1620
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Decrypt.h133
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Dict.cc145
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Dict.h89
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/DisplayState.cc223
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/DisplayState.h173
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Error.cc92
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Error.h48
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/ErrorCodes.h36
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/FontEncodingTables.cc1825
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/FontEncodingTables.h20
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Function.cc1602
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Function.h240
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc5338
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h362
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc2303
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h410
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc4778
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h1365
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc3799
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h612
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc826
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.h94
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc453
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h116
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/JArithmeticDecoder.cc370
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/JArithmeticDecoder.h114
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc4207
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h154
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc3540
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h359
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc555
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.h85
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Link.cc926
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Link.h453
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/NameToCharCode.cc117
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/NameToCharCode.h42
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/NameToUnicodeTable.h4257
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc245
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Object.h320
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc537
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.h139
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Outline.cc186
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Outline.h84
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc140
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h251
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PDF417Barcode.cc1916
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PDF417Barcode.h32
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc2009
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h357
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc688
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h224
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PDFDocEncoding.cc46
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PDFDocEncoding.h16
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc8438
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h510
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PSTokenizer.cc136
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PSTokenizer.h41
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc508
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Page.h207
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Parser.cc269
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Parser.h65
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc308
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h149
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/SecurityHandler.cc373
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/SecurityHandler.h127
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc1327
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h106
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc4497
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h320
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Stream-CCITT.h464
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc6000
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h1056
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc6348
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h779
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc213
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h71
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TileCache.cc562
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TileCache.h78
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TileCompositor.cc452
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TileCompositor.h77
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TileMap.cc1690
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/TileMap.h247
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/UTF8.cc183
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/UTF8.h42
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeMap.cc281
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeMap.h124
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeMapTables.h366
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeRemapping.cc178
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeRemapping.h51
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeTypeTable.cc972
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeTypeTable.h26
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc387
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.h69
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc675
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h166
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc1404
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h187
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc1072
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h271
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/config.h103
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc212
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc451
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc164
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc551
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc260
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc366
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc260
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc386
-rw-r--r--Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc315
232 files changed, 147318 insertions, 0 deletions
diff --git a/Build/source/libs/xpdf/xpdf-src/ANNOUNCE b/Build/source/libs/xpdf/xpdf-src/ANNOUNCE
new file mode 100644
index 00000000000..e070ad95d5b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/ANNOUNCE
@@ -0,0 +1,37 @@
+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
+also includes a PDF text extractor, PDF-to-PostScript converter, and
+various other utilities.
+
+Xpdf uses the Qt toolkit and runs on Linux and Windows. The
+non-graphical components (pdftops, pdftotext, etc.) run on Linux,
+Unix, Windows, MacOSX, and pretty much any other system with a decent
+C++ compiler.
+
+4.03 is primarily a bug fix release. There are some new features:
+
+* 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 new command line options for pdftotext, pdftohtml, pdftoppm,
+ pdftopng, and xpdf.
+
+See the `CHANGES' file for a complete list of changes.
+
+Source (C++ and C) is available, and it should be fairly easy to
+compile for Linux and Windows.
+
+More information, source code, and precompiled binaries are on the
+xpdf web page:
+
+ http://www.xpdfreader.com/
+
+For information on commercial licensing and consulting, please see the
+Glyph & Cog web site:
+
+ http://www.glyphandcog.com/
diff --git a/Build/source/libs/xpdf/xpdf-src/CHANGES b/Build/source/libs/xpdf/xpdf-src/CHANGES
new file mode 100644
index 00000000000..f2dccc095fd
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/CHANGES
@@ -0,0 +1,2742 @@
+0.2 (95-dec-12)
+---------------
+First public release.
+
+
+0.3 (96-jan-13)
+---------------
+LZW patent workaround.
+Implemented inline images.
+Fixed (mostly) disjoint polygon fills.
+Added remote server stuff.
+Added page number on command line.
+Fixed problem with font encodings which caused character misalignment.
+Fixed inverted CCITT decoding and inverted image mask drawing.
+Now compiles under gcc 2.7.x (ignore those stupid 'unused parameter'
+ warnings).
+Many minor bug fixes and optimizations.
+
+
+0.4 (96-apr-24)
+---------------
+Implemented DCT filter.
+Implemented PostScript output; wrote pdftops program.
+Implemented links.
+Implemented font rotation -- I was wrong: X11R6 servers *do* support
+ font rotation (by specifying a matrix in place of a size).
+Added bindings for Home/End, Page Up/Down, arrow keys.
+Added initialZoom resource and -z option.
+Added geometry resource and -g option.
+Fixed image size off-by-one bug.
+Fixed bug where page content is reference to an array of streams.
+Cleaned up uninitialized variables which were causing random problems
+ on various platforms.
+Manually skip whitespace before calling atoi() for startxref.
+Replaced calls to XrmCombineFileDatabase() with calls to
+ XrmGetFileDatabase() and XrmMergeDatabases() so it will work under
+ older versions of X.
+Fixed problem with reading multiple xref tables in updated PDF files.
+Check for encryption and print appropriate error message.
+Rudimentary dithering of images.
+Fixed bug in CCITTFax filter (pass mode followed by horizontal mode).
+Optimized drawImage() and drawImageMask().
+Changed several things to ease porting:
+ - changed '__inline' to 'inline' (to adhere to the ANSI standard)
+ - surrounded interface/implementation pragmas with #ifdef _GNUC__
+ - got rid of empty array initializer lists
+ - moved Operator type definition from Gfx.cc to Gfx.h
+ - renamed String, uint, etc.
+ - ability to uncompress to file (NO_POPEN and USE_GZIP flags)
+ - added definitions of XK_Page_Up/Down and XPointer for old versions
+ of X
+For VMS port:
+ - use correct Xdefaults name for VMS, get rid of ltkGetHomeDir()
+ - added '#include <stddef.h>' before all X includes
+ - renamed files with multiple periods in their names
+Fixed window resizing infinite oscillation bug.
+Fixed problem with string-type (as opposed to stream-type) indexed
+ color space lookup tables (which are used in inline images).
+If an X font is not found, try smaller and then larger sizes (this is
+ useful for old or broken X servers which can't scale bitmap fonts).
+Added -rgb (color cube size) option.
+Various minor bug fixes.
+
+
+0.5 (96-may-23)
+---------------
+Fixed bug in LTKWindow which broke the remote server mode.
+Fixed PostScript output:
+ - doesn't seg fault if file is unwritable.
+ - incorrect DSC comment - need colon in '%%Page:'.
+ - use 'imagemask' command for masks.
+ - output filters in the correct order.
+ - Stream::isBinary() checks the next-to-bottom, not top, stream.
+ - if page width > height, rotate it 90 degrees.
+ - if page is larger than paper size, scale it down.
+Set default MediaBox to 8.5" x 11" to deal with non-compliant PDF
+ files which don't specify a MediaBox.
+Added DEBUG_MEM stuff in gmem.c and gmempp.cc.
+Fixed memory leaks:
+ - LTKWindow didn't delete the LTKBox.
+ - LinkAction needs a virtual destructor.
+Use $(RANLIB) variable in goo/Makefile and ltk/Makefile.
+Allocate image data after calling XCreateImage, using
+ image->bytes_per_line -- works in 24-bit mode now.
+DCTStream rounds width of rowBuf lines up to the next multiple of
+ mcuWidth, so last MCU doesn't run off end of buffer.
+Increase size of block (from 255 to 1024 bytes) read at end of file to
+ search for 'startxref'.
+Skip past garbage at start of file, look for '%PDF'.
+Moved more compiler options out of Makefiles into Makefile.config.
+Top-level Makefile uses '$(MAKE)' instead of 'make' for making
+ subdirectories.
+Space/PageDown/Next and Backspace/PageUp/Previous now moves to
+ next/previous page if already scrolled to bottom/top of current
+ page.
+
+
+0.5a (96-jul-09)
+----------------
+[not a public release]
+For PDF 1.2 (a.k.a. Amber, a.k.a. Acrobat 3) support:
+ - look for trailer after first xref instead of at end of file.
+Deal with font subsets by converting character names of the form
+ 'Cnnnn' to the appropriate character from the standard encoding.
+Extract encoding from embedded Type 1 fonts.
+Kludge to fill one-pixel thick polygons.
+Changed X font encoding to use endash for hyphen (gets rid of too-long
+ hyphens).
+Handle Resources key in Pages dictionaries (needed for pstoedit
+ output).
+Fix comment handling in Parser (needed for pstoedit output).
+Move Bezier curve conversion from GfxState to XOutputDev; look at
+ flatness parameter in GfxState.
+Change all of the path functions in XOutputDev (stroke, fill, clip) to
+ use the same path transformation/conversion function.
+Rewrote PostScript output driver as a subclass of OutputDev; removed
+ duplicated code (ps_ functions) from Gfx.
+Fixed bug in xref code with small (< 1024 bytes) PDF files.
+Implemented BX/EX operators.
+Added PDFDoc class.
+
+
+0.6 (96-nov-12)
+---------------
+Add support for PostScript output to stdout (-) and to a command
+ (|lpr); added -ps option and psFile resource.
+Decryption is implemented but not included in the distribution due to
+ legal restrictions: the decryption algorithm is a trade secret of
+ RSA, Inc., and the U.S.A. still has bogus export controls on
+ cryptography software.
+Added .xpdfrc config file:
+ - Added fontmap parameter: user can map PDF font names to X fonts.
+ - Added fontpath parameter: search for Type 1 font if encoding is
+ not in PDF file.
+Incremental display: display is updated after every 200 commands.
+Added forward and backward by-10-page buttons.
+Links:
+ - Implement links with "Launch" actions that point to PDF files.
+ - Draw borders around links.
+ - Handle links with named destinations.
+ - GoToR links specify a page number instead of a page reference.
+Optimizations:
+ - Rewrote Stream to use buffering, and added lookChar() functions;
+ rewrote Lexer to take advantage of this.
+ - Use hash tables for name->code mapping in font encodings.
+ - Made XOutputDev::doCurve() iterative, changed /2 to *0.5, and
+ changed the flatness test.
+Added file name to window title.
+Implemented RunLength filter.
+Implemented forms.
+Convert ObjType to an enum.
+Changed isDict("Pages") to isDict() (in Catalog.cc) to deal with
+ incorrect PDF files.
+Changed color selection so that very pale colors don't map to white.
+Fixed bug in CCITTFax filter (multiple make-up codes).
+In GString::clear(): need to set length to 0 before calling resize().
+Base initial window size on first displayed page, not page 1; deal
+ correctly with rotated pages.
+Added ltkGetIntResource() and LTKApp::getIntResource().
+PostScript output fixes:
+ - Escape backslashes in strings.
+ - When doing ASCII85 encoding, keep both chars of EOF marker ('~>')
+ on same line.
+ - Add extra line '%-EOD-' after image data streams; call wrapper
+ functions for image and imagemask which look for this line -- this
+ should fix the 'too much data in stream' bug.
+ - Font tags can be reused for different fonts on different pages --
+ so use font object reference (number/generation) instead.
+Initialize character widths to zero (this caused crashes on OSF/1).
+Handle image masks which go outside of pixmap.
+Makefile.config changes:
+ - Remove -o in C++ compile rule.
+ - Add $(AR) variable.
+Code which read char widths from font dictionary read all but the last
+ width.
+Add 'return 0;' to main() in xpdf and pdftops.
+Allow fonts to use StandardEncoding.
+Convert man pages to VMS help files.
+
+0.7 (97-may-28)
+---------------
+Implemented FlateDecode filter (for PDF 1.2).
+Basic xref table reconstruction for damaged files
+New pdftotext program converts PDF to plain text.
+Implemented menus in LTK; added a menu to xpdf.
+Added open and save functions; allow xpdf to start without any PDF
+ file.
+Implemented text find.
+Implemented text select/copy.
+Change mouse cursor when it's over a link.
+Embed Type 1 fonts in PostScript output.
+Moved rotate functions to menu; added quit to menu.
+Fixed stroke color bug in PostScript output (was using fill color
+ instead of stroke color; this sometimes caused lines to be missing
+ (white) in PostScript output).
+Support Launch-type links -- pops up a dialog before executing
+ anything. Expects the A (action) dictionary to contain a Unix
+ dictionary with F (file) and P (paremeter) keys just like the Win
+ dictionary.
+A moveto op all by itself should just be discarded, instead of
+ generating a subpath with one point (this was causing seg faults).
+Balanced parentheses in strings don't need to be escaped.
+Tj operator in PostScript prolog didn't check for zero when dividing
+ by length of string.
+Implemented selection in LTK; TextIn widgets support dragging/copy/
+ paste.
+Handle font subsets that use hex character codes.
+Added icon pixmap; added the XPMLIB and NO_XPM variables to
+ Makefile.config.
+Fixed subtle bug in use of horizontal scaling parameter (it affects
+ only the width of drawn characters, not positioning done in text
+ space).
+Memory testing (with DEBUG_MEM):
+ - gmalloc now fills memory blocks with garbage to catch unitialized
+ fields.
+ - gfree fills memory blocks with garbage to catch uses of freed
+ blocks.
+Handle image masks which go off the pixmap on the top and/or left.
+Fixed inline functions which had two return statements (to make the
+ HP, SCO, and other cfront-based compilers happy).
+Fixed bug which caused seg faults when following a link to a different
+ file (info in LinkGoto object was used after link was deleted by
+ loadFile).
+If page content is an array of streams, the streams are concatenated;
+ objects and commands can span multiple streams.
+If file open fails, try lower-casing and upper-casing the file name.
+Commands should end when lexer sees a '/' character.
+GString::append(char *, int) was broken.
+Changed LTKScrollingCanvas redraw to be more efficient: copy as much
+ as possible from window before copying from off-screen pixmap.
+Ignore gs (set extended graphics state) operator.
+Handle colorspaces (CalGray/RGB are treated as DeviceGray/RGB; the
+ weird colorspaces are not yet implemented).
+Named destinations (for links) can be strings as well as names; deal
+ with the names tree in the catalog.
+Clip to the page CropBox.
+Added '-q' to gzip options (to suppress warnings, in case user has -v
+ in GZIP env var).
+Added 'include Makefile.config' to top-level Makefile.
+Added INSTALL variable to Makefile.config; used in top-level
+ Makefile.
+Always initialize LinkDest left/bottom/top/right/zoom fields (bogus
+ floating point values were causing crashes on Alpha).
+Added Makefile.config options for Digital Unix (DEC compilers), HP-UX
+ (HP compilers), SCO Unix, and Evans & Sutherland ES/OS.
+Added flag to set stream mode in fopen call for VMS.
+Rewrote Link module.
+Pages with no contents shouldn't cause an error message.
+In PostScript output: pdfImM needs to set fill color before doing
+ imagemask.
+If font doesn't specify character widths, use widths from built-in
+ font, based on font flags.
+Fixed LTK scrollbar to delay before repeating and to control the
+ period between repeats.
+Removed window/widget copy() methods (they were untested and unused).
+Unknown filter types produce a single error message instead of a
+ stream of errors.
+Added a dummy target in top-level Makefile so making individual
+ executables (e.g., 'make pdftops') should now work.
+Added optional xpdf-flip.ltk with buttons on right side instead of
+ bottom of window.
+
+0.7a (98-feb-22)
+----------------
+Moved find command from menu to toolbar button ('f' key still works).
+Support TrueColor visuals.
+Added a -cmap option and a installCmap resource to install a private
+ colormap.
+Mouse button 2 pans the window.
+Selecting a URI link now executes a configurable command (from the
+ urlCommand resource).
+Added a "link info" display which shows the URL or file for the link
+ under the mouse.
+Don't draw (or convert to PostScript) text drawn in render modes 3 and
+ 7 -- this is invisible text, used by Acrobat Capture; this text is
+ still passed to the TextPage object so that selection works.
+Recognize (and quietly ignore) marked content operators (BMC, BDC,
+ EMC, MP, DP).
+Recognize new color-setting operators (scn, SCN).
+Added A4_PAPER option.
+Embed external Type 1 font files (this currently only works with PFA
+ files).
+Added "-level1" option (in xpdf and pdftops) to generate Level 1
+ PostScript.
+Setup autoconf -- replaced Makefile.config. Added SELECT_TAKES_INT
+ flag, and use configure to autodetect (for HP-UX).
+Fixed appendToPath() to behave reasonably when appending ".." to root
+ directory.
+Fixed array size in FlateStream::compHuffmanCodes() (was causing xpdf
+ to crash under OSF/1).
+ASCII85Stream, ASCIIHexStream, and DCTStream didn't check for EOF and
+ could run past the end of the stream in damaged files.
+Handle hex escapes (#xx) in names. Still allow the name /# for
+ backward-compatibility.
+Check for NULL characters in encoding array in GfxFont.cc (was calling
+ strcmp() with NULL which crashed under Solaris).
+PageAttrs::PageAttrs() didn't initialize crop box boundaries.
+Changed uses of lookup() to lookupNF() in XRef.cc.
+Fixed type checking of operators which take a variable number of
+ args.
+Gfx::buildImageStream() doesn't need to check for parser (since I got
+ rid of the bogus array-of-command thing).
+XOutputFont matches on font reference instead of font tag (similar to
+ PSOutputDev fix).
+Fixed bug in position calculation for multi-char substitutions in
+ XOutputDev.
+Cleaned up local variables which hid class variables.
+Optimized variable length decoding in CCITTFaxStream.
+Set link border width to zero if Border dictionary entry is missing.
+Throw away zero-length strings in TextOutputDev -- they don't have
+ valid xMin/xMax values.
+Swapped order of XLIBS and XPMLIB in xpdf/Makefile.
+Deleted 'LTKApp::' in function declaration in LTKApp.h.
+Changed '(XKeyEvent *)&event' to '&event.xkey' in LTKApp.cc.
+Check that the link rectangle coordinates are in the correct order,
+ and swap if necessary.
+TextOutputDev didn't set text to NULL, which caused pdftotext to
+ segfault if it couldn't open it's output file.
+Fixed a hash table search bug in GfxFontEncoding::getCharCode().
+Cleaned up colorspace code: rewrote GfxColorSpace and added
+ GfxImageColorMap; cleaned up PSOutputDev::doImage.
+Handle named colorspaces in images.
+Correctly set the default color after a colorspace change.
+Old setcolor operators now set the colorspace.
+Fixed bug with uncompressed blocks in FlateStream.
+Fixed bug with fixed Huffman code table in FlateStream.
+Added hash table of X windows (for LTKWindow and LTKWidget) to LTKApp
+ and replaced calls to XQueryTree with hash table searches -- this
+ avoids a roundtrip to the server for each event and also fixes the
+ problem where XQueryTree crashed if the window no longer existed
+ (with leftover events from a destroyed window). (Thanks to Yair
+ Lenga for the suggestion.)
+Create a new GC for selection -- xor black and white (instead of LTK
+ foreground and background).
+Fixed crash with blank lines in .xpdfrc.
+Allow spaces in font descriptors in fontmap lines in .xpdfrc.
+Check for bogus object number in XRef::fetch().
+Use MacRomanEncoding for TrueType fonts that don't specify an
+ encoding.
+Certain PDF generators apparently don't include FontDescriptors for
+ Arial, TimesNewRoman, and CourierNew -- set GfxFont flags
+ appropriately.
+Fixed a bug in width guessing in GfxFont -- sans serif and serif were
+ swapped.
+Rewrote XRef::readXRef() to avoid using a parser to read the xref
+ entries.
+Added NO_TEXT_SELECT option.
+Ignore APPn/COM/etc. markers in DCT streams.
+Replaced select() with XMultiplexInput() in LTKApp.cc for VMS.
+Handle WM_DELETE_WINDOW protocol -- if you ask the window manager to
+ delete the xpdf window, xpdf will exit cleanly; other
+ windows/dialogs are simply closed.
+Optimized DCT decoder; switched to integer arithmetic.
+The "/Type /Annots" field in an annotation dictionary is optional.
+Check for null nameTree in Catalog::findDest().
+In XOutputDev, search user font map before default font map.
+Added "normal" SETWIDTH parameter to all font descriptors in
+ XOutputDev (some systems have a narrow-width Helvetica font).
+Added FOPEN_READ_BIN and FOPEN_WRITE_BIN to support Win32.
+Added a hack which allows better font substitution for some Type 3
+ fonts. Also allow character names of the form /nn and /nnn.
+Added <strings.h> and <bstring.h> to LTKApp.cc (needed by AIX and IRIX
+ for bzero() declaration for FD_ZERO).
+
+0.80 (98-nov-27)
+----------------
+Support for some Japanese fonts (Type 0 fonts using the Adobe-Japan1-2
+ character collection, horizontal only).
+Added pdfinfo application.
+Added pdftopbm application.
+Added pdfimages application.
+Added -papercolor option and .paperColor resource.
+Fixed divide-by-zero problem in XOutputDev Type 3 font matrix kludge.
+Font subset char names can be 'Bxx' as well as 'Cxx' and 'Gxx'.
+Fixed bug in color space conversion in DCTStream filter (YCC->RGB was
+ correct, YCCK->CMYK was broken).
+Added XRef::getDocInfo() and PDFDoc::getDocInfo() to support pdfinfo.
+Optimized GfxImageColorMap.
+Lexer::getStream(), getPos(), and setPos() check for null stream.
+Decryption code now does strings as well as streams.
+ASCII85 decoder rounds short tuples up instead of down.
+CropBox and MediaBox can be non-integers.
+PostScript output:
+ - Use a rectangle operator.
+ - Call setpagedevice with page size.
+ - Insert %%PageOrientation comments.
+ - Add paper size flags (-paperw and -paperh) to xpdf and pdftops.
+ - If HAVE_POPEN is not defined, and user tries to print to '|...',
+ the PSOutputDev destructor tried to write to the PS file.
+ - Added support for forms (pdftops -form).
+Removed error messages for empty paths in stroke, fill, etc. operators.
+Don't allow flatnesses less than 1 in XOutputDev (this speeds up
+ rendering a little bit when there are lots of tiny curves).
+Moved the font subset character name guessing from GfxFont to
+ XOutputDev and TextOutputDev - now these files print correctly.
+Cast argument to XFree() to XPointer; add XPointer definition where
+ necessary (portability fixes).
+Various minor VMS fixes.
+Changes to configure script and Makefiles:
+ - Print a warning if X is missing.
+ - Use C++ when checking select() argument type (HP-UX).
+ - Use 0 instead of NULL when checking select().
+ - Default to gcc instead of c++.
+ - Get rid of AC_C_INLINE -- this is meant for C, not C++.
+ - Changed -USE_GZIP to -DUSE_GZIP.
+ - Added ability to compile ouside of the source tree.
+ - Added .cc.o rule to {goo,ltk,xpdf}/Makefile.in.
+ - Added @LIBS@ to XLIBS in xpdf/Makefile.in.
+ - In top-level Makefile.in: added '-' to clean commands; added
+ distclean rule.
+ - Create install directories.
+ - Use INSTALL_DATA (instead of INSTALL) for man pages.
+ - Changed xpdf-ltk.h rule to avoid leaving an empty file when
+ ltkbuild fails.
+ - Change things so that by default, ltkbuild is not built and
+ xpdf-ltk.h is not rebuilt.
+ - Use AM_PROG_CC_STDC to add compiler flags for ANSI C.
+ - Modify autoconf's builtin macros to check for xlC.
+ - Use Steve Robbins' smr_CHECK_LIB to test for pixmap library (Xpm)
+ -- this should fix the problems on systems that have the library
+ but not the include file.
+ - Added better test for sys/select.h, sys/bsdtypes.h, strings.h,
+ bstring.h.
+ - New VMS make scripts from Martin P.J. Zinser.
+ - Moved dependences into Makefile.in -- this gets rid of problems
+ with 'cc -MM' (which is gcc-specific) and 'include Makefile.dep'
+ (which isn't supported by all make implementations). Also changed
+ all non-system include files to '#include "..."' (from '<...>').
+Tweaked the TextOutputDev heuristics slightly.
+Modify Gfx to use a stack of resources -- this is necessary for Form
+ XObjects, which can define their own local resources; also modified
+ PSOutputDev to dump fonts used by forms.
+Look for excessively large MediaBox (compared to CropBox) and shrink
+ it to CropBox.
+Minor fix to scrolling when dragging a selection.
+Various patches for pdftex and Win32 support.
+Deal with Separation colorspaces by using their alternate colorspace
+ entry.
+Added PBMOutputDev for pdftopbm application.
+Added ImageOutputDev for pdfimages application.
+Separated XOutputDev into LTKOutputDev + XOutputDev.
+Added support for 1-D and mixed 1-D/2-D (Group 3) decoding to
+ CCITTDecode filter.
+Added resetImage(), getImagePixel(), and skipImageLine() to Stream
+ class; used these in XOutputDev, PSOutputDev, and ImageOutputDev.
+Implemented predictor for LZW and Flate filters.
+In pdfImM1 in PSOutputDev prolog: div should be idiv.
+Changed output from printUsage() function in parseargs to look nicer.
+
+0.90 (99-aug-02)
+----------------
+Added Type 1/1C font rendering, using t1lib.
+Added "backward" and "forward" buttons.
+Added fit-page and fit-page-width zoom factors; replaced zoom-in and
+ zoom-out buttons with a zoom popup menu.
+Type 1C fonts are converted to Type 1 and embedded in PostScript.
+Support vertical Japanese text.
+Added Japanese text support (EUC-JP) to pdftotext.
+Bumped PDF version to 1.3. Added stub functions for ri and sh
+ operators. (But there are still some missing 1.3 features.)
+Added -raw option to pdftotext.
+Minor changes to allow compiling under MS Visual C++ 5.0.
+Top-level makefile: changed 'mkdir -p' to '-mkdir -p'.
+Configure script: added X_CFLAGS to smr_CHECK_LIB(Xpm).
+Added Xpm_CFLAGS to xpdf/Makefile.in (this is needed to get the -I for
+ the xpm includes).
+Rewrote code that handles font encodings; added support for Type 1C
+ fonts.
+In the setpagedevice dictionary in PostScript output - added a
+ /Policies entry which tells the PS interpreter to scale the page to
+ fit the available paper.
+Changed PageUp behavior slightly: move to bottom (instead of top) of
+ previous page.
+TextPage used character's dx (width + char space) instead of just its
+ width.
+Read base URI from document Catalog (for URI-type links).
+Minor change to configure script to avoid using 'unset'.
+Fixed bugs in CropBox inheritance.
+Fixed a bug in resource stack handling for form objects.
+Display forms even if they have a missing/incorrect FormType.
+Fixed a bug in stream predictors -- the predictor parameters (width,
+ comps, bits) don't need to match the actual image parameters.
+ Completely rearranged the predictor code.
+Fixed PostScript output to correctly handle stream predictors.
+Don't segfault on empty (zero-page) documents.
+Added the xpdf.viKeys feature.
+Added the ffi and ffl ligatures to XOutputDev and TextOutputDev.
+Pdftotext and pdfimages now check okToCopy().
+Added a '-q' flag to all programs (except pdfinfo) to suppress
+ messages and errors.
+Deal with DeviceN colorspaces by using their alternate colorspace
+ entry.
+Change PostScript output so setpagedevice is only called once, at the
+ very beginning of the document (to avoid problems on duplex
+ printers).
+Changes to configure script and makefiles for DOS/DJGPP.
+FontEncoding::getCharCode() looked for (code>0) instead of (code>=0).
+Added keypad arrow keys, etc. to xpdf.
+Minor changes to gfile.{h,cpp} (<windows.h>) to compile under VC++.
+Fixed CCITTFaxStream to correctly handle all parameters.
+Modifications to gfile.{h,cc} for Acorn.
+Some minor changes for OS/2.
+Added 'SHELL = /bin/sh' to Makefiles.
+Compare file version to pdfVersionNum+0.0001 to avoid floating point
+ precision problems.
+Added LDFLAGS to Makefiles.
+Removed strip entirely from configure and Makefiles.
+Fixed a bug in choosing the correct DCTStream color transform.
+
+0.91 (2000-aug-14)
+------------------
+Added TrueType font rendering, using FreeType.
+Support for Chinese fonts (Type 0 fonts using the Adobe-GB1-2
+ character collection).
+Decryption code is included with the main distribution (now that the
+ US export regulations are a little bit less irrational).
+Added (very basic) support for generating PostScript with Japanese
+ fonts -- only tested with ghostscript so far.
+Added support for generating EPS files (pdftops -eps).
+Much-improved image filtering in xpdf (for downsampling and for
+ transforms other than 0/90/180/270-degree rotations).
+Implemented a basic full-screen (presentation) mode (xpdf
+ -fullscreen). (There is currently no way to switch between window
+ and full-screen modes on the fly -- this will be fixed in a later
+ release.)
+Added "reload" menu item.
+Do a better job with anti-aliased Type 1 fonts on non-white
+ backgrounds.
+Handle Lab color spaces.
+Handle non-null user passwords.
+Avoid security hole with tmpnam()/fopen() -- added openTempFile() in
+ goo/gfile.cc. [Thanks to Joseph S. Myers for pointing this out.]
+Filter out quote marks (' and ") in URLs before running urlCommand to
+ avoid a potential security hole. [Thanks to Frank Doepper for
+ pointing this out.]
+Fixed TrueColor detection to look at the complete list of available
+ visuals, not just the default visual.
+In gfile.h, changed NAMLEN(dirent) macro to NAMLEN(d).
+Removed copyright character from start-up banners.
+In the open and save dialogs, if the open/save button is pressed with
+ no file name, the dialog is not canceled.
+Added Xpm_CFLAGS to ltk/Makefile.
+XOutputDev::updateLineAttrs was using dashLength before it was
+ defined.
+In top-level Makefile.in, use INSTALL_PROGRAM instead of INSTALL.
+In man page, recommend -q instead of -err in .mailcap.
+Changes for GNOME / Bonobo support:
+ - Separated Stream classes into BaseStream and FilterStream trees.
+ - Got rid of all FileStream-specific stuff.
+ - Added a PDFDoc constructor that takes a BaseStream* instead of a
+ file name.
+Allow newlines inside strings (Photoshop does this).
+Don't require whitespace between tokens in consecutive content
+ streams (for pages that specify an array of streams).
+Look at MissingWidth when constructing font character widths array.
+Fixed a bug that caused incorrect PostScript output for images that
+ use 8-bit indexed color spaces with < 256 colors in the palette.
+Handle case where embedded font file is bad (this was seg faulting).
+Minor changes for Windows/pdftex.
+Work around a bug in PDF files from the IBM patent server.
+Fixed bugs in PostScript form generation: use pdfStartPage instead of
+ pdfSetup; problem with inline images.
+Minor bug fix in FlateStream::loadFixedCodes().
+Added %%DocumentMedia and %%PageMedia comments to the PostScript so
+ that gsview (a Windows frontend for ghostscript) gets the right
+ paper size.
+Draw AcroForm fields that have appearance annotations.
+Bounds check gray, CMYK, and RGB values (in GfxColor).
+Moved the link border drawing code into Page (from PDFDoc).
+Minor modifications for pdftohtml.
+PSOutputDev: use the Type 3 font scaling kludge from XOutputDev.
+Separation color spaces were handled incorrectly in images.
+Fixed a bug with form bounding boxes.
+Modified the t1lib support -- replace libt1x code with my own code.
+ Type 1 and TrueType fonts are now handled similarly, and clipping
+ works on Type 1 fonts.
+Don't print copyright banner (xpdf); add -v switch to get copyright
+ and version info (all apps); get rid of -err switch (xpdf).
+Automatically reload the PDF file if it has been changed, i.e., if the
+ modification time is different.
+Fixed a memory (malloc size) bug in CCITTFaxStream.
+Fixed two bugs in FontEncoding::hash() -- handle zero-length character
+ names (which were found in a (buggy?) PDF file), and handle
+ character names with high-bit-set characters (use unsigned ints).
+Added PDFDoc::isLinearized() and corresponding code in pdfinfo.cc.
+Handle files with an incorrect page count in the Pages dictionary
+ (FOP, from the Apache project produces a page count of 0).
+Handle TrueType equivalents to the Base14 fonts (Arial, TimesNewRoman,
+ CourierNew) -- Adobe's tools use these names without embedding the
+ fonts.
+Tweaked the Type 3 font sizing kludge.
+Changed pdfimages (ImageOutputDev) so it doesn't output JPEG files for
+ 4-component color spaces, since these seem to confuse most image
+ viewers.
+Added support for generating OPI comments (pdftops -opi).
+In XOutputDev::drawImage() and drawImageMask(), check for images that
+ are completely off-page.
+Use the provided alternate or a default (DeviceGray/RGB/CMYK) color
+ space for ICCBased color spaces.
+Incorporated MacOS-specific code from Leonard Rosenthol.
+Configure script switches to C++ for the strings.h/bstring.h test.
+Gfx::opRestore() calls clearPath() to handle (apparently) buggy PDF
+ files produced by FreeHand.
+The /Type field in most dictionaries is optional (PDF 1.3 change).
+Move printCommands variable definition into Gfx.cc.
+If page is smaller than paper, center the PostScript output.
+Fix a minor bug in the SELECT_TAKES_INT detection in the configure
+ script.
+TextOutputDev filters out control characters.
+Changed enough occurrences of 'char *' to 'const char *' to keep gcc
+ 2.95 from barfing.
+Support for Latin-2 and Latin-5 in pdftotext (however, this will only
+ work if the PDF file contains correct font encodings, which seems to
+ be rare).
+TextOutputDev converts "eightoldstyle" to "eight", etc.
+Don't use the return value from sprintf() -- most systems return the
+ length, but some return the string.
+Minor fixes for SunOS 4.
+Configure script looks for both select() and fd_set in sys/select.h.
+Configure script checks for gethostbyname() in -lbsd (for LynxOS).
+Fix missing closepath bug in PostScript output.
+Change PostScript portrait/landscape mode selection so it only uses
+ landscape if the page width is greater than the paper width.
+Tweaked the VMS code in makePathAbsolute().
+
+0.91a (2000-oct-11)
+-------------------
+Implemented separable CMYK PostScript output (the -level1sep switch to
+ pdftops).
+Implemented Pattern color spaces with tiling patterns (polygon fills
+ only).
+Implemented Stamp annotations.
+Implemented Named link actions.
+Fixed a really dumb bug in the TrueColor code in SFont (which affects
+ both Type 1 and TrueType font rendering on 16-bit displays).
+Rewrote the GfxColorSpace / GfxColor code.
+Switched from djgppcfg to dj_make.bat (from Michael Richmond).
+Bug in the Type 1 encoding parser -- couldn't handle lines of the form
+ 'dup NNN/name put' (with no space between the code and the name).
+Fixed the mkstemp() test in configure.in -- switched from
+ AC_TRY_COMPILE to AC_TRY_LINK and added <unistd.h>.
+Added DESTDIR to top-level Makefile.in.
+Fixed an incorrect OPI comment in PSOutputDev.
+Minor tweak to the CCITTFax code to avoid writing past the end of an
+ array on an invalid data stream.
+Xpdf crashed if the user selected 'reload' when no document was
+ loaded.
+Look for character names of the form "xx" (two hex digits with no
+ leading alphabetic char) and 'cNNN' (decimal digits with a leading
+ alphabetic char that happens to be a hex digit).
+FlateStream didn't correctly handle zero-length streams.
+Xref reconstruction didn't handle the case where the opening "<<"
+ immediately followed "trailer" with no intervening whitespace.
+Fix the %%DocumentSuppliedResources comment in EPS output.
+Scale annotations to fit their rectangles.
+Added Stream::close() to handle cases (e.g., patterns) where a Stream
+ object is used multiple times before it is deleted.
+Added the topLevel arg to Gfx::go() so it doesn't call out->dump() for
+ every pattern element (and form).
+Rearranged the GfxResources class.
+Clean up white space handling in Lexer.
+Make the dpi parameter to PDFDoc::displayPage etc. a double - this
+ avoids margin gaps with fit-page and fit-width.
+Fix a rounding problem in xpdf.cc that was causing the window to
+ sometimes be one pixel too small.
+Fixed a minor bug in dealing with Base-14 TrueType font names.
+Fixed Lab -> RGB color space conversion.
+Added support for opacity values (from PDF 1.4) to GfxState and
+ OutputDev. [Thanks to Leonard Rosenthol.]
+Implemented type 2 functions; rearranged the Function class
+ hierarchy.
+
+0.91b (2000-oct-29)
+-------------------
+Print a warning about Type 3 fonts (XOutputDev, PSOutputDev).
+Added the scroll lock behavior to 'n' and 'p' keys in xpdf.
+Change FileStream buffer size to a #define'd constant.
+Renamed Pattern to GfxPattern to avoid clashes with Windows and MacOS
+ types.
+Added CNS (Big5) Chinese font support (CHINESE_CNS_SUPPORT); renamed
+ CHINESE_SUPPORT to CHINESE_GB_SUPPORT.
+
+0.91c (2000-nov-19)
+-------------------
+Fix an endianness problem in the Type 1 font code which resulted in an
+ incorrect display with "-t1lib plain" on big-endian systems.
+CCITTFax stream decoder will skip over extra zero bits at end of line,
+ even if EncodedByteAlign flag wasn't set.
+Added Big5 support to pdftotext (with CHINESE_CNS_SUPPORT enabled).
+Fixed a typo in the CNS/Big5 encoding translation table.
+Change the form code in PSOutputDev to store images in arrays of
+ strings.
+The xref reconstruction (for damaged files) now also looks for
+ 'endstream' tags, and the parser uses this information when setting
+ up stream objects.
+In pdfinfo, convert Unicode chars in the 00xx range into 8-bit chars;
+ print a warning if there are any other Unicode chars.
+
+0.92 (2000-dec-03)
+------------------
+Fixed %%BeginResource comment (for xpdf procset) in PostScript
+ output.
+Added "-title" switch and "xpdf.title" resource to set the window
+ title.
+Check for <freetype.h> in addition to <freetype/freetype.h>.
+Upgraded the configure script to smr_macros 0.2.4 - this should fix a
+ bug where configure wasn't correctly finding t1lib.
+
+0.92a (2000-dec-17)
+-------------------
+Added 'extern "C" { ... }' in various places for ANSI C++ compliance.
+Tweaked the code that figures out DPI for fit-to-page and fit-to-width
+ modes.
+Fixed the image transformation code in XOutputDev -- no more missing
+ lines.
+Implemented color key image masking in XOutputDev.
+
+0.92b (2001-jan-07)
+-------------------
+Fixed a bug in the error-checking code in the Separation/DeviceN color
+ space parsing functions. [Thanks to Lidia Mirkin.]
+Added wheel mouse support (mouse buttons 4 and 5). [Thanks to
+ Thorsten Schreiner.]
+Added preliminary support for FreeType 2 (disabled by default).
+
+0.92c (2001-jun-04)
+-------------------
+Fixed a bug in the new image transformation code.
+Look for character names of the form "<letter><digit><digit>", instead
+ of looking for names beginning with a few specific letters.
+T1FontFile::T1FontFile wasn't initializing vars, and ~T1FontFile
+ wasn't checking before calling T1_DeleteFont -- this caused crashes
+ if it tried to open a nonexistent font file.
+Catalog::Catalog didn't set baseURI to NULL early enough.
+Tweak the check for strings.h in the configure script.
+Yet another fix for the image rotation code in XOutputDev --
+ off-by-one problem when upsampling.
+Handle Type 1/1C encodings when using FreeType 2.
+Allow FreeType2 to render user-supplied Type 1 base fonts.
+Opening a new file from full-screen mode tried to scroll.
+Fixed a bug in GfxFont constructor (missing check for NULL base font
+ name).
+Don't crash if a Type 1 font's FontBBox is non-integer.
+Pdfinfo prints page size.
+Tweak for the alpha hack in T1Font/TTFont: sample the middle pixel
+ instead of the top-left pixel.
+Automatically activate the text input widget in the find window.
+Changed a Japanese char code mapping in XOutputDev and TextOutputDev:
+ period was being incorrectly mapped to small circle (end-of-sentence
+ character).
+Add the 0/+/-/z/w key bindings to control the zoom setting.
+Fixed ImageOutputDev (pdfimages) to correctly handle inline image
+ masks.
+Extract ascent/descent info from font descriptor.
+
+0.92d (2001-jun-26)
+-------------------
+Embed TrueType fonts in PostScript output. (Added a "-noembtt" flag
+ to pdftops.)
+Extract encoding from TrueType fonts.
+Moved Function classes to a separate file (Function.h/cc).
+Implemented multi-dimensional sampled Functions.
+Implemented Type 4 (PostScript calculator) Functions.
+For Type 0 fonts, FontDescriptor is in descendant font, not parent.
+ [Thanks to Lidia Mirkin.]
+Added the "-htmlmeta" option to pdftotext.
+In TextOutputDev, when computing the number of blank lines to insert,
+ do a sanity check on the result.
+If both FlateDecode and some other filter (e.g., DCTDecode) were
+ applied to an image stream, getPSFilter() crashed instead of just
+ returning NULL.
+Handle the /Identity function.
+
+0.92e (2001-aug-23)
+-------------------
+Widths in font dict should override built-in font widths.
+Changed "rotate left/right" menu items to "rotate
+ clockwise/counterclockwise".
+The link parsing code choked if the Border array was incorrect (too
+ short).
+Modified PSOutputDev to output CMYK for fill/stroke colors.
+
+0.93 (2001-oct-25)
+------------------
+Implement PDF 1.4 (128-bit) decryption.
+Bump supported PDF version number to 1.4.
+Text output for Simplified Chinese. [Thanks to Cheung Siu Fai.]
+Read an app-defaults file for Xpdf.
+Read a system-wide config file (<prefix>/etc/xpdfrc) if ~/.xpdfrc
+ doesn't exist.
+Accept and verify owner password; if correct, allow all actions.
+Added a "-level2sep" option to pdftops to generate Level 2 separable
+ PostScript. The PostScript separation convention operators are used
+ to handle custom (spot) colors. [Thanks to Thomas Freitag for help
+ on this.]
+Add support for FreeType 2 to the configure script. Warning: this
+ requires FT 2.0.5 or newer.
+Fixed the bounding rectangle overlap test in the disconnected subpath
+ fill hack in XOutputDev.
+Stupid typo in font name table in PSOutputDev.
+Changing the zoom setting with a keyboard shortcut didn't update the
+ displayed setting.
+Modified the mouse wheel support and added the second wheel (mouse
+ buttons 6 and 7). [Thanks to Michal Pasternak.]
+Character and word spacing is affected by horizontal scaling (display
+ and PS output). [Thanks to Eddy Ng.]
+Rotation specified by the text matrix, character spacing, and
+ horizontal scaling interacted incorrectly (display and PS output).
+Some broken Type 1/1C fonts have a zero BBox -- kludge around this by
+ assuming a largeish BBox.
+Handle PDF files with an incorrect (too small) xref table size.
+Allow "-?" and "--help" as aliases for "-h" (all apps).
+Correctly handle unescaped parens in strings in Lexer.
+Fixed a bug in LTK where a menu got posted multiple times if you right
+ clicked while a page was being rendered.
+Removed a comma inside a string in configure.in.
+Kludge around broken PDF files that use char 32 but encode it as
+ .notdef instead of space.
+Clean up various compiler warnings: use constructor args like "fooA"
+ if there is a field named "foo". Everything now compiles cleanly
+ under gcc 2.91.66, 2.95.2, and 3.0.1.
+Page objects now read all of the page rectangles (MediaBox, CropBox,
+ BleedBox, TrimBox, ArtBox), as requested by the pdfTeX folks.
+ Added a new PDFRectangle struct to hold these.
+Use XOutputDev's Type 3 font size hack in TextOutputDev too, so it
+ does a little better job of extracting text in Type 3 fonts.
+Modify pdfimages to write one-bit images as PBM files.
+Work around a bug in cygwin's implementation of fseek.
+
+0.93a (2001-nov-21)
+-------------------
+Implemented the sh (shaded fill) operator for the axial shading type.
+Minor fixes to avoid compiler warnings.
+Cleaned up global variables -- moved many into instance vars and
+ function args.
+Minor fixes for OS/2.
+Fix the system config file path for VMS.
+Fix an uninitialized var in XOutputDev that caused crashes on Alphas.
+Don't incrementally update the display in full-screen mode.
+For Type 1/1C fonts, use the FontBBox from the PDF FontDescriptor
+ (instead of the one in the font file) if present -- this avoids
+ problems with fonts that have non-standard FontMatrixes.
+Add the Euro character to WinAnsiEncoding.
+Track the bounding box of the clip region to make rendering patterns
+ more efficient.
+Fix openTempFile() for Win32.
+
+0.93b (2001-dec-11)
+-------------------
+Added a duplex option to PSOutputDev and a -duplex switch to pdftops.
+Added XRef::PDFgetDocInfoNF() for pdftex project.
+Updated the VMS build script.
+
+0.93c (2001-dec-12)
+-------------------
+Completely rewrote the code that handles font encodings:
+ - everything is Unicode-based
+ - 16-bit fonts are handled much more cleanly
+ - text output encoding can be set more flexibly
+New .xpdfrc config files.
+
+1.00 (2002-feb-01)
+------------------
+More work on the font encoding rewrite:
+ - use the ToUnicode font dict entry
+ - pdfinfo and pdftotext (with '-htmlmeta') convert info strings to
+ the selected text encoding
+Added key bindings for forward ('v') and backward ('b').
+Added the pdffonts program which lists the fonts used in a PDF file.
+Fixed several problems in the TrueType font embedding code (for
+ PostScript output).
+Accept named destination on command line.
+Added several new items to pdfinfo: file size, PDF version, tagged
+ (yes or no), XML metadata (with the -meta option).
+Pdftops didn't get the portrait/landscape setting correct for PDF
+ files with rotated pages.
+The TrueTypeFontFile class (including the Type 42 converter) now
+ understands cmap format 6.
+Improved the "about" window -- mention the GPL, add a list of key
+ bindings.
+Added Zcaron and zcaron characters to WinAnsiEncoding.
+The '0' keyboard shortcut didn't update the zoom popup menu.
+Handle the complete list of alternate names for the Base14 fonts.
+Fixed substitute font scaling in XOutputDev - scale only the width,
+ not the height.
+Implemented stitching (type 3) functions.
+Handle the case of moveto/closepath/clip, which defines an empty
+ clipping region.
+Move dependences into separate Makefile.dep files; get rid of the
+ distdepend target.
+Move all of the configure-script-generated -D options out of the
+ Makefiles and into a top-level .h file (aconf.h).
+Cleaned up the FreeType 1/2 detection code in the configure script.
+Pdfinfo prints dates in a more readable format.
+Fixed a bug in the Paeth image predictor.
+Handle annotations with multiple states.
+Another workaround for buggy X servers: clip points that are way out
+ of bounds.
+Added libpaper support (for Debian).
+Generate PostScript DSC resource comments for PS (not just EPS)
+ files.
+The save and restore (q/Q) operators shouldn't save/restore the path.
+Performance optimization: disable pattern drawing in TextOutputDev.
+
+1.00a (2002-feb-25)
+-------------------
+Added an optimized special case for one-bit images in XOutputDev.
+Implemented CID TrueType font embedding; added a psEmbedCIDTrueType
+ option.
+The initialZoom X resource was broken.
+The reverse MacRoman encoding should return 32 for "space" (not 202,
+ which is an alternate encoding).
+Tweaks to the FreeType 2 support: only disable hinting if the bytecode
+ interpreter is disabled (i.e., disable autohinting but not bytecode
+ hinting); add some padding to the glyph cache for CJK fonts.
+Added level3 and level3Sep options for the psLevel setting and
+ corresponding -level3 and -level3Sep options to pdftops.
+Added a -level2 option to pdftops for consistency.
+Avoid a divide by zero in pdftotext. [Thanks to William Bader.]
+Added a Greek language support package. [Thanks to Alexandros
+ Diamantidis and Maria Adaloglou.]
+Don't bother trying to extract a "builtin" encoding from a TrueType
+ font.
+Accept either a page number or a page reference in a link
+ destination.
+Update the fontFixedWidth flag in GfxFont after reading the char
+ widths (used by the Acorn RiscOS port).
+Removed yet another (illegal but not caught by gcc) class specified
+ from a .h file.
+Avoid using snprintf - it's not available everywhere.
+Improved the CMYK->RGB transform.
+Use mkstemps where available.
+
+1.01 (2002-may-20)
+------------------
+Implemented Type 3 fonts.
+Implemented PostScript CID font embedding; added a
+ psEmbedCIDPostScriptFonts option.
+Implemented PostScript 16-bit font substitution; added psNamedFont16
+ and psFont16 options.
+Moved the initialZoom setting from X resources to the xpdfrc file.
+Implemented the radial shading type in the sh (shaded fill) operator.
+ [Thanks to Mike Sweet.]
+Added an 'include' command to the xpdfrc format.
+Added the displayNamedCIDFontX option so different fonts can be used
+ within one character collection.
+Added a simple reverse video mode (-rv switch, xpdf.reverseVideo
+ resource).
+Implemented stroked text in XOutputDev (with t1lib and FreeType2).
+ [Thanks to Leonard Rosenthol.]
+Implemented stroked text in PSOutputDev.
+Added a built-in Unicode map for UCS-2.
+New key binding in xpdf: 'g' activates the page number text field.
+PSOutputDev will now embed external TrueType fonts in addition to
+ external Type 1 fonts.
+The psEmbedType1Fonts and psEmbedTrueTypeFonts options were missing
+ the "Fonts" suffix.
+Documentation in xpdf.1 for -freetype option was wrong.
+Added the Big5ascii Unicode map to the Chinese-traditional support
+ package (maps 7-bit ASCII straight through). [Thanks to Lawrence
+ Lai.]
+Modified the EUC-CN and EUC-JP encodings to pass 7-bit ASCII straight
+ through. [Thanks to Lawrence Lai.]
+Avoid a divide by zero in XOutputDev. [Thanks to Simon Burge.]
+Remove old code in openTempFile that removed an extension from the
+ name returned by tmpnam.
+Tweak the scrolling behavior when switching pages. [Thanks to Case
+ Jones.]
+In the code that guesses character names (for font subsets), also
+ handle names of the form 'ABnnn'. [Thanks to Colin Granville.]
+Fix the transform code for annotations.
+Improved the CMap file parser to handle more general PostScript
+ lexical conventions.
+Added '-enc' option to pdfinfo.
+Added the small caps and oldstyle numbers from Adobe's Unicode
+ corporate use area to the Latin1 and ASCII7 Unicode maps.
+The code in TextOutputDev that guesses Type 3 font size could generate
+ a zero size, which resulted in div-by-zero errors.
+Various tools (including Adobe's) occasionally embed Type 1 fonts but
+ label them Type 1C - so check for a '%!' at the start.
+Some tools embed Type 1C fonts with an extra whitespace char at the
+ beginning - just skip over it.
+Fixed a typo in the Simplified Chinese add-to-xpdfrc file.
+Updates to dj_make.bat and the djgpp build instructions.
+Added a Turkish language support package.
+Updated VMS build scripts. [Thanks to Martin Zinser.]
+Modify the incremental display update code to redraw less often if
+ most of the commands are vector graphics, as opposed to text and
+ images.
+Tweak the Type 1 font bbox code to look at the bboxes in both the PDF
+ font object and the embedded font file.
+Fixed the ETenms-B5-H CMap file (for traditional Chinese) to map the
+ Latin characters to their proportional versions.
+Added an optional displayCIDFontX entry for one of the Arphic TrueType
+ fonts in the traditional Chinese 'add-to-xpdfrc' file.
+Remove leading '-' on include statements in Makefiles.
+Added psASCIIHex parameter.
+Added the GBK Unicode map to the simplified Chinese language pack.
+Pdftotext now opens the text file in binary mode to avoid Microsoft's
+ annoying automatic end-of-line translation stuff.
+Added an executeCommand function in goo/gfile.cc. [Thanks to Mikhail
+ Kruk.]
+The %ALDImagePosition OPI comment was wrong if the page was scaled to
+ a different paper size.
+The OPI code was saving the default transform matrix before calling
+ setpagedevice, which can change the matrix.
+Fixed a crash when an inline image dictionary contains garbage.
+Upgraded to autoconf 2.53.
+Use unsigned int file offsets, to allow access to PDF files in the 2-4
+ GB size range; use fseek64/ftell64 if available.
+Fixed two floating point exception cases that came up with broken PDF
+ files.
+Avoid a crash when printing an error message regarding an unnamed
+ font.
+Default link border width should be 1. [Thanks to Michael Pfeiffer.]
+Minor tweak to build with FreeType 2.1.0.
+Handle "weird" characters in PostScript font names.
+PSOutputDev now handles PostScript XObjects.
+Added several more page attributes for the pdftex project.
+Transferred the copyright to Glyph & Cog, LLC.
+
+2.00 (2002-nov-04)
+------------------
+Switched to the Motif toolkit.
+Support multiple open documents (in separate windows).
+Added document outlines to the viewer.
+Modified the text extraction (placement) algorithm.
+Implemented the JBIG2 decoder.
+Added a Latin2 language support package.
+Added support for movie annotations.
+Switched back to native LZW decompression code.
+Text extraction from Type 3 fonts was (partly) broken.
+The owner password checking code was missing a step in the case of
+ 128-bit encryption.
+Added the 'printCommands' option to the xpdfrc file.
+Added key binding for '?' to bring up the about/help dialog.
+In TextOutputDev, ignore any text that's outside the page bounding
+ box.
+Text extraction throws away "tiny" characters after the first 20000
+ per page, to avoid really slow runtimes with PDF files that use
+ special fonts to do shading or cross-hatching; added the
+ 'textKeepTinyChars' option to disable this behavior.
+Text extraction discards duplicated text (fake boldface, shadow
+ effects).
+Added ctrl-F as a key binding for find.
+Added a "find next" function, bound to ctrl-G.
+Added ctrl-P as a key binding for print.
+Modified the DCT decoder to handle progressive and non-interleaved
+ JPEG streams.
+Added key bindings for ctrl-Home and ctrl-End.
+Allow the initialZoom setting to be made in either the xpdfrc file or
+ as an X resource.
+Added a Hebrew language support package. [Thanks to Roy Arav.]
+The "make distclean" target now creates (empty) Makefile.dep files in
+ the three subdirectories.
+Initialize XRef::ownerPasswordOk.
+Correctly handle stroking of Type 3 fonts in PSOutputDev.
+Generate correct PostScript for fonts with "weird" character names
+ (e.g., "(").
+Generate correct PostScript for images using Indexed color spaces with
+ DeviceN base color spaces.
+Added lowercase Roman numerals to ISO-2022-CN.unicodeMap (simplified
+ Chinese support package).
+Tweak the image scaling code to better handle flipped (top-bottom
+ and/or left-right) images.
+Generate correct PostScript code for inline images and images in Type
+ 3 fonts which are too large for a single PS string.
+Correctly handle indexed color spaces whose base color spaces have
+ component ranges other than [0,1].
+Optimized the DCT decoder.
+Fixed mistakes in the list of key bindings in the about/help dialog.
+Optimized the Flate decoder.
+Add literal names for punctuation and digits to the Unicode name
+ table.
+Cygwin's popen wants mode "r", not "rb".
+Fixed a bug in the Type 4 function parser (the "if" operator wasn't
+ parsed correctly).
+Fix a bug in PS output for TrueType fonts with no PDF encoding.
+Make the bbox size in FTFont more liberal (to avoid problems with
+ fonts that have incorrect bboxes).
+Reverse the colors in PBM files generated by pdfimages, so the common
+ case (an image mask filled with black) comes out correct.
+Add fseeko/ftello support which is basically identical to
+ fseek64/ftell64. [Thanks to Nassib Nassar.]
+Modified column assignment in text extractor to account for characters
+ that convert to multiple characters in the output encoding.
+Fix TrueType fonts which have an incorrect cmap table length.
+Work around a pragma bug in the version of gcc that ships with MacOS X
+ 10.2. [Thanks to Frank Siegert and Andrew Stone.]
+Fix a problem that was causing an infinite loop when a damaged content
+ stream contains an 'ID' command inside a dictionary.
+Handle the case where systempapername() returns NULL (libpaper
+ support).
+Handle fonts which are defined directly in the font resource
+ dictionary rather than as separate objects.
+Track process colors in Level 1 separable PostScript.
+Pdfinfo now checks the return value from mktime to avoid seg faults in
+ flakey strftime implementations.
+If duplex is not enabled in PostScript output, leave the duplex
+ setting alone, allowing the spooler to insert its own setting.
+Added three missing fclose calls.
+Change the default encoding for TrueType fonts (used when the PDF file
+ doesn't specify an encoding) from MacRomanEncoding to
+ WinAnsiEncoding.
+Move X_CFLAGS to the end of the list in CXXFLAGS (in Makefile.in) to
+ avoid some of the FreeType2 include path problems.
+Fixed an obscure bug in the LZW decoder. [Thanks to Martin
+ Schroeder.]
+Fixed a bug in decryption when using the newer (PDF 1.4) algorithm
+ with shorter-than-128-bit keys.
+Minor optimization for image data streams: the ImageStream class can
+ return an entire buffered line.
+
+2.01 (2002-dec-05)
+------------------
+Redesigned the text extraction process:
+ - process the text into "reading order"
+ - added a "-layout" flag to pdftotext to switch back to the old
+ style, where physical layout is maintained
+ - use of the "-raw" flag is no longer recommended
+Added the -reload option for xpdf (in remote mode).
+Added support for external CID fonts; added the displayCIDFontT1 and
+ displayNamedCIDFontT1 commands to the xpdfrc file.
+Handle the case of moveto/newpath/clip, which defines an empty
+ clipping region (just like moveto/closepath/clip).
+Accept XYZ link destinations with missing array elements.
+Fix some problems with state save/restore triggered by Type 3 fonts
+ that reference other fonts.
+Accept bogus font names based on "Symbol": Symbol,{Bold,Italic,
+ BoldItalic}.
+Fixed color and font resource names in the xpdf man page.
+Was using delete instead of gfree in OutlineItem::~OutlineItem.
+Set the busy cursor in the find dialog while searching.
+Map variants of the copyright, trademark, and registered trademark
+ symbols to the proper Unicode codes, not to Adobe's corporate use
+ area codes.
+Fixed a floating point exception bug in TextOutputDev (check for a
+ too-small denominator).
+Fixed a typo in TextOutputDev, in the code that generating blank lines
+ to add vertical whitespace.
+Config files whose last line didn't end with a LF (or CR+LF) weren't
+ being handled correctly.
+The code that handled CIDToGIDMaps in Type 2 CIDFonts was broken.
+Check the per-glyph bounding box in Type 3 fonts, and don't try to
+ cache glyphs with bogus bboxes.
+Allow ToUnicode CMaps to use fewer than four hex digits in the Unicode
+ char indexes.
+Added multithreading protection to the GlobalParams class.
+Fixed a bug in end-of-stream detection with the TIFF predictor.
+Added some characters to MacRomanEncoding to match up with Apple's
+ definition.
+
+2.02 (2003-mar-24)
+------------------
+Rewrote the text extractor code that assembles words into lines to
+ better handle vertically overlapping lines.
+Add the "match" option for paper size (in PostScript output).
+Added support for external 16-bit TrueType fonts; added the
+ displayCIDFontTT and displayNamedCIDFontTT commands to the xpdfrc
+ file.
+Added an Arabic language support package.
+Added the Windows-1255 encoding to the Hebrew language package.
+A missing NULL check was causing a crash when closing the file in a
+ single window (which clears out the window, but leaves it open).
+Deal with TrueType fonts whose glyph data is out of order - this
+ affected both FreeType rasterization and PostScript generation.
+Munge font names in PSOutputDev to avoid names that are problematic
+ for ghostscript because they start with an out-of-limits number
+ (e.g., 1e999foo).
+Modify the TrueType font encoding deciphering algorithm in yet another
+ attempt to match up with Acrobat's behavior.
+Bounds check the indexHigh value in indexed color spaces.
+The text extractor no longer bothers trying to get an average
+ character width for Type 3 fonts, since it generally doesn't work
+ very well (because Type 3 metrics are unreliable).
+Don't crash if the user hits ctrl-G ("find again") before doing a
+ find.
+Set the button pixmap foreground color correctly.
+Handle text drawn backward on 180 degree rotated pages.
+Added a magic call to XtUngrabButton after calling XmCreatePopupMenu
+ which appears to prevent some very odd problems (idea taken from the
+ DDD source code).
+Fix the MacOS X fix (needed to include <AvailabilityMacros.h>).
+Fixed a bunch of Motif 1.x / X11R5 incompatibilities. [Thanks to
+ William Bader and Albert Chin-A-Young.]
+Fixed various bugs in previously untested code in the JBIG2 decoder.
+Modify the XPDFCore destructor to avoid a bogus warning message from
+ OpenMotif 2.2.
+Modified the Type 1C font parser to do proper bounds checking.
+Fixed the bounds checking in the TrueType font parser.
+Text extractor shouldn't do block merging in physical layout mode.
+Fixed a problem in PSOutputDev in level2sep mode with images in a
+ Separation color space and with a non-default Decode array.
+Text extraction with "-raw" was concatenating lines from the bottom of
+ one column and the top of the next.
+Handle Type 1C subroutines in the font converters.
+Correctly handle progressive JPEG images whose scans are slightly
+ different sizes (e.g., the Y scan rounds up to a multiple of 8
+ pixels and the Cb/Cr scans round up to 16 pixels).
+Avoid a potential divide-by-zero problem in TextOutputDev.
+Modified the T1Font and FTFont modules to correctly handle glyphs that
+ are larger than the font's claimed bounding box.
+Tweak dupMaxDeltaX parameter in TextOutputDev to avoid triggering on
+ double characters.
+Improved detection in pdfinfo for ISO paper sizes. [Thanks to Hartmut
+ Henkel.]
+Xpdf wasn't responding to the TARGETS atom, which prevented pasting
+ the selection into various applications. [Thanks to Phillip Ezolt.]
+Handle XObjects with recursive references in their Resources
+ dictionaries (in PSOutputDev).
+Change PSOutputDev to deal with invalid PDF files that use
+ non-embedded TrueType fonts with no encoding.
+Check for undersized Widths arrays in fonts.
+Add bounds checking code to Array class.
+Updated VMS build scripts. [Thanks to Martin Zinser.]
+Tweak the TrueType font handling code (again):
+ - char codes in symbolic fonts may or may not be offset by 0xf000
+ - discard empty tables because they sometimes confuse FreeType
+Fixed bounds checking in the Flate decoder.
+Removed a bogus error message for exponential functions without
+ explicit C0/C1 values. [Thanks to Hartmut Henkel.]
+Handle the other Unicode cmap type (platform=0) in TrueType fonts.
+Added support for the SGI Motif horizontal paned window widget.
+ [Thanks to Felix Ritter.]
+Ignore extra elements in link destination arrays.
+Accept external Type 1 font files with a suffix of ".ps" or no suffix
+ at all.
+Add a bounds check in the DCT decoder.
+Added instructions for building xpdf.exe under cygwin/XFree86.
+Tweaked the word separation parameter for raw-mode text extraction.
+
+2.03 (2003-oct-10)
+------------------
+Rewrote the text extractor to:
+ - do a better job with rotated text;
+ - handle right-to-left scripts;
+ - be faster.
+Changed the zoom setting to use a percentage (relative to 72 dpi)
+ instead of a zoom "factor".
+If the PDF file has an outline, open the outline pane initially.
+Added -f and -l options to pdfinfo; print multiple page sizes.
+The HAVE_XTAPPSETEXITFLAG test in XPDFApp.cc was backwards.
+The BitsPerComponent entry is optional in image mask objects.
+Render any annotation with an appearance stream, instead of just
+ Widget and Stamp annotations.
+Fix a bug in the TrueType font checker: the test for an unsorted
+ 'loca' table was wrong.
+Modify the TrueType cmap selection algorithm yet again to try to match
+ Adobe's behavior.
+Changed sqrt(2) to sqrt(2.0) in pdfinfo.cc to make various compilers
+ happy.
+Fixed a deadlock problem (when MULTITHREADING is set); cleaned up some
+ other problems with the locking code.
+Fixed a bug in the interpolation code for type 0 (sampled) functions.
+Implemented type 1 (function-based) shaded fills.
+Fixed some stupid bugs in the JBIG2 decoder (introduced with the
+ previous optimization work).
+Fixed a typo in the code that parses vertical font metrics for CID
+ fonts that was causing a seg fault.
+Fixed a couple of bugs that were causing seg faults with badly damaged
+ PDF files.
+Limit the number of nested Forms to avoid infinite recursion (in buggy
+ PDF files).
+Add a special case for rectangular clip regions - make sure these
+ don't drop pixels on the right and bottom edges.
+Tell FreeType not to use glyph bitmaps when in anti-aliased mode.
+Read all of the border style info for links.
+All of the shaded fill types now do at least one bisection to avoid
+ problems when the colors at the endpoints of the domain are the
+ same.
+If the Length2 parameter for an embedded Type 1 font was incorrect
+ (too small), pdftops was losing font data.
+Deal with (broken) DCT streams that use the same component ID number
+ for different components.
+The MediaBox page attribute was not being inherited correctly.
+Fixed a bug in the Type 1C font converter related to local
+ subroutines.
+The Type 1C -> Type 1 font converter was allocating the font dictionary
+ one slot too small.
+Added a missing private dictionary entry to Type 1 fonts generated by
+ the Type 1C converter. [Thanks to Michael Shell.]
+Fixed bugs in the tiling pattern fill code.
+Try the TrueType 0xf000 char code offset hack for the MacRoman
+ encoding too (in addition to MS Symbol).
+Update the font metrics info for the Base 14 fonts to include the Euro
+ character.
+SECURITY HOLE: Escape various characters in URLs before running a web
+ browser (or movie viewer). [Fixed in 2.02p11]
+SECURITY HOLE: In the dialog used to verify "launch" links, provide a
+ scrolling view if the command to be run is excessively long. [Fixed
+ in 2.02p11]
+Added an option to disable insertion of page breaks (form feed
+ characters) in extracted text (pdftotext -nopgbrk; xpdfrc
+ "textPageBreaks" option).
+Check for 8-bit fonts that specify an out-of-range FirstChar or
+ LastChar.
+Correctly handle an obsolete Type 2 charstring op (in the Type
+ 1C-to-Type 1 font converter). [Thanks to Helge Blischke.]
+Use the font encoding info to fill in holes in the ToUnicode map.
+Added character names for Bulgarian (in the Cyrillic support pacakage)
+ and Greek.
+Handle clipping to text in xpdf and pdftops.
+Fix color space detection in DCT decoder. [Thanks to Dwight Kelly.]
+Added the "unicodeToUnicode" xpdfrc option, intended (initially) for
+ Arabic support.
+Handle the case in PSOutputDev where two font objects refer to the
+ same embedded TrueType font, but with different encodings. [Thanks
+ to Frank Siegert.]
+Kill any pre-existing path before drawing a form (or annotation).
+Save state before rendering page content; restore state afterward.
+Fix Stream::reset/close to work correctly with encoder streams; fix
+ PSOutputDev to use Stream::close consistently.
+Fix a seg fault when hitting the 'back' button after closing a file.
+GfxState::getStrokeGray was returning the fill gray value (this only
+ affected Level 1 PS output).
+Change PSOutputDev to reuse dictionaries in Level 1 mode (since Level
+ 1 PS interpreters don't do garbage collection). [Thanks to Frank
+ Siegert.]
+PSOutputDev was generating incorrect translations for landscape-mode
+ pages.
+Implemented shading pattern color spaces.
+PSOutputDev wasn't correctly handling Type 3 fonts which used image
+ resources (as opposed to inline images). [Thanks to Frank Siegert.]
+The fix from 1.00 which clipped out-of-bounds points was a bit too
+ aggressive.
+Do proper Floyd-Steinberg dithering in XOutputDev.
+Don't automatically check for a null owner password (to match Adobe's
+ behavior).
+Allow the FlateDecode filter in Level 3 PostScript output.
+Fixed small bugs in the Type 1C -> Type 1 converter and Type 1C ->
+ Type 0 converter. [Thanks to Tom Kacvinsky.]
+Work around another weird Motif problem with the right button menu
+ (which was sometimes causing the menu to not be displayed).
+Make the code that handles fonts defined directly in the resource dict
+ more robust.
+Add a brief description of the outline pane to the xpdf man page.
+Ignore extra operands to content stream operators.
+Fixed a bug in the CCITTFax decoder.
+Allow the Count entry in a Pages dictionary to be a real number
+ (because some PDF generators actually do this).
+Shading pattern fills weren't being clipped correctly.
+Incorrect shallow copies in GfxRadialShading and StitchingFunction.
+The StitchingFunction destructor wasn't checking for funcs being
+ NULL.
+Change the TrueType code-to-GID mapping code so it looks at the
+ TrueType 'post' table.
+Set the print command in the print dialog once at startup, don't
+ change it each time a file is (re)loaded.
+Generate the %%BoundingBox comment in regular PostScript files (not
+ just EPS files).
+Fixed a bug in the Unicode CMap parser.
+
+3.00 (2004-jan-22)
+------------------
+New PDF rasterizer ("Splash").
+Added support for PDF 1.5:
+ - JPX (JPEG 2000) decoder
+ - XRef streams
+ - object streams
+ - DeviceN color spaces with up to 32 components
+ - Added new CMaps to the CJK language support packages
+Replaced pdftopbm with pdftoppm (which can generate PBM, PGM, and PPM
+ files).
+Reorganized the font file parser code into a new library ("Fofi").
+Removed support for FreeType 1.x.
+Removed support for X server fonts - Xpdf (and pdftoppm) will now
+ search for the URW fonts (from ghostscript).
+Changed the "-t1lib" and "-freetype" switches; replaced the
+ "t1libControl" and "freetypeControl" config file options with
+ "enableT1lib", "enableFreeType", and "antialias".
+Added the "-box" option to pdfinfo.
+Added imageable area support to PSOutputDev (for CUPS); added the
+ "psImageableArea" config file option.
+Added the "-nocrop", "-expand", "-noshrink", and "-nocenter" switches
+ to pdftops; added the "psCrop", "psExpandSmaller", "psShrinkLarger",
+ and "psCenter" config file options.
+Dictionary size was in PostScript code generated for Type 3 fonts.
+The PS code generated for images in Type 3 characters was broken.
+Tweaked the text extractor.
+Accept xref entries that are one byte too short (matching Adobe's
+ behavior).
+Change things so "xpdf -h" and "xpdf -v" work if DISPLAY isn't set.
+Fix a problem in the damaged file repair code that handles the trailer
+ dictionary.
+Use the "Last" entries in "Outlines" objects - this avoids a problem
+ with PDF files generated by buggy software that, e.g., sets the last
+ item's Next pointer to point to itself.
+PSOutputDev was not handling DeviceN color spaces correctly in Level 2
+ images.
+Fixed a stupid little bug that broke PS output for JBIG2 images.
+Work around a Lesstif bug: set up an extra callback so hitting <Enter>
+ in the find dialog performs a search. [Thanks to Elliott Hughes.]
+Pdftops was crashing on zero page PDF files.
+Add an AC_PREREQ call to configure.in.
+Change the 'find' dialog so the text entry box resizes with the
+ dialog.
+Skip extraneous zero bits at the start of a CCITTFax stream.
+The PostScript text clipping operator was missing a 'newpath'.
+ [Thanks to Frank Siegert.]
+Fix a bug in tiling patterns with bboxes that don't start at (0,0).
+Fix a bug in Type 3 font handling with rotated text.
+The tiled pattern fill code was destroying the current path, which
+ broke the fill+stroke operators when the fill color space was a
+ tiled pattern.
+ICCBased color spaces don't always set their Ranges values correctly,
+ so just use the values from the alternate color space.
+Modified GHash to accept int or void* - this avoids some conversion
+ warnings.
+Check for missing Type 3 CharProcs - avoid a segfault.
+Pdffonts now marks all Type 3 fonts as embedded.
+Outline entries with no Title string weren't being handled correctly,
+ resulting in segfaults.
+PSOutputDev now forces the text horizontal scale factor to be non-zero
+ to avoid singular font matrices in the PS code.
+Tweaked the error recovery in the CCITTFax decoder.
+The LZW/Flate predictor should treat any Predictor value (in the
+ stream dictionary) >= 10 identically.
+PSOutputDev and pdffonts check for NULL font objects (which can
+ happen, e.g., because of missing CMap files).
+Swap the left and right mouse wheel button numbers.
+EPS output ("pdftops -eps") now uses the CropBox instead of the
+ MediaBox as the EPS bounding box.
+
+3.01 (2005-aug-17)
+------------------
+Added the continuous view mode, including the '-cont' switch and the
+ 'continuousView' config file option.
+At high zoom levels, don't rasterize the entire page - this avoids
+ problems running out of memory.
+Added "search backward" and "match case" options to the find dialog.
+Support explicitly masked images and soft masked images.
+Add support to DCTStream for 16-bit quant tables.
+Don't segfault if the user clicks on an outline entry with a broken
+ destination.
+Changed the makefiles and configure script to skip building pdftoppm
+ (in addition to xpdf) if X, Motif, or FreeType is not found; changed
+ the error message in the configure script to match.
+Move an inline function in JArithmeticDecoder.cc to avoid compiler
+ errors.
+Fixed a bug in the rasterizer that was sometimes causing infinite
+ loops with round line caps on vertical lines.
+Various rasterizer optimizations.
+Look for intermediate resize events - try to avoid lagging when the
+ user is doing an opaque resize.
+The FormType key in Form XObjects is optional.
+Handle external 16-bit TrueType fonts correctly, using the Unicode
+ cmap.
+Add class declarations to TextOutputDev.h to work with stricter C++
+ compilers.
+Support FreeType's weird include file stuff (ft2build.h, etc.).
+Fixed a bug handling empty paths.
+Fixed a text positioning problem in PostScript output.
+Handle TrueType collections in FoFiTrueType.cc.
+FoFiTrueType constructor was reporting a failure if the post table was
+ bad - this should be non-fatal.
+Type 1 font parser was missing a NULL test.
+Mask chars passed to isdigit in goo/parseargs.c to avoid problems with
+ signed chars.
+Added more error checking to the CCITTFax decoder.
+Fixed a bug (computing the MCU size) in the DCT decoder.
+Change a test in the Splash stroke code to avoid x86 floating point
+ weirdness.
+Reorganized the decryption code to allow security handler plugins;
+ removed the NO_DECRYPTION #ifdefs.
+Added a plugin interface, initially just for security handlers.
+Support color key masked images and explicitly masked images in PS
+ output (Level 2 only).
+When checking for aliases of the Base 14 fonts, ignore spaces in the
+ specified font name.
+Handle encrypted PDF files that are missing the file ID string.
+Handle tiling patterns more efficiently in the PostScript output.
+Rewrote the code that handles color spaces in PostScript output.
+Fixed a bug in the Type 1C font parser - zero-length indexes (and
+ zero-length names) weren't handled correctly.
+Handle shaded fills more efficiently in the PostScript output.
+Implement the remaining shading types (4-7).
+Rearranged the Splash color modes.
+Add the EarlyChange parameter to LZWStream when generating PostScript.
+Check for zero values in line dash arrays in PSOutputDev.
+Fixed an uninitialized variable in JArithmeticDecoder which was
+ causing crashes.
+Treat unknown CMap names as identity mappings (to match Adobe's
+ behavior).
+Fixed bugs in the XRef parser related to XRef streams in updated
+ files.
+Added a missing call to FT_Done_Glyph which was causing a memory leak.
+ [Thanks to Dave Formanek.]
+Fixed a bug in text copying that was causing the last word to be
+ dropped on some pages.
+Tweaked the image width/height computation in Splash::drawImage and
+ Splash::fillImageMask to make striped images work better.
+Ignore minus signs in the middle of numbers (to match Adobe's
+ behavior).
+Missing '%s' in format strings for dates in pdftotext '-htmlmeta'
+ mode.
+Change the TrueType code-to-GID mapping code so it looks at the
+ standard name-to-Unicode mapping before the ToUnicode mapping
+ defined in the font object.
+Added a matteColor setting (command line option and X resource).
+Tweaked the CMYK->RGB transform.
+Fix some problems in tracking the character position (to match up with
+ Adobe's highlight file format).
+Handle moveto/closepath/stroke correctly.
+Check for singular text matrices and font size of zero in PSOutputDev.
+Clip PS output to the size of the page (avoiding any gibberish that
+ lies outside the MediaBox, in the case where the MediaBox is smaller
+ than the paper).
+If the line dash element in an annotation's Border array is of an
+ invalid type (i.e., not an array), don't draw the link at all (this
+ matches Adobe's behavior).
+Don't remap small caps and oldstyle glyphs in the name-to-Unicode
+ table - it messes up TrueType font encodings.
+Pdftoppm wasn't setting the paper color correctly in mono and gray
+ modes (this only showed up on big-endian machines).
+Missing NULL check was causing crashes when attempting to read non-PDF
+ files that happened to contain the string '%PDF'.
+Fixed a problem in the text extractor that was breaking up words.
+Handle vertical text (CJK fonts) in PS output with TrueType fonts that
+ are missing the vertical metrics tables.
+Handle the case where a font object and the corresponding embedded
+ font are different types.
+Handle basic crypt filter functionality.
+Added more value checking in the XRef parser, to avoid potential
+ security problems.
+Updated the CJK language support packages: replaced the
+ displayCIDFontX references with displayCIDFontTT; added pointers to
+ free TrueType fonts.
+Added a missing error message when SplashOutputDev can't parse an
+ embedded TrueType font file.
+PDFCore and TextOutputDev now correctly handle searching for Unicode
+ strings, including real Unicode case-folding.
+Throw away tiling pattern fills that are completely outside the clip
+ region.
+The JPEG 2000 inverse reversible multiple component transform code was
+ wrong.
+Fixed some bugs in shading pattern fills: clipping was wrong, and
+ background color was not implemented.
+Added tool tips for the toolbar buttons.
+Decrease the max depth of recursive patch mesh filling if the pattern
+ has a large number of patches.
+Highlight the find text whenever the find dialog is mapped.
+Handle page boundary boxes with reversed coordinates.
+Fixed a bug in the text extractor code that handles duplicated text.
+Optimization work on SampledFunction::transform().
+Use the CropBox instead of the MediaBox as the display region.
+Dither for PseudoColor (8-bit) displays.
+Fix a bug in DCTStream that was causing an infinite loop with
+ corrupted DCT image data.
+Fix a bug in the ToUnicode CMap parser.
+Fix a bug in the text extractor - negative font sizes weren't being
+ handled correctly.
+Fix a bug in the text extractor - in certain cases, out-of-bounds text
+ could cause crashes (generally only in damaged PDF files).
+Fix a read-past-end-of-array bug in the JBIG2 decoder.
+Fix a case where pdftops was generating lines longer than 255 chars.
+Optimize redraws - don't regenerate the XImage every time redrawRect
+ is called.
+The ASCII85 decoder wasn't skipping whitespace properly.
+Optimize text extraction: skip (non-inline) image setup entirely.
+Added initial transparency support (stroke/fill alpha and blend mode).
+Added support for the overprint setting in PostScript output.
+Fixed various buffer overflow bugs.
+Handle negative font sizes and horizontal scaling correctly - this
+ affected PSOutputDev for all text operators, as well as the TJ
+ operator for all OutputDevs.
+Fixed a buffer overflow in the CCITTFax decoder.
+Fixed an out-of-order entry in the list of font name aliases.
+Fixed a backward loop in the PostScriptFunction code.
+Treat a zero-length base URI the same way as a nonexistent base URI.
+Add a divide-by-zero check in TextOutputDev (the problem was happening
+ in cases of mixed horizontal and vertical text).
+PSOutputDev wasn't rounding the page bounding box coordinates
+ correctly.
+Support the SOF1 marker in DCT (JPEG) image streams.
+Minor changes to GlobalParams.h and JPXStream.h because some compilers
+ don't like anonymous structs inside anonymous unions.
+Xpdf now complains about a negative page number.
+Changed GString::cmp and GString::cmpN to correctly handle '\0' chars
+ in the middle of strings.
+Fixed the radial shading code; corrected the handling of the 'extend'
+ parameters.
+Added the gmallocn and greallocn functions.
+Fixed a bug in the TIFF image component predictor which shows up with
+ components that are not 1 or 8 bits wide.
+Optimized FlateStream::loadFixedCodes().
+For non-embedded Base-14 fonts, don't use the ascent/descent/bbox
+ values from the FontDescriptor - various PDF generators get them
+ wrong.
+Fixed a bug in the text extractor - words on the same line (especially
+ in tables) were being split vertically onto multiple lines.
+Automatically select the correct radio button ("print with command"
+ vs. "print to file") in the print dialog.
+Don't create the "open" and "save as" dialogs until needed - this
+ avoids stat-ing every file in the directory at startup.
+Changed the Big5 and Big5ascii encodings (in the traditional Chinese
+ language support package) to include characters from the Unicode
+ database (which aren't mentioned in the Adobe character collection
+ documentation).
+Added the '-pagecrop' switch to pdftops.
+Tweaked the RGB->gray and CMYK->gray conversion functions to match the
+ PDF spec.
+The JPEG 2000 decoder wasn't correctly handling codeblocks split
+ across multiple packets/layers.
+Fixed a typecast that caused compile errors on 64-bit systems.
+The CMap parser wasn't handling the 'cidchar' construct.
+Handle the case in PSOutputDev where two font objects refer to the
+ same embedded 16-bit TrueType font, but with different CIDToGIDMaps.
+Changed the configure script to report more accurate warnings when
+ it can't find X / Motif / FreeType.
+Encryption with revision=2 always uses a 40-bit key, regardless of the
+ specified Length value.
+Yet another minor change to the TrueType font encoding deciphering
+ algorithm.
+Don't completely invalidate the Catalog if one (or more) of the page
+ objects are bogus -- just skip over those pages.
+Removed the workaround in pdftops for too-small Length2 values in Type
+ 1 fonts -- it was causing problems on various PostScript printers.
+Started adding error checking to the JBIG2 decoder (this is nowhere
+ near complete yet).
+Extended the "unicodeToUnicode" config option to also apply to CID
+ fonts.
+Added the narrow Latin characters to the Adobe-Korea1.cidToUnicode
+ file in the Korean language support package.
+Fixed the code that handles page rotation in PSOutputDev.
+When converting a Type 1C glyph to a Type 1 glyph, insert closepath
+ operators as appropriate.
+Check for a sane 'loca' table in TrueType fonts (FoFiTrueType::parse).
+Fix PSOutputDev to correctly handle the case of an empty name in a
+ font encoding.
+
+3.02 (2007-feb-27)
+------------------
+Added anti-aliasing for vector graphics; added the vectorAntialias
+ xpdfrc option; added the "-aaVector" switch to xpdf and pdftoppm.
+Implemented stroke adjustment (always enabled by default, ignoring the
+ SA parameter, to match Adobe's behavior), and added the strokeAdjust
+ xpdfrc command.
+Support PDF 1.6 and PDF 1.7.
+Added support for AES decryption.
+Added support for OpenType fonts (only tested with 8-bit CFF data so
+ far).
+Added user-configurable key/mouse bindings - the bind/unbind xpdfrc
+ commands.
+Cleaned up the full-screen mode code and added the ability to toggle
+ it on the fly (the default key binding is alt-f).
+Pdfimages with the -j option now writes JPEG files for 1-component
+ (grayscale) DCT images, in addition to 3-component (RGB) images.
+Fixed bugs in handling sampled (type 0) functions with 32-bit
+ samples.
+Fixed some things to support DeviceN color spaces with up to 32
+ colorants.
+Pdftops now constructs the %%Creator and %%Title DSC comments from the
+ relevant information in the PDF Info dictionary.
+Tweak the TrueType font encoding deciphering algorithm.
+Added the "mapUnkownCharNames" xpdfrc option.
+Fix a bug (that only showed up with certain window managers) in the
+ intermediate resize event optimization. [Thanks to Michael Rogers.]
+Check for a broken/missing embedded font (this was causing xpdf to
+ crash).
+Added support for transfer functions in PostScript output.
+Be a bit more tolerant of Link destinations that contain null values
+ for positioning parameters.
+Use ordered dot dithering instead of clustered dot dithering at
+ resolutions below 300 dpi (for monochrome output).
+Fixed security holes (bounds checking issues) in several places.
+Don't bother creating a SplashFont (allocating memory) for fonts that
+ are only used for hidden text - this avoids problems with fonts of
+ unreasonably large sizes.
+Clipping in TextOutputDev was off for characters on the left edge of
+ the page.
+The scn and SCN operators weren't correctly handling colors with more
+ than four components.
+FoFiType1::writeEncoded wasn't always correctly finding the end of the
+ encoding.
+Use the ColorTransform parameter in the DCTDecode stream dictionary.
+Type 3 fonts are allowed to have a bbox of [0 0 0 0], which means
+ "unspecified" -- don't issue error messages in that case.
+Perform the transform (to device space) in Splash instead of in
+ SplashOutputDev -- this is needed to correctly handle round joins
+ and caps on stroked paths.
+PSOutputDev now rasterizes any pages that use transparency.
+Limit the crop, bleed, trim, and art boxes to the edges of the media
+ box (per the PDF spec).
+Change GString to increase the allocation increment by powers of two.
+Handle whitespace in hex strings in CMap files/streams.
+Use strings instead of names for separation colorant names in
+ PSOutputDev.
+For explicitly masked images where the mask is higher resolution than
+ the image, use the soft mask code.
+Avoid problems with very large x-steps in the PostScript output for
+ tiling pattern fills.
+Avoid a divide-by-zero in stitching functions which have a subfunction
+ with empty bounds.
+Honor the "Hidden", "NoView", and "Print" flags on annotations.
+Rewrote the pixel rendering code in Splash to use a single set of
+ pixel pipeline functions.
+Added support for transparency groups and soft masks.
+Fixed the transparency blend functions to match the addendum published
+ by Adobe.
+Changed Splash/SplashBitmap to store alpha in a separate plane.
+Setting the color space now selects the correct default color for that
+ color space.
+Remove the mutex lock from GlobalParams::getErrQuiet() to avoid a
+ deadlock when parseCIDToUnicode() or parseUnicodeToUnicode() calls
+ it from inside a locked section.
+Added error checking (on the argument count) in the sc/SC/scn/SCN
+ operators.
+Skip over notdef glyphs in TrueType fonts (which sometimes get drawn
+ as little boxes), to match Adobe's behavior.
+Painting operations in a Separation color space with the "None"
+ colorant or a DeviceN color space with all colorants set to "None"
+ never mark the page.
+Fixed an obscure bug in the JPX decoder - it wasn't reading the extra
+ stuffing byte in the case where the last byte of a packet header was
+ 0xff.
+Change the TrueType font parser (FoFiTrueType) to change the glyph
+ count rather than report an error if the 'loca' table is too small.
+Fixed a couple of bugs in the JBIG2 decoder.
+Added stochastic clustered dot dithering.
+Added the screenType, screenSize, screenDotRadius, screenGamma,
+ screenBlackThreshold, and screenWhiteThreshold xpdfrc settings.
+PSOutputDev now correctly handles invalid Type 3 charprocs which don't
+ start with a d0 or d1 operator
+FreeType 2.2.x support - get rid of the FT_INTERNAL_OBJECTS_H include,
+ and add some 'const' declarations.
+Handle PDFDocEncoding in Info dictionary strings.
+Tweak the xref repair code - ignore whitespace at the start of lines
+ when looking for objects.
+Added the "-exec" switch to xpdf.
+Removed the xpdf.viKeys X resource.
+Changed the color key / explicit masked image code in PSOutputDev to
+ generate better PS code, including a Level 3 option.
+Tweaked the DEBUG_MEM code for performance.
+Move the JBIG2 global stream reading code into reset() instead of the
+ constructor - this way, pdftotext doesn't end up reading the global
+ stream.
+Added the "-preload" option to pdftops and the psPreload xpdfrc
+ command.
+Added the "zoom to selection" command (on the popup menu).
+Fix a bug (in xpdf/pdftoppm/pdftops) with tiling patterns whose bbox
+ size is different from their xStep/yStep.
+Implemented stroke with pattern color spaces.
+Following a link to a page whose CropBox was different from the
+ MediaBox was resulting in an incorrect scroll position.
+Parse truncated date strings from the Info dictionary correctly.
+Change FoFiType1 to handle Type 1 fonts with two /Encoding keys.
+Extend the PSOutputDev shaded fill code to handle DeviceCMYK shaded
+ fills in level2sep and level3sep modes.
+Detect infinite loops in the Page tree.
+Optimized the ASCII85Encoder code.
+Tweaked the text extractor to do a better job of lining up rows of
+ text.
+Leave images compressed (or re-compress them with RLE) in PostScript
+ output when setting up images for forms and Type 3 fonts (or with
+ -preload).
+Extend FoFiType1 to handle Type 1 fonts with octal character codes in
+ their encodings.
+Use a custom string formatter to avoid problems with locale-based decimal
+ formatting (commas instead of periods) in PS output.
+Allow comments in PostScript-type functions.
+Change the TrueType font parser (FoFiTrueType) to delete glyf table
+ entries that are too short.
+
+3.03 (2011-aug-15)
+------------------
+Added the "fixed pitch" text extraction mode.
+Modified "pdftops -paper match" to handle PDF files with
+ different-sized pages, i.e., it will now select the matching paper
+ size on a page-by-page basis.
+Add ability for pdftoppm to write to stdout.
+Added the pdfdetach tool.
+Implemented 256-bit AES decryption.
+Commented out the t1lib section in the configure script -- t1lib has
+ some potential security holes, and hasn't been updated in years.
+Redesigned the font configuration xpdfrc commands: removed the
+ displayFontT1, displayFontTT, displayNamedCIDFontT1,
+ displayCIDFontT1, displayNamedCIDFontTT, displayCIDFontTT, psFont,
+ psNamedFont16, and psFont16 commands; added the fontFile,
+ fontFileCC, psResidentFont, psResidentFont16, and psResidentFontCC
+ commands.
+Switched from GPLv2 to dual v2/v3 licensing.
+Performance: cache tiling patterns.
+Implemented text fills with pattern color spaces.
+Rewrote the image and image mask rendering code to be more accurate
+ and faster.
+Fixed a bug in PDFCore that could sometimes cause crashes at high zoom
+ levels.
+Implemented embedded CMap streams.
+Added the 'setSelection' command.
+Added the 'rotateCCW' and 'rotateCW' commands.
+Added the 'psFontPassthrough' xpdfrc command.
+Added the 'launchCommand' xpdfrc command.
+Implemented alpha-type soft masks.
+Added "Form: AcroForm|XFA|none" to pdfinfo output.
+Added support for image masks filled with pattern color spaces.
+Text search wasn't finding all occurrences of a string on rotated pages
+ (i.e., pages where the primary rotation, as displayed, was not
+ horizontal).
+The text extractor now uses "ActualText" spans if they are present.
+Modified PSOutputDev so it always check for transparency; if Splash is
+ not available, it now prints a warning.
+Handle the blending color space for soft masks.
+Added the disableFreeTypeHinting xpdfrc option.
+Added the psAlwaysRasterize xpdfrc option.
+Added support for transfer functions in the rasterizer.
+Optimized the JPEG 2000 decoder to use less memory.
+Do fill adjustment (similar to stroke adjustment) on simple
+ rectangular fills.
+Added the antialiasPrinting xpdfrc setting.
+Added '%i', '%j', and '%k' (mouse pointer page and position) to the
+ available options for the 'run' command.
+Links with the underlined border style were being drawn with the lines
+ over, instead of under, the text.
+Add #include <aconf.h> to XPDFTree.cc (to avoid problems with certain
+ compilers).
+Change XRef::fetch() to avoid infinite loops caused by odd damage to
+ the xref table (e.g., where a stream object's "Length" value is an
+ indirect reference that points to another stream object).
+Minor fix in GString.cc to deal with an error in newer C++ compilers
+ (pow() can take int or double args).
+Handle embedded fonts which are declared with the wrong font type,
+ including 8-bit fonts declared as CID fonts and vice versa; this
+ was causing various problems, including crashes and invalid
+ PostScript output.
+In text extractor, don't drop horizontally overlapping words onto a
+ separate line.
+The numbers in the operand to the TJ operator should be multiplied by
+ the current horizontal scaling parameter.
+Fixed a bug in the Type 1C-to-Type 1 font converter -- need to escape
+ strings in the font dictionary.
+The zero-font-size check in PSOutputDev.cc was broken.
+Fixes for the form field appearance regeneration code: handle Unicode
+ strings (by downconverting to Latin1); check for "True" in addition
+ to "Yes" as a button value.
+Modify XPDFTree to limit the widget height to 32767 pixels -- this
+ avoids crashes with very large outlines.
+Modify FoFiType1 to handle PFB headers in Type 1 font files.
+Allow image mask decode arrays to be [1.0 0.0] in addition to [1 0].
+Tweak the form field appearance regeneration code.
+PSOutputDev now sets up resources for DR dictionaries in forms (in
+ case the form field appearances are regenerated).
+For TrueType fonts which are marked symbolic, the cmaps should be
+ ignored.
+Change the handling of numeric characters in Unicode text output --
+ they are now treated as left-to-right, which isn't strictly correct,
+ but does result in correct visual formatting.
+Modify FoFiTrueType to handle bogus loca table entries where the
+ offset is past the end of the glyf table.
+Fixed shading pattern fills to do clipping and background fills
+ correctly.
+Change the code that reads the page tree to be more flexible with
+ regard to improperly constructed trees.
+Changed the PostScript header comment from "Produced by xpdf/pdftops
+ x.yy" to "XpdfVersion: x.yy" to make it (mostly) DSC-compliant.
+Fixed PSOutputDev to handle page rotation correctly where the PDF page
+ is rotated 90 or 270 degrees and the page width (before rotation) is
+ greater than its height (and vice versa for 0 or 180 degrees).
+Unbalanced save/restores inside form XObjects (which can be happen in
+ damaged or incorrectly constructed PDF files) could cause crashes.
+The CCITTFax decoder could go into an infinite loop on certain types
+ of corrupt input.
+Added the "drawAnnotations" xpdfrc command.
+Added the "psUncompressPreloadedImages" xpdfrc command.
+Escape newlines and other non-printable characters in the custom color
+ DSC comments in PostScript output.
+Added the tilingPatternFill and *ShadedFill functions to
+ PreScanOutputDev to speed it up.
+Too many elements in an image's Decode array should not be a fatal
+ error.
+Fixed a buffer overflow security hole in StreamPredictor.
+Empty pages (zero-length content streams and null contents) were
+ causing crashes in continuous view mode.
+Handle line dash arrays that start with a zero element correctly.
+PreScanOutputDev was not correctly detecting all transparency - in
+ some places, it was looking only at the blending mode, and not at
+ the opacity setting.
+Force halftone screen size to be a power of 2. This allows optimizing
+ the halftoning code.
+Fixed a bug in the JBIG2 MMR decoder (and also in the CCITTFax
+ decoder) that was causing array references with negative indexes.
+Fixed a bug in the transparency code that was causing memory overruns.
+Fixed a 64-bit bug in the ASCII85 encoder.
+The focusToPageNum was crashing in full-screen mode - it should simply
+ do nothing.
+Added '%p' (current page number) to the available options for the 'run'
+ command.
+Tweak the behavior with PDF files that have pages of different widths
+ in continuous mode: scroll horizontally to avoid blank space on the
+ left side (when changing pages via entering a page number or
+ clicking on a link).
+A closepath followed by a lineto should create a new subpath for the
+ lineto.
+Fixed a buffer overflow in the CCITTFax decoder.
+Adobe Acrobat appears to ignore the flatness setting when rasterizing
+ curves, so Xpdf now does the same. (Oddly, Acrobat passes the
+ flatness setting through to PostScript output, which ends up making
+ the PS file look different from the PDF file - so Xpdf continues to
+ pass flatness through to PS files, too.)
+Pdfimages now ignores tiling pattern fills (for performance reasons).
+Fixed a bug in PDFCore that could sometimes cause crashes at high zoom
+ levels.
+Use std::sort (with functors) in place of qsort (if available) - this
+ can be significantly faster.
+Hitting "close" while Xpdf is in full-screen mode (with only one file
+ open) was crashing.
+Tweak the TrueType font encoding deciphering algorithm.
+Rewrote the CCITTFax decoder inner loop - this fixes a security hole.
+Fixed two security holes (missing bounds checks) in the DCT decoder.
+Do the correct zooming for "Fit" and "FitR" destinations.
+Implement the rotation value in the form field appearance regeneration
+ code.
+When PSOutputDev rasterizes a page (because it contains transparency),
+ the image is broken into stripes if it is above a size threshold.
+Don't clip the other page boxes to the MediaBox at the intermediate
+ (Pages) nodes; only do it at the leaf (Page) nodes - the other boxes
+ can be specified before the MediaBox is specified.
+Split several special cases off from Splash::pipeRun, for performance.
+Add a sanity check for excessively large font sizes (which caused
+ problems because Xpdf tried to allocate memory for a font cache).
+Fixed a bug in the GfxCIDFont constructor involving reading the
+ vertical metrics.
+Rewrote the code that handles annotation transforms - it was not
+ handling non-rectangular transforms correctly.
+Tweak the Type 3 bbox code to allow some slack on the left/bottom
+ edges, as well as the top/right edges.
+Fixed a bug that was causing problems rendering Type 3 fonts to 1-bit
+ monochrome output.
+Handle the case where AES padding is invalid.
+Changed XRef::getNumObjects to return the number of used entries,
+ instead of the same value as returned by XRef::getSize().
+Ignore bogus entries in the TrueType table directory instead of
+ immediately giving up on the font.
+Fixed a bug in the radial shading code that could cause divide-by-zero
+ errors.
+Tweaked the TrueType font fixup code to deal with an extra zero entry
+ at the end of the loca table.
+Increased font cache sizes to improve performance.
+Tweaked the TrueType notdef kludge to skip the notdef glyph only if it
+ was caused by a character for which a Unicode/MacRoman mapping could
+ not be found.
+Added another font size sanity check, this time for Type 3 glyphs.
+Added initial support for optional content (layers) - no GUI yet.
+For CID fonts that specify the 'Adobe-Identity' or 'Adobe-UCS'
+ collection, use an identity mapping for char code-to-Unicode
+ conversion.
+Updated the error function to take a category argument, and to use
+ GString::format instead of printf; added an error callback.
+The PDF spec claims that names are limited to 127 chars, but Distiller
+ 8 will produce longer names, and Acrobat 8 will accept longer names
+ -- Xpdf will now handle them, too.
+Change the Catalog code so it doesn't load the entire page tree at
+ startup (this only helps the command line tools - the viewer scans
+ all of the pages at startup).
+Clip opacity values to the range [0,1].
+Handle glyph names of the form 'unixxxx' (similar to Ann, Axx, etc.).
+Resolution of rasterized pages in PostScript output was not being
+ computed correctly (resulting in overly large images).
+Extend the mapUnknownCharNames config command to cover CID fonts.
+Zero-length segments in the middle of stroked paths were not being
+ handled correctly -- they should be completely ignored, except for
+ the special case of a zero-length path with round line caps.
+Various optimizations to the Splash rasterizer.
+Allow "Identity" and "Default" as the transfer function in soft masks.
+Tweaked the ToUnicode CMap parser to allow <00xx> char codes for 8-bit
+ fonts.
+TextPage::clear() was not clearing the lists of underlines and links.
+Changed the CCITTFax decoder to correctly handle the interaction
+ between the EndOfLine and EncodedByteAlign parameters.
+Fixed a bug where xpdf wouldn't go to a destination specified on the
+ command line, if continuous mode was enabled
+ (xpdf -cont file.pdf +foo).
+Fixed a bug in the FreeType interface code which was causing incorrect
+ positioning/sizing of characters.
+Tweaked the FreeType interface code - use 'light' hinting with Type 1
+ fonts (because it generally looks better).
+Tweak the Windows font searching code to handle "Black" fonts.
+Fixed a bug in the PostScript-type function parser -- real numbers
+ that start with a decimal point weren't being handled correctly.
+Changed the way filled-and-stroked text is handled -- use Splash to do
+ both the fill and the stroke (rather than using the font ending to
+ do the fill and Splash to do the stroke); also turn off stroke
+ adjustment when drawing stroked text.
+Ignore generation numbers on compressed objects (to match Adobe's
+ behavior).
+Changed the PostScript PageSize policy from 3 to 6 (choose next
+ largest paper size, don't rescale).
+Check that the mask values for color key masked images are in range --
+ both Ghostscript and Distiller choke on the PostScript if invalid
+ mask colors are specified.
+Fixed a bug in generic region decoding in the JBIG2 decoder.
+Fixed the lexer to handle large real numbers correctly.
+Pdftops wasn't correctly handling tiling patterns that use both fill
+ and stroke operators.
+Report an error if the Size array in a sampled function includes any
+ zeros.
+The PostScriptFunction.codeString field wasn't being initialized
+ correctly.
+Invalid object streams were not being handled correctly.
+Check for loops in xref tables.
+Handle the case where a content stream ends after the 'ID' operator
+ which starts inline image data.
+Check for invalid object streams (i.e., an object stream can't be
+ inside another object stream.
+Add a recursion limit to the object parser - this avoids stack
+ overflows with various sorts of damaged PDF files.
+Handle various parameter settings in the extended graphics state
+ dictionary.
+An invalid document outline (missing fields) could cause a crash.
+Added an overprint preview mode (for CMYK output only).
+Correctly handle FitH/BH/V/BV link destinations that contain null
+ values for positioning parameters.
+If the PDF file doesn't define a BaseURI, set one based on the
+ location of the PDF file -- this allows relative links to be handled
+ correctly.
+Use ResusableStreamDecode when generating Level 3 PostScript for an
+ explicitly masked image.
+Tweak the Type 1 font parser to handle encodings with multiple
+ characters on one line.
+Invert subtractive color components before passing them to the
+ blending function.
+Fix an invalid array access in SplashOutputDev::setSoftMask() in CMYK
+ mode.
+A PS interpreter may attempt to read past the end of a preloaded
+ image, so we need to check for that.
+Fixed several overflow/uninit bugs in JBIG2Stream.
+Fix the CCITTFax decoder to correctly find end-of-file (RTC/EOFB)
+ markers when the EndOfLine parameter is false.
+Don't limit the startxref offset to 10 digits - some PDF generators
+ use extra leading zeros.
+Use the "DV" (default value) field in text annotations if the "V"
+ (value) field is missing.
+Increase the number of digits printed for floating point numbers in
+ PostScript output -- it was running into numerical accuracy problems
+ on large pages.
+Fixed integer overflow bugs in Catalog.cc and GfxFont.cc.
+Zero-length tables in TrueType fonts should be treated as missing.
+ToUnicode CMaps map char codes to Unicode; .cidToUnicode files map
+ CIDs to Unicode -- ToUnicodeCMaps were being handled incorrectly.
+Added the "psRasterResolution" and "psRasterMono" xpdfrc commands.
+Added code to FoFiTrueType to check for entries in the table directory
+ with bogus tags -- this handles the case where the number of tables
+ given in the header is too high.
+Negative shift values in the PostScript bitshift operator were being
+ handled incorrectly.
+GfxICCBasedColorSpace was not correctly bounds-checking the number of
+ components.
+Check SampledFunction input values for NaNs.
+Fix a divide-by-zero when the page width or height is zero.
+Fix a bug positioning text in PostScript output - if the last char in
+ a string has an incorrect width in the PDF font object, that messes
+ up the total string width, so we have to position individual chars.
+In PostScript output, if CID font substitution fails, drop all text in
+ that font.
+Handle PDF files that set the stream/string decryption filters to
+ Identity (i.e., no encryption).
+Avoid passing a zero font size to FreeType.
+Tweak raw mode in the text extractor to handle words on the same line
+ drawn in the wrong order.
+Pdftops was generating a singular transform matrix for annotations
+ whose bounding boxes had zero width or height.
+Handle embedded OpenType CFF fonts with CIDToGIDMaps.
+Remove the old kludge from PSOutputDev that was converting char 32
+ from ".notdef" to "space".
+Allow CCITTFax images to be more than 32k pixels wide.
+Modified the CMap parser to handle usecmap with Identity-H/V.
+Added some parameter checking in the JPX decoder.
+Added checks for infinite loops in PDF objects - for color spaces,
+ functions, optional content, and outline items.
+Add support for mouse button bindings up to button 32 (old limit was
+ 7).
+Fixed a bug in the decryption code for revision 3 with keyLength < 16
+ -- the owner password was not being handled correctly. [Thanks to
+ Matthias Franz.]
+Optimize SampledFunction: pull index computation code out of the
+ transform function; cache the last transform.
+Tweaked the font naming algorithm in PSOutputDev.
+Treat mirrored (as opposed to rotated) text the same as upright text
+ in pdftotext.
+Fixed buffer overflows in Splash and SplashBitmap.
+Check for bogus character codes (e.g., <ffffffff>) in ToUnicode CMaps.
+The radial shading code (in both Gfx.cc and PSOutputDev.cc) was not
+ computing the s bounds properly.
+Drop empty subpaths in clip and fill (but not stroke) operations -
+ this can significantly speed up clip performance in the weird case
+ where a PDF file does "<x> <y> <w> <h> re <x> <y> m h W n".
+Added code to FoFiTrueType to check for an invalid loca format field
+ in the head table.
+The axial shading code (in Gfx.cc, but not PSOutputDev.cc) was not
+ computing the incremental polygon vertices correctly.
+Set the character width in TextOutputDev to something sensible for
+ vertical fonts.
+Added basic support to the text extractor for vertical writing mode.
+The non-interactive tools (pdftotext, pdftops) now free Page objects
+ after using them, avoiding performance problems with pages that have
+ huge resource dictionaries.
+Check for line dash arrays like [0], and draw nothing at all (to match
+ Acrobat's behavior).
+Add a sanity check for the ascent/descent values in FontDescriptors.
+Single-point paths with a line dash were causing a crash.
+Correctly handle Level 3 PostScript output with masked images inside
+ patterns.
+Tweaked the xref repair code so that it runs if the catalog is messed
+ up (in addition to running if the xref table is damaged).
+If Indexed color space tables (streams or strings) are too small,
+ reduce the max index instead of throwing away the color space.
+Change the CMap parser to allocate memory only when it sees a mapping,
+ not when it sees a 'codespacerange' declaration -- this avoids
+ allocating huge amounts of memory for CMaps with large, unused
+ codespaceranges.
+In monochrome mode, treat lines with width <= 1 as hairlines (to match
+ Acrobat's behavior).
+Cache the last transform for PostScript-type functions.
+Added the "-rawdates" option to pdfinfo.
+Optimized ImageStream::getLine().
+Fixed the Hue, Saturation, Color, and Luminosity blend functions to
+ match Adobe's spec.
+Fixed the non-isolated group compositing computation.
+Skip extraneous unused data at the end of JBIG2 segments.
+Change the algorithm that stroke/fill adjustment uses so that the
+ edges of adjacent strokes/fills line up.
+Do stroke adjustment on end caps when cap style is butt or projecting.
+Fixed a security hole: Gfx.parser was not being initialized to NULL.
+Fixed a security hole: integer bounds check in the Type 1 encoding
+ parser in FoFiType1.cc.
+If an embedded font object is invalid or non-existent, do font
+ substitution (same as if there were no embedded font).
+TextOutputDev was reusing an old font in the case where the font
+ changed but the font size and character positioning stayed the same.
+When starting a transparency group, copy the fill/stroke colors from
+ the graphics state.
+Tweaked the fixed-point code.
+When a TrueType font is declared resident (with a psFont command),
+ don't munge the encoding.
+Look for URIs starting with "www." and treat them as absolute "http:"
+ URIs, not as relative URIs (to match Adobe's behavior).
+Added code to FoFiTrueType to check for a zero-entry cmap table.
+Tweaked the font substitution code to do a better job of scaling the
+ substituted font.
+Require at least two splits in the axial shading color bisection.
+Optimized JBIG2Stream::readGenericBitmap().
+JPXStream wasn't correctly handling row padding for images with fewer
+ than 8 bits per component.
+Optimized the ToUnicode CMap parser.
+Added a "whole words only" option to text searches.
+Check for valid component parameters in the DCT decoder.
+Implement embedding of external 16-bit fonts (without subsetting) in
+ PostScript output.
+Added the minLineWidth xpdfrc command.
+Added warning messages for font substitutions.
+
+3.04 (2014-may-28)
+------------------
+New text extractor.
+Added the pdftohtml tool.
+Added the pdftopng tool.
+New trapezoid-based rasterizer core (for performance).
+Generate appearance streams for Line, PolyLine, and Polygon
+ annotations.
+Added the closeWindowOrQuit command, and changed the default binding
+ for ctrl-W from closeWindow to closeWindowOrQuit.
+Implemented the new AES-256 mode (R=6, Acrobat X).
+Add an object cache.
+Added a small cache for object streams.
+Modify PSOutputDev to use LZW compression instead of RLE, with a
+ fallback to RLE if the "psLZW no" setting is given.
+Pdfinfo now prints page rotation info.
+Modified ImageOutputDev, used by pdfimages, to output the masks and
+ soft masks used when drawing images.
+Remove non-printable characters from error output, just in case they
+ might cause problems for the terminal program.
+Added initial support for Code3of9 bar codes in XFA forms.
+Added the mapExtTrueTypeFontsViaUnicode xpdfrc command.
+Apply stroke adjustment to rectangular images and clipping regions (in
+ addition to strokes and fills).
+Decode JPEG 2000 images at less than full resolution if the full res
+ image isn't needed (i.e., if the raw image is higher resolution than
+ the output).
+Implemented knockout groups.
+Removed t1lib support.
+Added support for images with 16-bit components.
+Rewrote the Dict class to use a hash table; as a side effect, this
+ handles dictionaries with multiple definitions for a key, which are
+ in violation of the spec, but Acrobat appears to handle.
+The transformed line width computation -- used to implement the
+ minLineWidth setting, and the hairline threshold in monochrome mode --
+ was incorrect.
+Pdftops was not correctly handling the case where it couldn't find
+ a 16-bit font -- this led to crashes and/or invalid PostScript.
+A bug in FlateStream::getBlock() was causing problems with narrow
+ images.
+Use the correct _WIN32 define instead of WIN32.
+Use copy-on-write for the clip path in SplashState (when doing gsave),
+ for performance.
+Added a Solaris-specific entry to the ghostscript font search path.
+SplashState was initializing line width to 0 instead of 1.
+Abort processing on a content stream after getting 500 errors
+ (undefined operator, wrong number of args) -- this avoids very long
+ processing time for malicious PDF files using bogus RLE encoded
+ content streams.
+Added the psUseCropBoxAsPage xpdfrc option; "pdftops -pagecrop" now
+ sets psUseCropBoxAsPage; "pdftops -pagecrop -paper match" now uses
+ the CropBox as the page size.
+Re-architected the AcroForm support code into a separate AcroForm
+ module.
+Fixed the handling of overprinting/transparency interaction, using
+ the CompatibleOverprint blend mode.
+The TIFF predictor code for the 1-bit-per-pixel case was broken.
+For triangle and patch mesh shadings (types 4-7) with color functions,
+ interpolate the function parameter not the color.
+Check the fontFile/fontDir commands before (instead of after) doing
+ Base-14 substitution in PS output.
+Correctly handle non-embedded TrueType fonts that have an Identity
+ ToUnicode mapping (display and PS output were failing).
+Added support for XFA form rendering, including an "enableXFA" xpdfrc
+ setting.
+Handle PFB Type 1 fonts when generating PostScript output.
+Unwind any extraneous saved graphics state at the end of the page
+ (before drawing annotations).
+Added some integer overflow checks in the GString class.
+Handle 16-bit components in JPEG 2000 images.
+ActualText spans can end without a valid font, in which case
+ TextPage::beginWord was crashing.
+The Domain entry in function shadings wasn't being parsed correctly.
+Fixed a bug in the JPEG decoder - successive approximation
+ (progressive mode) coefficients weren't being handled correctly.
+Added a better infinite loop test to the xref parser.
+When generating PostScript, merge reused TrueType fonts (if their
+ code-to-GID mappings are the same).
+Tweak the Gouraud triangle shaded fill code to end the recursive
+ splitting if the triangles get sufficiently small.
+Do bilinear interpolation when upsampling images.
+When skipping extraneous image data from an inline image, look for
+ EI<whitespace> instead of just EI.
+When writing to stdout on Windows, pdftoppm now sets the file mode to
+ binary. [Thanks to Robert Frunzke.]
+Accept strings as well as names for the BaseFont entry in font
+ objects.
+Removed the TEXTOUT_WORD_LIST config option (with the new text
+ extractor, this is always enabled).
+Fixed a bug in the JBIG2 decoder (the TPGD context for template #3 in
+ readGenericBitmap was incorrect).
+Rewrote the PostScriptFunction code for performance.
+Handle 8-bit OpenType CFF fonts that are missing required tables in
+ the OpenType wrapper.
+Handle tiling patterns with reversed coordinates in their bounding
+ boxes.
+Added support for 64-bit file offsets, i.e., PDF files larger than
+ 2GB.
+Optimize the code that rasterizes pattern-filled image masks.
+Added support for Mac OS X system fonts (Base-14 only).
+The backdrop color in luminosity-type soft mask groups was not being
+ handled correctly.
+Modified behavior of "pdftops -paper match -duplex ..." - it will now
+ duplex consecutive same-sized pages.
+Tweak the handling of degenerate fills ('moveto lineto fill') to
+ match Adobe.
+Don't honor the OPM=1 setting with ICCBased CMYK color spaces.
+Whole-word searches were treating certain punctuation (Unicode number
+ separators and terminators) as part of the word, e.g., searching for
+ "foo" would not match "foo,".
+Use the TextString class everywhere it makes sense.
+Removed the unnecessary segment sort in Splash (performance
+ optimization).
+Handle hyperlinks that use Widget-type annotations.
+Fix up the integer overflow checks to avoid issues with clever
+ compilers. [Thanks to Nickolai Zeldovich.]
+Correctly handle streams with missing Length entries in damaged PDF
+ files.
+Added a compile-time option (LOAD_FONTS_FROM_MEM) to load fonts from
+ memory rather than temporary files on disk.
+Added the psRasterSliceSize xpdfrc option.
+Fixed a case in the JPEG 2000 arithmetic decoder where extra data is
+ present in packet i, and needs to be saved for use in packet i+1.
+Fixed a bug in the JPEG 2000 decoder related to images with fewer than
+ 8 bits per component.
+Handle the case in PSOutputDev where slice size overflows a 32-bit
+ integer.
+Add (partial) support for TrueType cmap format 2.
+Always pass FT_LOAD_NO_BITMAP to FreeType -- bitmaps apparently fail
+ with rotated characters.
+Support fonts specified in ExtGState dictionaries.
+Annotations with empty Border arrays should not draw a border.
+Fix the CMap parser to handle large CID ranges.
+Check for Type 3 CharProcs that call q or Q before the d0/d1 operator,
+ and treat them as uncacheable.
+Invert the selection color when starting in reverse video mode.
+Device{Gray,RGB,CMYK} cannot be mapped via a resource dict.
+Changed the PS output for masked images (explicit and color key
+ masking): use a plain old clip path instead of rectclip to avoid
+ array overflows.
+Check the StemSnapH/V arrays when converting Type 1C fonts to Type 1 -
+ if there are any duplicate or out-of-order values, skip that
+ StemSnapH/V array.
+Added the psMinLineWidth xpdfrc setting.
+Fix an obscure issue in converting TrueType fonts to Type 42, related
+ to empty glyph descriptions (12 zero bytes).
+Pdftops now reports an error if there were any I/O errors writing to
+ the PS output file.
+Fix vertical text (CJK fonts) in PS output -- offset the character
+ origin correctly.
+Increased the number of digits used by pdfimages for the image number
+ from three to four.
+Handle right-to-left (e.g., Arabic) ligatures correctly in the text
+ extractor.
+Added the -loc and -locPS options to pdffonts.
+Extend the object parser recursion limit to cover Stream::addFilters()
+ / Stream::makeFilters() - to avoid another possibility of stack
+ overflow.
+Disable FreeType autohinting, because it can fail badly with font
+ subsets that use invalid glyph names -- except in the case of Type 1
+ fonts, which look much better with light autohinting.
+Modified the rasterizer pipeline functions to process a scan line at a
+ time (for performance).
+Removed VMS build support (it hasn't been updated in ages).
+Removed pdftotext's '-htmlmeta' option (use pdftohtml instead).
+PSOutputDev's font/form setup code, and pdffonts, were not scanning
+ soft mask groups in ExtGState dictionaries.
+Invalid DCT input (e.g., from a damaged PDF file) could overflow the
+ dctClip array.
+When upsampling an image mask or image with a large resulting image
+ size, do it in stream mode instead of prescaling the whole image
+ (to avoid running out of memory).
+Added infinite loop detection to pdffonts.
+
+4.00 (2017-aug-10)
+------------------
+Complete rewrite of the xpdf GUI, using Qt instead of Motif.
+Multithreaded rasterization in the viewer - pages are now rasterized
+ in a separate thread so that scrolling and other UI activity is not
+ blocked.
+Added a linear text selection mode.
+Limit transparency groups to the clip rectangle (performance
+ optimization).
+Optimized the SplashScreen code, used in 1-bit monochrome mode.
+Added setmode calls to pdftopng, when writing to stdout on Windows.
+ [Thanks to Robert Schroll.]
+Regenerate the appearance stream for an AcroForm field if there is
+ no appearance stream, even if NeedAppearances isn't set.
+When regenerating AcroForm field appearance streams, use a default
+ font (Helvetica) for fields that don't have a valid font.
+When the line width is increased by the minLineWidth setting, force
+ the line join mode to "bevel" to avoid "whiskers" at sharp corners.
+Change the pdftoppm/pdftopng resolution (-r) argument from an integer
+ to floating point.
+Use the FontDescriptor 'CapHeight' value in place of 'Ascent' if it's
+ available.
+OC (optional content) entries in image XObjects were being ignored.
+Added support for preblended soft-masked images.
+Added optional support for using libjpeg to decode DCT (JPEG)
+ streams.
+Transparency groups weren't being composited correctly in mono1 mode.
+Switched to a different IDCT transform in the DCT (JPEG) decoder, to
+ get better accuracy; the new transform is also a bit faster.
+Modified the polygon scan converter to avoid O(n^2) performance, which
+ can get really bad with paths that have extremely large numbers of
+ segments.
+Added the maxTileWidth, maxTileHeight, tileCacheSize, and
+ workerThreads xpdfrc settings.
+Modified PSOutputDev so it doesn't output resources (fonts, etc.) for
+ pages that are rasterized (because they use transparency).
+Added the -alpha flag to pdftopng.
+Handle TrueType fonts with out-of-bounds loca entries.
+Optimize consectutive "q" (gsave) operations in PostScript output.
+Various improvements to the XFA rendering code.
+Added the -nodiag flag to pdftotext.
+Switched to cmake instead of autoconf.
+Look for Widget-type annotations that aren't attached to the
+ AcroForm, and add them to the AcroForm.
+Added the "-z" option to pdftohtml.
+The JPEG 2000 decoder now treats invalid boxes after the image
+ codestream as non-fatal errors.
+Improved the handling of drop caps in pdftohtml.
+Added the "psNeverRasterize" setting.
+Pdftohtml now converts invisible PDF text to transparent HTML text.
+Check for zero-width/height bitmaps in the JBIG2 decoder -- these can
+ happen in corrupted JBIG2 streams.
+Pdfimages was inverting 1-bit monochrome images.
+Added CCITTFaxStream::getBlock() to speed up the CCITTFax decoder.
+Added resolution info to PNG files generated by pdftopng.
+The DCT decoder now ignores non-Adobe APP14 markers (rather than
+ generating an error).
+Regenerate appearance streams for FreeText annotations.
+The TextString class now handles UTF-16LE in addition to UTF-16BE.
+ (UTF-16LE isn't allowed by the PDF spec, but I've seen it used.)
+Added the simple one-column text extraction mode (pdftotext -simple).
+Added a -cmyk option to pdftoppm to write CMYK PAM files.
+Modified pdfinfo to output "static XFA" or "dynamic XFA", rather than
+ just "XFA".
+Added the "drawFormFields" xpdfrc command.
+Various performance optimizations in the rasterizer.
+Fixed the tiling pattern code to avoid stripes between pattern cells,
+ and to honor the TilingType flag.
+Always split axial shadings into 256 slices.
+Handle the case where an optional content block is left open at the
+ end of a page content stream.
+Accept code-to-CID-type CMaps as ToUnicode CMaps (to work around buggy
+ PDF generators).
+Unicode-to-unicode maps weren't working correctly with fonts that have
+ an "Identity" encoding.
+Modify the lexer to interpret "--123.4" as -123.4, "--123" as 0, and
+ "123-45" as "123" to match Adobe's behavior.
+The text extractor now reverses the order of side-by-side columns for
+ right-to-left scripts.
+Worked on right-to-left script support in pdftohtml (still not
+ complete).
+Switched the default display mode to continuous (from single page).
+Added the "-bom" switch to pdftotext.
+Share font objects when multiple font tags point to the same object.
+The transfer function goes before alpha blending in the rasterizer
+ pipeline.
+Added the "-allinvisible" switch to pdftohtml.
+Added overprint mode (OPM) support in Level 3 PostScript output.
+Added support for DeviceN color spaces in Level 3 PostScript output.
+Added the "-userunit" option to pdftops.
+Added the "-raw" and "-list" options to pdfimages.
+Pdfimages now writes PGM files (instead of PPM files) for DeviceGray
+ and CalGray images.
+Added grayscale PostScript output -- added the '-level2gray' and
+ '-level3gray' switches to pdftops.
+Fix up Type 1 fonts when embedding in PostScript.
+Added the "problematic" column to pdffonts output.
+Generate an appearance stream for signature fields in AcroForms.
+Correctly handle line dash patterns with zero-length segments when the
+ line cap is butt or project.
+Added the "strokeAdjust cad" xpdfrc option; fixed some issues with
+ stroke adjustment and projecting line caps.
+Added support for Code128B bar codes in XFA forms.
+Look at both the ON and OFF arrays in the default OCCD (optional
+ content config dict), to work around certain broken PDF files.
+Added support for XFA 'numericEdit' fields.
+Added support for 'picture' formatting in XFA forms.
+Added mouseClickN events (in addition to mousePressN and
+ mouseReleaseN).
+External CID OpenType CFF fonts should map through Unicode (similarly
+ to external CID TrueType fonts).
+Handle axial and radial shadings in SplashOutputDev, in device space.
+Use the PDF 2.0 inline image length field.
+Handle duplicate embedded Type 1 font names in PostScript output.
+Handle the case of a stroked closed path with a dash pattern -- if the
+ dash pattern is "on" at both the start and end of a subpath, it
+ should be joined.
+If the line dash phase is negative, add a multiple of 2x the total
+ dash length.
+Indexed color space indexes should be clipped to [0,hival].
+Fix the handling of the text render mode with Type 3 fonts, in the
+ rasterizer and in PostScript output.
+CID font metrics shouldn't be sorted - the PDF 2.0 spec says that, in
+ the case of duplicate entries, the first entry should be used.
+PSOutputDev now converts tiling patterns to PostScript tiling patterns
+ (in Level 2/3).
+Fixed a security hole in SecurityHandler.cc (uninitialized variables).
+ This vulnerability was discovered by Kushal Shah of Fortinet's
+ FortiGuard Labs.
+Fixed a security hole in Function.cc (write past end of array).
+Fixed a security hole with the use of d0/d1 operators outside of a
+ Type3 CharProc [CVE-2016-9027].
+
+4.01 (2019-feb-18)
+------------------
+Added remote server mode back into xpdf.
+Add support for Unicode file names for the command line tools on
+ Windows.
+Added the tabStateFile setting, the saveTabState/loadTabState
+ commands, and the "-tabstate" switch to xpdf. [Thanks to Christian
+ Barthel for the suggestion.]
+Added the defaultPrinter xpdfrc setting.
+Removed the psFile xpdfrc option (should have been removed in the 4.00
+ release).
+Tweaked the TrueType encoding/cmap code again, to get closer to the
+ PDF 2.0 spec, and to fix a problem (difference between Xpdf and
+ Adobe) with a specific PDF file.
+Optimized transparency group rasterization: only composite the part of
+ the group bitmap that was modified.
+If vector antialiasing is disabled, use the narrow stroke code for
+ lines less than one pixel wide.
+Do not map through Unicode for non-embedded CID TrueType fonts with
+ encoding = Identity-H and char collection = Adobe-Identity -- this
+ matches Adobe's behavior on a somewhat common case.
+The CCITTFax decoder now aborts after 1000 error messages, avoiding
+ problems with huge data expansion for badly corrupted CCITTFax
+ streams.
+The xref loop detection code was only looking at xref tables, and not
+ xref streams. (CVE-2018-7174) [Thanks to skysider for the bug
+ report.]
+The JPX decoder wasn't checking to make sure the number of components
+ is non-zero. (CVE-2018-7175) [Thanks to skysider for the bug
+ report.]
+Modify the lexer to accept leading '+' signs on numbers.
+Pdftohtml wasn't setting the background image width/height attributes
+ correctly for rotated pages.
+Ignore color-setting operators in cached Type 3 characters.
+Support color-key masking in Level3Gray PS output with preload.
+Xpdf was crashing on zero-page PDF files.
+Added the unicodeRemapping xpdfrc option.
+Change the cursor when the mouse is over text (in linear selection
+ mode).
+Support external OpenType CFF fonts for PostScript output.
+Handle choice-type AcroForm fields that have separate display values
+ and export values.
+Increased the supported PDF version from 1.7 to 2.0. (This isn't a
+ change in functionality. The code was already in place -- I just
+ forgot to bump the PDF version number.)
+Added the dropFont xpdfrc option.
+Added support for PDF417 barcodes in XFA forms.
+Modified pdfinfo to look at both the Info dict and the XML metadata.
+Check for invalid line cap/join values. [Thanks to
+ fish@360TeamSeri0us for the bug report.]
+Modified the object stream cache to hold onto more object streams, if
+ they're being used.
+Don't look at the "DV" (default value) key in AcroForm fields -- that
+ should only be used when a form is explicitly reset.
+Accept 8-byte values in xref streams.
+Two DCTStream bugs: don't allow SOF headers between progressive scans;
+ check for invalid Huffman table indexes. [Thanks to
+ fish@360TeamSeri0us for the bug reports.]
+Check for uses of the scn/SCN operators with no arguments. [Thanks to
+ fish@360TeamSeri0us for the bug report.]
+Correctly handle indexed color spaces with empty lookup tables.
+ [Thanks to fish@360TeamSeri0us for the bug report.]
+Support large bitmaps (>4GB) on 64-bit systems.
+Soft masks are always isolated.
+When "undoing" preblended image/softmask data, clip the resulting
+ color values to avoid problems with incorrect data.
+Set the default text encoding to UCS-2 in Xpdf, so that copying to the
+ clipboard works as expected.
+Added a sanity check for JBIG2 symbol height. (CVE-2018-7173)
+ [Reported by skysider.]
+Check for uninitialized value in JPXStream::fillReadBuf.
+ (CVE-2018-7452) [Thanks to skysider for the bug report.]
+Don't try to load an XFAForm if the PDF file has zero pages.
+ (CVE-2018-7454) [Thanks to skysider for the bug report.]
+Check for invalid line cap/join values. (CVE-2018-16368) [Thanks to
+ fish@360TeamSeri0us for the bug report.]
+If the page count is greater than 50k, rescan the page tree and count
+ the pages. This prevents long load times where various tools try to
+ read pages 1 through n. (CVE-2018-18651) [Thanks to krace for the
+ bug report.]
+Handle a null font name in PSOutputDev::setupEmbeddedType1Font.
+ [Thanks to r4xis for the bug report.]
+Check for invalid quant table indexes in the DCT decoder. [Thanks to
+ r4xis for the bug report.]
+
+4.01.01 (2019-mar-14)
+---------------------
+The PDFDoc(BaseStream) initializer wasn't working correctly.
+Fixed a missing array bounds check in PSOutputDev. [Thanks to
+ Loginsoft for the bug report.]
+If the "U" string used for RC4 decryption is short, Adobe apparently
+ zero-pads it, so Xpdf now does the same.
+Pdffonts now checks more carefully for loops between objects.
+Fixed a problem parsing large real numbers. [Thanks to Loginsoft for
+ the bug report.]
+
+4.02 (2019-sep-25)
+------------------
+Pdftohtml now extracts embedded fonts (TrueType and CFF only).
+Added the margin settings (-marginl -marginr -margint -marginb) to
+ pdftotext.
+Various user interface tweaks:
+ - added a toolbar button to toggle (show/hide) the sidebar
+ - added view menu items to toggle (show/hide) the sidebar and
+ toolbar
+ - added the 'showToolbar', 'hideToolbar', 'toggleToolbar',
+ 'expandSidebar', and 'shrinkSidebar' commands
+ - added the 'initialDisplayMode', 'initialToolbarState', and
+ 'initialSelectMode' xpdfrc settings
+ - rearranged the xpdf toolbar
+Implemented drag-and-drop, so that PDF files can be drag-and-dropped
+ onto an already-open xpdf window.
+Added the option to display page labels, rather than page numbers.
+Implemented the missing 'openInNewWin' command.
+Added the 'openFileIn', 'openFileAtDestIn', 'openFileAtPageIn', and
+ 'openIn' commands; removed the 'openInNewWin' command.
+Added the '-open' switch to xpdf.
+Added the reverseVideoInvertImages setting.
+Page-up and page-down now "snap" to the nearest page, if it's within a
+ few pixels. This avoids annoying behavior when the window is a tiny
+ bit too short or too tall for a page.
+Properly handle overprint in non-isolated transparency groups.
+Missing null check in Gfx::opSetExtGState(). [Thanks to
+ pwd@360TeamSeri0us for the bug report.]
+The DCT decoder doesn't handle a sampling factor of 3 -- check for
+ this and report an error. [Thanks for Agostino Sarubbo of Gentoo
+ for the bug report.]
+Check for images with a Pattern color space, and report an error.
+ [Thanks to TeamSeri0us for the bug report.]
+Check that the Width, Height, and BitsPerComponent are valid in image
+ soft masks. [Thanks to TeamSeri0us for the bug report.]
+Check for zero page width/height in PSOutputDev. [Thanks to
+ TeamSeri0us for the bug report.]
+Check for divide-by-zero in PostScript functions. [Thanks to
+ TeamSeri0us for the bug report.]
+Properly handle overprint in shading pattern strokes.
+The "save image" feature in Xpdf wasn't getting the user-specified
+ page number correctly.
+PostScript doesn't support progressive or non-interleaved DCT (JPEG)
+ streams, so check for those and re-encode them in PS output.
+Splash now caches the most recent scaled image, in case it is
+ immediately reused -- this results in a significant speedup in
+ certain cases.
+Fixed a problem with parsing the TrueType loca table. [Thanks to
+ Pangu Lab for the bug report.]
+Fixed a problem with int overflow on image bounds. [Thanks to
+ Pangu Lab for the bug report.]
+Fixed a problem with TrueType font parsing where there is gibberish in
+ the TrueType table directory. [Thanks to Pangu Lab for the bug
+ report.]
+Fixed a problem with JPX image resolution reduction. [Thanks to Pangu
+ Lab for the bug report.]
+Fixed a problem with non-isolated transparency groups in 1-bit
+ monochrome mode. [Thanks to Pangu Lab for the bug report.]
+Fixed various bugs in FoFi. [Thanks to Pangu Lab for the bug
+ reports.]
+Added a missing bounds check to Annot::setFillColor(). [Thanks to
+ Pangu Lab for the bug report.]
+Added a check on the DCT quant table selector. [Thanks to Pangu Lab
+ for the bug report.]
+Fixed a problem with the Type 3 font cache running out of entries.
+ [Thanks to Pangu Lab for the bug report.]
+Fixed an integer overflow bug in SampledFunction. [Thanks to Pangu
+ Lab for the bug report.]
+Fixed an integer overflow bug in the tiling pattern size. [Thanks to
+ Martin Muskens at Ergosoft for the bug report.]
+Fixed a read-past-end-of-buffer in the Type 1 font parser. [Thanks to
+ Pangu Lab for the bug report.]
+Fixed an int overflow bug in the JBIG2 decoder. [Thanks to
+ TeamSeri0us for the bug report.]
+Added a missing bounds check to GfxPatchMeshShading::parse(). [Thanks
+ to TeamSeri0us for the bug report.]
+JPEG 2000 tile indexes were being computed incorrectly. [Thanks to
+ TeamSeri0us for the bug report.]
+Large sample separation values in a JPX stream were resulting in
+ zero-width/height tiles, which caused problems. [Thanks to Pangu
+ Lab for the bug report.]
+The XFA parser now looks for "ancestor matches" when searching for
+ data to fill a form field.
+Fixed an uninitialized variable in BuiltinFontWidths::getWidth().
+ [Thanks to Martin Muskens at Ergosoft for the bug report.]
+If there are no popupMenuCmd instances, construct a popup menu with
+ basic instructions pointing to 'popupMenuCmd'.
+Ignore color operators in uncolored tiling patterns. [Thanks to
+ Martin Muskens at Ergosoft for the bug report.]
+Pdftotext raw mode output looks for space characters.
+Increased the width of the font name column in the pdffonts output.
+Function objects weren't checking their input/output counts properly.
+ [Thanks to Pangu Lab for the bug report.]
+TextPage::findGaps() wasn't checking the x/y min/max values for int
+ overflow. [Thanks to Taolaw for the bug report.]
+
+4.03 (2021-jan-28)
+------------------
+Implemented selection extension via shift-click, and word/line
+ selection via double/triple click.
+Added default bindings for ctrl-mousewheel-up/down to zoom in/out.
+Added the "-nofonts" option to pdftohtml.
+Added the "simple2" mode to pdftotext.
+Added the "-rot" flag to xpdf, pdftoppm, and pdftopng.
+Added the "-listencodings" flag to pdftotext.
+Added the 'copyLinkTarget' command.
+Added the 'selectionColor' xpdfrc setting.
+Added the 'initialSidebarWidth' xpdfrc setting.
+Added support for @"..." strings in xpdfrc files. This includes using
+ '%' as an escape character, and also the '${DATADIR}' variable.
+Added a help menu item that shows all of the key bindings.
+Reorganized the gradient shading rasterization code to avoid
+ transparency artifacts between triangles/patches.
+Use interpolation rather than subdivision to rasterize Gouraud
+ triangle shadings.
+Allow escaping (via the \x01 character) in command args, so that "xpdf
+ -open foo(123).pdf" works correctly.
+Extended the damaged file repair code to handle PDF files that use
+ xref streams and object streams.
+Modified pdfinfo to print the encryption algorithm used (if any).
+Handle Windows shortcut (.lnk) files in Xpdf and in the command line
+ tools.
+Display an error dialog when trying to copy text from a protected
+ file.
+Updated all four of the CJK language support packages with the latest
+ Adobe data.
+Modified pdftohtml to draw 'invisible' text over non-horizontal words.
+Catalog.pageLabels wasn't being properly initialized to NULL. [Thanks
+ to Dhiraj for the bug report.]
+Added a sanity check for JBIG2 symbol width. [Reported by Marc
+ Schoenefeld.]
+Fixed a bug in the fontconfig calls that was causing Xpdf to crash.
+The Type 3 font cache code wasn't correctly handling the case where a
+ Type 3 char refers to another char in the same T3 font. [Thanks to
+ Pangu Lab for the bug report.]
+Xpdf was crashing if 'view page labels' was enabled without any
+ document open. [Thanks to TeamSeri0us for the bug report.]
+The Type 1C-to-Type 1 font converter wasn't checking for
+ divide-by-zero with rational numbers. [Thanks to TeamSeri0us for
+ the bug report.]
+Fixed a bug in the TrueType font parser - zero-length loca tables
+ weren't handled correctly. [Thanks to TeamSeri0us for the bug
+ report.]
+Fixed an integer overflow bug in the transparency group setup code.
+ [Thanks to TeamSeri0us for the bug report.]
+Pdfinfo with the "-box" option was crashing on zero-page PDF files.
+ [Thanks to TeamSeri0us for the bug report.]
+The code that caches scaled images wasn't checking the rendering
+ intent. [Thanks to Martin Muskens at Ergosoft for the bug report.]
+The JBIG2 decoder now checks for gibberish values in various segment
+ headers, but keeps any data decoded prior to that.
+Fixed two bugs in the FoFi code that handles CFF fonts. [Thanks to
+ dsmic for the bug report.]
+Redesigned the image scaling code, for performance.
+Cleaned up the code that sanity-checks font sizes to avoid trying to
+ allocate too much memory for the font cache.
+Implemented precincts in the JPEG 2000 decoder.
+The page-up/down snapping code was calling TileMap::getPageTopY() with
+ invalid page numbers. [Thanks to dfandrich for the bug report.]
+Added missing error-checking when parsing an annotation appearance
+ stream's bbox.
+Handled UTF-16 sequences in ToUnicode maps.
+Rewrote the code that draws static XFA forms: combined it with the
+ AcroForm code, to match Adobe's behavior. Removed the enableXFA
+ xpdfrc setting.
+Check for indirect references in content streams and report an error.
+Doing overprint preview in tiling patterns requires tracking the
+ overprint mask at each pixel.
+Check for "decompression bombs" in Flate and LZW streams.
+The nextPageNoScroll and prevPageNoScroll commands weren't working
+ correctly in continuous mode.
+Disable stroke adjustment when drawing Type 3 characters.
+The "xpdf -open" flag now constructs an absolute path, so it's not
+ dependent on the current directory when xpdf was started.
+Check for infinite loops in Type 1C charstring subroutines. [Thanks
+ to blbi for the bug report.]
+Fixed an incomplete test for headless CFF files. [Thanks to Chengbin
+ for the bug report.]
+Tweaked the code that computes segment slope in SplashXPath.cc to
+ avoid problems with extremely small coordinate values. [Thanks to
+ Chengbin for the bug report.]
+Check for an invalid segment length in PSOutputDev's PFB parse.
+ [Thanks to Chengbin for the bug report.]
+Check for JPEG 2000 segments that are only allowed in the first
+ tile-part of a tile. [Thanks to Chengbin for the bug report.]
+Check for invalid AcroForm ListBox field top index. [Thanks to
+ Chengbin for the bug report.]
+Check for invalid mesh shading parameters. [Thanks to Chengbin for
+ the bug report.]
diff --git a/Build/source/libs/xpdf/xpdf-src/CMakeLists.txt b/Build/source/libs/xpdf/xpdf-src/CMakeLists.txt
new file mode 100644
index 00000000000..a99cd253231
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/CMakeLists.txt
@@ -0,0 +1,41 @@
+#========================================================================
+#
+# cmake-xpdf.txt
+#
+# CMake script for the Xpdf package.
+#
+# Copyright 2015 Glyph & Cog, LLC
+#
+#========================================================================
+
+cmake_minimum_required(VERSION 2.8.8)
+
+project(xpdf)
+
+include(cmake-config.txt)
+
+add_subdirectory(goo)
+add_subdirectory(fofi)
+add_subdirectory(splash)
+add_subdirectory(xpdf)
+add_subdirectory(xpdf-qt)
+
+if (NOT HAVE_FREETYPE_H)
+ message(WARNING "Couldn't find FreeType -- will not build pdftoppm, pdftopng, pdftohtml, or xpdf.")
+endif ()
+
+if (NOT PNG_FOUND)
+ message(WARNING "Couldn't find libpng -- will not build pdftopng or pdftohtml.")
+endif ()
+
+if (NOT QT4_FOUND AND NOT Qt5Widgets_FOUND)
+ message(WARNING "Couldn't find Qt4 or Qt5 -- will not build xpdf.")
+endif ()
+
+if (NOT MULTITHREADED)
+ message(WARNING "Multithreading is disabled -- will not build xpdf.")
+endif ()
+
+if (NOT USE_EXCEPTIONS)
+ message(WARNING "C++ exceptions are disabled -- will not build xpdf.")
+endif ()
diff --git a/Build/source/libs/xpdf/xpdf-src/COPYING b/Build/source/libs/xpdf/xpdf-src/COPYING
new file mode 100644
index 00000000000..a43ea2126fb
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Build/source/libs/xpdf/xpdf-src/COPYING3 b/Build/source/libs/xpdf/xpdf-src/COPYING3
new file mode 100644
index 00000000000..94a9ed024d3
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/COPYING3
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/Build/source/libs/xpdf/xpdf-src/INSTALL b/Build/source/libs/xpdf/xpdf-src/INSTALL
new file mode 100644
index 00000000000..5a7eb6423f4
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/INSTALL
@@ -0,0 +1,196 @@
+Xpdf
+====
+
+version 4.03
+2021-jan-28
+
+The Xpdf software and documentation are
+copyright 1996-2021 Glyph & Cog, LLC.
+
+Email: xpdf@xpdfreader.com
+WWW: http://www.xpdfreader.com/
+
+
+Compiling xpdf
+--------------
+
+Xpdf is written in C++ (with a little bit of C). It should work with
+any ANSI-compliant C++ and C compilers. The systems and compilers
+it's been tested with are listed on the xpdf web page.
+
+Xpdf requires the Qt toolkit. The non-GUI command line tools do not
+use Qt.
+
+The following notes give specific instructions for compiling on
+different systems.
+
+
+*********************
+*** Linux, OS X ***
+*********************
+
+* Make sure you have the following installed:
+
+ - CMake 2.8.8 or newer
+ - FreeType 2.0.5 or newer
+ - Qt 4.8.x or 5.x (for xpdf only)
+ - libpng (for pdftoppm and pdftohtml)
+ - zlib (for pdftoppm and pdftohtml)
+
+ If Qt isn't found, the GUI viewer (xpdf) won't be built, but the
+ command line tools will still be built.
+
+* Create a build directory. These instructions assume that you create
+ a directory called "build" under the top-level xpdf source
+ directory, but the build directory can be anywhere you like. Create
+ the directory and cd into it:
+
+ cd ..../xpdf-X.YY
+ mkdir build
+ cd build
+
+* Run cmake:
+
+ cmake -DCMAKE_BUILD_TYPE=Release {other options - see below} ..
+
+ If your build directory isn't under the source, use the full path to
+ the source directory:
+
+ cmake -DCMAKE_BUILD_TYPE=Release {other options} ..../xpdf-X.YY
+
+* If cmake doesn't find FreeType, you can set FREETYPE_DIR to point to
+ the FreeType install directory. Add this to the cmake command line:
+
+ -DFREETYPE_DIR=/opt/freetype
+
+ Cmake will look for ${FREETYPE_DIR}/include.
+
+ You can also set the FreeType library location with:
+
+ -DFREETYPE_LIBRARY=/opt/freetype/lib/libfreetype.so
+
+* If cmake doesn't find Qt, make sure qmake is on your executable
+ search path.
+
+* To change the xpdf install directory, use:
+
+ -DCMAKE_INSTALL_PREFIX=/opt/xpdf
+
+ If you're familiar with autoconf, this is similar to the "--prefix"
+ option.
+
+* Other configuration options are:
+
+ -DSPLASH_CMYK=ON
+ Enable CMYK rasterization support.
+
+ -DA4_PAPER=ON
+ Switches the default paper size for PostScript output (xpdf
+ and pdftops) to A4. The default is Letter size.
+
+ -DNO_TEXT_SELECT=ON
+ With this option, xpdf will not copy text. (This is only
+ useful on closed systems where the user can't get at the PDF
+ file directly.)
+
+ -DOPI_SUPPORT=ON
+ Enables support for generation of OPI (Open Prepress
+ Interface) comments with pdftops.
+
+ -DNO_FONTCONFIG=ON
+ Disables use of libfontconfig, which is used to locate system
+ fonts on Linux/Unix systems. The default is to search for the
+ library, and to use it if found.
+
+ -DMULTITHREADED=0
+ Disables multithreading, which also disables building the GUI
+ viewer (xpdf). This does not affect the command line tools.
+ Disabling multithreading should only be necessary if you're
+ building with a compiler other than gcc, clang, or Microsoft
+ Visual Studio.
+
+ -DXPDFWIDGET_PRINTING=OFF
+ Disable printing support.
+
+ -DSYSTEM_XPDFRC="/etc/xpdfrc"
+ Look for a system-wide xpdfrc config file in this directory.
+
+ -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="..."
+ Set additional options to pass to the C and/or C++ compilers.
+
+ -DCMAKE_EXE_LINKER_FLAGS="..."
+ Set additional options to pass to the linker.
+
+ -DCMAKE_INSTALL_BINDIR
+ Set the bin directory, relative to CMAKE_INSTALL_PREFIX
+ (typically "bin").
+
+ -DCMAKE_INSTALL_MANDIR
+ Set the man directory, relative to CMAKE_INSTALL_PREFIX
+ (typically "man" or "share/man").
+
+* Build:
+
+ make
+
+ This will build the executables in the build directory:
+
+ xpdf/xpdf
+ xpdf/pdftops
+ xpdf/pdftotext
+ xpdf/pdftohtml
+ xpdf/pdfinfo
+ xpdf/pdffonts
+ xpdf/pdfdetach
+ xpdf/pdftoppm
+ xpdf/pdftopng
+ xpdf/pdfimages
+ xpdf-qt/xpdf
+
+* If desired, install the binaries and man pages:
+
+ make install
+
+If you want to run a quick test, there is a tiny PDF file included
+with xpdf, as misc/hello.pdf .
+
+
+*****************
+*** Windows ***
+*****************
+
+The Windows build procedure is essentially the same as for Unix. If
+building with cygwin, the procedure is identical to Unix.
+
+If building with Visual Studio:
+
+* Make sure you have CMake, FreeType, libpng, and zlib installed.
+
+* You'll need to set the FREETYPE_DIR variable to point to your
+ FreeType installation, CMAKE_INCLUDE_PATH to point to your libpng
+ includes, CMAKE_LIBRARY_PATH to point to your libpng library, and
+ ZLIB_ROOT to point to your zlib installation.
+
+ cmake -G "NMake Makefiles"
+ -DCMAKE_BUILD_TYPE=Release
+ -DFREETYPE_DIR="c:/software/freetype"
+ -DCMAKE_INCLUDE_PATH="c:/software/libpng"
+ -DCMAKE_LIBRARY_PATH="c:/software/libpng"
+ -DZLIB_ROOT="c:/software/zlib"
+ ..
+
+* Build:
+
+ nmake
diff --git a/Build/source/libs/xpdf/xpdf-src/README b/Build/source/libs/xpdf/xpdf-src/README
new file mode 100644
index 00000000000..e5a114262f0
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/README
@@ -0,0 +1,391 @@
+Xpdf
+====
+
+version 4.03
+2021-jan-28
+
+The Xpdf software and documentation are
+copyright 1996-2021 Glyph & Cog, LLC.
+
+Email: xpdf@xpdfreader.com
+WWW: http://www.xpdfreader.com/
+
+The PDF data structures, operators, and specification are
+documented in ISO 32000-2:2020.
+
+
+What is Xpdf?
+-------------
+
+Xpdf is an open source viewer for Portable Document Format (PDF)
+files. (These are also sometimes also called 'Acrobat' files, from
+the name of Adobe's PDF software.) The Xpdf project also includes a
+PDF text extractor, PDF-to-PostScript converter, and various other
+utilities.
+
+The Xpdf viewer uses the Qt cross-platform GUI toolkit. The other
+command line utilties do not require Qt.
+
+
+License & Distribution
+----------------------
+
+Xpdf is licensed under the GNU General Public License (GPL), version 2
+or 3. This means that you can distribute derivatives of Xpdf under
+any of the following:
+ - GPL v2 only
+ - GPL v3 only
+ - GPL v2 or v3
+
+The Xpdf source package includes the text of both GPL versions:
+COPYING for GPL v2, COPYING3 for GPL v3.
+
+Please note that Xpdf is NOT licensed under "any later version" of the
+GPL, as I have no idea what those versions will look like.
+
+If you are redistributing unmodified copies of Xpdf (or any of the
+Xpdf tools) in binary form, you need to include all of the
+documentation: README, man pages (or help files), COPYING, and
+COPYING3.
+
+If you want to incorporate the Xpdf source code into another program
+(or create a modified version of Xpdf), and you are distributing that
+program, you have two options: release your program under the GPL (v2
+and/or v3), or purchase a commercial Xpdf source license.
+
+If you're interested in commercial licensing, please see the Glyph &
+Cog web site:
+
+ http://www.glyphandcog.com/
+
+
+Compatibility
+-------------
+
+Xpdf uses the Qt toolkit and runs on Linux, Windows, and OS X -- and
+probably other systems that have a Qt port.
+
+The non-GUI command line utilities (pdftops, pdftotext, pdftohtml,
+pdfinfo, pdffonts, pdfdetach, pdftoppm, pdftopng, and pdfimages) run
+on Linux, Windows, and OS X -- and should run on pretty much any
+system with a decent C++ compiler.
+
+If you compile Xpdf for a system not listed on the web page, please
+let me know. If you're willing to make your binary available by ftp
+or on the web, I'll be happy to add a link from the Xpdf web page. I
+have decided not to host any binaries I didn't compile myself (for
+disk space and support reasons).
+
+If you can't get Xpdf to compile on your system, send me email and
+I'll try to help.
+
+
+Getting Xpdf
+------------
+
+The latest version is available from:
+
+ http://www.xpdfreader.com/
+
+Source code and several precompiled executables are available.
+
+Announcements of new versions are posted to comp.text.pdf and emailed
+to a list of people. If you'd like to receive email notification of
+new versions, just let me know.
+
+
+Running Xpdf
+------------
+
+To run xpdf, simply type:
+
+ xpdf file.pdf
+
+To generate a PostScript file, run pdftops:
+
+ pdftops file.pdf
+
+To generate a plain text file, run pdftotext:
+
+ pdftotext file.pdf
+
+There are several additional utilities (which are fully described in
+their man pages):
+
+ pdftohtml -- converts a PDF file to HTML
+ pdfinfo -- dumps a PDF file's Info dictionary (plus some other
+ useful information)
+ pdffonts -- lists the fonts used in a PDF file along with various
+ information for each font
+ pdfdetach -- lists or extracts embedded files (attachments) from a
+ PDF file
+ pdftoppm -- converts a PDF file to a series of PPM/PGM/PBM-format
+ bitmaps
+ pdftopng -- converts a PDF file to a series of PNG image files
+ pdfimages -- extracts the images from a PDF file
+
+Command line options and many other details are described in the man
+pages: xpdf(1), etc.
+
+All of these utilities read an optional configuration file: see the
+xpdfrc(5) man page.
+
+
+Compiling Xpdf
+--------------
+
+See the separate file, INSTALL.
+
+
+Bugs
+----
+
+If you find a bug in Xpdf, i.e., if it prints an error message,
+crashes, or incorrectly displays a document, and you don't see that
+bug listed here, please send me email, with a pointer (URL, ftp site,
+etc.) to the PDF file.
+
+
+Third-Party Libraries
+---------------------
+
+Xpdf uses the following libraries:
+* FreeType [http://www.freetype.org/]
+* libpng [http://www.libpng.com/pub/png/libpng.html] (used by pdftohtml
+ and pdftopng)
+* zlib [http://zlib.net/] (used by pdftohtml)
+
+
+Acknowledgments
+---------------
+
+Thanks to:
+
+* Patrick Voigt for help with the remote server code.
+* Patrick Moreau, Martin P.J. Zinser, and David Mathog for the VMS
+ port.
+* David Boldt and Rick Rodgers for sample man pages.
+* Brendan Miller for the icon idea.
+* Olly Betts for help testing pdftotext.
+* Peter Ganten for the OS/2 port.
+* Michael Richmond for the Win32 port of pdftops and pdftotext and the
+ xpdf/cygwin/XFree86 build instructions.
+* Frank M. Siegert for improvements in the PostScript code.
+* Leo Smiers for the decryption patches.
+* Rainer Menzner for creating t1lib, and for helping me adapt it to
+ xpdf.
+* Pine Tree Systems A/S for funding the OPI and EPS support in
+ pdftops.
+* Easy Software Products for funding several improvements to the
+ PostScript output code.
+* Tom Kacvinsky for help with FreeType and for being my interface to
+ the FreeType team.
+* Theppitak Karoonboonyanan for help with Thai support.
+* Leonard Rosenthol for help and contributions on a bunch of things.
+* Alexandros Diamantidis and Maria Adaloglou for help with Greek
+ support.
+* Lawrence Lai for help with the CJK Unicode maps.
+
+Various people have contributed modifications made for use by the
+pdftex project:
+
+* Han The Thanh
+* Martin Schröder of ArtCom GmbH
+
+
+References
+----------
+
+Adobe Systems Inc., _PostScript Language Reference_, 3rd ed.
+Addison-Wesley, 1999, ISBN 0-201-37922-8.
+[The official PostScript manual.]
+
+Adobe Systems, Inc., _The Type 42 Font Format Specification_,
+Adobe Developer Support Technical Specification #5012. 1998.
+http://partners.adobe.com/asn/developer/pdfs/tn/5012.Type42_Spec.pdf
+[Type 42 is the format used to embed TrueType fonts in PostScript
+files.]
+
+Adobe Systems, Inc., _Adobe CMap and CIDFont Files Specification_,
+Adobe Developer Support Technical Specification #5014. 1995.
+http://www.adobe.com/supportservice/devrelations/PDFS/TN/5014.CIDFont_Spec.pdf
+[CMap file format needed for Japanese and Chinese font support.]
+
+Adobe Systems, Inc., _Adobe-Japan1-4 Character Collection for
+CID-Keyed Fonts_, Adobe Developer Support Technical Note #5078.
+2000.
+http://partners.adobe.com/asn/developer/PDFS/TN/5078.CID_Glyph.pdf
+[The Adobe Japanese character set.]
+
+Adobe Systems, Inc., _Adobe-GB1-4 Character Collection for
+CID-Keyed Fonts_, Adobe Developer Support Technical Note #5079.
+2000.
+http://partners.adobe.com/asn/developer/pdfs/tn/5079.Adobe-GB1-4.pdf
+[The Adobe Chinese GB (simplified) character set.]
+
+Adobe Systems, Inc., _Adobe-CNS1-3 Character Collection for
+CID-Keyed Fonts_, Adobe Developer Support Technical Note #5080.
+2000.
+http://partners.adobe.com/asn/developer/PDFS/TN/5080.CNS_CharColl.pdf
+[The Adobe Chinese CNS (traditional) character set.]
+
+Adobe Systems Inc., _Supporting the DCT Filters in PostScript Level
+2_, Adobe Developer Support Technical Note #5116. 1992.
+http://www.adobe.com/supportservice/devrelations/PDFS/TN/5116.PS2_DCT.PDF
+[Description of the DCTDecode filter parameters.]
+
+Adobe Systems Inc., _Open Prepress Interface (OPI) Specification -
+Version 2.0_, Adobe Developer Support Technical Note #5660. 2000.
+http://partners.adobe.com/asn/developer/PDFS/TN/5660.OPI_2.0.pdf
+
+Adobe Systems Inc., CMap files.
+ftp://ftp.oreilly.com/pub/examples/nutshell/cjkv/adobe/
+[The actual CMap files for the 16-bit CJK encodings.]
+
+Adobe Systems Inc., Unicode glyph lists.
+http://partners.adobe.com/asn/developer/type/unicodegn.html
+http://partners.adobe.com/asn/developer/type/glyphlist.txt
+http://partners.adobe.com/asn/developer/type/corporateuse.txt
+http://partners.adobe.com/asn/developer/type/zapfdingbats.txt
+[Mappings between character names to Unicode.]
+
+Adobe Systems Inc., OpenType Specification v. 1.4.
+http://partners.adobe.com/public/developer/opentype/index_spec.html
+[The OpenType font format spec.]
+
+Aldus Corp., _OPI: Open Prepress Interface Specification 1.3_. 1993.
+http://partners.adobe.com/asn/developer/PDFS/TN/OPI_13.pdf
+
+Anonymous, RC4 source code.
+ftp://ftp.ox.ac.uk/pub/crypto/misc/rc4.tar.gz
+ftp://idea.sec.dsi.unimi.it/pub/crypt/code/rc4.tar.gz
+[This is the algorithm used to encrypt PDF files.]
+
+T. Boutell, et al., "PNG (Portable Network Graphics) Specification,
+Version 1.0". RFC 2083.
+[PDF uses the PNG filter algorithms.]
+
+CCITT, "Information Technology - Digital Compression and Coding of
+Continuous-tone Still Images - Requirements and Guidelines", CCITT
+Recommendation T.81.
+http://www.w3.org/Graphics/JPEG/
+[The official JPEG spec.]
+
+A. Chernov, "Registration of a Cyrillic Character Set". RFC 1489.
+[Documentation for the KOI8-R Cyrillic encoding.]
+
+Roman Czyborra, "The ISO 8859 Alphabet Soup".
+http://czyborra.com/charsets/iso8859.html
+[Documentation on the various ISO 859 encodings.]
+
+L. Peter Deutsch, "ZLIB Compressed Data Format Specification version
+3.3". RFC 1950.
+[Information on the general format used in FlateDecode streams.]
+
+L. Peter Deutsch, "DEFLATE Compressed Data Format Specification
+version 1.3". RFC 1951.
+[The definition of the compression algorithm used in FlateDecode
+streams.]
+
+Morris Dworkin, "Recommendation for Block Cipher Modes of Operation",
+National Institute of Standards, NIST Special Publication 800-38A,
+2001.
+[The cipher block chaining (CBC) mode used with AES in PDF files.]
+
+Federal Information Processing Standards Publication 197 (FIPS PUBS
+197), "Advanced Encryption Standard (AES)", November 26, 2001.
+[AES encryption, used in PDF 1.6.]
+
+Jim Flowers, "X Logical Font Description Conventions", Version 1.5, X
+Consortium Standard, X Version 11, Release 6.1.
+ftp://ftp.x.org/pub/R6.1/xc/doc/hardcopy/XLFD/xlfd.PS.Z
+[The official specification of X font descriptors, including font
+transformation matrices.]
+
+Foley, van Dam, Feiner, and Hughes, _Computer Graphics: Principles and
+Practice_, 2nd ed. Addison-Wesley, 1990, ISBN 0-201-12110-7.
+[Colorspace conversion functions, Bezier spline math.]
+
+Robert L. Hummel, _Programmer's Technical Reference: Data and Fax
+Communications_. Ziff-Davis Press, 1993, ISBN 1-56276-077-7.
+[CCITT Group 3 and 4 fax decoding.]
+
+ISO/IEC, _Information technology -- Lossy/lossless coding of bi-level
+images_. ISO/IEC 14492, First edition (2001-12-15).
+http://webstore.ansi.org/
+[The official JBIG2 standard. The final draft of this spec is
+available from http://www.jpeg.org/jbighomepage.html.]
+
+ISO/IEC, _Information technology -- JPEG 2000 image coding system --
+Part 1: Core coding system_. ISO/IEC 15444-1, First edition
+(2000-12-15).
+http://webstore.ansi.org/
+[The official JPEG 2000 standard. The final committee draft of this
+spec is available from http://www.jpeg.org/JPEG2000.html, but there
+were changes made to the bitstream format between that draft and the
+published spec.]
+
+ISO, International Standard -- Document Management - Portable document
+format - Part 2: PDF 2.0. ISO 32000-2:2020.
+[The specification for PDF version 2.0.]
+
+ITU, "Standardization of Group 3 facsimile terminals for document
+transmission", ITU-T Recommendation T.4, 1999.
+ITU, "Facsimile coding schemes and coding control functions for Group 4
+facsimile apparatus", ITU-T Recommendation T.6, 1993.
+http://www.itu.int/
+[The official Group 3 and 4 fax standards - used by the CCITTFaxDecode
+stream, as well as the JBIG2Decode stream.]
+
+B. Kaliski, "PKCS #5: Password-Based Cryptography Specification,
+Version 2.0". RFC 2898.
+[Defines the padding scheme used with AES encryption in PDF files.]
+
+Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, "Practical
+Fast 1-D DCT Algorithms with 11 Multiplications". IEEE Intl. Conf. on
+Acoustics, Speech & Signal Processing, 1989, 988-991.
+[The fast IDCT algorithm used in the DCTDecode filter.]
+
+Microsoft, _TrueType 1.0 Font Files_, rev. 1.66. 1995.
+http://www.microsoft.com/typography/tt/tt.htm
+[The TrueType font spec (in MS Word format, naturally).]
+
+V. Ostromoukhov, R.D. Hersch, "Stochastic Clustered-Dot Dithering",
+Conf. Color Imaging: Device-Independent Color, Color Hardcopy, and
+Graphic Arts IV, 1999, SPIE Vol. 3648, 496-505.
+http://diwww.epfl.ch/w3lsp/publications/colour/scd.html
+[The stochastic dithering algorithm used in Xpdf.]
+
+P. Peterlin, "ISO 8859-2 (Latin 2) Resources".
+http://sizif.mf.uni-lj.si/linux/cee/iso8859-2.html
+[This is a web page with all sorts of useful Latin-2 character set and
+font information.]
+
+Charles Poynton, "Color FAQ".
+http://www.inforamp.net/~poynton/ColorFAQ.html
+[The mapping from the CIE 1931 (XYZ) color space to RGB.]
+
+R. Rivest, "The MD5 Message-Digest Algorithm". RFC 1321.
+[MD5 is used in PDF document encryption.]
+
+Thai Industrial Standard, "Standard for Thai Character Codes for
+Computers", TIS-620-2533 (1990).
+http://www.nectec.or.th/it-standards/std620/std620.htm
+[The TIS-620 Thai encoding.]
+
+Unicode Consortium, "Unicode Home Page".
+http://www.unicode.org/
+[Online copy of the Unicode spec.]
+
+W3C Recommendation, "PNG (Portable Network Graphics) Specification
+Version 1.0".
+http://www.w3.org/Graphics/PNG/
+[Defines the PNG image predictor.]
+
+Gregory K. Wallace, "The JPEG Still Picture Compression Standard".
+ftp://ftp.uu.net/graphics/jpeg/wallace.ps.gz
+[Good description of the JPEG standard. Also published in CACM, April
+1991, and submitted to IEEE Transactions on Consumer Electronics.]
+
+F. Yergeau, "UTF-8, a transformation format of ISO 10646". RFC 2279.
+[A commonly used Unicode encoding.]
diff --git a/Build/source/libs/xpdf/xpdf-src/aconf2.h b/Build/source/libs/xpdf/xpdf-src/aconf2.h
new file mode 100644
index 00000000000..05d785c274a
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/aconf2.h
@@ -0,0 +1,41 @@
+/*
+ * aconf2.h
+ *
+ * This gets included by aconf.h, and contains miscellaneous global
+ * settings not directly controlled by autoconf. This is a separate
+ * file because otherwise the configure script will munge any
+ * #define/#undef constructs.
+ *
+ * Copyright 2002-2003 Glyph & Cog, LLC
+ */
+
+#ifndef ACONF2_H
+#define ACONF2_H
+
+/*
+ * This controls the use of the interface/implementation pragmas.
+ */
+#if defined(__GNUC__) && !defined(__clang__)
+#define USE_GCC_PRAGMAS
+#endif
+/* There is a bug in the version of gcc which ships with MacOS X 10.2 */
+#if defined(__APPLE__) && defined(__MACH__)
+# include <AvailabilityMacros.h>
+#endif
+#ifdef MAC_OS_X_VERSION_MAX_ALLOWED
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2
+# undef USE_GCC_PRAGMAS
+# endif
+#endif
+
+/*
+ * Speed up Windows compilation. This will only work for the command
+ * line tools.
+ */
+/*
+ *#ifdef _WIN32
+ *# define WIN32_LEAN_AND_MEAN
+ *#endif
+ */
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/cmake-config.txt b/Build/source/libs/xpdf/xpdf-src/cmake-config.txt
new file mode 100644
index 00000000000..e4b3650fcb3
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/cmake-config.txt
@@ -0,0 +1,306 @@
+#========================================================================
+#
+# cmake-config.txt
+#
+# CMake script to do misc cmake config and platform-specific config.
+#
+# Copyright 2015 Glyph & Cog, LLC
+#
+#========================================================================
+
+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)
+
+enable_language(CXX)
+
+#--- set default C/C++ compiler flags for Unix
+# and add a 'Profiling' build mode
+if (CMAKE_GENERATOR STREQUAL "Unix Makefiles")
+ if (CYGWIN)
+ set(PIC_FLAG "")
+ elseif (CMAKE_HOST_SYSTEM_NAME MATCHES "AIX")
+ set(PIC_FLAG "-qPIC")
+ else ()
+ set(PIC_FLAG "-fPIC")
+ endif ()
+ set(CMAKE_C_FLAGS_DEBUG "-g -O -Wall ${PIC_FLAG}")
+ set(CMAKE_CXX_FLAGS_DEBUG "-g -O -Wall ${PIC_FLAG}")
+ set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-g -O -Wall ${PIC_FLAG}")
+ set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall ${PIC_FLAG}")
+ set(CMAKE_CXX_FLAGS_RELEASE "-O3 -Wall ${PIC_FLAG}")
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-O2 -Wall ${PIC_FLAG}")
+ set(CMAKE_C_FLAGS_PROFILING "-g -pg -O -Wall ${PIC_FLAG}"
+ CACHE STRING "C compiler flags for profiling mode")
+ set(CMAKE_CXX_FLAGS_PROFILING "-g -pg -O -Wall ${PIC_FLAG}"
+ CACHE STRING "C++ compiler flags for profiling mode")
+ set(CMAKE_EXE_LINKER_FLAGS_PROFILING "-g -pg -O -Wall ${PIC_FLAG}"
+ CACHE STRING "linker flags for profiling mode")
+ mark_as_advanced(
+ CMAKE_C_FLAGS_PROFILING
+ 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 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
+ CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+ # note: this converts /MD to /MT and /MDd to /MTd
+ string(REGEX REPLACE "/MD" "/MT" ${var} "${${var}}")
+ endforeach ()
+ 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
+ CMAKE_C_FLAGS_RELWITHDEBINFO)
+ set(${var} "${${var}} /D_CRT_SECURE_NO_WARNINGS /wd4290 /wd4996")
+ endforeach ()
+ foreach (var CMAKE_CXX_FLAGS_DEBUG
+ CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_CXX_FLAGS_MINSIZEREL
+ CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+ set(${var} "${${var}} /D_CRT_SECURE_NO_WARNINGS /wd4290 /wd4996 /EHsc")
+ endforeach ()
+endif ()
+
+#--- don't set an rpath
+set(CMAKE_SKIP_RPATH FALSE)
+
+#--- miscellaneous options
+option(A4_PAPER "use A4 (instead of Letter) paper size by default for PostScript output" OFF)
+option(NO_TEXT_SELECT "do not allow text selection" OFF)
+option(OPI_SUPPORT "include support for OPI comments" OFF)
+option(MULTITHREADED "include support for multithreading" ON)
+option(USE_EXCEPTIONS "use C++ exceptions" ON)
+option(USE_FIXEDPOINT "use fixed point (instead of floating point) arithmetic" OFF)
+option(SPLASH_CMYK "include support for CMYK rasterization" OFF)
+option(NO_FONTCONFIG "disable support for libfontconfig" OFF)
+option(SYSTEM_XPDFRC "full path for system-wide xpdfrc file" "")
+if (SYSTEM_XPDFRC)
+ set(SYSTEM_XPDFRC_DEFINE "#define SYSTEM_XPDFRC \"${SYSTEM_XPDFRC}\"")
+else ()
+ set(SYSTEM_XPDFRC_DEFINE "/* #undef SYSTEM_XPDFRC */")
+endif ()
+option(XPDFRC_DATADIR "directory to use for the DATADIR xpdfrc variable" "")
+if (XPDFRC_DATADIR)
+ set(XPDFRC_DATADIR_DEFINE "#define XPDFRC_DATADIR \"${XPDFRC_DATADIR}\"")
+else ()
+ set(XPDFRC_DATADIR_DEFINE "/* #undef SYSTEM_XPDFRC */")
+endif ()
+if (WIN32)
+ option(XPDFWIDGET_PRINTING "include printing support in XpdfWidget" OFF)
+else ()
+ option(XPDFWIDGET_PRINTING "include printing support in XpdfWidget" ON)
+endif ()
+
+#--- check for various library functions
+check_function_exists(mkstemp HAVE_MKSTEMP)
+check_function_exists(mkstemps HAVE_MKSTEMPS)
+check_function_exists(popen HAVE_POPEN)
+check_cxx_source_compiles(
+ "#include <algorithm>
+ bool cmp(const int &x, const int &y) { return x < y; }
+ int a[100];
+ int main() { std::sort(a, a+100, &cmp); }"
+ HAVE_STD_SORT)
+
+#--- set up 64-bit file seeks
+check_function_exists(fseeko HAVE_FSEEKO)
+check_function_exists(fseek64 HAVE_FSEEK64)
+check_function_exists(_fseeki64 HAVE_FSEEKI64)
+
+#--- look for FreeType
+# This could use cmake's FindFreetype, except that it would require
+# a newer version of cmake -- because FreeType changed their include
+# paths around. Older versions of FreeType require two separate
+# -I switches (typically ..../include and ..../include/freetype2);
+# while newer versions require just one (typically ..../include/freetype2)
+find_path(FREETYPE_INCLUDE_DIR_ft2build ft2build.h
+ HINTS
+ ${FREETYPE_DIR}
+ PATHS
+ /usr/local/include
+ /usr/local/X11R6/include
+ /usr/local/X11/include
+ /usr/freeware/include
+ PATH_SUFFIXES freetype2 include/freetype2 include
+)
+find_path(FREETYPE_INCLUDE_DIR_freetype freetype.h
+ HINTS
+ ${FREETYPE_DIR}
+ PATHS
+ /usr/local/include
+ /usr/local/X11R6/include
+ /usr/local/X11/include
+ /usr/freeware/include
+ PATH_SUFFIXES freetype2 include/freetype2 include
+)
+find_path(FREETYPE_INCLUDE_DIR_freetype_freetype freetype/freetype.h
+ HINTS
+ ${FREETYPE_DIR}
+ PATHS
+ /usr/local/include
+ /usr/local/X11R6/include
+ /usr/local/X11/include
+ /usr/freeware/include
+ PATH_SUFFIXES freetype2 include/freetype2 include
+)
+find_library(FREETYPE_LIBRARY
+ NAMES freetype libfreetype freetype2 freetype219
+ HINTS
+ ${FREETYPE_DIR}
+ PATH_SUFFIXES lib64 lib
+ PATHS
+ /usr/local
+ /usr/local/X11R6
+ /usr/local/X11
+ /usr/freeware
+)
+if (FREETYPE_INCLUDE_DIR_ft2build
+ AND FREETYPE_INCLUDE_DIR_freetype
+ AND (FREETYPE_INCLUDE_DIR_ft2build STREQUAL FREETYPE_INCLUDE_DIR_freetype)
+ AND FREETYPE_LIBRARY)
+ set(FREETYPE_INCLUDE_DIRS "${FREETYPE_INCLUDE_DIR_ft2build}")
+ set(HAVE_FREETYPE_H TRUE)
+ message(STATUS "Found FreeType (new-style includes): ${FREETYPE_LIBRARY}")
+elseif (FREETYPE_INCLUDE_DIR_ft2build
+ AND FREETYPE_INCLUDE_DIR_freetype_freetype
+ AND FREETYPE_LIBRARY)
+ set(FREETYPE_INCLUDE_DIRS
+ "${FREETYPE_INCLUDE_DIR_ft2build};${FREETYPE_INCLUDE_DIR_freetype_freetype}")
+ set(HAVE_FREETYPE_H TRUE)
+ message(STATUS "Found FreeType (old-style includes): ${FREETYPE_LIBRARY}")
+else ()
+ message(STATUS "FreeType not found")
+endif ()
+if (HAVE_FREETYPE_H)
+ set(HAVE_SPLASH TRUE)
+endif ()
+
+
+#--- look for zlib
+find_package(ZLIB)
+
+#--- look for libpng
+find_package(PNG)
+
+
+
+
+#--- look for Qt
+find_package(Qt5Widgets QUIET)
+if (Qt5Widgets_FOUND)
+ find_package(Qt5Network)
+ find_package(Qt5PrintSupport)
+else ()
+ find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork)
+endif ()
+if(Qt5Widgets_FOUND)
+ message(STATUS "Qt5 found")
+ if (XPDFWIDGET_PRINTING)
+ set(QT_INCLUDES "${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5PrintSupport_INCLUDE_DIRS}")
+ set(QT_DEFINITIONS "${Qt5Widgets_DEFINITIONS} ${Qt5Network_DEFINITIONS} ${Qt5PrintSupport_DEFINITIONS}")
+ set(QT_LIBRARIES Qt5::Widgets Qt5::Network Qt5::PrintSupport)
+ else ()
+ set(QT_INCLUDES "${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS}")
+ set(QT_DEFINITIONS "${Qt5Widgets_DEFINITIONS} ${Qt5Network_DEFINITIONS}")
+ set(QT_LIBRARIES Qt5::Widgets Qt5::Network)
+ endif ()
+ if (XPDFWIDGET_PRINTING)
+ if (APPLE)
+ set(EXTRA_QT_LIBRARIES "-framework ApplicationServices")
+ elseif (UNIX)
+ find_package(Cups)
+ if (CUPS_FOUND)
+ set(EXTRA_QT_LIBRARIES ${CUPS_LIBRARIES})
+ else ()
+ set (XPDFWIDGET_PRINTING OFF)
+ endif ()
+ else ()
+ set(EXTRA_QT_LIBRARIES "")
+ endif ()
+ endif ()
+ # remove "-fPIE" here because we added "-fPIC" above
+ string(REPLACE "-fPIE" "" QT_CFLAGS "${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
+elseif(QT4_FOUND)
+ message(STATUS "Qt4 found")
+ if (XPDFWIDGET_PRINTING)
+ if (APPLE)
+ set(EXTRA_QT_LIBRARIES "-framework ApplicationServices")
+ elseif (UNIX)
+ find_package(Cups)
+ if (CUPS_FOUND)
+ set(EXTRA_QT_LIBRARIES ${CUPS_LIBRARIES})
+ else ()
+ set (XPDFWIDGET_PRINTING OFF)
+ endif ()
+ else ()
+ set(EXTRA_QT_LIBRARIES "")
+ endif ()
+ endif ()
+else()
+ message(STATUS "No Qt library found")
+endif()
+
+#--- look for libpaper
+find_library(PAPER_LIBRARY
+ NAMES paper libpaper
+ PATH_SUFFIXES lib64 lib
+)
+if (PAPER_LIBRARY)
+ set(HAVE_PAPER_H TRUE)
+else ()
+ set(HAVE_PAPER_H FALSE)
+ set(PAPER_LIBRARY "")
+endif ()
+
+#--- look for fontconfig
+if (NOT NO_FONTCONFIG)
+ find_library(FONTCONFIG_LIBRARY
+ NAMES fontconfig libfontconfig
+ PATH_SUFFIXES lib64 lib
+ )
+ if (FONTCONFIG_LIBRARY)
+ set(HAVE_FONTCONFIG TRUE)
+ message(STATUS "Found fontconfig")
+ else ()
+ set(HAVE_FONTCONFIG FALSE)
+ set(FONTCONFIG_LIBRARY "")
+ endif ()
+else ()
+ set(HAVE_FONTCONFIG FALSE)
+ set(FONTCONFIG_LIBRARY "")
+endif ()
+
+#--- look for pthreads
+if (MULTITHREADED)
+ find_package(Threads)
+else ()
+ set(CMAKE_THREAD_LIBS_INIT "")
+endif ()
+
+#--- create aconf.h
+configure_file("aconf.h.in" "aconf.h")
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.1
new file mode 100644
index 00000000000..7fd3b8a5231
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.1
@@ -0,0 +1,107 @@
+.\" 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.03)
+.SH SYNOPSIS
+.B pdfdetach
+[options]
+.RI [ PDF-file ]
+.SH DESCRIPTION
+.B Pdfdetach
+lists or extracts embedded files (attachments) from a Portable
+Document Format (PDF) file.
+.SH CONFIGURATION FILE
+Pdfdetach reads a configuration file at startup. It first tries to
+find the user's private config file, ~/.xpdfrc. If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdfinfo
+is built). See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Some of the following options can be set with configuration file
+commands. These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.B \-list
+List all of the embedded files in the PDF file. File names are
+converted to the text encoding specified by the "\-enc" switch.
+.TP
+.BI \-save " number"
+Save the specified embedded file. By default, this uses the file name
+associated with the embedded file (as printed by the "\-list" switch);
+the file name can be changed with the "\-o" switch.
+.TP
+.BI \-saveall
+Save all of the embedded files. This uses the file names associated
+with the embedded files (as printed by the "\-list" switch). By
+default, the files are saved in the current directory; this can be
+changed with the "\-o" switch.
+.TP
+.BI \-o " path"
+Set the file name used when saving an embedded file with the "\-save"
+switch, or the directory used by "\-saveall".
+.TP
+.BI \-enc " encoding-name"
+Sets the encoding to use for text output (embedded file names). The
+.I encoding\-name
+must be defined with the unicodeMap command (see
+.BR xpdfrc (5)).
+This defaults to "Latin1" (which is a built-in encoding).
+.RB "[config file: " textEncoding ]
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file. Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.BI \-cfg " config-file"
+Read
+.I config-file
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdfinfo software and documentation are copyright 1996-2021 Glyph &
+Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat
new file mode 100644
index 00000000000..000eb66e5ae
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdfdetach.cat
@@ -0,0 +1,92 @@
+pdfdetach(1) General Commands Manual pdfdetach(1)
+
+
+
+NAME
+ pdfdetach - Portable Document Format (PDF) document embedded file
+ extractor (version 4.03)
+
+SYNOPSIS
+ pdfdetach [options] [PDF-file]
+
+DESCRIPTION
+ Pdfdetach lists or extracts embedded files (attachments) from a Porta-
+ ble Document Format (PDF) file.
+
+CONFIGURATION FILE
+ Pdfdetach reads a configuration file at startup. It first tries to
+ find the user's private config file, ~/.xpdfrc. If that doesn't exist,
+ it looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+ (but this location can be changed when pdfinfo is built). See the
+ xpdfrc(5) man page for details.
+
+OPTIONS
+ Some of the following options can be set with configuration file com-
+ mands. These are listed in square brackets with the description of the
+ corresponding command line option.
+
+ -list List all of the embedded files in the PDF file. File names are
+ converted to the text encoding specified by the "-enc" switch.
+
+ -save number
+ Save the specified embedded file. By default, this uses the
+ file name associated with the embedded file (as printed by the
+ "-list" switch); the file name can be changed with the "-o"
+ switch.
+
+ -saveall
+ Save all of the embedded files. This uses the file names asso-
+ ciated with the embedded files (as printed by the "-list"
+ switch). By default, the files are saved in the current direc-
+ tory; this can be changed with the "-o" switch.
+
+ -o path
+ Set the file name used when saving an embedded file with the
+ "-save" switch, or the directory used by "-saveall".
+
+ -enc encoding-name
+ Sets the encoding to use for text output (embedded file names).
+ The encoding-name must be defined with the unicodeMap command
+ (see xpdfrc(5)). This defaults to "Latin1" (which is a built-in
+ encoding). [config file: textEncoding]
+
+ -opw password
+ Specify the owner password for the PDF file. Providing this
+ will bypass all security restrictions.
+
+ -upw password
+ Specify the user password for the PDF file.
+
+ -cfg config-file
+ Read config-file in place of ~/.xpdfrc or the system-wide config
+ file.
+
+ -v Print copyright and version information.
+
+ -h Print usage information. (-help and --help are equivalent.)
+
+EXIT CODES
+ The Xpdf tools use the following exit codes:
+
+ 0 No error.
+
+ 1 Error opening a PDF file.
+
+ 2 Error opening an output file.
+
+ 3 Error related to PDF permissions.
+
+ 99 Other error.
+
+AUTHOR
+ The pdfinfo software and documentation are copyright 1996-2021 Glyph &
+ Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf-
+ fonts(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
+ http://www.xpdfreader.com/
+
+
+
+ 28 Jan 2021 pdfdetach(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.1
new file mode 100644
index 00000000000..0192bd3247e
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.1
@@ -0,0 +1,164 @@
+.\" Copyright 1999-2021 Glyph & Cog, LLC
+.TH pdffonts 1 "28 Jan 2021"
+.SH NAME
+pdffonts \- Portable Document Format (PDF) font analyzer (version
+4.03)
+.SH SYNOPSIS
+.B pdffonts
+[options]
+.RI [ PDF-file ]
+.SH DESCRIPTION
+.B Pdffonts
+lists the fonts used in a Portable Document Format (PDF) file along
+with various information for each font.
+.PP
+The following information is listed for each font:
+.TP
+.B name
+the font name, exactly as given in the PDF file (potentially including
+a subset prefix)
+.TP
+.B type
+the font type -- see below for details
+.TP
+.B emb
+"yes" if the font is embedded in the PDF file
+.TP
+.B sub
+"yes" if the font is a subset
+.TP
+.B uni
+"yes" if there is an explicit "ToUnicode" map in the PDF file (the
+absence of a ToUnicode map doesn't necessarily mean that the text
+can't be converted to Unicode)
+.TP
+.B prob
+"X" if this font is likely to be problematic when converting text to
+Unicode
+.TP
+.B object ID
+the font dictionary object ID (number and generation)
+.TP
+.B location
+the font location (see the
+.B \-loc
+and
+.B \-locPS
+options).
+.PP
+PDF files can contain the following types of fonts:
+.PP
+.RS
+Type 1
+.RE
+.RS
+Type 1C -- aka Compact Font Format (CFF)
+.RE
+.RS
+Type 1C (OT) -- OpenType with 8-bit CFF data
+.RE
+.RS
+Type 3
+.RE
+.RS
+TrueType
+.RE
+.RS
+TrueType (OT) -- OpenType with 8-bit TrueType data
+.RE
+.RS
+CID Type 0 -- 16-bit font with no specified type
+.RE
+.RS
+CID Type 0C -- 16-bit PostScript CFF font
+.RE
+.RS
+CID Type 0C (OT) -- OpenType with CID CFF data
+.RE
+.RS
+CID TrueType -- 16-bit TrueType font
+.RE
+.RS
+CID TrueType (OT) -- OpenType with CID TrueType data
+.RE
+.SH CONFIGURATION FILE
+Pdffonts reads a configuration file at startup. It first tries to
+find the user's private config file, ~/.xpdfrc. If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdffonts
+is built). See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands. These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to analyze.
+.TP
+.B \-loc
+Shows additional information on the location of the font that will be
+used when the PDF file is rasterized (with xpdf, pdftoppm, etc.).
+.TP
+.B \-locPS
+Shows additional information on the location of the font that will be
+used when the PDF file is converted to PostScript (with pdftops).
+.TP
+.BI \-l " number"
+Specifies the last page to analyze.
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file. Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.BI \-cfg " config-file"
+Read
+.I config-file
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdffonts software and documentation are copyright 1996-2021 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat
new file mode 100644
index 00000000000..59f4dd90ca9
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdffonts.cat
@@ -0,0 +1,118 @@
+pdffonts(1) General Commands Manual pdffonts(1)
+
+
+
+NAME
+ pdffonts - Portable Document Format (PDF) font analyzer (version 4.03)
+
+SYNOPSIS
+ pdffonts [options] [PDF-file]
+
+DESCRIPTION
+ Pdffonts lists the fonts used in a Portable Document Format (PDF) file
+ along with various information for each font.
+
+ The following information is listed for each font:
+
+ name the font name, exactly as given in the PDF file (potentially
+ including a subset prefix)
+
+ type the font type -- see below for details
+
+ emb "yes" if the font is embedded in the PDF file
+
+ sub "yes" if the font is a subset
+
+ uni "yes" if there is an explicit "ToUnicode" map in the PDF file
+ (the absence of a ToUnicode map doesn't necessarily mean that
+ the text can't be converted to Unicode)
+
+ prob "X" if this font is likely to be problematic when converting
+ text to Unicode
+
+ object ID
+ the font dictionary object ID (number and generation)
+
+ location
+ the font location (see the -loc and -locPS options).
+
+ PDF files can contain the following types of fonts:
+
+ Type 1
+ Type 1C -- aka Compact Font Format (CFF)
+ Type 1C (OT) -- OpenType with 8-bit CFF data
+ Type 3
+ TrueType
+ TrueType (OT) -- OpenType with 8-bit TrueType data
+ CID Type 0 -- 16-bit font with no specified type
+ CID Type 0C -- 16-bit PostScript CFF font
+ CID Type 0C (OT) -- OpenType with CID CFF data
+ CID TrueType -- 16-bit TrueType font
+ CID TrueType (OT) -- OpenType with CID TrueType data
+
+CONFIGURATION FILE
+ Pdffonts reads a configuration file at startup. It first tries to find
+ the user's private config file, ~/.xpdfrc. If that doesn't exist, it
+ looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+ (but this location can be changed when pdffonts is built). See the
+ xpdfrc(5) man page for details.
+
+OPTIONS
+ Many of the following options can be set with configuration file com-
+ mands. These are listed in square brackets with the description of the
+ corresponding command line option.
+
+ -f number
+ Specifies the first page to analyze.
+
+ -loc Shows additional information on the location of the font that
+ will be used when the PDF file is rasterized (with xpdf,
+ pdftoppm, etc.).
+
+ -locPS Shows additional information on the location of the font that
+ will be used when the PDF file is converted to PostScript (with
+ pdftops).
+
+ -l number
+ Specifies the last page to analyze.
+
+ -opw password
+ Specify the owner password for the PDF file. Providing this
+ will bypass all security restrictions.
+
+ -upw password
+ Specify the user password for the PDF file.
+
+ -cfg config-file
+ Read config-file in place of ~/.xpdfrc or the system-wide config
+ file.
+
+ -v Print copyright and version information.
+
+ -h Print usage information. (-help and --help are equivalent.)
+
+EXIT CODES
+ The Xpdf tools use the following exit codes:
+
+ 0 No error.
+
+ 1 Error opening a PDF file.
+
+ 2 Error opening an output file.
+
+ 3 Error related to PDF permissions.
+
+ 99 Other error.
+
+AUTHOR
+ The pdffonts software and documentation are copyright 1996-2021 Glyph &
+ Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdfde-
+ tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
+ http://www.xpdfreader.com/
+
+
+
+ 28 Jan 2021 pdffonts(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.1
new file mode 100644
index 00000000000..2d95ac79b66
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.1
@@ -0,0 +1,119 @@
+.\" Copyright 1998-2021 Glyph & Cog, LLC
+.TH pdfimages 1 "28 Jan 2021"
+.SH NAME
+pdfimages \- Portable Document Format (PDF) image extractor
+(version 4.03)
+.SH SYNOPSIS
+.B pdfimages
+[options]
+.I PDF-file image-root
+.SH DESCRIPTION
+.B Pdfimages
+saves images from a Portable Document Format (PDF) file as Portable
+Pixmap (PPM), Portable Graymap (PGM), Portable Bitmap (PBM), or JPEG
+files.
+.PP
+Pdfimages reads the PDF file, scans one or more pages,
+.IR PDF-file ,
+and writes one PPM, PGM, PBM, or JPEG file for each image,
+.IR image-root - nnnn . xxx ,
+where
+.I nnnn
+is the image number and
+.I xxx
+is the image type (.ppm, .pgm, .pbm, .jpg).
+.PP
+NB: pdfimages extracts the raw image data from the PDF file, without
+performing any additional transforms. Any rotation, clipping,
+color inversion, etc. done by the PDF content stream is ignored.
+.SH CONFIGURATION FILE
+Pdfimages reads a configuration file at startup. It first tries to
+find the user's private config file, ~/.xpdfrc. If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdfimages
+is built). See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands. These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to scan.
+.TP
+.BI \-l " number"
+Specifies the last page to scan.
+.TP
+.B \-j
+Normally, all images are written as PBM (for monochrome images), PGM
+(for grayscale images), or PPM (for color images) files. With this
+option, images in DCT format are saved as JPEG files. All non-DCT
+images are saved in PBM/PGM/PPM format as usual. (Inline images are
+always saved in PBM/PGM/PPM format.)
+.TP
+.B \-raw
+Write all images in PDF-native formats. Most of the formats are not
+standard image formats, so this option is primarily useful as input to
+a tool that generates PDF files. (Inline images are always saved in
+PBM/PGM/PPM format.)
+.TP
+.B \-list
+Write a one-line summary to stdout for each image. The summary
+provides the image file name, the page number, the image width and
+height, the horizontal and vertical resolution (DPI) as drawn, the
+color space type, and the number of bits per component (BPC).
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file. Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.RB "[config file: " errQuiet ]
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdfimages software and documentation are copyright 1998-2021 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat
new file mode 100644
index 00000000000..97a0371573b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdfimages.cat
@@ -0,0 +1,98 @@
+pdfimages(1) General Commands Manual pdfimages(1)
+
+
+
+NAME
+ pdfimages - Portable Document Format (PDF) image extractor (version
+ 4.03)
+
+SYNOPSIS
+ pdfimages [options] PDF-file image-root
+
+DESCRIPTION
+ Pdfimages saves images from a Portable Document Format (PDF) file as
+ Portable Pixmap (PPM), Portable Graymap (PGM), Portable Bitmap (PBM),
+ or JPEG files.
+
+ Pdfimages reads the PDF file, scans one or more pages, PDF-file, and
+ writes one PPM, PGM, PBM, or JPEG file for each image, image-root-
+ nnnn.xxx, where nnnn is the image number and xxx is the image type
+ (.ppm, .pgm, .pbm, .jpg).
+
+ NB: pdfimages extracts the raw image data from the PDF file, without
+ performing any additional transforms. Any rotation, clipping, color
+ inversion, etc. done by the PDF content stream is ignored.
+
+CONFIGURATION FILE
+ Pdfimages reads a configuration file at startup. It first tries to
+ find the user's private config file, ~/.xpdfrc. If that doesn't exist,
+ it looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+ (but this location can be changed when pdfimages is built). See the
+ xpdfrc(5) man page for details.
+
+OPTIONS
+ Many of the following options can be set with configuration file com-
+ mands. These are listed in square brackets with the description of the
+ corresponding command line option.
+
+ -f number
+ Specifies the first page to scan.
+
+ -l number
+ Specifies the last page to scan.
+
+ -j Normally, all images are written as PBM (for monochrome images),
+ PGM (for grayscale images), or PPM (for color images) files.
+ With this option, images in DCT format are saved as JPEG files.
+ All non-DCT images are saved in PBM/PGM/PPM format as usual.
+ (Inline images are always saved in PBM/PGM/PPM format.)
+
+ -raw Write all images in PDF-native formats. Most of the formats are
+ not standard image formats, so this option is primarily useful
+ as input to a tool that generates PDF files. (Inline images are
+ always saved in PBM/PGM/PPM format.)
+
+ -list Write a one-line summary to stdout for each image. The summary
+ provides the image file name, the page number, the image width
+ and height, the horizontal and vertical resolution (DPI) as
+ drawn, the color space type, and the number of bits per compo-
+ nent (BPC).
+
+ -opw password
+ Specify the owner password for the PDF file. Providing this
+ will bypass all security restrictions.
+
+ -upw password
+ Specify the user password for the PDF file.
+
+ -q Don't print any messages or errors. [config file: errQuiet]
+
+ -v Print copyright and version information.
+
+ -h Print usage information. (-help and --help are equivalent.)
+
+EXIT CODES
+ The Xpdf tools use the following exit codes:
+
+ 0 No error.
+
+ 1 Error opening a PDF file.
+
+ 2 Error opening an output file.
+
+ 3 Error related to PDF permissions.
+
+ 99 Other error.
+
+AUTHOR
+ The pdfimages software and documentation are copyright 1998-2021 Glyph
+ & Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf-
+ fonts(1), pdfdetach(1), pdftoppm(1), pdftopng(1), xpdfrc(5)
+ http://www.xpdfreader.com/
+
+
+
+ 28 Jan 2021 pdfimages(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.1
new file mode 100644
index 00000000000..f84badc9814
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.1
@@ -0,0 +1,167 @@
+.\" Copyright 1999-2021 Glyph & Cog, LLC
+.TH pdfinfo 1 "28 Jan 2021"
+.SH NAME
+pdfinfo \- Portable Document Format (PDF) document information
+extractor (version 4.03)
+.SH SYNOPSIS
+.B pdfinfo
+[options]
+.RI [ PDF-file ]
+.SH DESCRIPTION
+.B Pdfinfo
+prints the contents of the \'Info' dictionary (plus some other useful
+information) from a Portable Document Format (PDF) file.
+.PP
+The \'Info' dictionary contains the following values:
+.PP
+.RS
+title
+.RE
+.RS
+subject
+.RE
+.RS
+keywords
+.RE
+.RS
+author
+.RE
+.RS
+creator
+.RE
+.RS
+producer
+.RE
+.RS
+creation date
+.RE
+.RS
+modification date
+.RE
+.PP
+In addition, the following information is printed:
+.PP
+.RS
+tagged (yes/no)
+.RE
+.RS
+form (AcroForm / static XFA / dynamic XFA / none)
+.RE
+.RS
+page count
+.RE
+.RS
+encrypted flag (yes/no)
+.RE
+.RS
+print and copy permissions (if encrypted)
+.RE
+.RS
+page size and rotation
+.RE
+.RS
+file size
+.RE
+.RS
+linearized (yes/no)
+.RE
+.RS
+PDF version
+.RE
+.RS
+metadata (only if requested)
+.RE
+.SH CONFIGURATION FILE
+Pdfinfo reads a configuration file at startup. It first tries to find
+the user's private config file, ~/.xpdfrc. If that doesn't exist, it
+looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+(but this location can be changed when pdfinfo is built). See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands. These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to examine. If multiple pages are requested
+using the "\-f" and "\-l" options, the size of each requested page
+(and, optionally, the bounding boxes for each requested page) are
+printed. Otherwise, only page one is examined.
+.TP
+.BI \-l " number"
+Specifies the last page to examine.
+.TP
+.B \-box
+Prints the page box bounding boxes: MediaBox, CropBox, BleedBox,
+TrimBox, and ArtBox.
+.TP
+.B \-meta
+Prints document-level metadata. (This is the "Metadata" stream from
+the PDF file's Catalog object.)
+.TP
+.B \-rawdates
+Prints the raw (undecoded) date strings, directly from the PDF file.
+.TP
+.BI \-enc " encoding-name"
+Sets the encoding to use for text output. The
+.I encoding\-name
+must be defined with the unicodeMap command (see
+.BR xpdfrc (5)).
+This defaults to "Latin1" (which is a built-in encoding).
+.RB "[config file: " textEncoding ]
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file. Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.BI \-cfg " config-file"
+Read
+.I config-file
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdfinfo software and documentation are copyright 1996-2021 Glyph &
+Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat
new file mode 100644
index 00000000000..0478db677c4
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdfinfo.cat
@@ -0,0 +1,117 @@
+pdfinfo(1) General Commands Manual pdfinfo(1)
+
+
+
+NAME
+ pdfinfo - Portable Document Format (PDF) document information extractor
+ (version 4.03)
+
+SYNOPSIS
+ pdfinfo [options] [PDF-file]
+
+DESCRIPTION
+ Pdfinfo prints the contents of the 'Info' dictionary (plus some other
+ useful information) from a Portable Document Format (PDF) file.
+
+ The 'Info' dictionary contains the following values:
+
+ title
+ subject
+ keywords
+ author
+ creator
+ producer
+ creation date
+ modification date
+
+ In addition, the following information is printed:
+
+ tagged (yes/no)
+ form (AcroForm / static XFA / dynamic XFA / none)
+ page count
+ encrypted flag (yes/no)
+ print and copy permissions (if encrypted)
+ page size and rotation
+ file size
+ linearized (yes/no)
+ PDF version
+ metadata (only if requested)
+
+CONFIGURATION FILE
+ Pdfinfo reads a configuration file at startup. It first tries to find
+ the user's private config file, ~/.xpdfrc. If that doesn't exist, it
+ looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+ (but this location can be changed when pdfinfo is built). See the
+ xpdfrc(5) man page for details.
+
+OPTIONS
+ Many of the following options can be set with configuration file com-
+ mands. These are listed in square brackets with the description of the
+ corresponding command line option.
+
+ -f number
+ Specifies the first page to examine. If multiple pages are
+ requested using the "-f" and "-l" options, the size of each
+ requested page (and, optionally, the bounding boxes for each
+ requested page) are printed. Otherwise, only page one is exam-
+ ined.
+
+ -l number
+ Specifies the last page to examine.
+
+ -box Prints the page box bounding boxes: MediaBox, CropBox, BleedBox,
+ TrimBox, and ArtBox.
+
+ -meta Prints document-level metadata. (This is the "Metadata" stream
+ from the PDF file's Catalog object.)
+
+ -rawdates
+ Prints the raw (undecoded) date strings, directly from the PDF
+ file.
+
+ -enc encoding-name
+ Sets the encoding to use for text output. The encoding-name
+ must be defined with the unicodeMap command (see xpdfrc(5)).
+ This defaults to "Latin1" (which is a built-in encoding). [con-
+ fig file: textEncoding]
+
+ -opw password
+ Specify the owner password for the PDF file. Providing this
+ will bypass all security restrictions.
+
+ -upw password
+ Specify the user password for the PDF file.
+
+ -cfg config-file
+ Read config-file in place of ~/.xpdfrc or the system-wide config
+ file.
+
+ -v Print copyright and version information.
+
+ -h Print usage information. (-help and --help are equivalent.)
+
+EXIT CODES
+ The Xpdf tools use the following exit codes:
+
+ 0 No error.
+
+ 1 Error opening a PDF file.
+
+ 2 Error opening an output file.
+
+ 3 Error related to PDF permissions.
+
+ 99 Other error.
+
+AUTHOR
+ The pdfinfo software and documentation are copyright 1996-2021 Glyph &
+ Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdffonts(1), pdfde-
+ tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
+ http://www.xpdfreader.com/
+
+
+
+ 28 Jan 2021 pdfinfo(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.1
new file mode 100644
index 00000000000..4618a7a77a9
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.1
@@ -0,0 +1,131 @@
+.\" Copyright 1997-2021 Glyph & Cog, LLC
+.TH pdftohtml 1 "28 Jan 2021"
+.SH NAME
+pdftohtml \- Portable Document Format (PDF) to HTML converter
+(version 4.03)
+.SH SYNOPSIS
+.B pdftohtml
+[options]
+.I PDF-file
+.I HTML-dir
+.SH DESCRIPTION
+.B Pdftohtml
+converts Portable Document Format (PDF) files to HTML.
+.PP
+Pdftohtml reads the PDF file,
+.IR PDF-file ,
+and places an HTML file for each page, along with auxiliary images
+in the directory,
+.IR HTML-dir .
+The HTML directory will be created; if it already exists, pdftohtml
+will report an error.
+.SH CONFIGURATION FILE
+Pdftohtml reads a configuration file at startup. It first tries to
+find the user's private config file, ~/.xpdfrc. If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdftohtml
+is built). See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands. These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to convert.
+.TP
+.BI \-l " number"
+Specifies the last page to convert.
+.TP
+.BI \-z " number"
+Specifies the initial zoom level. The default is 1.0, which means
+72dpi, i.e., 1 point in the PDF file will be 1 pixel in the HTML.
+Using \'-z 1.5', for example, will make the initial view 50% larger.
+.TP
+.BI \-r " number"
+Specifies the resolution, in DPI, for background images. This
+controls the pixel size of the background image files. The initial
+zoom level is controlled by the \'-z' option. Specifying a larger
+\'-r' value will allow the viewer to zoom in farther without upscaling
+artifacts in the background.
+.TP
+.B \-nofonts
+Disable extraction of embedded fonts. By default, pdftohtml extracts
+TrueType and OpenType fonts. Disabling extraction can work around
+problems with buggy fonts.
+.TP
+.B \-skipinvisible
+Don't draw invisible text. By default, invisible text (commonly used
+in OCR'ed PDF files) is drawn as transparent (alpha=0) HTML text.
+This option tells pdftohtml to discard invisible text entirely.
+.TP
+.B \-allinvisible
+Treat all text as invisible. By default, regular (non-invisible) text
+is not drawn in the background image, and is instead drawn with HTML
+on top of the image. This option tells pdftohtml to include the
+regular text in the background image, and then draw it as transparent
+(alpha=0) HTML text.
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file. Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.RB "[config file: " errQuiet ]
+.TP
+.BI \-cfg " config-file"
+Read
+.I config-file
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH BUGS
+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.
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdftohtml software and documentation are copyright 1996-2021 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat
new file mode 100644
index 00000000000..3eea2e32ec1
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftohtml.cat
@@ -0,0 +1,115 @@
+pdftohtml(1) General Commands Manual pdftohtml(1)
+
+
+
+NAME
+ pdftohtml - Portable Document Format (PDF) to HTML converter (version
+ 4.03)
+
+SYNOPSIS
+ pdftohtml [options] PDF-file HTML-dir
+
+DESCRIPTION
+ Pdftohtml converts Portable Document Format (PDF) files to HTML.
+
+ Pdftohtml reads the PDF file, PDF-file, and places an HTML file for
+ each page, along with auxiliary images in the directory, HTML-dir. The
+ HTML directory will be created; if it already exists, pdftohtml will
+ report an error.
+
+CONFIGURATION FILE
+ Pdftohtml reads a configuration file at startup. It first tries to
+ find the user's private config file, ~/.xpdfrc. If that doesn't exist,
+ it looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+ (but this location can be changed when pdftohtml is built). See the
+ xpdfrc(5) man page for details.
+
+OPTIONS
+ Many of the following options can be set with configuration file com-
+ mands. These are listed in square brackets with the description of the
+ corresponding command line option.
+
+ -f number
+ Specifies the first page to convert.
+
+ -l number
+ Specifies the last page to convert.
+
+ -z number
+ Specifies the initial zoom level. The default is 1.0, which
+ means 72dpi, i.e., 1 point in the PDF file will be 1 pixel in
+ the HTML. Using '-z 1.5', for example, will make the initial
+ view 50% larger.
+
+ -r number
+ Specifies the resolution, in DPI, for background images. This
+ controls the pixel size of the background image files. The ini-
+ tial zoom level is controlled by the '-z' option. Specifying a
+ 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
+ text. This option tells pdftohtml to discard invisible text
+ entirely.
+
+ -allinvisible
+ Treat all text as invisible. By default, regular (non-invisi-
+ ble) text is not drawn in the background image, and is instead
+ drawn with HTML on top of the image. This option tells pdfto-
+ html to include the regular text in the background image, and
+ then draw it as transparent (alpha=0) HTML text.
+
+ -opw password
+ Specify the owner password for the PDF file. Providing this
+ will bypass all security restrictions.
+
+ -upw password
+ Specify the user password for the PDF file.
+
+ -q Don't print any messages or errors. [config file: errQuiet]
+
+ -cfg config-file
+ Read config-file in place of ~/.xpdfrc or the system-wide config
+ file.
+
+ -v Print copyright and version information.
+
+ -h Print usage information. (-help and --help are equivalent.)
+
+BUGS
+ 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.
+
+EXIT CODES
+ The Xpdf tools use the following exit codes:
+
+ 0 No error.
+
+ 1 Error opening a PDF file.
+
+ 2 Error opening an output file.
+
+ 3 Error related to PDF permissions.
+
+ 99 Other error.
+
+AUTHOR
+ The pdftohtml software and documentation are copyright 1996-2021 Glyph
+ & Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftotext(1), pdfinfo(1), pdffonts(1), pdfde-
+ tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
+ http://www.xpdfreader.com/
+
+
+
+ 28 Jan 2021 pdftohtml(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.1
new file mode 100644
index 00000000000..bd8f517358f
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.1
@@ -0,0 +1,127 @@
+.\" 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.03)
+.SH SYNOPSIS
+.B pdftopng
+[options]
+.I PDF-file PNG-root
+.SH DESCRIPTION
+.B Pdftopng
+converts Portable Document Format (PDF) files to color, grayscale, or
+monochrome image files in Portable Network Graphics (PNG) format.
+.PP
+Pdftopng reads the PDF file,
+.IR PDF-file ,
+and writes one PNG file for each page,
+.IR PNG-root - nnnnnn .png,
+where
+.I nnnnnn
+is the page number.
+If
+.I PNG-root
+is \'-', the image is sent to stdout (this is probably only useful
+when converting a single page).
+.SH CONFIGURATION FILE
+Pdftopng reads a configuration file at startup. It first tries to
+find the user's private config file, ~/.xpdfrc. If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdftopng
+is built). See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands. These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to convert.
+.TP
+.BI \-l " number"
+Specifies the last page to convert.
+.TP
+.BI \-r " number"
+Specifies the resolution, in DPI. The default is 150 DPI.
+.TP
+.B \-mono
+Generate a monochrome image (instead of a color image).
+.TP
+.B \-gray
+Generate a grayscale image (instead of a color image).
+.TP
+.B \-alpha
+Generate an alpha channel in the PNG file. This is only useful with
+PDF files that have been constructed with a transparent background.
+The \-alpha flag cannot be used with \-mono.
+.TP
+.BI \-rot " angle"
+Rotate pages by 0 (the default), 90, 180, or 270 degrees.
+.TP
+.BI \-freetype " yes | no"
+Enable or disable FreeType (a TrueType / Type 1 font rasterizer).
+This defaults to "yes".
+.RB "[config file: " enableFreeType ]
+.TP
+.BI \-aa " yes | no"
+Enable or disable font anti-aliasing. This defaults to "yes".
+.RB "[config file: " antialias ]
+.TP
+.BI \-aaVector " yes | no"
+Enable or disable vector anti-aliasing. This defaults to "yes".
+.RB "[config file: " vectorAntialias ]
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file. Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.RB "[config file: " errQuiet ]
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdftopng software and documentation are copyright 1996-2021 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat
new file mode 100644
index 00000000000..7e17c504b4b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftopng.cat
@@ -0,0 +1,103 @@
+pdftopng(1) General Commands Manual pdftopng(1)
+
+
+
+NAME
+ pdftopng - Portable Document Format (PDF) to Portable Network Graphics
+ (PNG) converter (version 4.03)
+
+SYNOPSIS
+ pdftopng [options] PDF-file PNG-root
+
+DESCRIPTION
+ Pdftopng converts Portable Document Format (PDF) files to color,
+ grayscale, or monochrome image files in Portable Network Graphics (PNG)
+ format.
+
+ Pdftopng reads the PDF file, PDF-file, and writes one PNG file for each
+ page, PNG-root-nnnnnn.png, where nnnnnn is the page number. If PNG-
+ root is '-', the image is sent to stdout (this is probably only useful
+ when converting a single page).
+
+CONFIGURATION FILE
+ Pdftopng reads a configuration file at startup. It first tries to find
+ the user's private config file, ~/.xpdfrc. If that doesn't exist, it
+ looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+ (but this location can be changed when pdftopng is built). See the
+ xpdfrc(5) man page for details.
+
+OPTIONS
+ Many of the following options can be set with configuration file com-
+ mands. These are listed in square brackets with the description of the
+ corresponding command line option.
+
+ -f number
+ Specifies the first page to convert.
+
+ -l number
+ Specifies the last page to convert.
+
+ -r number
+ Specifies the resolution, in DPI. The default is 150 DPI.
+
+ -mono Generate a monochrome image (instead of a color image).
+
+ -gray Generate a grayscale image (instead of a color image).
+
+ -alpha Generate an alpha channel in the PNG file. This is only useful
+ with PDF files that have been constructed with a transparent
+ background. The -alpha flag cannot be used with -mono.
+
+ -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]
+
+ -aa yes | no
+ Enable or disable font anti-aliasing. This defaults to "yes".
+ [config file: antialias]
+
+ -aaVector yes | no
+ Enable or disable vector anti-aliasing. This defaults to "yes".
+ [config file: vectorAntialias]
+
+ -opw password
+ Specify the owner password for the PDF file. Providing this
+ will bypass all security restrictions.
+
+ -upw password
+ Specify the user password for the PDF file.
+
+ -q Don't print any messages or errors. [config file: errQuiet]
+
+ -v Print copyright and version information.
+
+ -h Print usage information. (-help and --help are equivalent.)
+
+EXIT CODES
+ The Xpdf tools use the following exit codes:
+
+ 0 No error.
+
+ 1 Error opening a PDF file.
+
+ 2 Error opening an output file.
+
+ 3 Error related to PDF permissions.
+
+ 99 Other error.
+
+AUTHOR
+ The pdftopng software and documentation are copyright 1996-2021 Glyph &
+ Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf-
+ fonts(1), pdfdetach(1), pdftoppm(1), pdfimages(1), xpdfrc(5)
+ http://www.xpdfreader.com/
+
+
+
+ 28 Jan 2021 pdftopng(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.1
new file mode 100644
index 00000000000..23b5b06980c
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.1
@@ -0,0 +1,127 @@
+.\" 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.03)
+.SH SYNOPSIS
+.B pdftoppm
+[options]
+.I PDF-file PPM-root
+.SH DESCRIPTION
+.B Pdftoppm
+converts Portable Document Format (PDF) files to color image files in
+Portable Pixmap (PPM) format, grayscale image files in Portable
+Graymap (PGM) format, or monochrome image files in Portable Bitmap
+(PBM) format.
+.PP
+Pdftoppm reads the PDF file,
+.IR PDF-file ,
+and writes one PPM file for each page,
+.IR PPM-root - nnnnnn .ppm,
+where
+.I nnnnnn
+is the page number.
+If
+.I PPM-root
+is \'-', the image is sent to stdout (this is probably only useful
+when converting a single page).
+.SH CONFIGURATION FILE
+Pdftoppm reads a configuration file at startup. It first tries to
+find the user's private config file, ~/.xpdfrc. If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdftoppm
+is built). See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands. These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to convert.
+.TP
+.BI \-l " number"
+Specifies the last page to convert.
+.TP
+.BI \-r " number"
+Specifies the resolution, in DPI. The default is 150 DPI.
+.TP
+.B \-mono
+Generate a monochrome PBM file (instead of an RGB PPM file).
+.TP
+.B \-gray
+Generate a grayscale PGM file (instead of an RGB PPM file).
+.TP
+.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".
+.RB "[config file: " enableFreeType ]
+.TP
+.BI \-aa " yes | no"
+Enable or disable font anti-aliasing. This defaults to "yes".
+.RB "[config file: " antialias ]
+.TP
+.BI \-aaVector " yes | no"
+Enable or disable vector anti-aliasing. This defaults to "yes".
+.RB "[config file: " vectorAntialias ]
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file. Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.RB "[config file: " errQuiet ]
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdftoppm software and documentation are copyright 1996-2021 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat
new file mode 100644
index 00000000000..9621be3c3b0
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftoppm.cat
@@ -0,0 +1,102 @@
+pdftoppm(1) General Commands Manual pdftoppm(1)
+
+
+
+NAME
+ pdftoppm - Portable Document Format (PDF) to Portable Pixmap (PPM) con-
+ verter (version 4.03)
+
+SYNOPSIS
+ pdftoppm [options] PDF-file PPM-root
+
+DESCRIPTION
+ Pdftoppm converts Portable Document Format (PDF) files to color image
+ files in Portable Pixmap (PPM) format, grayscale image files in Porta-
+ ble Graymap (PGM) format, or monochrome image files in Portable Bitmap
+ (PBM) format.
+
+ Pdftoppm reads the PDF file, PDF-file, and writes one PPM file for each
+ page, PPM-root-nnnnnn.ppm, where nnnnnn is the page number. If PPM-
+ root is '-', the image is sent to stdout (this is probably only useful
+ when converting a single page).
+
+CONFIGURATION FILE
+ Pdftoppm reads a configuration file at startup. It first tries to find
+ the user's private config file, ~/.xpdfrc. If that doesn't exist, it
+ looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+ (but this location can be changed when pdftoppm is built). See the
+ xpdfrc(5) man page for details.
+
+OPTIONS
+ Many of the following options can be set with configuration file com-
+ mands. These are listed in square brackets with the description of the
+ corresponding command line option.
+
+ -f number
+ Specifies the first page to convert.
+
+ -l number
+ Specifies the last page to convert.
+
+ -r number
+ Specifies the resolution, in DPI. The default is 150 DPI.
+
+ -mono Generate a monochrome PBM file (instead of an RGB PPM file).
+
+ -gray Generate a grayscale PGM file (instead of an RGB PPM file).
+
+ -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]
+
+ -aa yes | no
+ Enable or disable font anti-aliasing. This defaults to "yes".
+ [config file: antialias]
+
+ -aaVector yes | no
+ Enable or disable vector anti-aliasing. This defaults to "yes".
+ [config file: vectorAntialias]
+
+ -opw password
+ Specify the owner password for the PDF file. Providing this
+ will bypass all security restrictions.
+
+ -upw password
+ Specify the user password for the PDF file.
+
+ -q Don't print any messages or errors. [config file: errQuiet]
+
+ -v Print copyright and version information.
+
+ -h Print usage information. (-help and --help are equivalent.)
+
+EXIT CODES
+ The Xpdf tools use the following exit codes:
+
+ 0 No error.
+
+ 1 Error opening a PDF file.
+
+ 2 Error opening an output file.
+
+ 3 Error related to PDF permissions.
+
+ 99 Other error.
+
+AUTHOR
+ The pdftoppm software and documentation are copyright 1996-2021 Glyph &
+ Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdf-
+ fonts(1), pdfdetach(1), pdftopng(1), pdfimages(1), xpdfrc(5)
+ http://www.xpdfreader.com/
+
+
+
+ 28 Jan 2021 pdftoppm(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftops.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdftops.1
new file mode 100644
index 00000000000..e23425d5bad
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftops.1
@@ -0,0 +1,253 @@
+.\" Copyright 1996-2021 Glyph & Cog, LLC
+.TH pdftops 1 "28 Jan 2021"
+.SH NAME
+pdftops \- Portable Document Format (PDF) to PostScript converter
+(version 4.03)
+.SH SYNOPSIS
+.B pdftops
+[options]
+.RI [ PDF-file
+.RI [ PS-file ]]
+.SH DESCRIPTION
+.B Pdftops
+converts Portable Document Format (PDF) files to PostScript so they
+can be printed.
+.PP
+Pdftops reads the PDF file,
+.IR PDF-file ,
+and writes a PostScript file,
+.IR PS-file .
+If
+.I PS-file
+is not specified, pdftops converts
+.I file.pdf
+to
+.I file.ps
+(or
+.I file.eps
+with the \-eps option). If
+.I PS-file
+is \'-', the PostScript is sent to stdout.
+.SH CONFIGURATION FILE
+Pdftops reads a configuration file at startup. It first tries to find
+the user's private config file, ~/.xpdfrc. If that doesn't exist, it
+looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+(but this location can be changed when pdftops is built). See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands. These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to print.
+.TP
+.BI \-l " number"
+Specifies the last page to print.
+.TP
+.B \-level1
+Generate Level 1 PostScript. The resulting PostScript files will be
+significantly larger (if they contain images), but will print on Level
+1 printers. This also converts all images to black and white. No
+more than one of the PostScript level options (\-level1, \-level1sep,
+\-level2, \-level2sep, \-level3, \-level3sep) may be given.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level1sep
+Generate Level 1 separable PostScript. All colors are converted to
+CMYK. Images are written with separate stream data for the four
+components.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level2
+Generate Level 2 PostScript. Level 2 supports color images and image
+compression. This is the default setting.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level2gray
+Generate grayscale Level 2 PostScript. All colors, including images,
+are converted to grayscale.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level2sep
+Generate Level 2 separable PostScript. All colors are converted to
+CMYK. The PostScript separation convention operators are used to
+handle custom (spot) colors.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level3
+Generate Level 3 PostScript. This enables all Level 2 features plus
+CID font embedding and masked image generation.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level3gray
+Generate grayscale Level 3 PostScript. All colors, including images,
+are converted to grayscale.
+.RB "[config file: " psLevel ]
+.TP
+.B \-level3sep
+Generate Level 3 separable PostScript. The separation handling is the
+same as for \-level2sep.
+.RB "[config file: " psLevel ]
+.TP
+.B \-eps
+Generate an Encapsulated PostScript (EPS) file. An EPS file contains
+a single image, so if you use this option with a multi-page PDF file,
+you must use \-f and \-l to specify a single page. No more than one of
+the mode options (\-eps, \-form) may be given.
+.TP
+.B \-form
+Generate a PostScript form which can be imported by software that
+understands forms. A form contains a single page, so if you use this
+option with a multi-page PDF file, you must use \-f and \-l to specify a
+single page. The \-level1 option cannot be used with \-form.
+.TP
+.B \-opi
+Generate OPI comments for all images and forms which have OPI
+information. (This option is only available if pdftops was compiled
+with OPI support.)
+.RB "[config file: " psOPI ]
+.TP
+.B \-noembt1
+By default, any Type 1 fonts which are embedded in the PDF file are
+copied into the PostScript file. This option causes pdftops to
+substitute base fonts instead. Embedded fonts make PostScript files
+larger, but may be necessary for readable output.
+.RB "[config file: " psEmbedType1Fonts ]
+.TP
+.B \-noembtt
+By default, any TrueType fonts which are embedded in the PDF file are
+copied into the PostScript file. This option causes pdftops to
+substitute base fonts instead. Embedded fonts make PostScript files
+larger, but may be necessary for readable output. Also, some
+PostScript interpreters do not have TrueType rasterizers.
+.RB "[config file: " psEmbedTrueTypeFonts ]
+.TP
+.B \-noembcidps
+By default, any CID PostScript fonts which are embedded in the PDF
+file are copied into the PostScript file. This option disables that
+embedding. No attempt is made to substitute for non-embedded CID
+PostScript fonts.
+.RB "[config file: " psEmbedCIDPostScriptFonts ]
+.TP
+.B \-noembcidtt
+By default, any CID TrueType fonts which are embedded in the PDF file
+are copied into the PostScript file. This option disables that
+embedding. No attempt is made to substitute for non-embedded CID
+TrueType fonts.
+.RB "[config file: " psEmbedCIDTrueTypeFonts ]
+.TP
+.B \-preload
+Convert PDF forms to PS procedures, and preload image data. This uses
+more memory in the PostScript interpreter, but generates significantly
+smaller PS files in situations where, e.g., the same image is drawn on
+every page of a long document.
+.TP
+.BI \-paper " size"
+Set the paper size to one of "letter", "legal", "A4", or "A3". This
+can also be set to "match", which will set the paper size to match the
+size specified in the PDF file.
+.RB "[config file: " psPaperSize ]
+.TP
+.BI \-paperw " size"
+Set the paper width, in points.
+.RB "[config file: " psPaperSize ]
+.TP
+.BI \-paperh " size"
+Set the paper height, in points.
+.RB "[config file: " psPaperSize ]
+.TP
+.B \-nocrop
+By default, output is cropped to the CropBox specified in the PDF
+file. This option disables cropping.
+.RB "[config file: " psCrop ]
+.TP
+.B \-expand
+Expand PDF pages smaller than the paper to fill the paper. By
+default, these pages are not scaled.
+.RB "[config file: " psExpandSmaller ]
+.TP
+.B \-noshrink
+Don't scale PDF pages which are larger than the paper. By default,
+pages larger than the paper are shrunk to fit.
+.RB "[config file: " psShrinkLarger ]
+.TP
+.B \-nocenter
+By default, PDF pages smaller than the paper (after any scaling) are
+centered on the paper. This option causes them to be aligned to the
+lower-left corner of the paper instead.
+.RB "[config file: " psCenter ]
+.TP
+.B \-pagecrop
+Treat the CropBox as the PDF page size. By default, the MediaBox is
+used as the page size.
+.RB "[config file: " psUseCropBoxAsPage ]
+.TP
+.B \-userunit
+Honor the UserUnit settings on PDF pages when computing page/paper
+size. By default, pdftops ignores UserUnit.
+.TP
+.B \-duplex
+Set the Duplex pagedevice entry in the PostScript file. This tells
+duplex-capable printers to enable duplexing.
+.RB "[config file: " psDuplex ]
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file. Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.RB "[config file: " errQuiet ]
+.TP
+.BI \-cfg " config-file"
+Read
+.I config-file
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdftops software and documentation are copyright 1996-2021 Glyph &
+Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat
new file mode 100644
index 00000000000..c985d686fea
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftops.cat
@@ -0,0 +1,214 @@
+pdftops(1) General Commands Manual pdftops(1)
+
+
+
+NAME
+ pdftops - Portable Document Format (PDF) to PostScript converter (ver-
+ sion 4.03)
+
+SYNOPSIS
+ pdftops [options] [PDF-file [PS-file]]
+
+DESCRIPTION
+ Pdftops converts Portable Document Format (PDF) files to PostScript so
+ they can be printed.
+
+ Pdftops reads the PDF file, PDF-file, and writes a PostScript file, PS-
+ file. If PS-file is not specified, pdftops converts file.pdf to
+ file.ps (or file.eps with the -eps option). If PS-file is '-', the
+ PostScript is sent to stdout.
+
+CONFIGURATION FILE
+ Pdftops reads a configuration file at startup. It first tries to find
+ the user's private config file, ~/.xpdfrc. If that doesn't exist, it
+ looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+ (but this location can be changed when pdftops is built). See the
+ xpdfrc(5) man page for details.
+
+OPTIONS
+ Many of the following options can be set with configuration file com-
+ mands. These are listed in square brackets with the description of the
+ corresponding command line option.
+
+ -f number
+ Specifies the first page to print.
+
+ -l number
+ Specifies the last page to print.
+
+ -level1
+ Generate Level 1 PostScript. The resulting PostScript files
+ will be significantly larger (if they contain images), but will
+ print on Level 1 printers. This also converts all images to
+ black and white. No more than one of the PostScript level
+ options (-level1, -level1sep, -level2, -level2sep, -level3,
+ -level3sep) may be given. [config file: psLevel]
+
+ -level1sep
+ Generate Level 1 separable PostScript. All colors are converted
+ to CMYK. Images are written with separate stream data for the
+ four components. [config file: psLevel]
+
+ -level2
+ Generate Level 2 PostScript. Level 2 supports color images and
+ image compression. This is the default setting. [config file:
+ psLevel]
+
+ -level2gray
+ Generate grayscale Level 2 PostScript. All colors, including
+ images, are converted to grayscale. [config file: psLevel]
+
+ -level2sep
+ Generate Level 2 separable PostScript. All colors are converted
+ to CMYK. The PostScript separation convention operators are
+ used to handle custom (spot) colors. [config file: psLevel]
+
+ -level3
+ Generate Level 3 PostScript. This enables all Level 2 features
+ plus CID font embedding and masked image generation. [config
+ file: psLevel]
+
+ -level3gray
+ Generate grayscale Level 3 PostScript. All colors, including
+ images, are converted to grayscale. [config file: psLevel]
+
+ -level3sep
+ Generate Level 3 separable PostScript. The separation handling
+ 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-
+ page PDF file, you must use -f and -l to specify a single page.
+ No more than one of the mode options (-eps, -form) may be given.
+
+ -form Generate a PostScript form which can be imported by software
+ that understands forms. A form contains a single page, so if
+ you use this option with a multi-page PDF file, you must use -f
+ and -l to specify a single page. The -level1 option cannot be
+ used with -form.
+
+ -opi Generate OPI comments for all images and forms which have OPI
+ information. (This option is only available if pdftops was com-
+ piled with OPI support.) [config file: psOPI]
+
+ -noembt1
+ By default, any Type 1 fonts which are embedded in the PDF file
+ are copied into the PostScript file. This option causes pdftops
+ to substitute base fonts instead. Embedded fonts make Post-
+ Script files larger, but may be necessary for readable output.
+ [config file: psEmbedType1Fonts]
+
+ -noembtt
+ By default, any TrueType fonts which are embedded in the PDF
+ file are copied into the PostScript file. This option causes
+ pdftops to substitute base fonts instead. Embedded fonts make
+ PostScript files larger, but may be necessary for readable out-
+ put. Also, some PostScript interpreters do not have TrueType
+ rasterizers. [config file: psEmbedTrueTypeFonts]
+
+ -noembcidps
+ By default, any CID PostScript fonts which are embedded in the
+ PDF file are copied into the PostScript file. This option dis-
+ ables that embedding. No attempt is made to substitute for non-
+ embedded CID PostScript fonts. [config file: psEmbedCID-
+ PostScriptFonts]
+
+ -noembcidtt
+ By default, any CID TrueType fonts which are embedded in the PDF
+ file are copied into the PostScript file. This option disables
+ that embedding. No attempt is made to substitute for non-embed-
+ ded CID TrueType fonts. [config file: psEmbedCIDTrueTypeFonts]
+
+ -preload
+ Convert PDF forms to PS procedures, and preload image data.
+ This uses more memory in the PostScript interpreter, but gener-
+ ates significantly smaller PS files in situations where, e.g.,
+ the same image is drawn on every page of a long document.
+
+ -paper size
+ Set the paper size to one of "letter", "legal", "A4", or "A3".
+ This can also be set to "match", which will set the paper size
+ to match the size specified in the PDF file. [config file:
+ psPaperSize]
+
+ -paperw size
+ Set the paper width, in points. [config file: psPaperSize]
+
+ -paperh size
+ Set the paper height, in points. [config file: psPaperSize]
+
+ -nocrop
+ By default, output is cropped to the CropBox specified in the
+ PDF file. This option disables cropping. [config file: psCrop]
+
+ -expand
+ Expand PDF pages smaller than the paper to fill the paper. By
+ default, these pages are not scaled. [config file: psExpandS-
+ maller]
+
+ -noshrink
+ Don't scale PDF pages which are larger than the paper. By
+ default, pages larger than the paper are shrunk to fit. [config
+ file: psShrinkLarger]
+
+ -nocenter
+ By default, PDF pages smaller than the paper (after any scaling)
+ are centered on the paper. This option causes them to be
+ aligned to the lower-left corner of the paper instead. [config
+ file: psCenter]
+
+ -pagecrop
+ Treat the CropBox as the PDF page size. By default, the Media-
+ Box is used as the page size. [config file: psUseCropBoxAsPage]
+
+ -userunit
+ Honor the UserUnit settings on PDF pages when computing
+ page/paper size. By default, pdftops ignores UserUnit.
+
+ -duplex
+ Set the Duplex pagedevice entry in the PostScript file. This
+ tells duplex-capable printers to enable duplexing. [config
+ file: psDuplex]
+
+ -opw password
+ Specify the owner password for the PDF file. Providing this
+ will bypass all security restrictions.
+
+ -upw password
+ Specify the user password for the PDF file.
+
+ -q Don't print any messages or errors. [config file: errQuiet]
+
+ -cfg config-file
+ Read config-file in place of ~/.xpdfrc or the system-wide config
+ file.
+
+ -v Print copyright and version information.
+
+ -h Print usage information. (-help and --help are equivalent.)
+
+EXIT CODES
+ The Xpdf tools use the following exit codes:
+
+ 0 No error.
+
+ 1 Error opening a PDF file.
+
+ 2 Error opening an output file.
+
+ 3 Error related to PDF permissions.
+
+ 99 Other error.
+
+AUTHOR
+ The pdftops software and documentation are copyright 1996-2021 Glyph &
+ Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdffonts(1), pdfde-
+ tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
+ http://www.xpdfreader.com/
+
+
+
+ 28 Jan 2021 pdftops(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.1 b/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.1
new file mode 100644
index 00000000000..d1e81d01f25
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.1
@@ -0,0 +1,218 @@
+.\" Copyright 1997-2021 Glyph & Cog, LLC
+.TH pdftotext 1 "28 Jan 2021"
+.SH NAME
+pdftotext \- Portable Document Format (PDF) to text converter
+(version 4.03)
+.SH SYNOPSIS
+.B pdftotext
+[options]
+.RI [ PDF-file
+.RI [ text-file ]]
+.SH DESCRIPTION
+.B Pdftotext
+converts Portable Document Format (PDF) files to plain text.
+.PP
+Pdftotext reads the PDF file,
+.IR PDF-file ,
+and writes a text file,
+.IR text-file .
+If
+.I text-file
+is not specified, pdftotext converts
+.I file.pdf
+to
+.IR file.txt .
+If
+.I text-file
+is \'-', the text is sent to stdout.
+.SH CONFIGURATION FILE
+Pdftotext reads a configuration file at startup. It first tries to
+find the user's private config file, ~/.xpdfrc. If that doesn't
+exist, it looks for a system-wide config file, typically
+/usr/local/etc/xpdfrc (but this location can be changed when pdftotext
+is built). See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+Many of the following options can be set with configuration file
+commands. These are listed in square brackets with the description of
+the corresponding command line option.
+.TP
+.BI \-f " number"
+Specifies the first page to convert.
+.TP
+.BI \-l " number"
+Specifies the last page to convert.
+.TP
+.B \-layout
+Maintain (as best as possible) the original physical layout of the
+text. The default is to \'undo' physical layout (columns,
+hyphenation, etc.) and output the text in reading order. If the
+.B \-fixed
+option is given, character spacing within each line will be determined
+by the specified character pitch.
+.TP
+.B \-simple
+Similar to
+.BR \-layout ,
+but optimized for simple one-column pages. This mode will do a better
+job of maintaining horizontal spacing, but it will only work properly
+with a single column of text.
+.TP
+.B \-simple2
+Similar to
+.BR \-simple ,
+but handles slightly rotated text (e.g., OCR output) better. Only works
+for pages with a single column of text.
+.TP
+.B \-table
+Table mode is similar to physical layout mode, but optimized for
+tabular data, with the goal of keeping rows and columns aligned (at
+the expense of inserting extra whitespace). If the
+.B \-fixed
+option is given, character spacing within each line will be determined
+by the specified character pitch.
+.TP
+.B \-lineprinter
+Line printer mode uses a strict fixed-character-pitch and -height
+layout. That is, the page is broken into a grid, and characters are
+placed into that grid. If the grid spacing is too small for the
+actual characters, the result is extra whitespace. If the grid
+spacing is too large, the result is missing whitespace. The grid
+spacing can be specified using the
+.B \-fixed
+and
+.B \-linespacing
+options.
+If one or both are not given on the command line, pdftotext will
+attempt to compute appropriate value(s).
+.TP
+.B \-raw
+Keep the text in content stream order. Depending on how the PDF file
+was generated, this may or may not be useful.
+.TP
+.BI \-fixed " number"
+Specify the character pitch (character width), in points, for physical
+layout, table, or line printer mode. This is ignored in all other
+modes.
+.TP
+.BI \-linespacing " number"
+Specify the line spacing, in points, for line printer mode. This is
+ignored in all other modes.
+.TP
+.B \-clip
+Text which is hidden because of clipping is removed before doing
+layout, and then added back in. This can be helpful for tables where
+clipped (invisible) text would overlap the next column.
+.TP
+.B \-nodiag
+Diagonal text, i.e., text that is not close to one of the 0, 90, 180,
+or 270 degree axes, is discarded. This is useful to skip watermarks
+drawn on top of body text, etc.
+.TP
+.BI \-enc " encoding-name"
+Sets the encoding to use for text output. The
+.I encoding\-name
+must be defined with the unicodeMap command (see
+.BR xpdfrc (5)).
+The encoding name is case-sensitive. This defaults to "Latin1" (which
+is a built-in encoding).
+.RB "[config file: " textEncoding ]
+.TP
+.BI \-eol " unix | dos | mac"
+Sets the end-of-line convention to use for text output.
+.RB "[config file: " textEOL ]
+.TP
+.B \-nopgbrk
+Don't insert page breaks (form feed characters) between pages.
+.RB "[config file: " textPageBreaks ]
+.TP
+.B \-bom
+Insert a Unicode byte order marker (BOM) at the start of the text
+output.
+.TP
+.BI \-marginl " number"
+Specifies the left margin, in points. Text in the left margin (i.e.,
+within that many points of the left edge of the page) is discarded.
+The default value is zero.
+.TP
+.BI \-marginr " number"
+Specifies the right margin, in points. Text in the right margin
+(i.e., within that many points of the right edge of the page) is
+discarded. The default value is zero.
+.TP
+.BI \-margint " number"
+Specifies the top margin, in points. Text in the top margin (i.e.,
+within that many points of the top edge of the page) is discarded.
+The default value is zero.
+.TP
+.BI \-marginb " number"
+Specifies the bottom margin, in points. Text in the bottom margin
+(i.e., within that many points of the bottom edge of the page) is
+discarded. The default value is zero.
+.TP
+.BI \-opw " password"
+Specify the owner password for the PDF file. Providing this will
+bypass all security restrictions.
+.TP
+.BI \-upw " password"
+Specify the user password for the PDF file.
+.TP
+.B \-q
+Don't print any messages or errors.
+.RB "[config file: " errQuiet ]
+.TP
+.BI \-cfg " config-file"
+Read
+.I config-file
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-listencodings
+List all available text output encodings, then exit.
+.TP
+.B \-v
+Print copyright and version information, then exit.
+.TP
+.B \-h
+Print usage information, then exit.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.SH BUGS
+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.
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The pdftotext software and documentation are copyright 1996-2021 Glyph
+& Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat b/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat
new file mode 100644
index 00000000000..d4a001d7c63
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/pdftotext.cat
@@ -0,0 +1,180 @@
+pdftotext(1) General Commands Manual pdftotext(1)
+
+
+
+NAME
+ pdftotext - Portable Document Format (PDF) to text converter (version
+ 4.03)
+
+SYNOPSIS
+ pdftotext [options] [PDF-file [text-file]]
+
+DESCRIPTION
+ Pdftotext converts Portable Document Format (PDF) files to plain text.
+
+ Pdftotext reads the PDF file, PDF-file, and writes a text file, text-
+ file. If text-file is not specified, pdftotext converts file.pdf to
+ file.txt. If text-file is '-', the text is sent to stdout.
+
+CONFIGURATION FILE
+ Pdftotext reads a configuration file at startup. It first tries to
+ find the user's private config file, ~/.xpdfrc. If that doesn't exist,
+ it looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+ (but this location can be changed when pdftotext is built). See the
+ xpdfrc(5) man page for details.
+
+OPTIONS
+ Many of the following options can be set with configuration file com-
+ mands. These are listed in square brackets with the description of the
+ corresponding command line option.
+
+ -f number
+ Specifies the first page to convert.
+
+ -l number
+ Specifies the last page to convert.
+
+ -layout
+ Maintain (as best as possible) the original physical layout of
+ the text. The default is to 'undo' physical layout (columns,
+ hyphenation, etc.) and output the text in reading order. If the
+ -fixed option is given, character spacing within each line will
+ be determined by the specified character pitch.
+
+ -simple
+ Similar to -layout, but optimized for simple one-column pages.
+ This mode will do a better job of maintaining horizontal spac-
+ 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
+ option is given, character spacing within each line will be
+ determined by the specified character pitch.
+
+ -lineprinter
+ Line printer mode uses a strict fixed-character-pitch and
+ -height layout. That is, the page is broken into a grid, and
+ characters are placed into that grid. If the grid spacing is
+ too small for the actual characters, the result is extra white-
+ space. If the grid spacing is too large, the result is missing
+ whitespace. The grid spacing can be specified using the -fixed
+ and -linespacing options. If one or both are not given on the
+ command line, pdftotext will attempt to compute appropriate
+ value(s).
+
+ -raw Keep the text in content stream order. Depending on how the PDF
+ file was generated, this may or may not be useful.
+
+ -fixed number
+ Specify the character pitch (character width), in points, for
+ physical layout, table, or line printer mode. This is ignored
+ in all other modes.
+
+ -linespacing number
+ Specify the line spacing, in points, for line printer mode.
+ This is ignored in all other modes.
+
+ -clip Text which is hidden because of clipping is removed before doing
+ layout, and then added back in. This can be helpful for tables
+ where clipped (invisible) text would overlap the next column.
+
+ -nodiag
+ Diagonal text, i.e., text that is not close to one of the 0, 90,
+ 180, or 270 degree axes, is discarded. This is useful to skip
+ watermarks drawn on top of body text, etc.
+
+ -enc encoding-name
+ Sets the encoding to use for text output. The encoding-name
+ must be defined with the unicodeMap command (see xpdfrc(5)).
+ The encoding name is case-sensitive. This defaults to "Latin1"
+ (which is a built-in encoding). [config file: textEncoding]
+
+ -eol unix | dos | mac
+ Sets the end-of-line convention to use for text output. [config
+ file: textEOL]
+
+ -nopgbrk
+ Don't insert page breaks (form feed characters) between pages.
+ [config file: textPageBreaks]
+
+ -bom Insert a Unicode byte order marker (BOM) at the start of the
+ text output.
+
+ -marginl number
+ Specifies the left margin, in points. Text in the left margin
+ (i.e., within that many points of the left edge of the page) is
+ discarded. The default value is zero.
+
+ -marginr number
+ Specifies the right margin, in points. Text in the right margin
+ (i.e., within that many points of the right edge of the page) is
+ discarded. The default value is zero.
+
+ -margint number
+ Specifies the top margin, in points. Text in the top margin
+ (i.e., within that many points of the top edge of the page) is
+ discarded. The default value is zero.
+
+ -marginb number
+ Specifies the bottom margin, in points. Text in the bottom mar-
+ gin (i.e., within that many points of the bottom edge of the
+ page) is discarded. The default value is zero.
+
+ -opw password
+ Specify the owner password for the PDF file. Providing this
+ will bypass all security restrictions.
+
+ -upw password
+ Specify the user password for the PDF file.
+
+ -q Don't print any messages or errors. [config file: errQuiet]
+
+ -cfg config-file
+ Read config-file in place of ~/.xpdfrc or the system-wide config
+ file.
+
+ -listencodings
+ List all available text output encodings, then exit.
+
+ -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
+ recognition. There is no way (short of OCR) to extract text from these
+ files.
+
+EXIT CODES
+ The Xpdf tools use the following exit codes:
+
+ 0 No error.
+
+ 1 Error opening a PDF file.
+
+ 2 Error opening an output file.
+
+ 3 Error related to PDF permissions.
+
+ 99 Other error.
+
+AUTHOR
+ The pdftotext software and documentation are copyright 1996-2021 Glyph
+ & Cog, LLC.
+
+SEE ALSO
+ xpdf(1), pdftops(1), pdftohtml(1), pdfinfo(1), pdffonts(1), pdfde-
+ tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
+ http://www.xpdfreader.com/
+
+
+
+ 28 Jan 2021 pdftotext(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/sample-xpdfrc b/Build/source/libs/xpdf/xpdf-src/doc/sample-xpdfrc
new file mode 100644
index 00000000000..76b45c66fbb
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/sample-xpdfrc
@@ -0,0 +1,86 @@
+#========================================================================
+#
+# Sample xpdfrc file
+#
+# The Xpdf tools look for a config file in two places:
+# 1. ~/.xpdfrc
+# 2. in a system-wide directory, typically /usr/local/etc/xpdfrc
+#
+# This sample config file demonstrates some of the more common
+# configuration options. Everything here is commented out. You
+# should edit things (especially the file/directory paths, since
+# they'll likely be different on your system), and uncomment whichever
+# options you want to use. For complete details on config file syntax
+# and available options, please see the xpdfrc(5) man page.
+#
+# Also, the Xpdf language support packages each include a set of
+# options to be added to the xpdfrc file.
+#
+# http://www.xpdfreader.com/
+#
+#========================================================================
+
+#----- display fonts
+
+# These map the Base-14 fonts to the Type 1 fonts that ship with
+# ghostscript. You'll almost certainly want to use something like
+# this, but you'll need to adjust this to point to wherever
+# ghostscript is installed on your system. (But if the fonts are
+# installed in a "standard" location, xpdf will find them
+# automatically.)
+
+#fontFile Times-Roman /usr/local/share/ghostscript/fonts/n021003l.pfb
+#fontFile Times-Italic /usr/local/share/ghostscript/fonts/n021023l.pfb
+#fontFile Times-Bold /usr/local/share/ghostscript/fonts/n021004l.pfb
+#fontFile Times-BoldItalic /usr/local/share/ghostscript/fonts/n021024l.pfb
+#fontFile Helvetica /usr/local/share/ghostscript/fonts/n019003l.pfb
+#fontFile Helvetica-Oblique /usr/local/share/ghostscript/fonts/n019023l.pfb
+#fontFile Helvetica-Bold /usr/local/share/ghostscript/fonts/n019004l.pfb
+#fontFile Helvetica-BoldOblique /usr/local/share/ghostscript/fonts/n019024l.pfb
+#fontFile Courier /usr/local/share/ghostscript/fonts/n022003l.pfb
+#fontFile Courier-Oblique /usr/local/share/ghostscript/fonts/n022023l.pfb
+#fontFile Courier-Bold /usr/local/share/ghostscript/fonts/n022004l.pfb
+#fontFile Courier-BoldOblique /usr/local/share/ghostscript/fonts/n022024l.pfb
+#fontFile Symbol /usr/local/share/ghostscript/fonts/s050000l.pfb
+#fontFile ZapfDingbats /usr/local/share/ghostscript/fonts/d050000l.pfb
+
+# If you need to display PDF files that refer to non-embedded fonts,
+# you should add one or more fontDir options to point to the
+# directories containing the font files. Xpdf will only look at .pfa,
+# .pfb, .ttf, and .ttc files in those directories (other files will
+# simply be ignored).
+
+#fontDir /usr/local/fonts/bakoma
+
+#----- PostScript output control
+
+# Set the default PostScript paper size -- this can be letter, legal,
+# A4, or A3. You can also specify a paper size as width and height
+# (in points).
+
+#psPaperSize letter
+
+#----- text output control
+
+# Choose a text encoding for copy-and-paste and for pdftotext output.
+# The Latin1, ASCII7, and UTF-8 encodings are built into Xpdf. Other
+# encodings are available in the language support packages.
+
+#textEncoding UTF-8
+
+# Choose the end-of-line convention for multi-line copy-and-past and
+# for pdftotext output. The available options are unix, mac, and dos.
+
+#textEOL unix
+
+#----- misc settings
+
+# Enable FreeType, and anti-aliased text.
+
+#enableFreeType yes
+#antialias yes
+
+# Set the command used to run a web browser when a URL hyperlink is
+# clicked.
+
+#launchCommand viewer-script
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/xpdf.1 b/Build/source/libs/xpdf/xpdf-src/doc/xpdf.1
new file mode 100644
index 00000000000..7cf06315f88
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/xpdf.1
@@ -0,0 +1,925 @@
+.\" Copyright 1996-2021 Glyph & Cog, LLC
+.TH xpdf 1 "28 Jan 2021"
+.SH NAME
+xpdf \- Portable Document Format (PDF) file viewer (version 4.03)
+.SH SYNOPSIS
+.B xpdf
+[options]
+.RI [ PDF-file
+.RI [: page " | +" dest "]] ..."
+.PP
+.B xpdf
+[options]
+.B -remote
+.IR remote-name " [" command " ...]"
+.PP
+.B xpdf
+[options]
+.B -open
+.RI "[" PDF-file "]"
+.SH DESCRIPTION
+.B Xpdf
+is a viewer for Portable Document Format (PDF) files. (These are also
+sometimes also called \'Acrobat' files, from the name of Adobe's PDF
+software.) Xpdf uses the Qt GUI toolkit and runs on Unix, OS X, and
+Windows.
+.PP
+To run xpdf, type:
+.PP
+.RS
+xpdf file.pdf
+.RE
+.PP
+where
+.I file.pdf
+is your PDF file. The file name can be followed by a page number to
+be displayed, prefixed with a colon:
+.PP
+.RS
+xpdf file.pdf :18
+.RE
+.PP
+or by a named destination, prefixed with \'+' (this is only useful
+with PDF files that provide named destination targets):
+.PP
+.RS
+xpdf file.pdf +destinationA
+.RE
+.PP
+If you specify multiple files, they will each be opened in a separate
+tab:
+.PP
+.RS
+xpdf file1.pdf file2.pdf :18 file3.pdf
+.RE
+.PP
+You can also start xpdf without opening any files:
+.PP
+.RS
+xpdf
+.RE
+.SH CONFIGURATION FILE
+Xpdf reads a configuration file at startup. It first tries to find
+the user's private config file, ~/.xpdfrc. If that doesn't exist, it
+looks for a system-wide config file, typically /usr/local/etc/xpdfrc
+(but this location can be changed when xpdf is built). See the
+.BR xpdfrc (5)
+man page for details.
+.SH OPTIONS
+The following command line options are available. All command line
+options must come before any PDF files to be opened.
+.PP
+Many of the options can be set with configuration file commands.
+These are listed in square brackets with the description of the
+corresponding command line option.
+.TP
+.BI \-geometry " geometry"
+Set the initial window geometry.
+.TP
+.BI \-title " title"
+Set the window title. By default, the title will be "xpdf: foo.pdf".
+.TP
+.BI \-open " \fR[\fPPDF-file\fR]\fP"
+This option sets up a default remote server. If Xpdf is already
+running (with the "-open" switch), the PDF file (if any) is opened in
+a new tab. If Xpdf (with the "-open" switch) is not already running,
+starts Xpdf and opens the PDF file (if any). This is useful for GUI
+desktop environments, e.g., the typical double-click on a PDF file
+case.
+.TP
+.B \-rv
+Set reverse video mode. This reverses the colors of everything except
+images. It may not always produce great results for PDF files which
+do weird things with color. This also causes the paper color to
+default to black.
+.TP
+.BI \-papercolor " color"
+Set the "paper color", i.e., the background of the page display. The
+color can be #RRGGBB (hexadecimal) or a named color. This option will
+not work well with PDF files that do things like filling in white
+behind the text.
+.RB "[config file: " paperColor ]
+.TP
+.BI \-mattecolor " color"
+Set the matte color, i.e., the color used for background outside the
+actual page area. The color can be #RRGGBB (hexadecimal) or a named
+color.
+.RB "[config file: " matteColor ]
+.TP
+.BI \-fsmattecolor " color"
+Set the matte color for full-screen mode. The color can be #RRGGBB
+(hexadecimal) or a named color.
+.RB "[config file: " fullScreenMatteColor ]
+.TP
+.BI \-z " zoom"
+Set the initial zoom factor. A number specifies a zoom percentage,
+where 100 means 72 dpi. You may also specify \'page', to fit the page
+to the window size, or \'width', to fit the page width to the window
+width.
+.RB "[config file: " initialZoom ]
+.TP
+.BI \-rot " angle"
+Rotate pages by 0 (the default), 90, 180, or 270 degrees.
+.TP
+.BI \-aa " yes | no"
+Enable or disable font anti-aliasing. This defaults to "yes".
+.RB "[config file: " antialias ]
+.TP
+.BI \-aaVector " yes | no"
+Enable or disable vector anti-aliasing. This defaults to "yes".
+.RB "[config file: " vectorAntialias ]
+.TP
+.BI \-enc " encoding-name"
+Sets the encoding to use for text output. The
+.I encoding\-name
+must be defined with the unicodeMap command (see
+.BR xpdfrc (5)).
+This defaults to "Latin1" (which is a built-in encoding).
+.RB "[config file: " textEncoding ]
+.TP
+.BI \-pw " password"
+Specify the password for the PDF file. This can be either the owner
+password (which will bypass all security restrictions) or the user
+password.
+.TP
+.B \-fullscreen
+Open xpdf in full-screen mode, useful for presentations.
+.TP
+.BI \-remote " remote-name"
+Start Xpdf in remote server mode. See the REMOVE SERVER MODE section.
+.TP
+.BI \-display " display"
+Set the X display (only available with X11).
+.TP
+.B \-cmd
+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
+in place of ~/.xpdfrc or the system-wide config file.
+.TP
+.B \-v
+Print copyright and version information.
+.TP
+.B \-h
+Print usage information.
+.RB ( \-help
+and
+.B \-\-help
+are equivalent.)
+.PP
+.SH CONTROLS
+.SS Toolbar
+.TP
+.B "toggle sidebar button"
+Toggles (i.e., shows or hides) the sidebar.
+.TP
+.B "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 opens the
+error dialog.
+.TP
+.B "selection mode"
+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.
+.TP
+.B "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 display and
+accept page labels, if the "view - page labels" menu item is checked.
+.TP
+.B "left/right arrow buttons"
+Go backward or forward along the history path.
+.TP
+.B "zoom out/in buttons"
+Zoom out or in (i.e., change magnification) incrementally.
+.TP
+.B "zoom popup menu"
+Change the zoom factor (see the description of the \-z option above).
+.TP
+.B "fit width button"
+Change the zoom factor to fit the page width to the window width.
+.TP
+.B "fit page button"
+Change the zoom factor to fit the page to the window size.
+.TP
+.B "find entry box"
+Find a text string. Click in the box to activate it, type a search
+string, then hit return.
+.TP
+.B "find next button"
+Find the next occurrence of the search string.
+.TP
+.B "find previous button"
+Find the previous occurrence of the search string.
+.TP
+.B "find settings button"
+Display the current find settings: case sensitive (on/off), find whole
+words (on/off).
+.PP
+.SS Menu bar
+The menu bar is above the toolbar. The menu items should be
+self-explanatory.
+.PP
+.SS Tab list
+The tab list is on the left, just below the toolbar. It lists all
+open tabs.
+.PP
+.SS Outline/layers/attachments pane
+This pane is on the left, below the tab list. The popup allows you to
+select from outline, layers, or attachments.
+.PP
+The outline is a tree-like structure of bookmarks that allows moving
+within the PDF file. Not all PDF files have outlines.
+.PP
+Layers (a.k.a. optional content) allow parts of the PDF content to be
+shown or hidden. Not all PDF files have layers.
+.PP
+Attachments are other files embedded within the PDF file. There is
+a 'save' button for each attached file. Not all PDF files have
+attachments.
+.PP
+.SS Text selection
+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.
+.PP
+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 way. (On UNIX, Qt uses the $BROWSER environment
+variable.)
+.PP
+.SS Mouse bindings
+The left mouse button is used to select text (see above).
+.PP
+Clicking on a link with the middle button opens the link in a new tab.
+.PP
+Dragging the mouse with the middle button held down pans the window.
+.PP
+The right mouse button opens a popup menu (see
+.BR popupMenuCmd " in " xpdfrc (5)).
+.PP
+.SS Key bindings
+This section lists the default key bindings. Bindings can be changed
+using the config file (see
+.BR xpdfrc (5)).
+.TP
+.B control-o
+Open a new PDF file via a file requester.
+.TP
+.B control-r
+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.
+.TP
+.B control-f
+Find a text string. This sets keyboard focus to the \'find' box.
+.TP
+.B control-G
+Find next occurrence.
+.TP
+.B control-C
+Copy selected text to the clipboard.
+.TP
+.B control-P
+Print.
+.TP
+.BR control-0 " (control-zero)"
+Set the zoom factor to 125%.
+.TP
+.BR control-+ " (control-plus)"
+Zoom in (increment the zoom factor by 1).
+.TP
+.BR control-- " (control-minus)"
+Zoom out (decrement the zoom factor by 1).
+.TP
+.B control-s
+Save PDF via a file requester.
+.TP
+.B control-t
+Open a new tab.
+.TP
+.B control-n
+Open a new window.
+.TP
+.B 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 window.
+.TP
+.B control-l
+Toggle between full-screen and window modes.
+.TP
+.B control-q
+Quit.
+.TP
+.B control-<tab>
+Next tab.
+.TP
+.B control-shift-<tab>
+Previous tab.
+.TP
+.B control-?
+Help.
+.TP
+.B alt-<left-arrow>
+Go backward along the history path.
+.TP
+.B alt-<right-arrow>
+Go forward along the history path.
+.TP
+.B home
+Go to the top left of current page.
+.TP
+.B control-<home>
+Go to the first page.
+.TP
+.B end
+Go to the bottom right of current page.
+.TP
+.B control-<end>
+Go to the last page.
+.TP
+.BR <space> " or " <PageDown>
+Scroll down on the current page; if already at bottom, move to next
+page.
+.TP
+.BR control-<PageDown> " or " control-<down-arrow>
+Go to the next page. If <ScrollLock> is active, this maintains the
+relative position on the page.
+.TP
+.B <PageUp>
+Scroll up on the current page; if already at top, move to previous
+page.
+.TP
+.BR control-<PageUp> " or " control-<up-arrow>
+Go to the previous page. If <ScrollLock> is active, this maintains
+the relative position on the page.
+.TP
+.B <esc>
+Exit full-screen mode.
+.TP
+.B arrows
+Scroll the current page.
+.TP
+.B g
+Set keyboard focus to the page number entry box.
+.TP
+.B z
+Set the zoom factor to \'page' (fit page to window).
+.TP
+.B w
+Set the zoom factor to \'width' (fit page width to window).
+.SH Full-screen mode
+Xpdf can be placed into full-screen mode via the
+.B \-fullscreen
+command line option, the \'full screen' menu item, or a binding to the
+.B fullScreenMode
+or
+.B toggleFullScreenMode
+command.
+.PP
+Entering full-screen mode automatically switches to single-page view
+mode and to the fit-page zoom factor.
+.PP
+Full-screen mode can be exited via the default <esc> key binding,
+or via a binding to the
+.B windowMode
+or
+.BR toggleFullScreenMode command .
+.SH COMMANDS
+Xpdf's key and mouse bindings are user-configurable, using the bind
+and unbind commands in the config file (see
+.BR xpdfrc (5)).
+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
+Open the \'about' dialog.
+.TP
+.B blockSelectMode
+Set block selection mode. In this mode, the selection is a simple
+rectangle. Any part of the page can be selected, regardless of the
+content on the page.
+.TP
+.BI checkOpenFile( file )
+Check that
+.I file
+is open in the current tab, and open it if not.
+.TP
+.BI checkOpenFileAtDest( file, dest )
+Check that
+.I file
+is open in the current tab, and open it if not. In either case go to
+the specified named destination.
+.TP
+.BI checkOpenFileAtPage( file, page )
+Check that
+.I file
+is open in the current tab, and open it if not. In either case go to
+the specified page.
+.TP
+.B closeSidebar
+Close the sidebar.
+.TP
+.B closeSidebarMoveResizeWin
+Close the sidebar, 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.
+.TP
+.B closeSidebarResizeWin
+Close the sidebar, resizing the window so that the document size
+doesn't change.
+.TP
+.B closeTabOrQuit
+Close the tab. If this was the last open tab, close the window. If
+this was the last window open, quit.
+.TP
+.B closeWindowOrQuit
+Close the window. If this was the last open window, quit.
+.TP
+.B continuousMode
+Switch to continuous view mode.
+.TP
+.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
+.B endSelection
+End a selection.
+.TP
+.BI expandSidebar( n )
+Expand the sidebar by
+.I n
+pixels. Opens the sidebar if it is currently closed.
+.TP
+.B find
+Set keyboard focus to the \'find' box.
+.TP
+.B findFirst
+Find the first occurrence of the search string.
+.TP
+.B findNext
+Find the next occurrence of the search string.
+.TP
+.B findPrevious
+Find the previous occurrence of the search string.
+.TP
+.B focusToDocWin
+Set keyboard focus to the main document window.
+.TP
+.B focusToPageNum
+Set keyboard focus to the page number text box.
+.TP
+.B followLink
+Follow a hyperlink (does nothing if the mouse is not over a link).
+.TP
+.B followLinkInNewTab
+Follow a hyperlink, opening PDF files in a new tab (does nothing if
+the mouse is not over a link). For links to non-PDF files, this
+command is identical to followLink.
+.TP
+.B followLinkInNewTabNoSel
+Same as followLinkInNewTab, but does nothing if there is a non-empty
+selection. (This is useful as a mouse button binding.)
+.TP
+.B followLinkInNewWin
+Follow a hyperlink, opening PDF files in a new window (does nothing if
+the mouse is not over a link). For links to non-PDF files, this
+command is identical to followLink.
+.TP
+.B followLinkInNewWinNoSel
+Same as followLinkInNewWin, but does nothing if there is a non-empty
+selection. (This is useful as a mouse button binding.)
+.TP
+.B followLinkNoSel
+Same as followLink, but does nothing if there is a non-empty selection.
+(This is useful as a mouse button binding.)
+.TP
+.B fullScreenMode
+Go to full-screen mode.
+.TP
+.B goBackward
+Move backward along the history path.
+.TP
+.B goForward
+Move forward along the history path.
+.TP
+.BI gotoDest( dest )
+Go to a named destination.
+.TP
+.B gotoLastPage
+Go to the last page in the PDF file.
+.TP
+.BI gotoPage( page )
+Go to the specified page.
+.TP
+.BI help
+Open the help URL.
+.TP
+.B hideToolbar
+Hide the toolbar.
+.TP
+.B horizontalContinuousMode
+Switch to horizontal continuous view mode.
+.TP
+.B linearSelectMode
+Set linear selection mode. In this mode, the selection follows text.
+Non-text regions cannot be selected.
+.TP
+.BI loadTabState
+Load the tab state file (which was written via the saveTabState
+command), and restore the tabs listed in that file. The path for the
+tab state file is specified with the tabStateFile setting (see
+.BR xpdfrc (5)).
+.TP
+.B newTab
+Open an empty new tab.
+.TP
+.B newWindow
+Open an empty new window.
+.TP
+.B nextPage
+Go to the next page.
+.TP
+.B nextPageNoScroll
+Go to the next page, with the current relative scroll position.
+.TP
+.B nextTab
+Switch to the next tab.
+.TP
+.B open
+Open a PDF file in the current tab, using the open dialog.
+.TP
+.B openErrorWindow
+Open the error window.
+.TP
+.BI openFile( file )
+Open the specified file in the current tab.
+.TP
+.BI openFileAtDest( file, dest )
+Open the specified file in the current tab at the specified named
+destination.
+.TP
+.BI openFileAtDestIn( file, dest, location )
+Open the specified file at the specified named destination. Location
+must be "win" for a new window or "tab" for a new tab.
+.TP
+.BI openFileAtPage( file, page )
+Open the specified file in the current tab at the specified page.
+.TP
+.BI openFileAtPageIn( file, page, location )
+Open the specified file at the specified page. Location must be "win"
+for a new window or "tab" for a new tab.
+.TP
+.BI openFileIn( file, location )
+Open the specified file. Location must be "win" for a new window or
+"tab" for a new tab.
+.TP
+.BI openIn( location )
+Open a PDF file, using the open dialog. Location must be "win" for a
+new window or "tab" for a new tab.
+.TP
+.B openSidebar
+Open the sidebar.
+.TP
+.B openSidebarMoveResizeWin
+Open the sidebar, 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.
+.TP
+.B openSidebarResizeWin
+Open the sidebar, resizing the window so that the document size
+doesn't change.
+.TP
+.B pageDown
+Scroll down by one screenful.
+.TP
+.B pageUp
+Scroll up by one screenful.
+.TP
+.B postPopupMenu
+Display the popup menu.
+.TP
+.B prevPage
+Go to the previous page.
+.TP
+.B prevPageNoScroll
+Go to the previous page, with the current relative scroll position.
+.TP
+.B prevTab
+Switch to the previous tab.
+.TP
+.B print
+Open the \'print' dialog.
+.TP
+.B quit
+Quit from xpdf.
+.TP
+.B raise
+Bring the xpdf window to the front.
+.TP
+.B reload
+Reload the current PDF file.
+.TP
+.B rotateCCW
+Rotate the page 90 degrees counterclockwise.
+.TP
+.B rotateCW
+Rotate the page 90 degrees clockwise.
+.TP
+.BI run( external-command-string )
+Run an external command. The following escapes are allowed in the
+command string:
+.nf
+
+ %f => PDF file name (or an empty string if no
+ file is open)
+ %b => PDF file base name, i.e., file name minus
+ the extension (or an empty string if no
+ file is open)
+ %u => link URL (or an empty string if not over
+ a URL link)
+ %p => current page number (or an empty string if
+ no file is open)
+ %x => selection upper-left x coordinate
+ (or 0 if there is no selection)
+ %y => selection upper-left y coordinate
+ (or 0 if there is no selection)
+ %X => selection lower-right x coordinate
+ (or 0 if there is no selection)
+ %Y => selection lower-right y coordinate
+ (or 0 if there is no selection)
+ %i => page containing the mouse pointer
+ %j => x coordinate of the mouse pointer
+ %k => y coordinate of the mouse pointer
+ %% => %
+
+.fi
+The external command string will often contain spaces, so the whole
+command must be quoted in the xpdfrc file:
+.nf
+
+ bind x "run(ls -l)"
+
+.fi
+The command string may not be run through a shell. It is recommended
+to keep the command simple, so that it doesn't depend on specific
+shell functionality. For complex things, you can have the command
+string run a shell script.
+.TP
+.B saveAs
+Save PDF via a file requester.
+.TP
+.B saveImage
+Open the \'save image' dialog.
+.TP
+.BI saveTabState
+Save a list of all tabs open in this window to the tab state file.
+For each tab, this writes the PDF file name and page number (on
+separate lines). This file can be loaded later with the loadTabState
+command. The path for the tab state file is specified with the
+tabStateFile setting (see
+.BR xpdfrc (5)).
+.TP
+.BI scrollDown( n )
+Scroll down by
+.I n
+pixels.
+.TP
+.BI scrollDownNextPage( n )
+Scroll down by
+.I n
+pixels, moving to the next page if appropriate.
+.TP
+.BI scrollLeft( n )
+Scroll left by
+.I n
+pixels.
+.TP
+.BI scrollOutlineDown( n )
+Scroll the outline down by
+.I n
+increments.
+.TP
+.BI scrollOutlineUp( n )
+Scroll the outline up by
+.I n
+increments.
+.TP
+.BI scrollRight( n )
+Scroll right by
+.I n
+pixels.
+.TP
+.B scrollToBottomEdge
+Scroll to the bottom edge of the last displayed page, with no
+horizontal movement.
+.TP
+.B scrollToBottomRight
+Scroll to the bottom-right corner of the last displayed page.
+.TP
+.B scrollToLeftEdge
+Scroll to the left edge of the current page, with no vertical
+movement.
+.TP
+.B scrollToRightEdge
+Scroll to the right edge of the current page, with no vertical
+movement.
+.TP
+.B scrollToTopEdge
+Scroll to the top edge of the first displayed page, with no horizontal
+movement.
+.TP
+.B scrollToTopLeft
+Scroll to the top-left corner of the first displayed page.
+.TP
+.BI scrollUp( n )
+Scroll up by
+.I n
+pixels.
+.TP
+.BI scrollUpPrevPage( n )
+Scroll up by
+.I n
+pixels, moving to the previous page if appropriate.
+.TP
+.B selectLine
+Set the selection to the line at the current mouse position.
+.TP
+.B selectWord
+Set the selection to the word at the current mouse position.
+.TP
+.BI setSelection( pg , ulx , uly , lrx , lry )
+Set the selection to the specified coordinates on the specified page.
+.TP
+.B showKeyBindings
+Open the key bindings dialog.
+.TP
+.B showToolbar
+Show the toolbar.
+.TP
+.BI shrinkSidebar( n )
+Shrink the sidebar by
+.I n
+pixels. Closes the sidebar if shrinking it would go below the minimum
+allowed side.
+.TP
+.B sideBySideContinuousMode
+Switch to side-by-side continuous view mode.
+.TP
+.B sideBySideSingleMode
+Switch to side-by-side two-page view mode.
+.TP
+.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.
+.TP
+.B startSelection
+Start a selection at the current mouse position, which will be
+extended as the mouse moves.
+.TP
+.B toggleContinuousMode
+Toggle between continuous and single page view modes.
+.TP
+.B toggleFullScreenMode
+Toggle between full-screen and window modes.
+.TP
+.B toggleSelectMode
+Toggle between block and linear selection mode.
+.TP
+.B toggleSidebar
+Toggle the sidebar between open and closed.
+.TP
+.B toggleSidebarMoveResizeWin
+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.
+.TP
+.B toggleSidebarResizeWin
+Toggle the sidebar between open and closed, resizing the window so
+that the document size doesn't change.
+.TP
+.B toggleToolbar
+Toggle the toolbar between shown and hidden.
+.TP
+.B viewPageLabels
+Show page labels (if the PDF file has them), rather than page numbers.
+.TP
+.B viewPageNumbers
+Show page numbers, rather than page labels.
+.TP
+.B windowMode
+Go to window (non-full-screen) mode.
+.TP
+.B zoomFitPage
+Set the zoom factor to fit-page.
+.TP
+.B zoomFitWidth
+Set the zoom factor to fit-width.
+.TP
+.B zoomIn
+Zoom in - go to the next higher zoom factor.
+.TP
+.B zoomOut
+Zoom out - go the next lower zoom factor.
+.TP
+.BI zoomPercent( z )
+Set the zoom factor to
+.IR z %.
+.TP
+.B zoomToSelection
+Zoom to the current selection.
+.SH REMOTE SERVER MODE
+Starting xpdf with the "-remote" switch puts it into remote server
+mode. All remaining command line options are commands (see the
+COMMANDS 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 changing pages.
+For example:
+.PP
+.RS
+ # Start up xpdf, and open something.pdf.
+ xpdf -remote foo 'openFile(something.pdf)'
+
+ # Switch to page 7 in the already-open something.pdf. If the user
+ # has closed xpdf in the meantime, this will restart it and reopen
+ # the file.
+ xpdf -remote foo 'checkOpenFileAtPage(something.pdf, 7)'
+.RE
+.PP
+.SH EXIT CODES
+The Xpdf tools use the following exit codes:
+.TP
+0
+No error.
+.TP
+1
+Error opening a PDF file.
+.TP
+2
+Error opening an output file.
+.TP
+3
+Error related to PDF permissions.
+.TP
+99
+Other error.
+.SH AUTHOR
+The xpdf software and documentation are copyright 1996-2021 Glyph &
+Cog, LLC.
+.SH "SEE ALSO"
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1),
+.BR xpdfrc (5)
+.br
+.B http://www.xpdfreader.com/
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat b/Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat
new file mode 100644
index 00000000000..3b71667f055
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/xpdf.cat
@@ -0,0 +1,838 @@
+xpdf(1) General Commands Manual xpdf(1)
+
+
+
+NAME
+ xpdf - Portable Document Format (PDF) file viewer (version 4.03)
+
+SYNOPSIS
+ xpdf [options] [PDF-file [:page | +dest]] ...
+
+ xpdf [options] -remote remote-name [command ...]
+
+ xpdf [options] -open [PDF-file]
+
+DESCRIPTION
+ Xpdf is a viewer for Portable Document Format (PDF) files. (These are
+ also sometimes also called 'Acrobat' files, from the name of Adobe's
+ PDF software.) Xpdf uses the Qt GUI toolkit and runs on Unix, OS X,
+ and Windows.
+
+ To run xpdf, type:
+
+ xpdf file.pdf
+
+ where file.pdf is your PDF file. The file name can be followed by a
+ page number to be displayed, prefixed with a colon:
+
+ xpdf file.pdf :18
+
+ or by a named destination, prefixed with '+' (this is only useful with
+ PDF files that provide named destination targets):
+
+ xpdf file.pdf +destinationA
+
+ If you specify multiple files, they will each be opened in a separate
+ tab:
+
+ xpdf file1.pdf file2.pdf :18 file3.pdf
+
+ You can also start xpdf without opening any files:
+
+ xpdf
+
+CONFIGURATION FILE
+ Xpdf reads a configuration file at startup. It first tries to find the
+ user's private config file, ~/.xpdfrc. If that doesn't exist, it looks
+ for a system-wide config file, typically /usr/local/etc/xpdfrc (but
+ this location can be changed when xpdf is built). See the xpdfrc(5)
+ man page for details.
+
+OPTIONS
+ The following command line options are available. All command line
+ options must come before any PDF files to be opened.
+
+ Many of the options can be set with configuration file commands. These
+ are listed in square brackets with the description of the corresponding
+ command line option.
+
+ -geometry geometry
+ Set the initial window geometry.
+
+ -title title
+ Set the window title. By default, the title will be "xpdf:
+ foo.pdf".
+
+ -open [PDF-file]
+ This option sets up a default remote server. If Xpdf is already
+ running (with the "-open" switch), the PDF file (if any) is
+ opened in a new tab. If Xpdf (with the "-open" switch) is not
+ already running, starts Xpdf and opens the PDF file (if any).
+ This is useful for GUI desktop environments, e.g., the typical
+ double-click on a PDF file case.
+
+ -rv Set reverse video mode. This reverses the colors of everything
+ except images. It may not always produce great results for PDF
+ files which do weird things with color. This also causes the
+ paper color to default to black.
+
+ -papercolor color
+ Set the "paper color", i.e., the background of the page display.
+ The color can be #RRGGBB (hexadecimal) or a named color. This
+ option will not work well with PDF files that do things like
+ filling in white behind the text. [config file: paperColor]
+
+ -mattecolor color
+ Set the matte color, i.e., the color used for background outside
+ the actual page area. The color can be #RRGGBB (hexadecimal) or
+ a named color. [config file: matteColor]
+
+ -fsmattecolor color
+ Set the matte color for full-screen mode. The color can be
+ #RRGGBB (hexadecimal) or a named color. [config file:
+ fullScreenMatteColor]
+
+ -z zoom
+ Set the initial zoom factor. A number specifies a zoom percent-
+ age, where 100 means 72 dpi. You may also specify 'page', to
+ 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]
+
+ -aaVector yes | no
+ Enable or disable vector anti-aliasing. This defaults to "yes".
+ [config file: vectorAntialias]
+
+ -enc encoding-name
+ Sets the encoding to use for text output. The encoding-name
+ must be defined with the unicodeMap command (see xpdfrc(5)).
+ This defaults to "Latin1" (which is a built-in encoding). [con-
+ fig file: textEncoding]
+
+ -pw password
+ Specify the password for the PDF file. This can be either the
+ owner password (which will bypass all security restrictions) or
+ the user password.
+
+ -fullscreen
+ Open xpdf in full-screen mode, useful for presentations.
+
+ -remote remote-name
+ Start Xpdf in remote server mode. See the REMOVE SERVER MODE
+ section.
+
+ -display display
+ Set the X display (only available with X11).
+
+ -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.
+
+ -v Print copyright and version information.
+
+ -h Print usage information. (-help and --help are equivalent.)
+
+CONTROLS
+ Toolbar
+ toggle sidebar button
+ 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
+ opens the error dialog.
+
+ selection mode
+ 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
+ display and accept page labels, if the "view - page labels" menu
+ item is checked.
+
+ left/right arrow buttons
+ Go backward or forward along the history path.
+
+ zoom out/in buttons
+ Zoom out or in (i.e., change magnification) incrementally.
+
+ zoom popup menu
+ 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
+ width.
+
+ fit page button
+ Change the zoom factor to fit the page to the window size.
+
+ find entry box
+ Find a text string. Click in the box to activate it, type a
+ search string, then hit return.
+
+ find next button
+ Find the next occurrence of the search string.
+
+ find previous button
+ Find the previous occurrence of the search string.
+
+ find settings button
+ Display the current find settings: case sensitive (on/off), find
+ whole words (on/off).
+
+ Menu bar
+ The menu bar is above the toolbar. The menu items should be self-
+ explanatory.
+
+ Tab list
+ The tab list is on the left, just below the toolbar. It lists all open
+ tabs.
+
+ Outline/layers/attachments pane
+ 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
+ within the PDF file. Not all PDF files have outlines.
+
+ 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-
+ ments.
+
+ Text selection
+ 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
+ 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
+ way. (On UNIX, Qt uses the $BROWSER environment variable.)
+
+ Mouse bindings
+ The left mouse button is used to select text (see above).
+
+ Clicking on a link with the middle button opens the link in a new tab.
+
+ Dragging the mouse with the middle button held down pans the window.
+
+ 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
+ using the config file (see xpdfrc(5)).
+
+ control-o
+ Open a new PDF file via a file requester.
+
+ control-r
+ Reload the current PDF file. Note that Xpdf will reload the
+ file automatically (on a page change or redraw) if it has
+ changed since it was last loaded.
+
+ control-f
+ Find a text string. This sets keyboard focus to the 'find' box.
+
+ control-G
+ Find next occurrence.
+
+ control-C
+ Copy selected text to the clipboard.
+
+ control-P
+ Print.
+
+ control-0 (control-zero)
+ Set the zoom factor to 125%.
+
+ control-+ (control-plus)
+ Zoom in (increment the zoom factor by 1).
+
+ control-- (control-minus)
+ Zoom out (decrement the zoom factor by 1).
+
+ control-s
+ Save PDF via a file requester.
+
+ control-t
+ Open a new tab.
+
+ control-n
+ 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-
+ dow.
+
+ control-l
+ Toggle between full-screen and window modes.
+
+ control-q
+ Quit.
+
+ control-<tab>
+ Next tab.
+
+ control-shift-<tab>
+ Previous tab.
+
+ control-?
+ Help.
+
+ alt-<left-arrow>
+ Go backward along the history path.
+
+ alt-<right-arrow>
+ Go forward along the history path.
+
+ home Go to the top left of current page.
+
+ control-<home>
+ Go to the first page.
+
+ end Go to the bottom right of current page.
+
+ control-<end>
+ Go to the last page.
+
+ <space> or <PageDown>
+ 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
+ the relative position on the page.
+
+ <PageUp>
+ Scroll up on the current page; if already at top, move to previ-
+ ous page.
+
+ control-<PageUp> or control-<up-arrow>
+ Go to the previous page. If <ScrollLock> is active, this main-
+ tains the relative position on the page.
+
+ <esc> Exit full-screen mode.
+
+ arrows Scroll the current page.
+
+ g Set keyboard focus to the page number entry box.
+
+ z Set the zoom factor to 'page' (fit page to window).
+
+ 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
+ fullScreenMode or toggleFullScreenMode command.
+
+ 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
+ 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
+ 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.
+
+ blockSelectMode
+ Set block selection mode. In this mode, the selection is a sim-
+ ple rectangle. Any part of the page can be selected, regardless
+ of the content on the page.
+
+ checkOpenFile(file)
+ 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.
+ 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.
+ In either case go to the specified page.
+
+ closeSidebar
+ Close the sidebar.
+
+ closeSidebarMoveResizeWin
+ Close the sidebar, 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.
+
+ closeSidebarResizeWin
+ Close the sidebar, resizing the window so that the document size
+ doesn't change.
+
+ closeTabOrQuit
+ Close the tab. If this was the last open tab, close the window.
+ If this was the last window open, quit.
+
+ closeWindowOrQuit
+ Close the window. If this was the last open window, quit.
+
+ continuousMode
+ Switch to continuous view mode.
+
+ 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
+ End a selection.
+
+ expandSidebar(n)
+ Expand the sidebar by n pixels. Opens the sidebar if it is cur-
+ rently closed.
+
+ find Set keyboard focus to the 'find' box.
+
+ findFirst
+ Find the first occurrence of the search string.
+
+ findNext
+ Find the next occurrence of the search string.
+
+ findPrevious
+ Find the previous occurrence of the search string.
+
+ focusToDocWin
+ Set keyboard focus to the main document window.
+
+ focusToPageNum
+ Set keyboard focus to the page number text box.
+
+ followLink
+ Follow a hyperlink (does nothing if the mouse is not over a
+ link).
+
+ followLinkInNewTab
+ Follow a hyperlink, opening PDF files in a new tab (does nothing
+ if the mouse is not over a link). For links to non-PDF files,
+ this command is identical to followLink.
+
+ followLinkInNewTabNoSel
+ Same as followLinkInNewTab, but does nothing if there is a non-
+ empty selection. (This is useful as a mouse button binding.)
+
+ followLinkInNewWin
+ Follow a hyperlink, opening PDF files in a new window (does
+ nothing if the mouse is not over a link). For links to non-PDF
+ files, this command is identical to followLink.
+
+ followLinkInNewWinNoSel
+ Same as followLinkInNewWin, but does nothing if there is a non-
+ empty selection. (This is useful as a mouse button binding.)
+
+ followLinkNoSel
+ Same as followLink, but does nothing if there is a non-empty
+ selection. (This is useful as a mouse button binding.)
+
+ fullScreenMode
+ Go to full-screen mode.
+
+ goBackward
+ Move backward along the history path.
+
+ goForward
+ Move forward along the history path.
+
+ gotoDest(dest)
+ Go to a named destination.
+
+ gotoLastPage
+ Go to the last page in the PDF file.
+
+ gotoPage(page)
+ Go to the specified page.
+
+ help Open the help URL.
+
+ hideToolbar
+ Hide the toolbar.
+
+ horizontalContinuousMode
+ Switch to horizontal continuous view mode.
+
+ linearSelectMode
+ Set linear selection mode. In this mode, the selection follows
+ text. Non-text regions cannot be selected.
+
+ loadTabState
+ Load the tab state file (which was written via the saveTabState
+ command), and restore the tabs listed in that file. The path
+ for the tab state file is specified with the tabStateFile set-
+ ting (see xpdfrc(5)).
+
+ newTab Open an empty new tab.
+
+ newWindow
+ Open an empty new window.
+
+ nextPage
+ Go to the next page.
+
+ nextPageNoScroll
+ Go to the next page, with the current relative scroll position.
+
+ nextTab
+ Switch to the next tab.
+
+ open Open a PDF file in the current tab, using the open dialog.
+
+ openErrorWindow
+ Open the error window.
+
+ openFile(file)
+ Open the specified file in the current tab.
+
+ openFileAtDest(file,dest)
+ Open the specified file in the current tab at the specified
+ named destination.
+
+ openFileAtDestIn(file,dest,location)
+ Open the specified file at the specified named destination.
+ Location must be "win" for a new window or "tab" for a new tab.
+
+ openFileAtPage(file,page)
+ Open the specified file in the current tab at the specified
+ page.
+
+ openFileAtPageIn(file,page,location)
+ Open the specified file at the specified page. Location must be
+ "win" for a new window or "tab" for a new tab.
+
+ openFileIn(file,location)
+ Open the specified file. Location must be "win" for a new win-
+ dow or "tab" for a new tab.
+
+ openIn(location)
+ Open a PDF file, using the open dialog. Location must be "win"
+ for a new window or "tab" for a new tab.
+
+ openSidebar
+ Open the sidebar.
+
+ openSidebarMoveResizeWin
+ Open the sidebar, 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.
+
+ openSidebarResizeWin
+ Open the sidebar, resizing the window so that the document size
+ doesn't change.
+
+ pageDown
+ Scroll down by one screenful.
+
+ pageUp Scroll up by one screenful.
+
+ postPopupMenu
+ Display the popup menu.
+
+ prevPage
+ Go to the previous page.
+
+ prevPageNoScroll
+ Go to the previous page, with the current relative scroll posi-
+ tion.
+
+ prevTab
+ Switch to the previous tab.
+
+ print Open the 'print' dialog.
+
+ quit Quit from xpdf.
+
+ raise Bring the xpdf window to the front.
+
+ reload Reload the current PDF file.
+
+ rotateCCW
+ Rotate the page 90 degrees counterclockwise.
+
+ rotateCW
+ Rotate the page 90 degrees clockwise.
+
+ run(external-command-string)
+ Run an external command. The following escapes are allowed in
+ the command string:
+
+ %f => PDF file name (or an empty string if no
+ file is open)
+ %b => PDF file base name, i.e., file name minus
+ the extension (or an empty string if no
+ file is open)
+ %u => link URL (or an empty string if not over
+ a URL link)
+ %p => current page number (or an empty string if
+ no file is open)
+ %x => selection upper-left x coordinate
+ (or 0 if there is no selection)
+ %y => selection upper-left y coordinate
+ (or 0 if there is no selection)
+ %X => selection lower-right x coordinate
+ (or 0 if there is no selection)
+ %Y => selection lower-right y coordinate
+ (or 0 if there is no selection)
+ %i => page containing the mouse pointer
+ %j => x coordinate of the mouse pointer
+ %k => y coordinate of the mouse pointer
+ %% => %
+
+ The external command string will often contain spaces, so the
+ whole command must be quoted in the xpdfrc file:
+
+ bind x "run(ls -l)"
+
+ The command string may not be run through a shell. It is recom-
+ mended to keep the command simple, so that it doesn't depend on
+ specific shell functionality. For complex things, you can have
+ the command string run a shell script.
+
+ saveAs Save PDF via a file requester.
+
+ saveImage
+ Open the 'save image' dialog.
+
+ saveTabState
+ Save a list of all tabs open in this window to the tab state
+ file. For each tab, this writes the PDF file name and page num-
+ ber (on separate lines). This file can be loaded later with the
+ loadTabState command. The path for the tab state file is speci-
+ fied with the tabStateFile setting (see xpdfrc(5)).
+
+ scrollDown(n)
+ Scroll down by n pixels.
+
+ scrollDownNextPage(n)
+ Scroll down by n pixels, moving to the next page if appropriate.
+
+ scrollLeft(n)
+ Scroll left by n pixels.
+
+ scrollOutlineDown(n)
+ Scroll the outline down by n increments.
+
+ scrollOutlineUp(n)
+ Scroll the outline up by n increments.
+
+ scrollRight(n)
+ Scroll right by n pixels.
+
+ scrollToBottomEdge
+ Scroll to the bottom edge of the last displayed page, with no
+ horizontal movement.
+
+ scrollToBottomRight
+ Scroll to the bottom-right corner of the last displayed page.
+
+ scrollToLeftEdge
+ Scroll to the left edge of the current page, with no vertical
+ movement.
+
+ scrollToRightEdge
+ Scroll to the right edge of the current page, with no vertical
+ movement.
+
+ scrollToTopEdge
+ Scroll to the top edge of the first displayed page, with no hor-
+ izontal movement.
+
+ scrollToTopLeft
+ Scroll to the top-left corner of the first displayed page.
+
+ scrollUp(n)
+ Scroll up by n pixels.
+
+ scrollUpPrevPage(n)
+ 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.
+
+ shrinkSidebar(n)
+ Shrink the sidebar by n pixels. Closes the sidebar if shrinking
+ it would go below the minimum allowed side.
+
+ sideBySideContinuousMode
+ Switch to side-by-side continuous view mode.
+
+ sideBySideSingleMode
+ Switch to side-by-side two-page view mode.
+
+ 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
+ scroll the document as the mouse moves.
+
+ startSelection
+ Start a selection at the current mouse position, which will be
+ extended as the mouse moves.
+
+ toggleContinuousMode
+ Toggle between continuous and single page view modes.
+
+ toggleFullScreenMode
+ Toggle between full-screen and window modes.
+
+ toggleSelectMode
+ Toggle between block and linear selection mode.
+
+ toggleSidebar
+ 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
+ so that the document stays in the same place on the screen.
+
+ toggleSidebarResizeWin
+ Toggle the sidebar between open and closed, resizing the window
+ so that the document size doesn't change.
+
+ toggleToolbar
+ Toggle the toolbar between shown and hidden.
+
+ viewPageLabels
+ Show page labels (if the PDF file has them), rather than page
+ numbers.
+
+ viewPageNumbers
+ Show page numbers, rather than page labels.
+
+ windowMode
+ Go to window (non-full-screen) mode.
+
+ zoomFitPage
+ Set the zoom factor to fit-page.
+
+ zoomFitWidth
+ Set the zoom factor to fit-width.
+
+ zoomIn Zoom in - go to the next higher zoom factor.
+
+ zoomOut
+ Zoom out - go the next lower zoom factor.
+
+ zoomPercent(z)
+ Set the zoom factor to z%.
+
+ zoomToSelection
+ 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-
+ 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
+ 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
+ the user
+ # has closed xpdf in the meantime, this will restart it and
+ reopen
+ # the file.
+ xpdf -remote foo 'checkOpenFileAtPage(something.pdf, 7)'
+
+EXIT CODES
+ The Xpdf tools use the following exit codes:
+
+ 0 No error.
+
+ 1 Error opening a PDF file.
+
+ 2 Error opening an output file.
+
+ 3 Error related to PDF permissions.
+
+ 99 Other error.
+
+AUTHOR
+ The xpdf software and documentation are copyright 1996-2021 Glyph &
+ Cog, LLC.
+
+SEE ALSO
+ pdftops(1), pdftotext(1), pdftohtml(1), pdfinfo(1), pdffonts(1), pdfde-
+ tach(1), pdftoppm(1), pdftopng(1), pdfimages(1), xpdfrc(5)
+ http://www.xpdfreader.com/
+
+
+
+ 28 Jan 2021 xpdf(1)
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.5 b/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.5
new file mode 100644
index 00000000000..07639316ec7
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.5
@@ -0,0 +1,823 @@
+.\" Copyright 2002-2021 Glyph & Cog, LLC
+.TH xpdfrc 5 "28 Jan 2021"
+.SH NAME
+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
+file in your home directory, it will be read. Otherwise, a
+system-wide configuration file will be read from
+.IR /usr/local/etc/xpdfrc ,
+if it exists. (This is its default location; depending on build
+options, it may be placed elsewhere.) On Win32 systems, the
+.I xpdfrc
+file should be placed in the same directory as the executables.
+.PP
+The xpdfrc file consists of a series of configuration options, one
+per line. Blank lines and lines starting with a \'#' (comments) are
+ignored.
+.PP
+Arguments 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
+.TP
+.BI include " config\-file"
+Includes the specified config file. The effect of this is equivalent
+to inserting the contents of
+.I config\-file
+directly into the parent config file in place of the
+.I include
+command. Config files can be nested arbitrarily deeply.
+.SH GENERAL FONT CONFIGURATION
+.TP
+.BI fontFile " PDF\-font\-name font\-file"
+Maps a PDF font,
+.IR PDF\-font\-name ,
+to a font for display or PostScript output. The font file,
+.IR font\-file ,
+can be any type allowed in a PDF file. This command can be used for
+8-bit or 16-bit (CID) fonts.
+.TP
+.BI fontDir " dir"
+Specifies a search directory for font files. There can be multiple
+fontDir commands; all of the specified directories will be searched in
+order. The font files can be Type 1 (.pfa or .pfb) or TrueType (.ttf
+or .ttc); other files in the directory will be ignored. The font file
+name (not including the extension) must exactly match the PDF font
+name. This search is performed if the font name doesn't match any of
+the fonts declared with the fontFile command. There are no default
+fontDir directories.
+.TP
+.BI fontFileCC " registry\-ordering font\-file"
+Maps the
+.I registry\-ordering
+character collection to a font for display or PostScript output. This
+mapping is used if the font name doesn't match any of the fonts
+declared with the fontFile, fontDir, psResidentFont16, or
+psResidentFontCC commands.
+.SH POSTSCRIPT FONT CONFIGURATION
+.TP
+.BR psFontPassthrough " yes | no"
+If set to "yes", pass 8-bit font names through to the PostScript
+output without substitution. Fonts which are not embedded in the PDF
+file are expected to be available on the printer. This defaults to
+"no".
+.TP
+.BI psResidentFont " PDF\-font\-name PS\-font\-name"
+When the 8-bit font
+.I PDF\-font\-name
+is used (without embedding) in a PDF file, it will be translated to
+the PostScript font
+.IR PS\-font\-name ,
+which is assumed to be resident in the printer. Typically,
+.I PDF\-font\-name
+and
+.I PS\-font\-name
+are the same. By default, only the Base-14 fonts are assumed to be
+resident.
+.TP
+.BI psResidentFont16 " PDF\-font\-name wMode PS\-font\-name encoding"
+When the 16-bit (CID) font
+.I PDF\-font\-name
+with writing mode
+.I wMode
+is used (without embedding) in a PDF file, it will be translated to
+the PostScript font
+.IR PS\-font\-name ,
+which is assumed to be resident in the printer. The writing mode
+must be either \'H' for horizontal or \'V' for vertical. The resident
+font is assumed to use the specified encoding (which must have been
+defined with the unicodeMap command).
+.TP
+.BI psResidentFontCC " registry\-ordering wMode PS\-font\-name encoding"
+When a 16-bit (CID) font using the
+.I registry\-ordering
+character collection and
+.I wMode
+writing mode is used (without embedding) in a PDF file, the PostScript
+font,
+.IR PS\-font\-name ,
+is substituted for it. The substituted font is assumed to be
+resident in the printer. The writing mode must be either \'H' for
+horizontal or \'V' for vertical. The resident font is assumed to use
+the specified encoding (which must have been defined with the
+unicodeMap command).
+.TP
+.BR psEmbedType1Fonts " yes | no"
+If set to "no", prevents embedding of Type 1 fonts in generated
+PostScript. This defaults to "yes".
+.TP
+.BR psEmbedTrueTypeFonts " yes | no"
+If set to "no", prevents embedding of TrueType fonts in generated
+PostScript. This defaults to "yes".
+.TP
+.BR psEmbedCIDTrueTypeFonts " yes | no"
+If set to "no", prevents embedding of CID TrueType fonts in generated
+PostScript. For Level 3 PostScript, this generates a CID font, for
+lower levels it generates a non-CID composite font. This defaults to
+"yes".
+.TP
+.BR psEmbedCIDPostScriptFonts " yes | no"
+If set to "no", prevents embedding of CID PostScript fonts in
+generated PostScript. For Level 3 PostScript, this generates a CID
+font, for lower levels it generates a non-CID composite font. This
+defaults to "yes".
+.SH POSTSCRIPT CONTROL
+.TP
+.BI psPaperSize " width(pts) height(pts)"
+Sets the paper size for PostScript output. The
+.I width
+and
+.I height
+parameters give the paper size in PostScript points (1 point = 1/72
+inch).
+.TP
+.BR psPaperSize " letter | legal | A4 | A3 | match"
+Sets the paper size for PostScript output to a standard size. The
+default paper size is set when xpdf and pdftops are built, typically
+to "letter" or "A4". This can also be set to "match", which will set
+the paper size to match the size specified in the PDF file.
+.TP
+.BR psImageableArea " llx lly urx ury"
+Sets the imageable area for PostScript output. The four integers are
+the coordinates of the lower-left and upper-right corners of the
+imageable region, specified in points (with the origin being the
+lower-left corner of the paper). This defaults to the full paper
+size; the psPaperSize option will reset the imageable area
+coordinates.
+.TP
+.BR psCrop " yes | no"
+If set to "yes", PostScript output is cropped to the CropBox specified
+in the PDF file; otherwise no cropping is done. This defaults to
+"yes".
+.TP
+.BR psUseCropBoxAsPage " yes | no"
+If set to "yes", PostScript output treats the CropBox as the page size.
+By default, this is "no", and the MediaBox is used as the page size.
+.TP
+.BR psExpandSmaller " yes | no"
+If set to "yes", PDF pages smaller than the PostScript imageable area
+are expanded to fill the imageable area. Otherwise, no scaling is
+done on smaller pages. This defaults to "no".
+.TP
+.BR psShrinkLarger " yes | no"
+If set to yes, PDF pages larger than the PostScript imageable area are
+shrunk to fit the imageable area. Otherwise, no scaling is done on
+larger pages. This defaults to "yes".
+.TP
+.BR psCenter " yes | no"
+If set to yes, PDF pages smaller than the PostScript imageable area
+(after any scaling) are centered in the imageable area. Otherwise,
+they are aligned at the lower-left corner of the imageable area. This
+defaults to "yes".
+.TP
+.BR psDuplex " yes | no"
+If set to "yes", the generated PostScript will set the "Duplex"
+pagedevice entry. This tells duplex-capable printers to enable
+duplexing. This defaults to "no".
+.TP
+.BR psLevel " level1 | level1sep | level2 | level2gray | level2sep | level3 | level3gray | level3Sep"
+Sets the PostScript level to generate. This defaults to "level2".
+.TP
+.BR psPreload " yes | no"
+If set to "yes", PDF forms are converted to PS procedures, and image
+data is preloaded. This uses more memory in the PostScript
+interpreter, but generates significantly smaller PS files in
+situations where, e.g., the same image is drawn on every page of a
+long document. This defaults to "no".
+.TP
+.BR psOPI " yes | no"
+If set to "yes", generates PostScript OPI comments for all images and
+forms which have OPI information. This option is only available if
+the Xpdf tools were compiled with OPI support. This defaults to "no".
+.TP
+.BR psASCIIHex " yes | no"
+If set to "yes", the ASCIIHexEncode filter will be used instead of
+ASCII85Encode for binary data. This defaults to "no".
+.TP
+.BR psLZW " yes | no"
+If set to "yes", the LZWEncode filter will be used for lossless
+compression in PostScript output; if set to "no", the RunLengthEncode
+filter will be used instead. LZW generates better compression
+(smaller PS files), but may not be supported by some printers. This
+defaults to "yes".
+.TP
+.BR psUncompressPreloadedImages " yes | no"
+If set to "yes", all preloaded images in PS files will uncompressed.
+If set to "no", the original compressed images will be used when
+possible. The "yes" setting is useful to work around certain buggy
+PostScript interpreters. This defaults to "no".
+.TP
+.BR psMinLineWidth " float"
+Set the minimum line width, in points, for PostScript output. The
+default value is 0 (no minimum).
+.TP
+.BR psRasterResolution " float"
+Set the resolution (in dpi) for rasterized pages in PostScript output.
+(Pdftops will rasterize pages which use transparency.) This defaults
+to 300.
+.TP
+.BR psRasterMono " yes | no"
+If set to "yes", rasterized pages in PS files will be monochrome
+(8-bit gray) instead of color. This defaults to "no".
+.TP
+.BR psRasterSliceSize " pixels"
+When rasterizing pages, pdftops splits the page into horizontal
+"slices", to limit memory usage. This option sets the maximum slice
+size, in pixels. This defaults to 20000000 (20 million).
+.TP
+.BR psAlwaysRasterize " yes | no"
+If set to "yes", all PostScript output will be rasterized. This
+defaults to "no".
+.TP
+.BR psNeverRasterize " yes | no"
+Pdftops rasterizes an pages that use transparency (because PostScript
+doesn't support transparency). If psNeverRasterize is set to "yes",
+rasterization is disabled: pages will never be rasterized, even if
+they contain transparency. This will likely result in incorrect
+output for PDF files that use transparency, and a warning message to
+that effect will be printed. This defaults to "no".
+.TP
+.BI fontDir " dir"
+See the description above, in the DISPLAY FONTS section.
+.SH TEXT CONTROL AND CHARACTER MAPPING
+.TP
+.BI textEncoding " encoding\-name"
+Sets the encoding to use for text output. (This can be overridden
+with the "\-enc" switch on the command line.) The
+.I encoding\-name
+must be defined with the unicodeMap command (see above). This
+defaults to "Latin1".
+.TP
+.BR textEOL " unix | dos | mac"
+Sets the end-of-line convention to use for text output. The options
+are:
+.nf
+
+ unix = LF
+ dos = CR+LF
+ mac = CR
+
+.fi
+(This can be overridden with the "\-eol" switch on the command line.)
+The default value is based on the OS where xpdf and pdftotext were
+built.
+.TP
+.BR textPageBreaks " yes | no"
+If set to "yes", text extraction will insert page breaks (form feed
+characters) between pages. This defaults to "yes".
+.TP
+.BR textKeepTinyChars " yes | no"
+If set to "yes", text extraction will keep all characters. If set to
+"no", text extraction will discard tiny (smaller than 3 point)
+characters after the first 50000 per page, avoiding extremely slow run
+times for PDF files that use special fonts to do shading or
+cross-hatching. This defaults to "yes".
+.TP
+.BI nameToUnicode " map\-file"
+Specifies a file with the mapping from character names to Unicode.
+This is used to handle PDF fonts that have valid encodings but no
+ToUnicode entry. Each line of a nameToUnicode file looks like this:
+
+.I " " hex\-string name
+
+The
+.I hex\-string
+is the Unicode (UCS-2) character index, and
+.I name
+is the corresponding character name. Multiple nameToUnicode files can
+be used; if a character name is given more than once, the code in the
+last specified file is used. There is a built-in default
+nameToUnicode table with all of Adobe's standard character names.
+.TP
+.BI cidToUnicode " registry\-ordering map\-file"
+Specifies the file with the mapping from character collection to
+Unicode. Each line of a cidToUnicode file represents one character:
+
+.I " " hex\-string
+
+The
+.I hex\-string
+is the Unicode (UCS-2) index for that character. The first line maps
+CID 0, the second line CID 1, etc. File size is determined by size of
+the character collection. Only one file is allowed per character
+collection; the last specified file is used. There are no built-in
+cidToUnicode mappings.
+.TP
+.BI unicodeToUnicode " font\-name\-substring map\-file"
+This is used to work around PDF fonts which have incorrect Unicode
+information. It specifies a file which maps from the given
+(incorrect) Unicode indexes to the correct ones. The mapping will be
+used for any font whose name contains
+.IR font\-name\-substring .
+Each line of a unicodeToUnicode file represents one Unicode character:
+
+.RI " " in\-hex " " out\-hex1 " " out\-hex2 " ..."
+
+The
+.I in\-hex
+field is an input (incorrect) Unicode index, and the rest of the
+fields are one or more output (correct) Unicode indexes. Each
+occurrence of
+.I in\-hex
+will be converted to the specified output sequence.
+.TP
+.BI unicodeRemapping " remap\-file"
+Remap Unicode characters when doing text extraction. This specifies a
+file that maps from a particular Unicode index to zero or more
+replacement Unicode indexes. Each line of the remap file represents
+one Unicode character:
+
+.RI " " in\-hex " " out\-hex1 " " out\-hex2 " ..."
+
+Any Unicode characters not listed will be left unchanged. This
+function is typically used to remap things like non-breaking spaces,
+soft hyphens, ligatures, etc.
+.TP
+.BI unicodeMap " encoding\-name map\-file"
+Specifies the file with mapping from Unicode to
+.IR encoding\-name .
+These encodings are used for text output (see below). Each line of a
+unicodeMap file represents a range of one or more Unicode characters
+which maps linearly to a range in the output encoding:
+.nf
+
+.I " " in\-start\-hex in\-end\-hex out\-start\-hex
+
+.fi
+Entries for single characters can be abbreviated to:
+.nf
+
+.I " " in\-hex out\-hex
+
+.fi
+The
+.I in\-start\-hex
+and
+.I in\-end\-hex
+fields (or the single
+.I in\-hex
+field) specify the Unicode range. The
+.I out\-start\-hex
+field (or the
+.I out\-hex
+field) specifies the start of the output encoding range. The length
+of the
+.I out\-start\-hex
+(or
+.IR out\-hex )
+string determines the length of the output characters (e.g., UTF-8
+uses different numbers of bytes to represent characters in different
+ranges). Entries must be given in increasing Unicode order. Only one
+file is allowed per encoding; the last specified file is used. The
+.IR Latin1 ,
+.IR ASCII7 ,
+.IR Symbol ,
+.IR ZapfDingbats ,
+.IR UTF-8 ,
+and
+.I UCS-2
+encodings are predefined.
+.TP
+.BI cMapDir " registry\-ordering dir"
+Specifies a search directory,
+.IR dir ,
+for CMaps for the
+.I registry\-ordering
+character collection. There can be multiple directories for a
+particular collection. There are no default CMap directories.
+.TP
+.BI toUnicodeDir " dir"
+Specifies a search directory,
+.IR dir ,
+for ToUnicode CMaps. There can be multiple ToUnicode directories.
+There are no default ToUnicode directories.
+.TP
+.BI mapNumericCharNames " yes | no"
+If set to "yes", the Xpdf tools will attempt to map various numeric
+character names sometimes used in font subsets. In some cases this
+leads to usable text, and in other cases it leads to gibberish --
+there is no way for Xpdf to tell. This defaults to "yes".
+.TP
+.BI mapUnknownCharNames " yes | no"
+If set to "yes", and mapNumericCharNames is set to "no", the Xpdf
+tools will apply a simple pass-through mapping (Unicode index =
+character code) for all unrecognized glyph names. (For CID fonts,
+setting mapNumericCharNames to "no" is unnecessary.) In some cases,
+this leads to usable text, and in other cases it leads to gibberish --
+there is no way for Xpdf to tell. This defaults to "no".
+.TP
+.BI mapExtTrueTypeFontsViaUnicode " yes | no"
+When rasterizing text using an external TrueType font, there are two
+options for handling character codes. If
+mapExtTrueTypeFontsViaUnicode is set to "yes", Xpdf will use the font
+encoding/ToUnicode info to map character codes to Unicode, and then
+use the font's Unicode cmap to map Unicode to GIDs. If
+mapExtTrueTypeFontsViaUnicode is set to "no", Xpdf will assume the
+character codes are GIDs (i.e., use an identity mapping). This
+defaults to "yes".
+.TP
+.BI dropFont " font-name"
+Drop all text drawn in the specified font. To drop text drawn in
+unnamed fonts, use:
+.nf
+
+ dropFont ""
+
+.fi
+There can be any number of dropFont commands.
+.SH RASTERIZER SETTINGS
+.TP
+.BR enableFreeType " yes | no"
+Enables or disables use of FreeType (a TrueType / Type 1 font
+rasterizer). This is only relevant if the Xpdf tools were built with
+FreeType support. ("enableFreeType" replaces the old
+"freetypeControl" option.) This option defaults to "yes".
+.TP
+.BR disableFreeTypeHinting " yes | no"
+If this is set to "yes", FreeType hinting will be forced off. This
+option defaults to "no".
+.TP
+.BR antialias " yes | no"
+Enables or disables font anti-aliasing in the PDF rasterizer. This
+option affects all font rasterizers. ("antialias" replaces the
+anti-aliasing control provided by the old "t1libControl" and
+"freetypeControl" options.) This default to "yes".
+.TP
+.BR vectorAntialias " yes | no"
+Enables or disables anti-aliasing of vector graphics in the PDF
+rasterizer. This defaults to "yes".
+.TP
+.BR antialiasPrinting " yes | no"
+If this is "yes", bitmaps sent to the printer will be antialiased
+(according to the "antialias" and "vectorAntialias" settings). If
+this is "no", printed bitmaps will not be antialiased. This defaults
+to "no".
+.TP
+.BR strokeAdjust " yes | no | cad"
+Sets the stroke adjustment mode. If set to "no", no stroke adjustment
+will be done. If set to "yes", normal stroke adjustment will be done:
+horizontal and vertical lines will be moved by up to half a pixel to
+make them look cleaner when vector anti-aliasing is enabled. If set
+to "cad", a slightly different stroke adjustment algorithm will be
+used to ensure that lines of the same original width will always have
+the same adjusted width (at the expense of allowing gaps and overlaps
+between adjacent lines). This defaults to "yes".
+.TP
+.BR forceAccurateTiling " yes | no"
+If this is set to "yes", the TilingType is forced to 2 (no distortion)
+for all tiling patterns, regardless of the setting in the pattern
+dictionary. This defaults to "no".
+.TP
+.BR screenType " dispersed | clustered | stochasticClustered"
+Sets the halftone screen type, which will be used when generating a
+monochrome (1-bit) bitmap. The three options are dispersed-dot
+dithering, clustered-dot dithering (with a round dot and 45-degree
+screen angle), and stochastic clustered-dot dithering. By default,
+"stochasticClustered" is used for resolutions of 300 dpi and higher,
+and "dispersed" is used for resolutions lower then 300 dpi.
+.TP
+.BI screenSize " integer"
+Sets the size of the (square) halftone screen threshold matrix. By
+default, this is 4 for dispersed-dot dithering, 10 for clustered-dot
+dithering, and 100 for stochastic clustered-dot dithering.
+.TP
+.BI screenDotRadius " integer"
+Sets the halftone screen dot radius. This is only used when
+screenType is set to stochasticClustered, and it defaults to 2. In
+clustered-dot mode, the dot radius is half of the screen size.
+Dispersed-dot dithering doesn't have a dot radius.
+.TP
+.BI screenGamma " float"
+Sets the halftone screen gamma correction parameter. Gamma values
+greater than 1 make the output brighter; gamma values less than 1 make
+it darker. The default value is 1.
+.TP
+.BI screenBlackThreshold " float"
+When halftoning, all values below this threshold are forced to solid
+black. This parameter is a floating point value between 0 (black) and
+1 (white). The default value is 0.
+.TP
+.BI screenWhiteThreshold " float"
+When halftoning, all values above this threshold are forced to solid
+white. This parameter is a floating point value between 0 (black) and
+1 (white). The default value is 1.
+.TP
+.BI minLineWidth " float"
+Set the minimum line width, in device pixels. This affects the
+rasterizer only, not the PostScript converter (except when it uses
+rasterization to handle transparency). The default value is 0 (no
+minimum).
+.TP
+.BI enablePathSimplification " yes | no"
+If set to "yes", simplify paths by removing points where it won't make
+a significant difference to the shape. The default value is "no".
+.TP
+.BI overprintPreview " yes | no"
+If set to "yes", generate overprint preview output, honoring the
+OP/op/OPM settings in the PDF file. Ignored for non-CMYK output. The
+default value is "no".
+.SH VIEWER SETTINGS
+These settings only apply to the Xpdf GUI PDF viewer.
+.TP
+.BR initialZoom " \fIpercentage\fR | page | width"
+Sets the initial zoom factor. A number specifies a zoom percentage,
+where 100 means 72 dpi. You may also specify \'page', to fit the page
+to the window size, or \'width', to fit the page width to the window
+width.
+.TP
+.BI defaultFitZoom " percentage"
+If xpdf is started with fit-page or fit-width zoom and no window
+geometry, it will calculate a desired window size based on the PDF
+page size and this defaultFitZoom value. I.e., the window size will
+be chosen such that exactly one page will fit in the window at this
+zoom factor (which must be a percentage). The default value is
+based on the screen resolution.
+.TP
+.BR initialDisplayMode " single | continuous | sideBySideSingle | sideBySideContinuous | horizontalContinuous"
+Sets the initial display mode. The default setting is "continuous".
+.TP
+.BI initialToolbarState " yes | no"
+If set to "yes", xpdf opens with the toolbar visible. If set to "no",
+xpdf opens with the toolbar hidden. The default is "yes".
+.TP
+.BI initialSidebarState " yes | no"
+If set to "yes", xpdf opens with the sidebar (tabs, outline, etc.)
+visible. If set to "no", xpdf opens with the sidebar collapsed. The
+default is "yes".
+.TP
+.BI initialSidebarWidth " width"
+Sets the initial sidebar width, in pixels. This is only relevant if
+initialSidebarState is "yes". The default value is zero, which tells
+xpdf to use an internal default size.
+.TP
+.BR initialSelectMode " block | linear"
+Sets the initial selection mode. The default setting is "linear".
+.TP
+.BI paperColor " color"
+Set the "paper color", i.e., the background of the page display. The
+color can be #RRGGBB (hexadecimal) or a named color. This option will
+not work well with PDF files that do things like filling in white
+behind the text.
+.TP
+.BI matteColor " color"
+Set the matte color, i.e., the color used for background outside the
+actual page area. The color can be #RRGGBB (hexadecimal) or a named
+color.
+.TP
+.BI fullScreenMatteColor " color"
+Set the matte color for full-screen mode. The color can be #RRGGBB
+(hexadecimal) or a named color.
+.TP
+.BI selectionColor " color"
+Set the selection color. The color can be #RRGGBB (hexadecimal) or a
+named color.
+.TP
+.BI reverseVideoInvertImages " yes | no"
+If set to "no", xpdf's reverse-video mode inverts text and vector
+graphic content, but not images. If set to "yes", xpdf inverts images
+as well. The default is "no".
+.TP
+.BI popupMenuCmd " title command ..."
+Add a command to the popup menu.
+.I Title
+is the text to be displayed in the menu.
+.I Command
+is an Xpdf command (see the COMMANDS section of the
+.BR xpdf (1)
+man page for details). Multiple commands are separated by whitespace.
+.TP
+.BI maxTileWidth " pixels"
+Set the maximum width of tiles to be used by xpdf when rasterizing
+pages. This defaults to 1500.
+.TP
+.BI maxTileHeight " pixels"
+Set the maximum height of tiles to be used by xpdf when rasterizing
+pages. This defaults to 1500.
+.TP
+.BI tileCacheSize " tiles"
+Set the maximum number of tiles to be cached by xpdf when rasterizing
+pages. This defaults to 10.
+.TP
+.BI workerThreads " numThreads"
+Set the number of worker threads to be used by xpdf when rasterizing
+pages. This defaults to 1.
+.TP
+.BI 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, followed 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).
+.TP
+.BI movieCommand " command"
+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.
+.TP
+.BI defaultPrinter " printer"
+Sets the default printer used in the viewer's print dialog.
+.TP
+.BI bind " modifiers-key context command ..."
+Add a key or mouse button binding.
+.I Modifiers
+can be zero or more of:
+.nf
+
+ shift-
+ ctrl-
+ alt-
+
+.fi
+.I Key
+can be a regular ASCII character, or any one of:
+.nf
+
+ space
+ tab
+ return
+ enter
+ backspace
+ esc
+ insert
+ delete
+ home
+ 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)
+ mouseDoubleClick1 .. mouseDoubleClick7 (mouse buttons)
+ mouseTripleClick1 .. mouseTripleClick7 (mouse buttons)
+
+.fi
+.I Context
+is either "any" or a comma-separated combination of:
+.nf
+
+ fullScreen / window (full screen mode on/off)
+ continuous / singlePage (continuous mode on/off)
+ overLink / offLink (mouse over link or not)
+ scrLockOn / scrLockOff (scroll lock on/off)
+
+.fi
+The context string can include only one of each pair in the above
+list.
+
+.I Command
+is an Xpdf command (see the COMMANDS section of the
+.BR xpdf (1)
+man page for details). Multiple commands are separated by whitespace.
+
+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.
+
+Example key bindings:
+.nf
+
+ # bind ctrl-a in any context to the nextPage
+ # command
+ bind ctrl-a any nextPage
+
+ # bind uppercase B, when in continuous mode
+ # with scroll lock on, to the reload command
+ # followed by the prevPage command
+ bind B continuous,scrLockOn reload prevPage
+
+.fi
+See the
+.BR xpdf (1)
+man page for more examples.
+.TP
+.BI unbind " modifiers-key context"
+Removes a key binding established with the bind command. This is most
+useful to remove default key bindings before establishing new ones
+(e.g., if the default key binding is given for "any" context, and you
+want to create new key bindings for multiple contexts).
+.TP
+.BI tabStateFile " path"
+Sets the file used by the loadTabState and saveTabState commands (see
+the
+.BR xpdf (1)
+man page for more information).
+.SH MISCELLANEOUS SETTINGS
+.TP
+.BI drawAnnotations " yes | no"
+If set to "no", annotations will not be drawn or printed. The default
+value is "yes".
+.TP
+.BI drawFormFields " yes | no"
+If set to "no", form fields will not be drawn or printed. The default
+value is "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".
+.TP
+.BI errQuiet " yes | no"
+If set to "yes", this suppresses all error and warning messages from
+all of the Xpdf tools. This defaults to "no".
+.SH EXAMPLES
+The following is a sample xpdfrc file.
+.nf
+
+# from the Thai support package
+nameToUnicode /usr/local/share/xpdf/Thai.nameToUnicode
+
+# from the Japanese support package
+cidToUnicode Adobe-Japan1 /usr/local/share/xpdf/Adobe-Japan1.cidToUnicode
+unicodeMap JISX0208 /usr/local/share/xpdf/JISX0208.unicodeMap
+cMapDir Adobe-Japan1 /usr/local/share/xpdf/cmap/Adobe-Japan1
+
+# use the Base-14 Type 1 fonts from ghostscript
+fontFile Times-Roman /usr/local/share/ghostscript/fonts/n021003l.pfb
+fontFile Times-Italic /usr/local/share/ghostscript/fonts/n021023l.pfb
+fontFile Times-Bold /usr/local/share/ghostscript/fonts/n021004l.pfb
+fontFile Times-BoldItalic /usr/local/share/ghostscript/fonts/n021024l.pfb
+fontFile Helvetica /usr/local/share/ghostscript/fonts/n019003l.pfb
+fontFile Helvetica-Oblique /usr/local/share/ghostscript/fonts/n019023l.pfb
+fontFile Helvetica-Bold /usr/local/share/ghostscript/fonts/n019004l.pfb
+fontFile Helvetica-BoldOblique /usr/local/share/ghostscript/fonts/n019024l.pfb
+fontFile Courier /usr/local/share/ghostscript/fonts/n022003l.pfb
+fontFile Courier-Oblique /usr/local/share/ghostscript/fonts/n022023l.pfb
+fontFile Courier-Bold /usr/local/share/ghostscript/fonts/n022004l.pfb
+fontFile Courier-BoldOblique /usr/local/share/ghostscript/fonts/n022024l.pfb
+fontFile Symbol /usr/local/share/ghostscript/fonts/s050000l.pfb
+fontFile ZapfDingbats /usr/local/share/ghostscript/fonts/d050000l.pfb
+
+# use the Bakoma Type 1 fonts
+# (this assumes they happen to be installed in /usr/local/fonts/bakoma)
+fontDir /usr/local/fonts/bakoma
+
+# set some PostScript options
+psPaperSize letter
+psDuplex no
+psLevel level2
+psEmbedType1Fonts yes
+psEmbedTrueTypeFonts yes
+
+# assume that the PostScript printer has the Univers and
+# Univers-Bold fonts
+psResidentFont Univers Univers
+psResidentFont Univers-Bold Univers-Bold
+
+# set the text output options
+textEncoding UTF-8
+textEOL unix
+
+# misc options
+enableFreeType yes
+launchCommand viewer-script
+
+.fi
+.SH FILES
+.TP
+.B /usr/local/etc/xpdfrc
+This is the default location for the system-wide configuration file.
+Depending on build options, it may be placed elsewhere.
+.TP
+.B $HOME/.xpdfrc
+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-2021 Glyph &
+Cog, LLC.
+.SH "SEE ALSO"
+.BR xpdf (1),
+.BR pdftops (1),
+.BR pdftotext (1),
+.BR pdftohtml (1),
+.BR pdfinfo (1),
+.BR pdffonts (1),
+.BR pdfdetach (1),
+.BR pdftoppm (1),
+.BR pdftopng (1),
+.BR pdfimages (1)
+.br
+.B http://www.xpdfreader.com/
diff --git a/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat b/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat
new file mode 100644
index 00000000000..c43e8005e2b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/doc/xpdfrc.cat
@@ -0,0 +1,755 @@
+xpdfrc(5) File Formats Manual xpdfrc(5)
+
+
+
+NAME
+ xpdfrc - configuration file for Xpdf tools (version 4.03)
+
+DESCRIPTION
+ All of the Xpdf tools read a single configuration file. If you have a
+ .xpdfrc file in your home directory, it will be read. Otherwise, a
+ system-wide configuration file will be read from /usr/local/etc/xpdfrc,
+ if it exists. (This is its default location; depending on build
+ options, it may be placed elsewhere.) On Win32 systems, the xpdfrc
+ file should be placed in the same directory as the executables.
+
+ The xpdfrc file consists of a series of configuration options, one per
+ line. Blank lines and lines starting with a '#' (comments) are
+ ignored.
+
+ 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.
+
+INCLUDE FILES
+ include config-file
+ Includes the specified config file. The effect of this is
+ equivalent to inserting the contents of config-file directly
+ into the parent config file in place of the include command.
+ Config files can be nested arbitrarily deeply.
+
+GENERAL FONT CONFIGURATION
+ fontFile PDF-font-name font-file
+ Maps a PDF font, PDF-font-name, to a font for display or Post-
+ Script output. The font file, font-file, can be any type
+ allowed in a PDF file. This command can be used for 8-bit or
+ 16-bit (CID) fonts.
+
+ fontDir dir
+ Specifies a search directory for font files. There can be mul-
+ tiple fontDir commands; all of the specified directories will be
+ searched in order. The font files can be Type 1 (.pfa or .pfb)
+ or TrueType (.ttf or .ttc); other files in the directory will be
+ ignored. The font file name (not including the extension) must
+ exactly match the PDF font name. This search is performed if
+ the font name doesn't match any of the fonts declared with the
+ fontFile command. There are no default fontDir directories.
+
+ fontFileCC registry-ordering font-file
+ Maps the registry-ordering character collection to a font for
+ display or PostScript output. This mapping is used if the font
+ name doesn't match any of the fonts declared with the fontFile,
+ fontDir, psResidentFont16, or psResidentFontCC commands.
+
+POSTSCRIPT FONT CONFIGURATION
+ psFontPassthrough yes | no
+ If set to "yes", pass 8-bit font names through to the PostScript
+ output without substitution. Fonts which are not embedded in
+ the PDF file are expected to be available on the printer. This
+ defaults to "no".
+
+ psResidentFont PDF-font-name PS-font-name
+ When the 8-bit font PDF-font-name is used (without embedding) in
+ a PDF file, it will be translated to the PostScript font
+ PS-font-name, which is assumed to be resident in the printer.
+ Typically, PDF-font-name and PS-font-name are the same. By
+ default, only the Base-14 fonts are assumed to be resident.
+
+ psResidentFont16 PDF-font-name wMode PS-font-name encoding
+ When the 16-bit (CID) font PDF-font-name with writing mode wMode
+ is used (without embedding) in a PDF file, it will be translated
+ to the PostScript font PS-font-name, which is assumed to be res-
+ ident in the printer. The writing mode must be either 'H' for
+ horizontal or 'V' for vertical. The resident font is assumed to
+ use the specified encoding (which must have been defined with
+ the unicodeMap command).
+
+ psResidentFontCC registry-ordering wMode PS-font-name encoding
+ When a 16-bit (CID) font using the registry-ordering character
+ collection and wMode writing mode is used (without embedding) in
+ a PDF file, the PostScript font, PS-font-name, is substituted
+ for it. The substituted font is assumed to be resident in the
+ printer. The writing mode must be either 'H' for horizontal or
+ 'V' for vertical. The resident font is assumed to use the spec-
+ ified encoding (which must have been defined with the unicodeMap
+ command).
+
+ psEmbedType1Fonts yes | no
+ If set to "no", prevents embedding of Type 1 fonts in generated
+ PostScript. This defaults to "yes".
+
+ psEmbedTrueTypeFonts yes | no
+ If set to "no", prevents embedding of TrueType fonts in gener-
+ ated PostScript. This defaults to "yes".
+
+ psEmbedCIDTrueTypeFonts yes | no
+ If set to "no", prevents embedding of CID TrueType fonts in gen-
+ erated PostScript. For Level 3 PostScript, this generates a CID
+ font, for lower levels it generates a non-CID composite font.
+ This defaults to "yes".
+
+ psEmbedCIDPostScriptFonts yes | no
+ If set to "no", prevents embedding of CID PostScript fonts in
+ generated PostScript. For Level 3 PostScript, this generates a
+ CID font, for lower levels it generates a non-CID composite
+ font. This defaults to "yes".
+
+POSTSCRIPT CONTROL
+ psPaperSize width(pts) height(pts)
+ Sets the paper size for PostScript output. The width and height
+ parameters give the paper size in PostScript points (1 point =
+ 1/72 inch).
+
+ psPaperSize letter | legal | A4 | A3 | match
+ Sets the paper size for PostScript output to a standard size.
+ The default paper size is set when xpdf and pdftops are built,
+ typically to "letter" or "A4". This can also be set to "match",
+ which will set the paper size to match the size specified in the
+ PDF file.
+
+ psImageableArea llx lly urx ury
+ Sets the imageable area for PostScript output. The four inte-
+ gers are the coordinates of the lower-left and upper-right cor-
+ ners of the imageable region, specified in points (with the ori-
+ gin being the lower-left corner of the paper). This defaults to
+ the full paper size; the psPaperSize option will reset the
+ imageable area coordinates.
+
+ psCrop yes | no
+ If set to "yes", PostScript output is cropped to the CropBox
+ specified in the PDF file; otherwise no cropping is done. This
+ defaults to "yes".
+
+ psUseCropBoxAsPage yes | no
+ If set to "yes", PostScript output treats the CropBox as the
+ page size. By default, this is "no", and the MediaBox is used
+ as the page size.
+
+ psExpandSmaller yes | no
+ If set to "yes", PDF pages smaller than the PostScript imageable
+ area are expanded to fill the imageable area. Otherwise, no
+ scaling is done on smaller pages. This defaults to "no".
+
+ psShrinkLarger yes | no
+ If set to yes, PDF pages larger than the PostScript imageable
+ area are shrunk to fit the imageable area. Otherwise, no scal-
+ ing is done on larger pages. This defaults to "yes".
+
+ psCenter yes | no
+ If set to yes, PDF pages smaller than the PostScript imageable
+ area (after any scaling) are centered in the imageable area.
+ Otherwise, they are aligned at the lower-left corner of the
+ imageable area. This defaults to "yes".
+
+ psDuplex yes | no
+ If set to "yes", the generated PostScript will set the "Duplex"
+ pagedevice entry. This tells duplex-capable printers to enable
+ duplexing. This defaults to "no".
+
+ psLevel level1 | level1sep | level2 | level2gray | level2sep | level3 |
+ level3gray | level3Sep
+ Sets the PostScript level to generate. This defaults to
+ "level2".
+
+ psPreload yes | no
+ If set to "yes", PDF forms are converted to PS procedures, and
+ image data is preloaded. This uses more memory in the Post-
+ Script interpreter, but generates significantly smaller PS files
+ in situations where, e.g., the same image is drawn on every page
+ of a long document. This defaults to "no".
+
+ psOPI yes | no
+ If set to "yes", generates PostScript OPI comments for all
+ images and forms which have OPI information. This option is
+ only available if the Xpdf tools were compiled with OPI support.
+ This defaults to "no".
+
+ psASCIIHex yes | no
+ If set to "yes", the ASCIIHexEncode filter will be used instead
+ of ASCII85Encode for binary data. This defaults to "no".
+
+ psLZW yes | no
+ If set to "yes", the LZWEncode filter will be used for lossless
+ compression in PostScript output; if set to "no", the RunLength-
+ Encode filter will be used instead. LZW generates better com-
+ pression (smaller PS files), but may not be supported by some
+ printers. This defaults to "yes".
+
+ psUncompressPreloadedImages yes | no
+ If set to "yes", all preloaded images in PS files will uncom-
+ pressed. If set to "no", the original compressed images will be
+ used when possible. The "yes" setting is useful to work around
+ certain buggy PostScript interpreters. This defaults to "no".
+
+ psMinLineWidth float
+ Set the minimum line width, in points, for PostScript output.
+ The default value is 0 (no minimum).
+
+ psRasterResolution float
+ Set the resolution (in dpi) for rasterized pages in PostScript
+ output. (Pdftops will rasterize pages which use transparency.)
+ This defaults to 300.
+
+ psRasterMono yes | no
+ If set to "yes", rasterized pages in PS files will be monochrome
+ (8-bit gray) instead of color. This defaults to "no".
+
+ psRasterSliceSize pixels
+ When rasterizing pages, pdftops splits the page into horizontal
+ "slices", to limit memory usage. This option sets the maximum
+ slice size, in pixels. This defaults to 20000000 (20 million).
+
+ psAlwaysRasterize yes | no
+ If set to "yes", all PostScript output will be rasterized. This
+ defaults to "no".
+
+ psNeverRasterize yes | no
+ Pdftops rasterizes an pages that use transparency (because Post-
+ Script doesn't support transparency). If psNeverRasterize is
+ set to "yes", rasterization is disabled: pages will never be
+ rasterized, even if they contain transparency. This will likely
+ result in incorrect output for PDF files that use transparency,
+ and a warning message to that effect will be printed. This
+ defaults to "no".
+
+ fontDir dir
+ See the description above, in the DISPLAY FONTS section.
+
+TEXT CONTROL AND CHARACTER MAPPING
+ textEncoding encoding-name
+ Sets the encoding to use for text output. (This can be overrid-
+ den with the "-enc" switch on the command line.) The encod-
+ ing-name must be defined with the unicodeMap command (see
+ above). This defaults to "Latin1".
+
+ textEOL unix | dos | mac
+ Sets the end-of-line convention to use for text output. The
+ options are:
+
+ unix = LF
+ dos = CR+LF
+ mac = CR
+
+ (This can be overridden with the "-eol" switch on the command
+ line.) The default value is based on the OS where xpdf and
+ pdftotext were built.
+
+ textPageBreaks yes | no
+ If set to "yes", text extraction will insert page breaks (form
+ feed characters) between pages. This defaults to "yes".
+
+ textKeepTinyChars yes | no
+ If set to "yes", text extraction will keep all characters. If
+ set to "no", text extraction will discard tiny (smaller than 3
+ point) characters after the first 50000 per page, avoiding
+ extremely slow run times for PDF files that use special fonts to
+ do shading or cross-hatching. This defaults to "yes".
+
+ nameToUnicode map-file
+ Specifies a file with the mapping from character names to Uni-
+ code. This is used to handle PDF fonts that have valid encod-
+ ings but no ToUnicode entry. Each line of a nameToUnicode file
+ looks like this:
+
+ hex-string name
+
+ The hex-string is the Unicode (UCS-2) character index, and name
+ is the corresponding character name. Multiple nameToUnicode
+ files can be used; if a character name is given more than once,
+ the code in the last specified file is used. There is a built-
+ in default nameToUnicode table with all of Adobe's standard
+ character names.
+
+ cidToUnicode registry-ordering map-file
+ Specifies the file with the mapping from character collection to
+ Unicode. Each line of a cidToUnicode file represents one char-
+ acter:
+
+ hex-string
+
+ The hex-string is the Unicode (UCS-2) index for that character.
+ The first line maps CID 0, the second line CID 1, etc. File
+ size is determined by size of the character collection. Only
+ one file is allowed per character collection; the last specified
+ file is used. There are no built-in cidToUnicode mappings.
+
+ unicodeToUnicode font-name-substring map-file
+ This is used to work around PDF fonts which have incorrect Uni-
+ code information. It specifies a file which maps from the given
+ (incorrect) Unicode indexes to the correct ones. The mapping
+ will be used for any font whose name contains font-name-sub-
+ string. Each line of a unicodeToUnicode file represents one
+ Unicode character:
+
+ in-hex out-hex1 out-hex2 ...
+
+ The in-hex field is an input (incorrect) Unicode index, and the
+ rest of the fields are one or more output (correct) Unicode
+ indexes. Each occurrence of in-hex will be converted to the
+ specified output sequence.
+
+ unicodeRemapping remap-file
+ Remap Unicode characters when doing text extraction. This spec-
+ ifies a file that maps from a particular Unicode index to zero
+ or more replacement Unicode indexes. Each line of the remap
+ file represents one Unicode character:
+
+ in-hex out-hex1 out-hex2 ...
+
+ Any Unicode characters not listed will be left unchanged. This
+ function is typically used to remap things like non-breaking
+ spaces, soft hyphens, ligatures, etc.
+
+ unicodeMap encoding-name map-file
+ Specifies the file with mapping from Unicode to encoding-name.
+ These encodings are used for text output (see below). Each line
+ of a unicodeMap file represents a range of one or more Unicode
+ characters which maps linearly to a range in the output encod-
+ ing:
+
+ in-start-hex in-end-hex out-start-hex
+
+ Entries for single characters can be abbreviated to:
+
+ in-hex out-hex
+
+ The in-start-hex and in-end-hex fields (or the single in-hex
+ field) specify the Unicode range. The out-start-hex field (or
+ the out-hex field) specifies the start of the output encoding
+ range. The length of the out-start-hex (or out-hex) string
+ determines the length of the output characters (e.g., UTF-8 uses
+ different numbers of bytes to represent characters in different
+ ranges). Entries must be given in increasing Unicode order.
+ Only one file is allowed per encoding; the last specified file
+ is used. The Latin1, ASCII7, Symbol, ZapfDingbats, UTF-8, and
+ UCS-2 encodings are predefined.
+
+ cMapDir registry-ordering dir
+ Specifies a search directory, dir, for CMaps for the reg-
+ istry-ordering character collection. There can be multiple
+ directories for a particular collection. There are no default
+ CMap directories.
+
+ toUnicodeDir dir
+ Specifies a search directory, dir, for ToUnicode CMaps. There
+ can be multiple ToUnicode directories. There are no default
+ ToUnicode directories.
+
+ mapNumericCharNames yes | no
+ If set to "yes", the Xpdf tools will attempt to map various
+ numeric character names sometimes used in font subsets. In some
+ cases this leads to usable text, and in other cases it leads to
+ gibberish -- there is no way for Xpdf to tell. This defaults to
+ "yes".
+
+ mapUnknownCharNames yes | no
+ If set to "yes", and mapNumericCharNames is set to "no", the
+ Xpdf tools will apply a simple pass-through mapping (Unicode
+ index = character code) for all unrecognized glyph names. (For
+ CID fonts, setting mapNumericCharNames to "no" is unnecessary.)
+ In some cases, this leads to usable text, and in other cases it
+ leads to gibberish -- there is no way for Xpdf to tell. This
+ defaults to "no".
+
+ mapExtTrueTypeFontsViaUnicode yes | no
+ When rasterizing text using an external TrueType font, there are
+ two options for handling character codes. If mapExtTrueType-
+ FontsViaUnicode is set to "yes", Xpdf will use the font encod-
+ ing/ToUnicode info to map character codes to Unicode, and then
+ use the font's Unicode cmap to map Unicode to GIDs. If mapExt-
+ TrueTypeFontsViaUnicode is set to "no", Xpdf will assume the
+ character codes are GIDs (i.e., use an identity mapping). This
+ defaults to "yes".
+
+ dropFont font-name
+ Drop all text drawn in the specified font. To drop text drawn
+ in unnamed fonts, use:
+
+ dropFont ""
+
+ There can be any number of dropFont commands.
+
+RASTERIZER SETTINGS
+ enableFreeType yes | no
+ Enables or disables use of FreeType (a TrueType / Type 1 font
+ rasterizer). This is only relevant if the Xpdf tools were built
+ with FreeType support. ("enableFreeType" replaces the old
+ "freetypeControl" option.) This option defaults to "yes".
+
+ disableFreeTypeHinting yes | no
+ If this is set to "yes", FreeType hinting will be forced off.
+ This option defaults to "no".
+
+ antialias yes | no
+ Enables or disables font anti-aliasing in the PDF rasterizer.
+ This option affects all font rasterizers. ("antialias" replaces
+ the anti-aliasing control provided by the old "t1libControl" and
+ "freetypeControl" options.) This default to "yes".
+
+ vectorAntialias yes | no
+ Enables or disables anti-aliasing of vector graphics in the PDF
+ rasterizer. This defaults to "yes".
+
+ antialiasPrinting yes | no
+ If this is "yes", bitmaps sent to the printer will be
+ antialiased (according to the "antialias" and "vectorAntialias"
+ settings). If this is "no", printed bitmaps will not be
+ antialiased. This defaults to "no".
+
+ strokeAdjust yes | no | cad
+ Sets the stroke adjustment mode. If set to "no", no stroke
+ adjustment will be done. If set to "yes", normal stroke adjust-
+ ment will be done: horizontal and vertical lines will be moved
+ by up to half a pixel to make them look cleaner when vector
+ anti-aliasing is enabled. If set to "cad", a slightly different
+ stroke adjustment algorithm will be used to ensure that lines of
+ the same original width will always have the same adjusted width
+ (at the expense of allowing gaps and overlaps between adjacent
+ lines). This defaults to "yes".
+
+ forceAccurateTiling yes | no
+ If this is set to "yes", the TilingType is forced to 2 (no dis-
+ tortion) for all tiling patterns, regardless of the setting in
+ the pattern dictionary. This defaults to "no".
+
+ screenType dispersed | clustered | stochasticClustered
+ Sets the halftone screen type, which will be used when generat-
+ ing a monochrome (1-bit) bitmap. The three options are dis-
+ persed-dot dithering, clustered-dot dithering (with a round dot
+ and 45-degree screen angle), and stochastic clustered-dot
+ dithering. By default, "stochasticClustered" is used for reso-
+ lutions of 300 dpi and higher, and "dispersed" is used for reso-
+ lutions lower then 300 dpi.
+
+ screenSize integer
+ Sets the size of the (square) halftone screen threshold matrix.
+ By default, this is 4 for dispersed-dot dithering, 10 for clus-
+ tered-dot dithering, and 100 for stochastic clustered-dot
+ dithering.
+
+ screenDotRadius integer
+ Sets the halftone screen dot radius. This is only used when
+ screenType is set to stochasticClustered, and it defaults to 2.
+ In clustered-dot mode, the dot radius is half of the screen
+ size. Dispersed-dot dithering doesn't have a dot radius.
+
+ screenGamma float
+ Sets the halftone screen gamma correction parameter. Gamma val-
+ ues greater than 1 make the output brighter; gamma values less
+ than 1 make it darker. The default value is 1.
+
+ screenBlackThreshold float
+ When halftoning, all values below this threshold are forced to
+ solid black. This parameter is a floating point value between 0
+ (black) and 1 (white). The default value is 0.
+
+ screenWhiteThreshold float
+ When halftoning, all values above this threshold are forced to
+ solid white. This parameter is a floating point value between 0
+ (black) and 1 (white). The default value is 1.
+
+ minLineWidth float
+ Set the minimum line width, in device pixels. This affects the
+ rasterizer only, not the PostScript converter (except when it
+ uses rasterization to handle transparency). The default value
+ is 0 (no minimum).
+
+ enablePathSimplification yes | no
+ If set to "yes", simplify paths by removing points where it
+ won't make a significant difference to the shape. The default
+ value is "no".
+
+ overprintPreview yes | no
+ If set to "yes", generate overprint preview output, honoring the
+ OP/op/OPM settings in the PDF file. Ignored for non-CMYK out-
+ put. The default value is "no".
+
+VIEWER SETTINGS
+ These settings only apply to the Xpdf GUI PDF viewer.
+
+ initialZoom percentage | page | width
+ Sets the initial zoom factor. A number specifies a zoom per-
+ centage, where 100 means 72 dpi. You may also specify 'page',
+ to fit the page to the window size, or 'width', to fit the page
+ width to the window width.
+
+ defaultFitZoom percentage
+ If xpdf is started with fit-page or fit-width zoom and no window
+ geometry, it will calculate a desired window size based on the
+ PDF page size and this defaultFitZoom value. I.e., the window
+ size will be chosen such that exactly one page will fit in the
+ window at this zoom factor (which must be a percentage). The
+ default value is based on the screen resolution.
+
+ initialDisplayMode single | continuous | sideBySideSingle | sideBySide-
+ Continuous | horizontalContinuous
+ Sets the initial display mode. The default setting is "continu-
+ ous".
+
+ initialToolbarState yes | no
+ If set to "yes", xpdf opens with the toolbar visible. If set to
+ "no", xpdf opens with the toolbar hidden. The default is "yes".
+
+ initialSidebarState yes | no
+ If set to "yes", xpdf opens with the sidebar (tabs, outline,
+ etc.) visible. If set to "no", xpdf opens with the sidebar
+ collapsed. The default is "yes".
+
+ 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".
+
+ paperColor color
+ Set the "paper color", i.e., the background of the page display.
+ The color can be #RRGGBB (hexadecimal) or a named color. This
+ option will not work well with PDF files that do things like
+ filling in white behind the text.
+
+ matteColor color
+ Set the matte color, i.e., the color used for background outside
+ the actual page area. The color can be #RRGGBB (hexadecimal) or
+ a named color.
+
+ fullScreenMatteColor color
+ 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
+ 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
+ commands are separated by whitespace.
+
+ maxTileWidth pixels
+ Set the maximum width of tiles to be used by xpdf when rasteriz-
+ ing pages. This defaults to 1500.
+
+ maxTileHeight pixels
+ 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-
+ terizing pages. This defaults to 10.
+
+ workerThreads numThreads
+ Set the number of worker threads to be used by xpdf when raster-
+ 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
+ simply try to execute the file (after prompting the user).
+
+ movieCommand command
+ 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.
+
+ defaultPrinter printer
+ 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
+ more of:
+
+ shift-
+ ctrl-
+ alt-
+
+ Key can be a regular ASCII character, or any one of:
+
+ space
+ tab
+ return
+ enter
+ backspace
+ esc
+ insert
+ delete
+ home
+ 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)
+ mouseDoubleClick1 .. mouseDoubleClick7 (mouse buttons)
+ mouseTripleClick1 .. mouseTripleClick7 (mouse buttons)
+
+ Context is either "any" or a comma-separated combination of:
+
+ fullScreen / window (full screen mode on/off)
+ continuous / singlePage (continuous mode on/off)
+ 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
+ above list.
+
+ 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
+ was defined for the exact same modifiers, key, and context. All
+ tokens (modifiers, key, context, commands) are case-sensitive.
+
+ Example key bindings:
+
+ # bind ctrl-a in any context to the nextPage
+ # command
+ bind ctrl-a any nextPage
+
+ # bind uppercase B, when in continuous mode
+ # with scroll lock on, to the reload command
+ # followed by the prevPage command
+ bind B continuous,scrLockOn reload prevPage
+
+ 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-
+ tiple contexts).
+
+ tabStateFile path
+ Sets the file used by the loadTabState and saveTabState commands
+ (see the xpdf(1) man page for more information).
+
+MISCELLANEOUS SETTINGS
+ drawAnnotations yes | no
+ 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
+ default value is "yes".
+
+ printCommands yes | no
+ 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
+ from all of the Xpdf tools. This defaults to "no".
+
+EXAMPLES
+ The following is a sample xpdfrc file.
+
+ # from the Thai support package
+ nameToUnicode /usr/local/share/xpdf/Thai.nameToUnicode
+
+ # from the Japanese support package
+ cidToUnicode Adobe-Japan1 /usr/local/share/xpdf/Adobe-Japan1.cidToUnicode
+ unicodeMap JISX0208 /usr/local/share/xpdf/JISX0208.unicodeMap
+ cMapDir Adobe-Japan1 /usr/local/share/xpdf/cmap/Adobe-Japan1
+
+ # use the Base-14 Type 1 fonts from ghostscript
+ fontFile Times-Roman /usr/local/share/ghostscript/fonts/n021003l.pfb
+ fontFile Times-Italic /usr/local/share/ghostscript/fonts/n021023l.pfb
+ fontFile Times-Bold /usr/local/share/ghostscript/fonts/n021004l.pfb
+ fontFile Times-BoldItalic /usr/local/share/ghostscript/fonts/n021024l.pfb
+ fontFile Helvetica /usr/local/share/ghostscript/fonts/n019003l.pfb
+ fontFile Helvetica-Oblique /usr/local/share/ghostscript/fonts/n019023l.pfb
+ fontFile Helvetica-Bold /usr/local/share/ghostscript/fonts/n019004l.pfb
+ fontFile Helvetica-BoldOblique /usr/local/share/ghostscript/fonts/n019024l.pfb
+ fontFile Courier /usr/local/share/ghostscript/fonts/n022003l.pfb
+ fontFile Courier-Oblique /usr/local/share/ghostscript/fonts/n022023l.pfb
+ fontFile Courier-Bold /usr/local/share/ghostscript/fonts/n022004l.pfb
+ fontFile Courier-BoldOblique /usr/local/share/ghostscript/fonts/n022024l.pfb
+ fontFile Symbol /usr/local/share/ghostscript/fonts/s050000l.pfb
+ fontFile ZapfDingbats /usr/local/share/ghostscript/fonts/d050000l.pfb
+
+ # use the Bakoma Type 1 fonts
+ # (this assumes they happen to be installed in /usr/local/fonts/bakoma)
+ fontDir /usr/local/fonts/bakoma
+
+ # set some PostScript options
+ psPaperSize letter
+ psDuplex no
+ psLevel level2
+ psEmbedType1Fonts yes
+ psEmbedTrueTypeFonts yes
+
+ # assume that the PostScript printer has the Univers and
+ # Univers-Bold fonts
+ psResidentFont Univers Univers
+ psResidentFont Univers-Bold Univers-Bold
+
+ # set the text output options
+ textEncoding UTF-8
+ textEOL unix
+
+ # misc options
+ enableFreeType yes
+ launchCommand viewer-script
+
+
+FILES
+ /usr/local/etc/xpdfrc
+ This is the default location for the system-wide configuration
+ file. Depending on build options, it may be placed elsewhere.
+
+ $HOME/.xpdfrc
+ This is the user's configuration file. If it exists, it will be
+ read in place of the system-wide file.
+
+AUTHOR
+ 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-
+ fonts(1), pdfdetach(1), pdftoppm(1), pdftopng(1), pdfimages(1)
+ http://www.xpdfreader.com/
+
+
+
+ 28 Jan 2021 xpdfrc(5)
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/CMakeLists.txt b/Build/source/libs/xpdf/xpdf-src/fofi/CMakeLists.txt
new file mode 100644
index 00000000000..076cfafb247
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/CMakeLists.txt
@@ -0,0 +1,26 @@
+#========================================================================
+#
+# fofi/CMakeLists.txt
+#
+# CMake script for the fofi library.
+#
+# Copyright 2015 Glyph & Cog, LLC
+#
+#========================================================================
+
+include_directories("${PROJECT_SOURCE_DIR}")
+include_directories("${PROJECT_BINARY_DIR}")
+include_directories("${PROJECT_SOURCE_DIR}/goo")
+
+add_library(fofi_objs OBJECT
+ FoFiBase.cc
+ FoFiEncodings.cc
+ FoFiIdentifier.cc
+ FoFiTrueType.cc
+ FoFiType1.cc
+ FoFiType1C.cc
+)
+
+add_library(fofi
+ $<TARGET_OBJECTS:fofi_objs>
+)
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiBase.cc b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiBase.cc
new file mode 100644
index 00000000000..e1951b08b9f
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiBase.cc
@@ -0,0 +1,177 @@
+//========================================================================
+//
+// FoFiBase.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <limits.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "FoFiBase.h"
+
+//------------------------------------------------------------------------
+// FoFiBase
+//------------------------------------------------------------------------
+
+FoFiBase::FoFiBase(char *fileA, int lenA, GBool freeFileDataA) {
+ fileData = file = (Guchar *)fileA;
+ len = lenA;
+ freeFileData = freeFileDataA;
+}
+
+FoFiBase::~FoFiBase() {
+ if (freeFileData) {
+ gfree(fileData);
+ }
+}
+
+char *FoFiBase::readFile(char *fileName, int *fileLen) {
+ FILE *f;
+ char *buf;
+ int n;
+
+ if (!(f = fopen(fileName, "rb"))) {
+ return NULL;
+ }
+ fseek(f, 0, SEEK_END);
+ n = (int)ftell(f);
+ if (n < 0) {
+ fclose(f);
+ return NULL;
+ }
+ fseek(f, 0, SEEK_SET);
+ buf = (char *)gmalloc(n);
+ if ((int)fread(buf, 1, n, f) != n) {
+ gfree(buf);
+ fclose(f);
+ return NULL;
+ }
+ fclose(f);
+ *fileLen = n;
+ return buf;
+}
+
+int FoFiBase::getS8(int pos, GBool *ok) {
+ int x;
+
+ if (pos < 0 || pos >= len) {
+ *ok = gFalse;
+ return 0;
+ }
+ x = file[pos];
+ if (x & 0x80) {
+ x |= ~0xff;
+ }
+ return x;
+}
+
+int FoFiBase::getU8(int pos, GBool *ok) {
+ if (pos < 0 || pos >= len) {
+ *ok = gFalse;
+ return 0;
+ }
+ return file[pos];
+}
+
+int FoFiBase::getS16BE(int pos, GBool *ok) {
+ int x;
+
+ if (pos < 0 || pos > INT_MAX - 1 || pos+1 >= len) {
+ *ok = gFalse;
+ return 0;
+ }
+ x = file[pos];
+ x = (x << 8) + file[pos+1];
+ if (x & 0x8000) {
+ x |= ~0xffff;
+ }
+ return x;
+}
+
+int FoFiBase::getU16BE(int pos, GBool *ok) {
+ int x;
+
+ if (pos < 0 || pos > INT_MAX - 1 || pos+1 >= len) {
+ *ok = gFalse;
+ return 0;
+ }
+ x = file[pos];
+ x = (x << 8) + file[pos+1];
+ return x;
+}
+
+int FoFiBase::getS32BE(int pos, GBool *ok) {
+ int x;
+
+ if (pos < 0 || pos > INT_MAX - 3 || pos+3 >= len) {
+ *ok = gFalse;
+ return 0;
+ }
+ x = file[pos];
+ x = (x << 8) + file[pos+1];
+ x = (x << 8) + file[pos+2];
+ x = (x << 8) + file[pos+3];
+ if (x & 0x80000000) {
+ x |= ~0xffffffff;
+ }
+ return x;
+}
+
+Guint FoFiBase::getU32BE(int pos, GBool *ok) {
+ Guint x;
+
+ if (pos < 0 || pos > INT_MAX - 3 || pos+3 >= len) {
+ *ok = gFalse;
+ return 0;
+ }
+ x = file[pos];
+ x = (x << 8) + file[pos+1];
+ x = (x << 8) + file[pos+2];
+ x = (x << 8) + file[pos+3];
+ return x;
+}
+
+Guint FoFiBase::getU32LE(int pos, GBool *ok) {
+ Guint x;
+
+ if (pos < 0 || pos > INT_MAX - 3 || pos+3 >= len) {
+ *ok = gFalse;
+ return 0;
+ }
+ x = file[pos+3];
+ x = (x << 8) + file[pos+2];
+ x = (x << 8) + file[pos+1];
+ x = (x << 8) + file[pos];
+ return x;
+}
+
+Guint FoFiBase::getUVarBE(int pos, int size, GBool *ok) {
+ Guint x;
+ int i;
+
+ if (pos < 0 || pos > INT_MAX - size || pos + size > len) {
+ *ok = gFalse;
+ return 0;
+ }
+ x = 0;
+ for (i = 0; i < size; ++i) {
+ x = (x << 8) + file[pos + i];
+ }
+ return x;
+}
+
+GBool FoFiBase::checkRegion(int pos, int size) {
+ return pos >= 0 &&
+ size >= 0 &&
+ size <= INT_MAX - pos &&
+ pos + size <= len;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiBase.h b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiBase.h
new file mode 100644
index 00000000000..c9f93ebacda
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiBase.h
@@ -0,0 +1,58 @@
+//========================================================================
+//
+// FoFiBase.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFIBASE_H
+#define FOFIBASE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+
+typedef void (*FoFiOutputFunc)(void *stream, const char *data, int len);
+
+//------------------------------------------------------------------------
+// FoFiBase
+//------------------------------------------------------------------------
+
+class FoFiBase {
+public:
+
+ virtual ~FoFiBase();
+
+protected:
+
+ FoFiBase(char *fileA, int lenA, GBool freeFileDataA);
+ static char *readFile(char *fileName, int *fileLen);
+
+ // S = signed / U = unsigned
+ // 8/16/32/Var = word length, in bytes
+ // BE = big endian
+ int getS8(int pos, GBool *ok);
+ int getU8(int pos, GBool *ok);
+ int getS16BE(int pos, GBool *ok);
+ int getU16BE(int pos, GBool *ok);
+ int getS32BE(int pos, GBool *ok);
+ Guint getU32BE(int pos, GBool *ok);
+ Guint getU32LE(int pos, GBool *ok);
+ Guint getUVarBE(int pos, int size, GBool *ok);
+
+ GBool checkRegion(int pos, int size);
+
+ Guchar *fileData;
+ Guchar *file;
+ int len;
+ GBool freeFileData;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiEncodings.cc b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiEncodings.cc
new file mode 100644
index 00000000000..6b3debb9dab
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiEncodings.cc
@@ -0,0 +1,995 @@
+//========================================================================
+//
+// FoFiEncodings.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include "gmempp.h"
+#include "FoFiEncodings.h"
+
+//------------------------------------------------------------------------
+// Type 1 and 1C font data
+//------------------------------------------------------------------------
+
+const char *fofiType1StandardEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quoteright",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "quoteleft",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "exclamdown",
+ "cent",
+ "sterling",
+ "fraction",
+ "yen",
+ "florin",
+ "section",
+ "currency",
+ "quotesingle",
+ "quotedblleft",
+ "guillemotleft",
+ "guilsinglleft",
+ "guilsinglright",
+ "fi",
+ "fl",
+ NULL,
+ "endash",
+ "dagger",
+ "daggerdbl",
+ "periodcentered",
+ NULL,
+ "paragraph",
+ "bullet",
+ "quotesinglbase",
+ "quotedblbase",
+ "quotedblright",
+ "guillemotright",
+ "ellipsis",
+ "perthousand",
+ NULL,
+ "questiondown",
+ NULL,
+ "grave",
+ "acute",
+ "circumflex",
+ "tilde",
+ "macron",
+ "breve",
+ "dotaccent",
+ "dieresis",
+ NULL,
+ "ring",
+ "cedilla",
+ NULL,
+ "hungarumlaut",
+ "ogonek",
+ "caron",
+ "emdash",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "AE",
+ NULL,
+ "ordfeminine",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Lslash",
+ "Oslash",
+ "OE",
+ "ordmasculine",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "ae",
+ NULL,
+ NULL,
+ NULL,
+ "dotlessi",
+ NULL,
+ NULL,
+ "lslash",
+ "oslash",
+ "oe",
+ "germandbls",
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+const char *fofiType1ExpertEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclamsmall",
+ "Hungarumlautsmall",
+ NULL,
+ "dollaroldstyle",
+ "dollarsuperior",
+ "ampersandsmall",
+ "Acutesmall",
+ "parenleftsuperior",
+ "parenrightsuperior",
+ "twodotenleader",
+ "onedotenleader",
+ "comma",
+ "hyphen",
+ "period",
+ "fraction",
+ "zerooldstyle",
+ "oneoldstyle",
+ "twooldstyle",
+ "threeoldstyle",
+ "fouroldstyle",
+ "fiveoldstyle",
+ "sixoldstyle",
+ "sevenoldstyle",
+ "eightoldstyle",
+ "nineoldstyle",
+ "colon",
+ "semicolon",
+ "commasuperior",
+ "threequartersemdash",
+ "periodsuperior",
+ "questionsmall",
+ NULL,
+ "asuperior",
+ "bsuperior",
+ "centsuperior",
+ "dsuperior",
+ "esuperior",
+ NULL,
+ NULL,
+ NULL,
+ "isuperior",
+ NULL,
+ NULL,
+ "lsuperior",
+ "msuperior",
+ "nsuperior",
+ "osuperior",
+ NULL,
+ NULL,
+ "rsuperior",
+ "ssuperior",
+ "tsuperior",
+ NULL,
+ "ff",
+ "fi",
+ "fl",
+ "ffi",
+ "ffl",
+ "parenleftinferior",
+ NULL,
+ "parenrightinferior",
+ "Circumflexsmall",
+ "hyphensuperior",
+ "Gravesmall",
+ "Asmall",
+ "Bsmall",
+ "Csmall",
+ "Dsmall",
+ "Esmall",
+ "Fsmall",
+ "Gsmall",
+ "Hsmall",
+ "Ismall",
+ "Jsmall",
+ "Ksmall",
+ "Lsmall",
+ "Msmall",
+ "Nsmall",
+ "Osmall",
+ "Psmall",
+ "Qsmall",
+ "Rsmall",
+ "Ssmall",
+ "Tsmall",
+ "Usmall",
+ "Vsmall",
+ "Wsmall",
+ "Xsmall",
+ "Ysmall",
+ "Zsmall",
+ "colonmonetary",
+ "onefitted",
+ "rupiah",
+ "Tildesmall",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "exclamdownsmall",
+ "centoldstyle",
+ "Lslashsmall",
+ NULL,
+ NULL,
+ "Scaronsmall",
+ "Zcaronsmall",
+ "Dieresissmall",
+ "Brevesmall",
+ "Caronsmall",
+ NULL,
+ "Dotaccentsmall",
+ NULL,
+ NULL,
+ "Macronsmall",
+ NULL,
+ NULL,
+ "figuredash",
+ "hypheninferior",
+ NULL,
+ NULL,
+ "Ogoneksmall",
+ "Ringsmall",
+ "Cedillasmall",
+ NULL,
+ NULL,
+ NULL,
+ "onequarter",
+ "onehalf",
+ "threequarters",
+ "questiondownsmall",
+ "oneeighth",
+ "threeeighths",
+ "fiveeighths",
+ "seveneighths",
+ "onethird",
+ "twothirds",
+ NULL,
+ NULL,
+ "zerosuperior",
+ "onesuperior",
+ "twosuperior",
+ "threesuperior",
+ "foursuperior",
+ "fivesuperior",
+ "sixsuperior",
+ "sevensuperior",
+ "eightsuperior",
+ "ninesuperior",
+ "zeroinferior",
+ "oneinferior",
+ "twoinferior",
+ "threeinferior",
+ "fourinferior",
+ "fiveinferior",
+ "sixinferior",
+ "seveninferior",
+ "eightinferior",
+ "nineinferior",
+ "centinferior",
+ "dollarinferior",
+ "periodinferior",
+ "commainferior",
+ "Agravesmall",
+ "Aacutesmall",
+ "Acircumflexsmall",
+ "Atildesmall",
+ "Adieresissmall",
+ "Aringsmall",
+ "AEsmall",
+ "Ccedillasmall",
+ "Egravesmall",
+ "Eacutesmall",
+ "Ecircumflexsmall",
+ "Edieresissmall",
+ "Igravesmall",
+ "Iacutesmall",
+ "Icircumflexsmall",
+ "Idieresissmall",
+ "Ethsmall",
+ "Ntildesmall",
+ "Ogravesmall",
+ "Oacutesmall",
+ "Ocircumflexsmall",
+ "Otildesmall",
+ "Odieresissmall",
+ "OEsmall",
+ "Oslashsmall",
+ "Ugravesmall",
+ "Uacutesmall",
+ "Ucircumflexsmall",
+ "Udieresissmall",
+ "Yacutesmall",
+ "Thornsmall",
+ "Ydieresissmall"
+};
+
+//------------------------------------------------------------------------
+// Type 1C font data
+//------------------------------------------------------------------------
+
+const char *fofiType1CStdStrings[391] = {
+ ".notdef",
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quoteright",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "quoteleft",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ "exclamdown",
+ "cent",
+ "sterling",
+ "fraction",
+ "yen",
+ "florin",
+ "section",
+ "currency",
+ "quotesingle",
+ "quotedblleft",
+ "guillemotleft",
+ "guilsinglleft",
+ "guilsinglright",
+ "fi",
+ "fl",
+ "endash",
+ "dagger",
+ "daggerdbl",
+ "periodcentered",
+ "paragraph",
+ "bullet",
+ "quotesinglbase",
+ "quotedblbase",
+ "quotedblright",
+ "guillemotright",
+ "ellipsis",
+ "perthousand",
+ "questiondown",
+ "grave",
+ "acute",
+ "circumflex",
+ "tilde",
+ "macron",
+ "breve",
+ "dotaccent",
+ "dieresis",
+ "ring",
+ "cedilla",
+ "hungarumlaut",
+ "ogonek",
+ "caron",
+ "emdash",
+ "AE",
+ "ordfeminine",
+ "Lslash",
+ "Oslash",
+ "OE",
+ "ordmasculine",
+ "ae",
+ "dotlessi",
+ "lslash",
+ "oslash",
+ "oe",
+ "germandbls",
+ "onesuperior",
+ "logicalnot",
+ "mu",
+ "trademark",
+ "Eth",
+ "onehalf",
+ "plusminus",
+ "Thorn",
+ "onequarter",
+ "divide",
+ "brokenbar",
+ "degree",
+ "thorn",
+ "threequarters",
+ "twosuperior",
+ "registered",
+ "minus",
+ "eth",
+ "multiply",
+ "threesuperior",
+ "copyright",
+ "Aacute",
+ "Acircumflex",
+ "Adieresis",
+ "Agrave",
+ "Aring",
+ "Atilde",
+ "Ccedilla",
+ "Eacute",
+ "Ecircumflex",
+ "Edieresis",
+ "Egrave",
+ "Iacute",
+ "Icircumflex",
+ "Idieresis",
+ "Igrave",
+ "Ntilde",
+ "Oacute",
+ "Ocircumflex",
+ "Odieresis",
+ "Ograve",
+ "Otilde",
+ "Scaron",
+ "Uacute",
+ "Ucircumflex",
+ "Udieresis",
+ "Ugrave",
+ "Yacute",
+ "Ydieresis",
+ "Zcaron",
+ "aacute",
+ "acircumflex",
+ "adieresis",
+ "agrave",
+ "aring",
+ "atilde",
+ "ccedilla",
+ "eacute",
+ "ecircumflex",
+ "edieresis",
+ "egrave",
+ "iacute",
+ "icircumflex",
+ "idieresis",
+ "igrave",
+ "ntilde",
+ "oacute",
+ "ocircumflex",
+ "odieresis",
+ "ograve",
+ "otilde",
+ "scaron",
+ "uacute",
+ "ucircumflex",
+ "udieresis",
+ "ugrave",
+ "yacute",
+ "ydieresis",
+ "zcaron",
+ "exclamsmall",
+ "Hungarumlautsmall",
+ "dollaroldstyle",
+ "dollarsuperior",
+ "ampersandsmall",
+ "Acutesmall",
+ "parenleftsuperior",
+ "parenrightsuperior",
+ "twodotenleader",
+ "onedotenleader",
+ "zerooldstyle",
+ "oneoldstyle",
+ "twooldstyle",
+ "threeoldstyle",
+ "fouroldstyle",
+ "fiveoldstyle",
+ "sixoldstyle",
+ "sevenoldstyle",
+ "eightoldstyle",
+ "nineoldstyle",
+ "commasuperior",
+ "threequartersemdash",
+ "periodsuperior",
+ "questionsmall",
+ "asuperior",
+ "bsuperior",
+ "centsuperior",
+ "dsuperior",
+ "esuperior",
+ "isuperior",
+ "lsuperior",
+ "msuperior",
+ "nsuperior",
+ "osuperior",
+ "rsuperior",
+ "ssuperior",
+ "tsuperior",
+ "ff",
+ "ffi",
+ "ffl",
+ "parenleftinferior",
+ "parenrightinferior",
+ "Circumflexsmall",
+ "hyphensuperior",
+ "Gravesmall",
+ "Asmall",
+ "Bsmall",
+ "Csmall",
+ "Dsmall",
+ "Esmall",
+ "Fsmall",
+ "Gsmall",
+ "Hsmall",
+ "Ismall",
+ "Jsmall",
+ "Ksmall",
+ "Lsmall",
+ "Msmall",
+ "Nsmall",
+ "Osmall",
+ "Psmall",
+ "Qsmall",
+ "Rsmall",
+ "Ssmall",
+ "Tsmall",
+ "Usmall",
+ "Vsmall",
+ "Wsmall",
+ "Xsmall",
+ "Ysmall",
+ "Zsmall",
+ "colonmonetary",
+ "onefitted",
+ "rupiah",
+ "Tildesmall",
+ "exclamdownsmall",
+ "centoldstyle",
+ "Lslashsmall",
+ "Scaronsmall",
+ "Zcaronsmall",
+ "Dieresissmall",
+ "Brevesmall",
+ "Caronsmall",
+ "Dotaccentsmall",
+ "Macronsmall",
+ "figuredash",
+ "hypheninferior",
+ "Ogoneksmall",
+ "Ringsmall",
+ "Cedillasmall",
+ "questiondownsmall",
+ "oneeighth",
+ "threeeighths",
+ "fiveeighths",
+ "seveneighths",
+ "onethird",
+ "twothirds",
+ "zerosuperior",
+ "foursuperior",
+ "fivesuperior",
+ "sixsuperior",
+ "sevensuperior",
+ "eightsuperior",
+ "ninesuperior",
+ "zeroinferior",
+ "oneinferior",
+ "twoinferior",
+ "threeinferior",
+ "fourinferior",
+ "fiveinferior",
+ "sixinferior",
+ "seveninferior",
+ "eightinferior",
+ "nineinferior",
+ "centinferior",
+ "dollarinferior",
+ "periodinferior",
+ "commainferior",
+ "Agravesmall",
+ "Aacutesmall",
+ "Acircumflexsmall",
+ "Atildesmall",
+ "Adieresissmall",
+ "Aringsmall",
+ "AEsmall",
+ "Ccedillasmall",
+ "Egravesmall",
+ "Eacutesmall",
+ "Ecircumflexsmall",
+ "Edieresissmall",
+ "Igravesmall",
+ "Iacutesmall",
+ "Icircumflexsmall",
+ "Idieresissmall",
+ "Ethsmall",
+ "Ntildesmall",
+ "Ogravesmall",
+ "Oacutesmall",
+ "Ocircumflexsmall",
+ "Otildesmall",
+ "Odieresissmall",
+ "OEsmall",
+ "Oslashsmall",
+ "Ugravesmall",
+ "Uacutesmall",
+ "Ucircumflexsmall",
+ "Udieresissmall",
+ "Yacutesmall",
+ "Thornsmall",
+ "Ydieresissmall",
+ "001.000",
+ "001.001",
+ "001.002",
+ "001.003",
+ "Black",
+ "Bold",
+ "Book",
+ "Light",
+ "Medium",
+ "Regular",
+ "Roman",
+ "Semibold"
+};
+
+Gushort fofiType1CISOAdobeCharset[229] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228
+};
+
+Gushort fofiType1CExpertCharset[166] = {
+ 0, 1, 229, 230, 231, 232, 233, 234, 235, 236,
+ 237, 238, 13, 14, 15, 99, 239, 240, 241, 242,
+ 243, 244, 245, 246, 247, 248, 27, 28, 249, 250,
+ 251, 252, 253, 254, 255, 256, 257, 258, 259, 260,
+ 261, 262, 263, 264, 265, 266, 109, 110, 267, 268,
+ 269, 270, 271, 272, 273, 274, 275, 276, 277, 278,
+ 279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, 318,
+ 158, 155, 163, 319, 320, 321, 322, 323, 324, 325,
+ 326, 150, 164, 169, 327, 328, 329, 330, 331, 332,
+ 333, 334, 335, 336, 337, 338, 339, 340, 341, 342,
+ 343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
+ 353, 354, 355, 356, 357, 358, 359, 360, 361, 362,
+ 363, 364, 365, 366, 367, 368, 369, 370, 371, 372,
+ 373, 374, 375, 376, 377, 378
+};
+
+Gushort fofiType1CExpertSubsetCharset[87] = {
+ 0, 1, 231, 232, 235, 236, 237, 238, 13, 14,
+ 15, 99, 239, 240, 241, 242, 243, 244, 245, 246,
+ 247, 248, 27, 28, 249, 250, 251, 253, 254, 255,
+ 256, 257, 258, 259, 260, 261, 262, 263, 264, 265,
+ 266, 109, 110, 267, 268, 269, 270, 272, 300, 301,
+ 302, 305, 314, 315, 158, 155, 163, 320, 321, 322,
+ 323, 324, 325, 326, 150, 164, 169, 327, 328, 329,
+ 330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
+ 340, 341, 342, 343, 344, 345, 346
+};
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiEncodings.h b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiEncodings.h
new file mode 100644
index 00000000000..dd85458c070
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiEncodings.h
@@ -0,0 +1,36 @@
+//========================================================================
+//
+// FoFiEncodings.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFIENCODINGS_H
+#define FOFIENCODINGS_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+// Type 1 and 1C font data
+//------------------------------------------------------------------------
+
+extern const char *fofiType1StandardEncoding[256];
+extern const char *fofiType1ExpertEncoding[256];
+
+//------------------------------------------------------------------------
+// Type 1C font data
+//------------------------------------------------------------------------
+
+extern const char *fofiType1CStdStrings[391];
+extern Gushort fofiType1CISOAdobeCharset[229];
+extern Gushort fofiType1CExpertCharset[166];
+extern Gushort fofiType1CExpertSubsetCharset[87];
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc
new file mode 100644
index 00000000000..4c6797ba9d6
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.cc
@@ -0,0 +1,887 @@
+//========================================================================
+//
+// FoFiIdentifier.cc
+//
+// Copyright 2009 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include "gtypes.h"
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "FoFiIdentifier.h"
+
+//------------------------------------------------------------------------
+
+class Reader {
+public:
+
+ virtual ~Reader() {}
+
+ // Read one byte. Returns -1 if past EOF.
+ virtual int getByte(int pos) = 0;
+
+ // Read a big-endian unsigned 16-bit integer. Fills in *val and
+ // returns true if successful.
+ virtual GBool getU16BE(int pos, int *val) = 0;
+
+ // Read a big-endian unsigned 32-bit integer. Fills in *val and
+ // returns true if successful.
+ virtual GBool getU32BE(int pos, Guint *val) = 0;
+
+ // Read a little-endian unsigned 32-bit integer. Fills in *val and
+ // returns true if successful.
+ virtual GBool getU32LE(int pos, Guint *val) = 0;
+
+ // Read a big-endian unsigned <size>-byte integer, where 1 <= size
+ // <= 4. Fills in *val and returns true if successful.
+ virtual GBool getUVarBE(int pos, int size, Guint *val) = 0;
+
+ // Compare against a string. Returns true if equal.
+ virtual GBool cmp(int pos, const char *s) = 0;
+
+};
+
+//------------------------------------------------------------------------
+
+class MemReader: public Reader {
+public:
+
+ static MemReader *make(char *bufA, int lenA);
+ virtual ~MemReader();
+ virtual int getByte(int pos);
+ virtual GBool getU16BE(int pos, int *val);
+ virtual GBool getU32BE(int pos, Guint *val);
+ virtual GBool getU32LE(int pos, Guint *val);
+ virtual GBool getUVarBE(int pos, int size, Guint *val);
+ virtual GBool cmp(int pos, const char *s);
+
+private:
+
+ MemReader(char *bufA, int lenA);
+
+ char *buf;
+ int len;
+};
+
+MemReader *MemReader::make(char *bufA, int lenA) {
+ return new MemReader(bufA, lenA);
+}
+
+MemReader::MemReader(char *bufA, int lenA) {
+ buf = bufA;
+ len = lenA;
+}
+
+MemReader::~MemReader() {
+}
+
+int MemReader::getByte(int pos) {
+ if (pos < 0 || pos >= len) {
+ return -1;
+ }
+ return buf[pos] & 0xff;
+}
+
+GBool MemReader::getU16BE(int pos, int *val) {
+ if (pos < 0 || pos > len - 2) {
+ return gFalse;
+ }
+ *val = ((buf[pos] & 0xff) << 8) +
+ (buf[pos+1] & 0xff);
+ return gTrue;
+}
+
+GBool MemReader::getU32BE(int pos, Guint *val) {
+ if (pos < 0 || pos > len - 4) {
+ return gFalse;
+ }
+ *val = ((buf[pos] & 0xff) << 24) +
+ ((buf[pos+1] & 0xff) << 16) +
+ ((buf[pos+2] & 0xff) << 8) +
+ (buf[pos+3] & 0xff);
+ return gTrue;
+}
+
+GBool MemReader::getU32LE(int pos, Guint *val) {
+ if (pos < 0 || pos > len - 4) {
+ return gFalse;
+ }
+ *val = (buf[pos] & 0xff) +
+ ((buf[pos+1] & 0xff) << 8) +
+ ((buf[pos+2] & 0xff) << 16) +
+ ((buf[pos+3] & 0xff) << 24);
+ return gTrue;
+}
+
+GBool MemReader::getUVarBE(int pos, int size, Guint *val) {
+ int i;
+
+ if (size < 1 || size > 4 || pos < 0 || pos > len - size) {
+ return gFalse;
+ }
+ *val = 0;
+ for (i = 0; i < size; ++i) {
+ *val = (*val << 8) + (buf[pos + i] & 0xff);
+ }
+ return gTrue;
+}
+
+GBool MemReader::cmp(int pos, const char *s) {
+ int n;
+
+ n = (int)strlen(s);
+ if (pos < 0 || len < n || pos > len - n) {
+ return gFalse;
+ }
+ return !memcmp(buf + pos, s, n);
+}
+
+//------------------------------------------------------------------------
+
+class FileReader: public Reader {
+public:
+
+ static FileReader *make(char *fileName);
+ virtual ~FileReader();
+ virtual int getByte(int pos);
+ virtual GBool getU16BE(int pos, int *val);
+ virtual GBool getU32BE(int pos, Guint *val);
+ virtual GBool getU32LE(int pos, Guint *val);
+ virtual GBool getUVarBE(int pos, int size, Guint *val);
+ virtual GBool cmp(int pos, const char *s);
+
+private:
+
+ FileReader(FILE *fA);
+ GBool fillBuf(int pos, int len);
+
+ FILE *f;
+ char buf[1024];
+ int bufPos, bufLen;
+};
+
+FileReader *FileReader::make(char *fileName) {
+ FILE *fA;
+
+ if (!(fA = fopen(fileName, "rb"))) {
+ return NULL;
+ }
+ return new FileReader(fA);
+}
+
+FileReader::FileReader(FILE *fA) {
+ f = fA;
+ bufPos = 0;
+ bufLen = 0;
+}
+
+FileReader::~FileReader() {
+ fclose(f);
+}
+
+int FileReader::getByte(int pos) {
+ if (!fillBuf(pos, 1)) {
+ return -1;
+ }
+ return buf[pos - bufPos] & 0xff;
+}
+
+GBool FileReader::getU16BE(int pos, int *val) {
+ if (!fillBuf(pos, 2)) {
+ return gFalse;
+ }
+ *val = ((buf[pos - bufPos] & 0xff) << 8) +
+ (buf[pos - bufPos + 1] & 0xff);
+ return gTrue;
+}
+
+GBool FileReader::getU32BE(int pos, Guint *val) {
+ if (!fillBuf(pos, 4)) {
+ return gFalse;
+ }
+ *val = ((buf[pos - bufPos] & 0xff) << 24) +
+ ((buf[pos - bufPos + 1] & 0xff) << 16) +
+ ((buf[pos - bufPos + 2] & 0xff) << 8) +
+ (buf[pos - bufPos + 3] & 0xff);
+ return gTrue;
+}
+
+GBool FileReader::getU32LE(int pos, Guint *val) {
+ if (!fillBuf(pos, 4)) {
+ return gFalse;
+ }
+ *val = (buf[pos - bufPos] & 0xff) +
+ ((buf[pos - bufPos + 1] & 0xff) << 8) +
+ ((buf[pos - bufPos + 2] & 0xff) << 16) +
+ ((buf[pos - bufPos + 3] & 0xff) << 24);
+ return gTrue;
+}
+
+GBool FileReader::getUVarBE(int pos, int size, Guint *val) {
+ int i;
+
+ if (size < 1 || size > 4 || !fillBuf(pos, size)) {
+ return gFalse;
+ }
+ *val = 0;
+ for (i = 0; i < size; ++i) {
+ *val = (*val << 8) + (buf[pos - bufPos + i] & 0xff);
+ }
+ return gTrue;
+}
+
+GBool FileReader::cmp(int pos, const char *s) {
+ int n;
+
+ n = (int)strlen(s);
+ if (!fillBuf(pos, n)) {
+ return gFalse;
+ }
+ return !memcmp(buf - bufPos + pos, s, n);
+}
+
+GBool FileReader::fillBuf(int pos, int len) {
+ if (pos < 0 || len < 0 || len > (int)sizeof(buf) ||
+ pos > INT_MAX - (int)sizeof(buf)) {
+ return gFalse;
+ }
+ if (pos >= bufPos && pos + len <= bufPos + bufLen) {
+ return gTrue;
+ }
+ if (fseek(f, pos, SEEK_SET)) {
+ return gFalse;
+ }
+ bufPos = pos;
+ bufLen = (int)fread(buf, 1, sizeof(buf), f);
+ if (bufLen < len) {
+ return gFalse;
+ }
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+
+class StreamReader: public Reader {
+public:
+
+ static StreamReader *make(int (*getCharA)(void *data), void *dataA);
+ virtual ~StreamReader();
+ virtual int getByte(int pos);
+ virtual GBool getU16BE(int pos, int *val);
+ virtual GBool getU32BE(int pos, Guint *val);
+ virtual GBool getU32LE(int pos, Guint *val);
+ virtual GBool getUVarBE(int pos, int size, Guint *val);
+ virtual GBool cmp(int pos, const char *s);
+
+private:
+
+ StreamReader(int (*getCharA)(void *data), void *dataA);
+ GBool fillBuf(int pos, int len);
+
+ int (*getChar)(void *data);
+ void *data;
+ int streamPos;
+ char buf[1024];
+ int bufPos, bufLen;
+};
+
+StreamReader *StreamReader::make(int (*getCharA)(void *data), void *dataA) {
+ return new StreamReader(getCharA, dataA);
+}
+
+StreamReader::StreamReader(int (*getCharA)(void *data), void *dataA) {
+ getChar = getCharA;
+ data = dataA;
+ streamPos = 0;
+ bufPos = 0;
+ bufLen = 0;
+}
+
+StreamReader::~StreamReader() {
+}
+
+int StreamReader::getByte(int pos) {
+ if (!fillBuf(pos, 1)) {
+ return -1;
+ }
+ return buf[pos - bufPos] & 0xff;
+}
+
+GBool StreamReader::getU16BE(int pos, int *val) {
+ if (!fillBuf(pos, 2)) {
+ return gFalse;
+ }
+ *val = ((buf[pos - bufPos] & 0xff) << 8) +
+ (buf[pos - bufPos + 1] & 0xff);
+ return gTrue;
+}
+
+GBool StreamReader::getU32BE(int pos, Guint *val) {
+ if (!fillBuf(pos, 4)) {
+ return gFalse;
+ }
+ *val = ((buf[pos - bufPos] & 0xff) << 24) +
+ ((buf[pos - bufPos + 1] & 0xff) << 16) +
+ ((buf[pos - bufPos + 2] & 0xff) << 8) +
+ (buf[pos - bufPos + 3] & 0xff);
+ return gTrue;
+}
+
+GBool StreamReader::getU32LE(int pos, Guint *val) {
+ if (!fillBuf(pos, 4)) {
+ return gFalse;
+ }
+ *val = (buf[pos - bufPos] & 0xff) +
+ ((buf[pos - bufPos + 1] & 0xff) << 8) +
+ ((buf[pos - bufPos + 2] & 0xff) << 16) +
+ ((buf[pos - bufPos + 3] & 0xff) << 24);
+ return gTrue;
+}
+
+GBool StreamReader::getUVarBE(int pos, int size, Guint *val) {
+ int i;
+
+ if (size < 1 || size > 4 || !fillBuf(pos, size)) {
+ return gFalse;
+ }
+ *val = 0;
+ for (i = 0; i < size; ++i) {
+ *val = (*val << 8) + (buf[pos - bufPos + i] & 0xff);
+ }
+ return gTrue;
+}
+
+GBool StreamReader::cmp(int pos, const char *s) {
+ int n;
+
+ n = (int)strlen(s);
+ if (!fillBuf(pos, n)) {
+ return gFalse;
+ }
+ return !memcmp(buf - bufPos + pos, s, n);
+}
+
+GBool StreamReader::fillBuf(int pos, int len) {
+ int c;
+
+ if (pos < 0 || len < 0 || len > (int)sizeof(buf) ||
+ pos > INT_MAX - (int)sizeof(buf)) {
+ return gFalse;
+ }
+ if (pos < bufPos) {
+ return gFalse;
+ }
+
+ // if requested region will not fit in the current buffer...
+ if (pos + len > bufPos + (int)sizeof(buf)) {
+
+ // if the start of the requested data is already in the buffer, move
+ // it to the start of the buffer
+ if (pos < bufPos + bufLen) {
+ bufLen -= pos - bufPos;
+ memmove(buf, buf + (pos - bufPos), bufLen);
+ bufPos = pos;
+
+ // otherwise discard data from the
+ // stream until we get to the requested position
+ } else {
+ bufPos += bufLen;
+ bufLen = 0;
+ while (bufPos < pos) {
+ if ((c = (*getChar)(data)) < 0) {
+ return gFalse;
+ }
+ ++bufPos;
+ }
+ }
+ }
+
+ // read the rest of the requested data
+ while (bufPos + bufLen < pos + len) {
+ if ((c = (*getChar)(data)) < 0) {
+ return gFalse;
+ }
+ buf[bufLen++] = (char)c;
+ }
+
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+
+static FoFiIdentifierType identify(Reader *reader);
+static FoFiIdentifierType identifyOpenType(Reader *reader);
+static FoFiIdentifierType identifyCFF(Reader *reader, int start);
+
+FoFiIdentifierType FoFiIdentifier::identifyMem(char *file, int len) {
+ MemReader *reader;
+ FoFiIdentifierType type;
+
+ if (!(reader = MemReader::make(file, len))) {
+ return fofiIdError;
+ }
+ type = identify(reader);
+ delete reader;
+ return type;
+}
+
+FoFiIdentifierType FoFiIdentifier::identifyFile(char *fileName) {
+ FileReader *reader;
+ FoFiIdentifierType type;
+ int n;
+
+ if (!(reader = FileReader::make(fileName))) {
+ return fofiIdError;
+ }
+ type = identify(reader);
+ delete reader;
+
+ // Mac OS X dfont files don't have any sort of header or magic number,
+ // so look at the file name extension
+ if (type == fofiIdUnknown) {
+ n = (int)strlen(fileName);
+ if (n >= 6 && !strcmp(fileName + n - 6, ".dfont")) {
+ type = fofiIdDfont;
+ }
+ }
+
+ return type;
+}
+
+FoFiIdentifierType FoFiIdentifier::identifyStream(int (*getChar)(void *data),
+ void *data) {
+ StreamReader *reader;
+ FoFiIdentifierType type;
+
+ if (!(reader = StreamReader::make(getChar, data))) {
+ return fofiIdError;
+ }
+ type = identify(reader);
+ delete reader;
+ return type;
+}
+
+static FoFiIdentifierType identify(Reader *reader) {
+ Guint n;
+
+ //----- PFA
+ if (reader->cmp(0, "%!PS-AdobeFont-1") ||
+ reader->cmp(0, "%!FontType1")) {
+ return fofiIdType1PFA;
+ }
+
+ //----- PFB
+ if (reader->getByte(0) == 0x80 &&
+ reader->getByte(1) == 0x01 &&
+ reader->getU32LE(2, &n)) {
+ if ((n >= 16 && reader->cmp(6, "%!PS-AdobeFont-1")) ||
+ (n >= 11 && reader->cmp(6, "%!FontType1"))) {
+ return fofiIdType1PFB;
+ }
+ }
+
+ //----- TrueType
+ if ((reader->getByte(0) == 0x00 &&
+ reader->getByte(1) == 0x01 &&
+ reader->getByte(2) == 0x00 &&
+ reader->getByte(3) == 0x00) ||
+ (reader->getByte(0) == 0x74 && // 'true'
+ reader->getByte(1) == 0x72 &&
+ reader->getByte(2) == 0x75 &&
+ reader->getByte(3) == 0x65)) {
+ return fofiIdTrueType;
+ }
+ if (reader->getByte(0) == 0x74 && // 'ttcf'
+ reader->getByte(1) == 0x74 &&
+ reader->getByte(2) == 0x63 &&
+ reader->getByte(3) == 0x66) {
+ return fofiIdTrueTypeCollection;
+ }
+
+ //----- OpenType
+ if (reader->getByte(0) == 0x4f && // 'OTTO
+ reader->getByte(1) == 0x54 &&
+ reader->getByte(2) == 0x54 &&
+ reader->getByte(3) == 0x4f) {
+ return identifyOpenType(reader);
+ }
+
+ //----- CFF
+ if (reader->getByte(0) == 0x01 &&
+ reader->getByte(1) == 0x00) {
+ return identifyCFF(reader, 0);
+ }
+ // some tools embed CFF fonts with an extra whitespace char at the
+ // beginning
+ if (reader->getByte(1) == 0x01 &&
+ reader->getByte(2) == 0x00) {
+ return identifyCFF(reader, 1);
+ }
+
+ return fofiIdUnknown;
+}
+
+static FoFiIdentifierType identifyOpenType(Reader *reader) {
+ FoFiIdentifierType type;
+ Guint offset;
+ int nTables, i;
+
+ if (!reader->getU16BE(4, &nTables)) {
+ return fofiIdUnknown;
+ }
+ for (i = 0; i < nTables; ++i) {
+ if (reader->cmp(12 + i*16, "CFF ")) {
+ if (reader->getU32BE(12 + i*16 + 8, &offset) &&
+ offset < (Guint)INT_MAX) {
+ type = identifyCFF(reader, (int)offset);
+ if (type == fofiIdCFF8Bit) {
+ type = fofiIdOpenTypeCFF8Bit;
+ } else if (type == fofiIdCFFCID) {
+ type = fofiIdOpenTypeCFFCID;
+ }
+ return type;
+ }
+ return fofiIdUnknown;
+ }
+ }
+ return fofiIdUnknown;
+}
+
+static FoFiIdentifierType identifyCFF(Reader *reader, int start) {
+ Guint offset0, offset1;
+ int hdrSize, offSize0, offSize1, pos, endPos, b0, n;
+
+ //----- read the header
+ if (reader->getByte(start) != 0x01 ||
+ reader->getByte(start + 1) != 0x00) {
+ return fofiIdUnknown;
+ }
+ if ((hdrSize = reader->getByte(start + 2)) < 0) {
+ return fofiIdUnknown;
+ }
+ if ((offSize0 = reader->getByte(start + 3)) < 1 || offSize0 > 4) {
+ return fofiIdUnknown;
+ }
+ pos = start + hdrSize;
+ if (pos < 0) {
+ return fofiIdUnknown;
+ }
+
+ //----- skip the name index
+ if (!reader->getU16BE(pos, &n)) {
+ return fofiIdUnknown;
+ }
+ if (n == 0) {
+ pos += 2;
+ } else {
+ if ((offSize1 = reader->getByte(pos + 2)) < 1 || offSize1 > 4) {
+ return fofiIdUnknown;
+ }
+ if (!reader->getUVarBE(pos + 3 + n * offSize1, offSize1, &offset1) ||
+ offset1 > (Guint)INT_MAX) {
+ return fofiIdUnknown;
+ }
+ pos += 3 + (n + 1) * offSize1 + (int)offset1 - 1;
+ }
+ if (pos < 0) {
+ return fofiIdUnknown;
+ }
+
+ //----- parse the top dict index
+ if (!reader->getU16BE(pos, &n) || n < 1) {
+ return fofiIdUnknown;
+ }
+ if ((offSize1 = reader->getByte(pos + 2)) < 1 || offSize1 > 4) {
+ return fofiIdUnknown;
+ }
+ if (!reader->getUVarBE(pos + 3, offSize1, &offset0) ||
+ offset0 > (Guint)INT_MAX ||
+ !reader->getUVarBE(pos + 3 + offSize1, offSize1, &offset1) ||
+ offset1 > (Guint)INT_MAX ||
+ offset0 > offset1) {
+ return fofiIdUnknown;
+ }
+ pos = pos + 3 + (n + 1) * offSize1 + (int)offset0 - 1;
+ endPos = pos + 3 + (n + 1) * offSize1 + (int)offset1 - 1;
+ if (pos < 0 || endPos < 0 || pos > endPos) {
+ return fofiIdUnknown;
+ }
+
+ //----- parse the top dict, look for ROS as first entry
+ // for a CID font, the top dict starts with:
+ // <int> <int> <int> ROS
+ while (pos >= 0 && pos < endPos) {
+ b0 = reader->getByte(pos);
+ if (b0 == 0x1c) {
+ pos += 3;
+ } else if (b0 == 0x1d) {
+ pos += 5;
+ } else if (b0 >= 0xf7 && b0 <= 0xfe) {
+ pos += 2;
+ } else if (b0 >= 0x20 && b0 <= 0xf6) {
+ pos += 1;
+ } else {
+ break;
+ }
+ }
+ if (pos + 1 < endPos &&
+ reader->getByte(pos) == 12 &&
+ reader->getByte(pos + 1) == 30) {
+ return fofiIdCFFCID;
+ } else {
+ return fofiIdCFF8Bit;
+ }
+}
+
+//------------------------------------------------------------------------
+
+static GList *getTTCFontList(FILE *f);
+static GList *getDfontFontList(FILE *f);
+
+GList *FoFiIdentifier::getFontList(char *fileName) {
+ FILE *f;
+ char buf[4];
+ GList *ret;
+
+ if (!(f = fopen(fileName, "rb"))) {
+ return NULL;
+ }
+ if (fread(buf, 1, 4, f) == 4 &&
+ buf[0] == 0x74 && // 'ttcf'
+ buf[1] == 0x74 &&
+ buf[2] == 0x63 &&
+ buf[3] == 0x66) {
+ ret = getTTCFontList(f);
+ } else {
+ ret = getDfontFontList(f);
+ }
+ fclose(f);
+ return ret;
+}
+
+static GList *getTTCFontList(FILE *f) {
+ Guchar buf[12];
+ Guchar *buf2;
+ int fileLength, nFonts;
+ int tabDirOffset, nTables, nameTabOffset, nNames, stringsOffset;
+ int stringPlatform, stringLength, stringOffset;
+ GBool stringUnicode;
+ int i, j;
+ GList *ret;
+
+ fseek(f, 0, SEEK_END);
+ fileLength = (int)ftell(f);
+ if (fileLength < 0) {
+ goto err1;
+ }
+ fseek(f, 8, SEEK_SET);
+ if (fread(buf, 1, 4, f) != 4) {
+ goto err1;
+ }
+ nFonts = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ if (nFonts < 0 ||
+ 12 + 4 * nFonts > fileLength) {
+ goto err1;
+ }
+ ret = new GList();
+ for (i = 0; i < nFonts; ++i) {
+ fseek(f, 12 + 4 * i, SEEK_SET);
+ if (fread(buf, 1, 4, f) != 4) {
+ goto err2;
+ }
+ tabDirOffset = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ if (tabDirOffset < 0 ||
+ tabDirOffset + 12 < 0 ||
+ tabDirOffset + 12 > fileLength) {
+ goto err2;
+ }
+ fseek(f, tabDirOffset, SEEK_SET);
+ if (fread(buf, 1, 12, f) != 12) {
+ goto err2;
+ }
+ nTables = (buf[4] << 8) | buf[5];
+ if (tabDirOffset + 12 + 16 * nTables < 0 ||
+ tabDirOffset + 12 + 16 * nTables > fileLength) {
+ goto err2;
+ }
+ buf2 = (Guchar *)gmallocn(nTables, 16);
+ if ((int)fread(buf2, 1, 16 * nTables, f) != 16 * nTables) {
+ goto err3;
+ }
+ nameTabOffset = 0; // make gcc happy
+ for (j = 0; j < nTables; ++j) {
+ if (buf2[16*j + 0] == 'n' &&
+ buf2[16*j + 1] == 'a' &&
+ buf2[16*j + 2] == 'm' &&
+ buf2[16*j + 3] == 'e') {
+ nameTabOffset = (buf2[16*j + 8] << 24) | (buf2[16*j + 9] << 16) |
+ (buf2[16*j + 10] << 8) | buf2[16*j + 11];
+ break;
+ }
+ }
+ gfree(buf2);
+ if (j >= nTables) {
+ goto err2;
+ }
+ if (nameTabOffset < 0 ||
+ nameTabOffset + 6 < 0 ||
+ nameTabOffset + 6 > fileLength) {
+ goto err2;
+ }
+ fseek(f, nameTabOffset, SEEK_SET);
+ if (fread(buf, 1, 6, f) != 6) {
+ goto err2;
+ }
+ nNames = (buf[2] << 8) | buf[3];
+ stringsOffset = (buf[4] << 8) | buf[5];
+ if (nameTabOffset + 6 + 12 * nNames < 0 ||
+ nameTabOffset + 6 + 12 * nNames > fileLength ||
+ nameTabOffset + stringsOffset < 0) {
+ goto err2;
+ }
+ buf2 = (Guchar *)gmallocn(nNames, 12);
+ if ((int)fread(buf2, 1, 12 * nNames, f) != 12 * nNames) {
+ goto err3;
+ }
+ for (j = 0; j < nNames; ++j) {
+ if (buf2[12*j + 6] == 0 && // 0x0004 = full name
+ buf2[12*j + 7] == 4) {
+ break;
+ }
+ }
+ if (j >= nNames) {
+ goto err3;
+ }
+ stringPlatform = (buf2[12*j] << 8) | buf2[12*j + 1];
+ // stringEncoding = (buf2[12*j + 2] << 8) | buf2[12*j + 3];
+ stringUnicode = stringPlatform == 0 || stringPlatform == 3;
+ stringLength = (buf2[12*j + 8] << 8) | buf2[12*j + 9];
+ stringOffset = nameTabOffset + stringsOffset +
+ ((buf2[12*j + 10] << 8) | buf2[12*j + 11]);
+ gfree(buf2);
+ if (stringOffset < 0 ||
+ stringOffset + stringLength < 0 ||
+ stringOffset + stringLength > fileLength) {
+ goto err2;
+ }
+ buf2 = (Guchar *)gmalloc(stringLength);
+ fseek(f, stringOffset, SEEK_SET);
+ if ((int)fread(buf2, 1, stringLength, f) != stringLength) {
+ goto err3;
+ }
+ if (stringUnicode) {
+ stringLength /= 2;
+ for (j = 0; j < stringLength; ++j) {
+ buf2[j] = buf2[2*j + 1];
+ }
+ }
+ ret->append(new GString((char *)buf2, stringLength));
+ gfree(buf2);
+ }
+ return ret;
+
+ err3:
+ gfree(buf2);
+ err2:
+ deleteGList(ret, GString);
+ err1:
+ return NULL;
+}
+
+static GList *getDfontFontList(FILE *f) {
+ Guchar buf[16];
+ Guchar *resMap;
+ int fileLength, resMapOffset, resMapLength;
+ int resTypeListOffset, resNameListOffset, nTypes;
+ int refListOffset, nFonts, nameOffset, nameLen;
+ int offset, i;
+ GList *ret;
+
+ fseek(f, 0, SEEK_END);
+ fileLength = (int)ftell(f);
+ if (fileLength < 0) {
+ goto err1;
+ }
+ fseek(f, 0, SEEK_SET);
+ if (fread((char *)buf, 1, 16, f) != 16) {
+ goto err1;
+ }
+ resMapOffset = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ resMapLength = (buf[12] << 24) | (buf[13] << 16) | (buf[14] << 8) | buf[15];
+ if (resMapOffset < 0 ||
+ resMapOffset >= fileLength ||
+ resMapLength < 0 ||
+ resMapOffset + resMapLength > fileLength ||
+ resMapOffset + resMapLength < 0) {
+ goto err1;
+ }
+ if (resMapLength > 32768) {
+ // sanity check - this probably isn't a dfont file
+ goto err1;
+ }
+ resMap = (Guchar *)gmalloc(resMapLength);
+ fseek(f, resMapOffset, SEEK_SET);
+ if ((int)fread((char *)resMap, 1, resMapLength, f) != resMapLength) {
+ goto err2;
+ }
+ resTypeListOffset = (resMap[24] << 8) | resMap[25];
+ resNameListOffset = (resMap[26] << 8) | resMap[27];
+ nTypes = ((resMap[28] << 8) | resMap[29]) + 1;
+ if (resTypeListOffset + 2 + nTypes * 8 > resMapLength ||
+ resNameListOffset >= resMapLength) {
+ goto err2;
+ }
+ for (i = 0; i < nTypes; ++i) {
+ offset = resTypeListOffset + 2 + 8 * i;
+ if (resMap[offset] == 0x73 && // 'sfnt'
+ resMap[offset+1] == 0x66 &&
+ resMap[offset+2] == 0x6e &&
+ resMap[offset+3] == 0x74) {
+ nFonts = ((resMap[offset+4] << 8) | resMap[offset+5]) + 1;
+ refListOffset = (resMap[offset+6] << 8) | resMap[offset+7];
+ break;
+ }
+ }
+ if (i >= nTypes) {
+ goto err2;
+ }
+ if (resTypeListOffset + refListOffset >= resMapLength ||
+ resTypeListOffset + refListOffset + nFonts * 12 > resMapLength) {
+ goto err2;
+ }
+ ret = new GList();
+ for (i = 0; i < nFonts; ++i) {
+ offset = resTypeListOffset + refListOffset + 12 * i;
+ nameOffset = (resMap[offset+2] << 8) | resMap[offset+3];
+ offset = resNameListOffset + nameOffset;
+ if (offset >= resMapLength) {
+ goto err3;
+ }
+ nameLen = resMap[offset];
+ if (offset + 1 + nameLen > resMapLength) {
+ goto err3;
+ }
+ ret->append(new GString((char *)resMap + offset + 1, nameLen));
+ }
+ gfree(resMap);
+ return ret;
+
+ err3:
+ deleteGList(ret, GString);
+ err2:
+ gfree(resMap);
+ err1:
+ return NULL;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.h b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.h
new file mode 100644
index 00000000000..b0307a0b8d2
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiIdentifier.h
@@ -0,0 +1,55 @@
+//========================================================================
+//
+// FoFiIdentifier.h
+//
+// Copyright 2009 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFIIDENTIFIER_H
+#define FOFIIDENTIFIER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class GList;
+
+//------------------------------------------------------------------------
+// FoFiIdentifier
+//------------------------------------------------------------------------
+
+enum FoFiIdentifierType {
+ fofiIdType1PFA, // Type 1 font in PFA format
+ fofiIdType1PFB, // Type 1 font in PFB format
+ fofiIdCFF8Bit, // 8-bit CFF font
+ fofiIdCFFCID, // CID CFF font
+ fofiIdTrueType, // TrueType font
+ fofiIdTrueTypeCollection, // TrueType collection
+ fofiIdOpenTypeCFF8Bit, // OpenType wrapper with 8-bit CFF font
+ fofiIdOpenTypeCFFCID, // OpenType wrapper with CID CFF font
+ fofiIdDfont, // Mac OS X dfont
+ fofiIdUnknown, // unknown type
+ fofiIdError // error in reading the file
+};
+
+class FoFiIdentifier {
+public:
+
+ // Identify a font file.
+ static FoFiIdentifierType identifyMem(char *file, int len);
+ static FoFiIdentifierType identifyFile(char *fileName);
+ static FoFiIdentifierType identifyStream(int (*getChar)(void *data),
+ void *data);
+
+ // Return a list of font names (GString *) in a font collection
+ // file. Indexes into the returned list are indexes into the
+ // collection. This function is only useful with TrueType
+ // collections and Mac dfont files. Returns NULL on error
+ // (including invalid font type).
+ static GList *getFontList(char *fileName);
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc
new file mode 100644
index 00000000000..57b12b14996
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.cc
@@ -0,0 +1,2461 @@
+//========================================================================
+//
+// FoFiTrueType.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#if HAVE_STD_SORT
+#include <algorithm>
+#endif
+#include "gtypes.h"
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "GHash.h"
+#include "FoFiType1C.h"
+#include "FoFiTrueType.h"
+
+//
+// Terminology
+// -----------
+//
+// character code = number used as an element of a text string
+//
+// character name = glyph name = name for a particular glyph within a
+// font
+//
+// glyph index = GID = position (within some internal table in the font)
+// where the instructions to draw a particular glyph are
+// stored
+//
+// Type 1 fonts
+// ------------
+//
+// Type 1 fonts contain:
+//
+// Encoding: array of glyph names, maps char codes to glyph names
+//
+// Encoding[charCode] = charName
+//
+// CharStrings: dictionary of instructions, keyed by character names,
+// maps character name to glyph data
+//
+// CharStrings[charName] = glyphData
+//
+// TrueType fonts
+// --------------
+//
+// TrueType fonts contain:
+//
+// 'cmap' table: mapping from character code to glyph index; there may
+// be multiple cmaps in a TrueType font
+//
+// cmap[charCode] = gid
+//
+// 'post' table: mapping from glyph index to glyph name
+//
+// post[gid] = glyphName
+//
+// Type 42 fonts
+// -------------
+//
+// Type 42 fonts contain:
+//
+// Encoding: array of glyph names, maps char codes to glyph names
+//
+// Encoding[charCode] = charName
+//
+// CharStrings: dictionary of glyph indexes, keyed by character names,
+// maps character name to glyph index
+//
+// CharStrings[charName] = gid
+//
+
+//------------------------------------------------------------------------
+
+#define ttcfTag 0x74746366
+
+//------------------------------------------------------------------------
+
+struct TrueTypeTable {
+ Guint tag;
+ Guint checksum;
+ int offset;
+ int origOffset;
+ int len;
+};
+
+struct TrueTypeCmap {
+ int platform;
+ int encoding;
+ int offset;
+ int len;
+ int fmt;
+};
+
+struct TrueTypeLoca {
+ int idx;
+ int origOffset;
+ int newOffset;
+ int len;
+};
+
+#define cmapTag 0x636d6170
+#define glyfTag 0x676c7966
+#define headTag 0x68656164
+#define hheaTag 0x68686561
+#define hmtxTag 0x686d7478
+#define locaTag 0x6c6f6361
+#define nameTag 0x6e616d65
+#define os2Tag 0x4f532f32
+#define postTag 0x706f7374
+
+#if HAVE_STD_SORT
+
+struct cmpTrueTypeLocaOffsetFunctor {
+ bool operator()(const TrueTypeLoca &loca1, const TrueTypeLoca &loca2) {
+ if (loca1.origOffset == loca2.origOffset) {
+ return loca1.idx < loca2.idx;
+ }
+ return loca1.origOffset < loca2.origOffset;
+ }
+};
+
+struct cmpTrueTypeLocaIdxFunctor {
+ bool operator()(const TrueTypeLoca &loca1, const TrueTypeLoca &loca2) {
+ return loca1.idx < loca2.idx;
+ }
+};
+
+struct cmpTrueTypeTableTagFunctor {
+ bool operator()(const TrueTypeTable &tab1, const TrueTypeTable &tab2) {
+ return tab1.tag < tab2.tag;
+ }
+};
+
+#else // HAVE_STD_SORT
+
+static int cmpTrueTypeLocaOffset(const void *p1, const void *p2) {
+ TrueTypeLoca *loca1 = (TrueTypeLoca *)p1;
+ TrueTypeLoca *loca2 = (TrueTypeLoca *)p2;
+
+ if (loca1->origOffset == loca2->origOffset) {
+ return loca1->idx - loca2->idx;
+ }
+ return loca1->origOffset - loca2->origOffset;
+}
+
+static int cmpTrueTypeLocaIdx(const void *p1, const void *p2) {
+ TrueTypeLoca *loca1 = (TrueTypeLoca *)p1;
+ TrueTypeLoca *loca2 = (TrueTypeLoca *)p2;
+
+ return loca1->idx - loca2->idx;
+}
+
+static int cmpTrueTypeTableTag(const void *p1, const void *p2) {
+ TrueTypeTable *tab1 = (TrueTypeTable *)p1;
+ TrueTypeTable *tab2 = (TrueTypeTable *)p2;
+
+ return (int)tab1->tag - (int)tab2->tag;
+}
+
+#endif // HAVE_STD_SORT
+
+//------------------------------------------------------------------------
+
+struct T42Table {
+ const char *tag; // 4-byte tag
+ GBool required; // required by the TrueType spec?
+};
+
+// TrueType tables to be embedded in Type 42 fonts.
+// NB: the table names must be in alphabetical order here.
+#define nT42Tables 11
+static T42Table t42Tables[nT42Tables] = {
+ { "cvt ", gTrue },
+ { "fpgm", gTrue },
+ { "glyf", gTrue },
+ { "head", gTrue },
+ { "hhea", gTrue },
+ { "hmtx", gTrue },
+ { "loca", gTrue },
+ { "maxp", gTrue },
+ { "prep", gTrue },
+ { "vhea", gFalse },
+ { "vmtx", gFalse }
+};
+#define t42HeadTable 3
+#define t42LocaTable 6
+#define t42GlyfTable 2
+#define t42VheaTable 9
+#define t42VmtxTable 10
+
+//------------------------------------------------------------------------
+
+// Glyph names in some arbitrary standard order that Apple uses for
+// their TrueType fonts.
+static const char *macGlyphNames[258] = {
+ ".notdef", "null", "CR", "space",
+ "exclam", "quotedbl", "numbersign", "dollar",
+ "percent", "ampersand", "quotesingle", "parenleft",
+ "parenright", "asterisk", "plus", "comma",
+ "hyphen", "period", "slash", "zero",
+ "one", "two", "three", "four",
+ "five", "six", "seven", "eight",
+ "nine", "colon", "semicolon", "less",
+ "equal", "greater", "question", "at",
+ "A", "B", "C", "D",
+ "E", "F", "G", "H",
+ "I", "J", "K", "L",
+ "M", "N", "O", "P",
+ "Q", "R", "S", "T",
+ "U", "V", "W", "X",
+ "Y", "Z", "bracketleft", "backslash",
+ "bracketright", "asciicircum", "underscore", "grave",
+ "a", "b", "c", "d",
+ "e", "f", "g", "h",
+ "i", "j", "k", "l",
+ "m", "n", "o", "p",
+ "q", "r", "s", "t",
+ "u", "v", "w", "x",
+ "y", "z", "braceleft", "bar",
+ "braceright", "asciitilde", "Adieresis", "Aring",
+ "Ccedilla", "Eacute", "Ntilde", "Odieresis",
+ "Udieresis", "aacute", "agrave", "acircumflex",
+ "adieresis", "atilde", "aring", "ccedilla",
+ "eacute", "egrave", "ecircumflex", "edieresis",
+ "iacute", "igrave", "icircumflex", "idieresis",
+ "ntilde", "oacute", "ograve", "ocircumflex",
+ "odieresis", "otilde", "uacute", "ugrave",
+ "ucircumflex", "udieresis", "dagger", "degree",
+ "cent", "sterling", "section", "bullet",
+ "paragraph", "germandbls", "registered", "copyright",
+ "trademark", "acute", "dieresis", "notequal",
+ "AE", "Oslash", "infinity", "plusminus",
+ "lessequal", "greaterequal", "yen", "mu1",
+ "partialdiff", "summation", "product", "pi",
+ "integral", "ordfeminine", "ordmasculine", "Ohm",
+ "ae", "oslash", "questiondown", "exclamdown",
+ "logicalnot", "radical", "florin", "approxequal",
+ "increment", "guillemotleft", "guillemotright", "ellipsis",
+ "nbspace", "Agrave", "Atilde", "Otilde",
+ "OE", "oe", "endash", "emdash",
+ "quotedblleft", "quotedblright", "quoteleft", "quoteright",
+ "divide", "lozenge", "ydieresis", "Ydieresis",
+ "fraction", "currency", "guilsinglleft", "guilsinglright",
+ "fi", "fl", "daggerdbl", "periodcentered",
+ "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex",
+ "Ecircumflex", "Aacute", "Edieresis", "Egrave",
+ "Iacute", "Icircumflex", "Idieresis", "Igrave",
+ "Oacute", "Ocircumflex", "applelogo", "Ograve",
+ "Uacute", "Ucircumflex", "Ugrave", "dotlessi",
+ "circumflex", "tilde", "overscore", "breve",
+ "dotaccent", "ring", "cedilla", "hungarumlaut",
+ "ogonek", "caron", "Lslash", "lslash",
+ "Scaron", "scaron", "Zcaron", "zcaron",
+ "brokenbar", "Eth", "eth", "Yacute",
+ "yacute", "Thorn", "thorn", "minus",
+ "multiply", "onesuperior", "twosuperior", "threesuperior",
+ "onehalf", "onequarter", "threequarters", "franc",
+ "Gbreve", "gbreve", "Idot", "Scedilla",
+ "scedilla", "Cacute", "cacute", "Ccaron",
+ "ccaron", "dmacron"
+};
+
+//------------------------------------------------------------------------
+// FoFiTrueType
+//------------------------------------------------------------------------
+
+FoFiTrueType *FoFiTrueType::make(char *fileA, int lenA, int fontNum,
+ GBool allowHeadlessCFF) {
+ FoFiTrueType *ff;
+
+ ff = new FoFiTrueType(fileA, lenA, gFalse, fontNum, gFalse, allowHeadlessCFF);
+ if (!ff->parsedOk) {
+ delete ff;
+ return NULL;
+ }
+ return ff;
+}
+
+FoFiTrueType *FoFiTrueType::load(char *fileName, int fontNum,
+ GBool allowHeadlessCFF) {
+ FoFiTrueType *ff;
+ char *fileA;
+ int lenA, n;
+ GBool isDfontA;
+
+ if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
+ return NULL;
+ }
+ n = (int)strlen(fileName);
+ isDfontA = n >= 6 && !strcmp(fileName + n - 6, ".dfont");
+ ff = new FoFiTrueType(fileA, lenA, gTrue, fontNum, isDfontA,
+ allowHeadlessCFF);
+ if (!ff->parsedOk) {
+ delete ff;
+ return NULL;
+ }
+ return ff;
+}
+
+FoFiTrueType::FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA,
+ int fontNum, GBool isDfontA,
+ GBool allowHeadlessCFF):
+ FoFiBase(fileA, lenA, freeFileDataA)
+{
+ tables = NULL;
+ nTables = 0;
+ cmaps = NULL;
+ nCmaps = 0;
+ nameToGID = NULL;
+ isDfont = isDfontA;
+ isTTC = gFalse;
+ parsedOk = gFalse;
+
+ parse(fontNum, allowHeadlessCFF);
+}
+
+FoFiTrueType::~FoFiTrueType() {
+ gfree(tables);
+ gfree(cmaps);
+ if (nameToGID) {
+ delete nameToGID;
+ }
+}
+
+int FoFiTrueType::getNumCmaps() {
+ return nCmaps;
+}
+
+int FoFiTrueType::getCmapPlatform(int i) {
+ return cmaps[i].platform;
+}
+
+int FoFiTrueType::getCmapEncoding(int i) {
+ return cmaps[i].encoding;
+}
+
+int FoFiTrueType::findCmap(int platform, int encoding) {
+ int i;
+
+ for (i = 0; i < nCmaps; ++i) {
+ if (cmaps[i].platform == platform && cmaps[i].encoding == encoding) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int FoFiTrueType::mapCodeToGID(int i, int c) {
+ int gid;
+ int segCnt, segEnd, segStart, segDelta, segOffset;
+ int cmapFirst, cmapLen;
+ int pos, a, b, m;
+ GBool ok;
+
+ if (i < 0 || i >= nCmaps) {
+ return 0;
+ }
+ ok = gTrue;
+ pos = cmaps[i].offset;
+ switch (cmaps[i].fmt) {
+ case 0:
+ if (c < 0 || c >= cmaps[i].len - 6) {
+ return 0;
+ }
+ gid = getU8(pos + 6 + c, &ok);
+ break;
+ case 2:
+ // this only handles single-byte codes
+ if (c < 0 || c > 0xff) {
+ return 0;
+ }
+ // check that: subHeaderKeys[0] = 0
+ // subHeaders[0].firstCode = 0
+ // subHeaders[0].entryCount = 256
+ // subHeaders[0].idDelta = 0
+ if (getU16BE(pos + 6, &ok) != 0 ||
+ getU16BE(pos + 518 + 0, &ok) != 0 ||
+ getU16BE(pos + 518 + 2, &ok) != 256 ||
+ getU16BE(pos + 518 + 4, &ok) != 0) {
+ return 0;
+ }
+ // subHeaders[0].idRangeOffset is a byte offset from itself
+ pos = pos + 518 + 6 + getU16BE(pos + 518 + 6, &ok);
+ gid = getU16BE(pos + 2 * c, &ok);
+ break;
+ case 4:
+ segCnt = getU16BE(pos + 6, &ok) / 2;
+ a = -1;
+ b = segCnt - 1;
+ segEnd = getU16BE(pos + 14 + 2*b, &ok);
+ if (c > segEnd) {
+ // malformed font -- the TrueType spec requires the last segEnd
+ // to be 0xffff
+ return 0;
+ }
+ // invariant: seg[a].end < code <= seg[b].end
+ while (b - a > 1 && ok) {
+ m = (a + b) / 2;
+ segEnd = getU16BE(pos + 14 + 2*m, &ok);
+ if (segEnd < c) {
+ a = m;
+ } else {
+ b = m;
+ }
+ }
+ segStart = getU16BE(pos + 16 + 2*segCnt + 2*b, &ok);
+ segDelta = getU16BE(pos + 16 + 4*segCnt + 2*b, &ok);
+ segOffset = getU16BE(pos + 16 + 6*segCnt + 2*b, &ok);
+ if (c < segStart) {
+ return 0;
+ }
+ if (segOffset == 0) {
+ gid = (c + segDelta) & 0xffff;
+ } else {
+ gid = getU16BE(pos + 16 + 6*segCnt + 2*b +
+ segOffset + 2 * (c - segStart), &ok);
+ if (gid != 0) {
+ gid = (gid + segDelta) & 0xffff;
+ }
+ }
+ break;
+ case 6:
+ cmapFirst = getU16BE(pos + 6, &ok);
+ cmapLen = getU16BE(pos + 8, &ok);
+ if (c < cmapFirst || c >= cmapFirst + cmapLen) {
+ return 0;
+ }
+ gid = getU16BE(pos + 10 + 2 * (c - cmapFirst), &ok);
+ break;
+ default:
+ return 0;
+ }
+ if (!ok) {
+ return 0;
+ }
+ return gid;
+}
+
+int FoFiTrueType::mapNameToGID(char *name) {
+ if (!nameToGID) {
+ return 0;
+ }
+ return nameToGID->lookupInt(name);
+}
+
+GBool FoFiTrueType::getCFFBlock(char **start, int *length) {
+ int i;
+
+ if (!openTypeCFF) {
+ return gFalse;
+ }
+ i = seekTable("CFF ");
+ if (!checkRegion(tables[i].offset, tables[i].len)) {
+ return gFalse;
+ }
+ *start = (char *)file + tables[i].offset;
+ *length = tables[i].len;
+ return gTrue;
+}
+
+int *FoFiTrueType::getCIDToGIDMap(int *nCIDs) {
+ char *start;
+ int length;
+ FoFiType1C *ff;
+ int *map;
+
+ *nCIDs = 0;
+ if (!getCFFBlock(&start, &length)) {
+ return NULL;
+ }
+ if (!(ff = FoFiType1C::make(start, length))) {
+ return NULL;
+ }
+ map = ff->getCIDToGIDMap(nCIDs);
+ delete ff;
+ return map;
+}
+
+int FoFiTrueType::getEmbeddingRights() {
+ int i, fsType;
+ GBool ok;
+
+ if ((i = seekTable("OS/2")) < 0) {
+ return 4;
+ }
+ ok = gTrue;
+ fsType = getU16BE(tables[i].offset + 8, &ok);
+ if (!ok) {
+ return 4;
+ }
+ if (fsType & 0x0008) {
+ return 2;
+ }
+ if (fsType & 0x0004) {
+ return 1;
+ }
+ if (fsType & 0x0002) {
+ return 0;
+ }
+ return 3;
+}
+
+void FoFiTrueType::getFontMatrix(double *mat) {
+ char *start;
+ int length;
+ FoFiType1C *ff;
+
+ if (!getCFFBlock(&start, &length)) {
+ return;
+ }
+ if (!(ff = FoFiType1C::make(start, length))) {
+ return;
+ }
+ ff->getFontMatrix(mat);
+ delete ff;
+}
+
+struct FoFiTrueTypeTrickyFont {
+ int cvtLen;
+ Guint cvtChecksum;
+ int fpgmLen;
+ Guint fpgmChecksum;
+ int prepLen;
+ Guint prepChecksum;
+};
+
+// This data was taken from the FreeType project.
+static FoFiTrueTypeTrickyFont trickyFonts[] = {
+ { 0x000002e4, 0x05bcf058, // MingLiU 1995
+ 0x000087c4, 0x28233bf1,
+ 0x000001e1, 0xa344a1ea },
+ { 0x000002e4, 0x05bcf058, // MingLiU 1996-
+ 0x000087c4, 0x28233bf1,
+ 0x000001e1, 0xa344a1eb },
+ { 0x00000350, 0x12c3ebb2, // DFGothic-EB
+ 0x000087a7, 0xb680ee64,
+ 0x00000758, 0xce939563 },
+ { 0x00000350, 0x11e5ead4, // DFGyoSho-Lt
+ 0x0000bc85, 0xce5956e9,
+ 0x00000045, 0x8272f416 },
+ { 0x00000350, 0x1262eb4e, // DFHSGothic-W5
+ 0x00007940, 0xe86a5d64,
+ 0x000005ff, 0x7850f729 },
+ { 0x00000350, 0x122deb0a, // DFHSMincho-W3
+ 0x0000859b, 0x3d16328a,
+ 0x000002cb, 0xa93fc33b },
+ { 0x00000350, 0x125feb26, // DFHSMincho-W7
+ 0x00007ee1, 0xa5acc982,
+ 0x0000041f, 0x90999196 },
+ { 0x00000350, 0x11e5ead4, // DFKaiShu
+ 0x00009063, 0x5a30ca3b,
+ 0x0000007e, 0x13a42602 },
+ { 0x00000350, 0x11e5ead4, // DFKaiShu2
+ 0x00008998, 0xa6e78c01,
+ 0x0000007e, 0x13a42602 },
+ { 0x00000008, 0xfffbfffc, // HuaTianKaiTi
+ 0x0000bea2, 0x9c9e48b8,
+ 0x00000008, 0x70020112 },
+ { 0x00000008, 0xfffbfffc, // HuaTianSongTi
+ 0x00017c39, 0x0a5a0483,
+ 0x00000008, 0x70020112 },
+ { 0x00000000, 0x00000000, // NEC fadpop7.ttf
+ 0x000000e5, 0x40c92555,
+ 0x0000117c, 0xa39b58e3 },
+ { 0x00000000, 0x00000000, // NEC fadrei5.ttf
+ 0x000000e5, 0x33c41652,
+ 0x00000f6a, 0x26d6c52a },
+ { 0x00000000, 0x00000000, // NEC fangot7.ttf
+ 0x0000019d, 0x6db1651d,
+ 0x00002492, 0x6c6e4b03 },
+ { 0x00000000, 0x00000000, // NEC fangyo5.ttf
+ 0x000000e5, 0x40c92555,
+ 0x0000117c, 0xde51fad0 },
+ { 0x00000000, 0x00000000, // NEC fankyo5.ttf
+ 0x000000e5, 0x85e47664,
+ 0x00001caa, 0xa6c62831 },
+ { 0x00000000, 0x00000000, // NEC fanrgo5.ttf
+ 0x0000019d, 0x2d891cfd,
+ 0x00001de8, 0xa0604633 },
+ { 0x00000000, 0x00000000, // NEC fangot5.ttc
+ 0x000001cb, 0x40aa774c,
+ 0x00001f9a, 0x9b5caa96 },
+ { 0x00000000, 0x00000000, // NEC fanmin3.ttc
+ 0x00000141, 0x0d3de9cb,
+ 0x00002280, 0xd4127766 },
+ { 0x00000000, 0x00000000, // NEC FA-Gothic, 1996
+ 0x000001f0, 0x4a692698,
+ 0x00001fca, 0x340d4346 },
+ { 0x00000000, 0x00000000, // NEC FA-Minchou, 1996
+ 0x00000166, 0xcd34c604,
+ 0x000022b0, 0x6cf31046 },
+ { 0x00000000, 0x00000000, // NEC FA-RoundGothicB, 1996
+ 0x0000019d, 0x5da75315,
+ 0x000022e0, 0x40745a5f },
+ { 0x00000000, 0x00000000, // NEC FA-RoundGothicM, 1996
+ 0x000001c2, 0xf055fc48,
+ 0x00001e18, 0x3900ded3 },
+ { 0x00000060, 0x00170003, // MINGLI, 1992
+ 0x000058aa, 0xdbb4306e,
+ 0x00000035, 0xd643482a }
+};
+
+#define nTrickyFonts ((int)(sizeof(trickyFonts) / sizeof(FoFiTrueTypeTrickyFont)))
+
+// This looks at the length and checksum values for the cvt, fpgm, and
+// prep tables. The theory is that those tables will be the same,
+// even if the font is renamed and/or subsetted.
+GBool FoFiTrueType::checkForTrickyCJK() {
+ int cvtIdx, fpgmIdx, prepIdx;
+ int cvtLen, fpgmLen, prepLen;
+ Guint cvtChecksum, fpgmChecksum, prepChecksum;
+ int i;
+
+ if ((cvtIdx = seekTable("cvt ")) >= 0) {
+ cvtLen = tables[cvtIdx].len;
+ cvtChecksum = tables[cvtIdx].checksum;
+ } else {
+ cvtLen = 0;
+ cvtChecksum = 0;
+ }
+ if ((fpgmIdx = seekTable("fpgm")) >= 0) {
+ fpgmLen = tables[fpgmIdx].len;
+ fpgmChecksum = tables[fpgmIdx].checksum;
+ } else {
+ fpgmLen = 0;
+ fpgmChecksum = 0;
+ }
+ if ((prepIdx = seekTable("prep")) >= 0) {
+ prepLen = tables[prepIdx].len;
+ prepChecksum = tables[prepIdx].checksum;
+ } else {
+ prepLen = 0;
+ prepChecksum = 0;
+ }
+
+ for (i = 0; i < nTrickyFonts; ++i) {
+ if (cvtLen == trickyFonts[i].cvtLen &&
+ cvtChecksum == trickyFonts[i].cvtChecksum &&
+ fpgmLen == trickyFonts[i].fpgmLen &&
+ fpgmChecksum == trickyFonts[i].fpgmChecksum &&
+ prepLen == trickyFonts[i].prepLen &&
+ prepChecksum == trickyFonts[i].prepChecksum) {
+ return gTrue;
+ }
+ }
+ return gFalse;
+}
+
+void FoFiTrueType::convertToType42(char *psName, char **encoding,
+ int *codeToGID,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ GString *buf;
+ int maxUsedGlyph;
+ GBool ok;
+
+ if (openTypeCFF) {
+ return;
+ }
+
+ // write the header
+ ok = gTrue;
+ buf = GString::format("%!PS-TrueTypeFont-{0:2g}\n",
+ (double)getS32BE(0, &ok) / 65536.0);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+
+ // begin the font dictionary
+ (*outputFunc)(outputStream, "10 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/FontName /", 11);
+ (*outputFunc)(outputStream, psName, (int)strlen(psName));
+ (*outputFunc)(outputStream, " def\n", 5);
+ (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ buf = GString::format("/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n",
+ bbox[0], bbox[1], bbox[2], bbox[3]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
+
+ // write the guts of the dictionary
+ cvtEncoding(encoding, outputFunc, outputStream);
+ cvtCharStrings(encoding, codeToGID, outputFunc, outputStream);
+ cvtSfnts(outputFunc, outputStream, NULL, gFalse, &maxUsedGlyph);
+
+ // end the dictionary and define the font
+ (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
+}
+
+void FoFiTrueType::convertToType1(char *psName, const char **newEncoding,
+ GBool ascii, FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ char *start;
+ int length;
+ FoFiType1C *ff;
+
+ if (!getCFFBlock(&start, &length)) {
+ return;
+ }
+ if (!(ff = FoFiType1C::make(start, length))) {
+ return;
+ }
+ ff->convertToType1(psName, newEncoding, ascii, outputFunc, outputStream);
+ delete ff;
+}
+
+void FoFiTrueType::convertToCIDType2(char *psName,
+ int *cidMap, int nCIDs,
+ GBool needVerticalMetrics,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ GString *buf;
+ int cid, maxUsedGlyph;
+ GBool ok;
+ int i, j, k;
+
+ if (openTypeCFF) {
+ return;
+ }
+
+ // write the header
+ ok = gTrue;
+ buf = GString::format("%!PS-TrueTypeFont-{0:2g}\n",
+ (double)getS32BE(0, &ok) / 65536.0);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+
+ // begin the font dictionary
+ (*outputFunc)(outputStream, "20 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/CIDFontName /", 14);
+ (*outputFunc)(outputStream, psName, (int)strlen(psName));
+ (*outputFunc)(outputStream, " def\n", 5);
+ (*outputFunc)(outputStream, "/CIDFontType 2 def\n", 19);
+ (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
+ (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32);
+ (*outputFunc)(outputStream, " /Registry (Adobe) def\n", 24);
+ (*outputFunc)(outputStream, " /Ordering (Identity) def\n", 27);
+ (*outputFunc)(outputStream, " /Supplement 0 def\n", 20);
+ (*outputFunc)(outputStream, " end def\n", 10);
+ (*outputFunc)(outputStream, "/GDBytes 2 def\n", 15);
+ if (cidMap) {
+ buf = GString::format("/CIDCount {0:d} def\n", nCIDs);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ if (nCIDs > 32767) {
+ (*outputFunc)(outputStream, "/CIDMap [", 9);
+ for (i = 0; i < nCIDs; i += 32768 - 16) {
+ (*outputFunc)(outputStream, "<\n", 2);
+ for (j = 0; j < 32768 - 16 && i+j < nCIDs; j += 16) {
+ (*outputFunc)(outputStream, " ", 2);
+ for (k = 0; k < 16 && i+j+k < nCIDs; ++k) {
+ cid = cidMap[i+j+k];
+ buf = GString::format("{0:02x}{1:02x}",
+ (cid >> 8) & 0xff, cid & 0xff);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+ (*outputFunc)(outputStream, " >", 3);
+ }
+ (*outputFunc)(outputStream, "\n", 1);
+ (*outputFunc)(outputStream, "] def\n", 6);
+ } else {
+ (*outputFunc)(outputStream, "/CIDMap <\n", 10);
+ for (i = 0; i < nCIDs; i += 16) {
+ (*outputFunc)(outputStream, " ", 2);
+ for (j = 0; j < 16 && i+j < nCIDs; ++j) {
+ cid = cidMap[i+j];
+ buf = GString::format("{0:02x}{1:02x}",
+ (cid >> 8) & 0xff, cid & 0xff);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+ (*outputFunc)(outputStream, "> def\n", 6);
+ }
+ } else {
+ // direct mapping - just fill the string(s) with s[i]=i
+ buf = GString::format("/CIDCount {0:d} def\n", nGlyphs);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ if (nGlyphs > 32767) {
+ (*outputFunc)(outputStream, "/CIDMap [\n", 10);
+ for (i = 0; i < nGlyphs; i += 32767) {
+ j = nGlyphs - i < 32767 ? nGlyphs - i : 32767;
+ buf = GString::format(" {0:d} string 0 1 {1:d} {{\n", 2 * j, j - 1);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ buf = GString::format(" 2 copy dup 2 mul exch {0:d} add -8 bitshift put\n",
+ i);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ buf = GString::format(" 1 index exch dup 2 mul 1 add exch {0:d} add"
+ " 255 and put\n", i);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, " } for\n", 8);
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ } else {
+ buf = GString::format("/CIDMap {0:d} string\n", 2 * nGlyphs);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ buf = GString::format(" 0 1 {0:d} {{\n", nGlyphs - 1);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream,
+ " 2 copy dup 2 mul exch -8 bitshift put\n", 42);
+ (*outputFunc)(outputStream,
+ " 1 index exch dup 2 mul 1 add exch 255 and put\n", 50);
+ (*outputFunc)(outputStream, " } for\n", 8);
+ (*outputFunc)(outputStream, "def\n", 4);
+ }
+ }
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ buf = GString::format("/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n",
+ bbox[0], bbox[1], bbox[2], bbox[3]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
+ (*outputFunc)(outputStream, "/Encoding [] readonly def\n", 26);
+ (*outputFunc)(outputStream, "/CharStrings 1 dict dup begin\n", 30);
+ (*outputFunc)(outputStream, " /.notdef 0 def\n", 17);
+ (*outputFunc)(outputStream, " end readonly def\n", 19);
+
+ // write the guts of the dictionary
+ cvtSfnts(outputFunc, outputStream, NULL, needVerticalMetrics, &maxUsedGlyph);
+
+ // end the dictionary and define the font
+ (*outputFunc)(outputStream,
+ "CIDFontName currentdict end /CIDFont defineresource pop\n",
+ 56);
+}
+
+void FoFiTrueType::convertToCIDType0(char *psName, int *cidMap, int nCIDs,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ char *start;
+ int length;
+ FoFiType1C *ff;
+
+ if (!getCFFBlock(&start, &length)) {
+ return;
+ }
+ if (!(ff = FoFiType1C::make(start, length))) {
+ return;
+ }
+ ff->convertToCIDType0(psName, cidMap, nCIDs, outputFunc, outputStream);
+ delete ff;
+}
+
+void FoFiTrueType::convertToType0(char *psName, int *cidMap, int nCIDs,
+ GBool needVerticalMetrics,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ GString *buf;
+ GString *sfntsName;
+ int maxUsedGlyph, n, i, j;
+
+ if (openTypeCFF) {
+ return;
+ }
+
+ // write the Type 42 sfnts array
+ sfntsName = (new GString(psName))->append("_sfnts");
+ cvtSfnts(outputFunc, outputStream, sfntsName, needVerticalMetrics,
+ &maxUsedGlyph);
+ delete sfntsName;
+
+ // write the descendant Type 42 fonts
+ // (The following is a kludge: nGlyphs is the glyph count from the
+ // maxp table; maxUsedGlyph is the max glyph number that has a
+ // non-zero-length description, from the loca table. The problem is
+ // that some TrueType font subsets fail to change the glyph count,
+ // i.e., nGlyphs is much larger than maxUsedGlyph+1, which results
+ // in an unnecessarily huge Type 0 font. But some other PDF files
+ // have fonts with only zero or one used glyph, and a content stream
+ // that refers to one of the unused glyphs -- this results in PS
+ // errors if we simply use maxUsedGlyph+1 for the Type 0 font. So
+ // we compromise by always defining at least 256 glyphs.)
+ if (cidMap) {
+ n = nCIDs;
+ } else if (nGlyphs > maxUsedGlyph + 256) {
+ if (maxUsedGlyph <= 255) {
+ n = 256;
+ } else {
+ n = maxUsedGlyph + 1;
+ }
+ } else {
+ n = nGlyphs;
+ }
+ for (i = 0; i < n; i += 256) {
+ (*outputFunc)(outputStream, "10 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/FontName /", 11);
+ (*outputFunc)(outputStream, psName, (int)strlen(psName));
+ buf = GString::format("_{0:02x} def\n", i >> 8);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ buf = GString::format("/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n",
+ bbox[0], bbox[1], bbox[2], bbox[3]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
+ (*outputFunc)(outputStream, "/sfnts ", 7);
+ (*outputFunc)(outputStream, psName, (int)strlen(psName));
+ (*outputFunc)(outputStream, "_sfnts def\n", 11);
+ (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+ for (j = 0; j < 256 && i+j < n; ++j) {
+ buf = GString::format("dup {0:d} /c{1:02x} put\n", j, j);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "readonly def\n", 13);
+ (*outputFunc)(outputStream, "/CharStrings 257 dict dup begin\n", 32);
+ (*outputFunc)(outputStream, "/.notdef 0 def\n", 15);
+ for (j = 0; j < 256 && i+j < n; ++j) {
+ buf = GString::format("/c{0:02x} {1:d} def\n",
+ j, cidMap ? cidMap[i+j] : i+j);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "end readonly def\n", 17);
+ (*outputFunc)(outputStream,
+ "FontName currentdict end definefont pop\n", 40);
+ }
+
+ // write the Type 0 parent font
+ (*outputFunc)(outputStream, "16 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/FontName /", 11);
+ (*outputFunc)(outputStream, psName, (int)strlen(psName));
+ (*outputFunc)(outputStream, " def\n", 5);
+ (*outputFunc)(outputStream, "/FontType 0 def\n", 16);
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ (*outputFunc)(outputStream, "/FMapType 2 def\n", 16);
+ (*outputFunc)(outputStream, "/Encoding [\n", 12);
+ for (i = 0; i < n; i += 256) {
+ buf = GString::format("{0:d}\n", i >> 8);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ (*outputFunc)(outputStream, "/FDepVector [\n", 14);
+ for (i = 0; i < n; i += 256) {
+ (*outputFunc)(outputStream, "/", 1);
+ (*outputFunc)(outputStream, psName, (int)strlen(psName));
+ buf = GString::format("_{0:02x} findfont\n", i >> 8);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
+}
+
+void FoFiTrueType::convertToType0(char *psName, int *cidMap, int nCIDs,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ char *start;
+ int length;
+ FoFiType1C *ff;
+
+ if (!getCFFBlock(&start, &length)) {
+ return;
+ }
+ if (!(ff = FoFiType1C::make(start, length))) {
+ return;
+ }
+ ff->convertToType0(psName, cidMap, nCIDs, outputFunc, outputStream);
+ delete ff;
+}
+
+GBool FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
+ void *outputStream, char *name,
+ int *codeToGID, Guchar *replacementCmapTable,
+ int replacementCmapTableLen) {
+ // this substitute cmap table maps char code ffff to glyph 0,
+ // with tables for MacRoman and MS Unicode
+ static char cmapTab[44] = {
+ 0, 0, // table version number
+ 0, 2, // number of encoding tables
+ 0, 1, // platform ID
+ 0, 0, // encoding ID
+ 0, 0, 0, 20, // offset of subtable
+ 0, 3, // platform ID
+ 0, 1, // encoding ID
+ 0, 0, 0, 20, // offset of subtable
+ 0, 4, // subtable format
+ 0, 24, // subtable length
+ 0, 0, // subtable version
+ 0, 2, // segment count * 2
+ 0, 2, // 2 * 2 ^ floor(log2(segCount))
+ 0, 0, // floor(log2(segCount))
+ 0, 0, // 2*segCount - 2*2^floor(log2(segCount))
+ (char)0xff, (char)0xff, // endCount[0]
+ 0, 0, // reserved
+ (char)0xff, (char)0xff, // startCount[0]
+ 0, 1, // idDelta[0]
+ 0, 0 // idRangeOffset[0]
+ };
+ static char nameTab[8] = {
+ 0, 0, // format
+ 0, 0, // number of name records
+ 0, 6, // offset to start of string storage
+ 0, 0 // pad to multiple of four bytes
+ };
+ static char postTab[32] = {
+ 0, 1, 0, 0, // format
+ 0, 0, 0, 0, // italic angle
+ 0, 0, // underline position
+ 0, 0, // underline thickness
+ 0, 0, 0, 0, // fixed pitch
+ 0, 0, 0, 0, // min Type 42 memory
+ 0, 0, 0, 0, // max Type 42 memory
+ 0, 0, 0, 0, // min Type 1 memory
+ 0, 0, 0, 0 // max Type 1 memory
+ };
+ static char os2Tab[86] = {
+ 0, 1, // version
+ 0, 1, // xAvgCharWidth
+ 0x01, (char)0x90, // usWeightClass
+ 0, 5, // usWidthClass
+ 0, 0, // fsType
+ 0, 0, // ySubscriptXSize
+ 0, 0, // ySubscriptYSize
+ 0, 0, // ySubscriptXOffset
+ 0, 0, // ySubscriptYOffset
+ 0, 0, // ySuperscriptXSize
+ 0, 0, // ySuperscriptYSize
+ 0, 0, // ySuperscriptXOffset
+ 0, 0, // ySuperscriptYOffset
+ 0, 0, // yStrikeoutSize
+ 0, 0, // yStrikeoutPosition
+ 0, 0, // sFamilyClass
+ 0, 0, 0, 0, 0, // panose
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, // ulUnicodeRange1
+ 0, 0, 0, 0, // ulUnicodeRange2
+ 0, 0, 0, 0, // ulUnicodeRange3
+ 0, 0, 0, 0, // ulUnicodeRange4
+ 0, 0, 0, 0, // achVendID
+ 0, 0, // fsSelection
+ 0, 0, // usFirstCharIndex
+ 0, 0, // usLastCharIndex
+ 0, 0, // sTypoAscender
+ 0, 0, // sTypoDescender
+ 0, 0, // sTypoLineGap
+ 0x20, 0x00, // usWinAscent
+ 0x20, 0x00, // usWinDescent
+ 0, 0, 0, 1, // ulCodePageRange1
+ 0, 0, 0, 0 // ulCodePageRange2
+ };
+ GBool missingCmap, missingName, missingPost, missingOS2;
+ GBool unsortedLoca, emptyCmap, badCmapLen, abbrevHMTX;
+ int nZeroLengthTables, nBogusTables;
+ int nHMetrics, advWidth, lsb;
+ TrueTypeLoca *locaTable;
+ TrueTypeTable *newTables;
+ char *newNameTab, *newCmapTab, *newHHEATab, *newHMTXTab;
+ int nNewTables, cmapIdx, cmapLen, glyfLen, newNameLen, newCmapLen, next;
+ int newHHEALen, newHMTXLen;
+ Guint locaChecksum, glyfChecksum, fileChecksum;
+ char *tableDir;
+ char locaBuf[4], checksumBuf[4];
+ GBool ok;
+ Guint t;
+ int pos, i, j, k, n;
+
+ if (openTypeCFF) {
+ return gFalse;
+ }
+
+ // check for missing tables
+ // (Note: if the OS/2 table is missing, the Microsoft PCL5 driver
+ // will embed a PCL TrueType font with the pitch field set to zero,
+ // which apparently causes divide-by-zero errors. As far as I can
+ // tell, the only important field in the OS/2 table is
+ // xAvgCharWidth.)
+ missingCmap = (cmapIdx = seekTable("cmap")) < 0;
+ missingName = seekTable("name") < 0;
+ missingPost = seekTable("post") < 0;
+ missingOS2 = seekTable("OS/2") < 0;
+
+ // read the loca table, check to see if it's sorted
+ locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca));
+ unsortedLoca = gFalse;
+ i = seekTable("loca");
+ pos = tables[i].offset;
+ glyfLen = tables[seekTable("glyf")].len;
+ ok = gTrue;
+ for (i = 0; i <= nGlyphs; ++i) {
+ if (locaFmt) {
+ locaTable[i].origOffset = (int)getU32BE(pos + i*4, &ok);
+ } else {
+ locaTable[i].origOffset = 2 * getU16BE(pos + i*2, &ok);
+ }
+ if (locaTable[i].origOffset < 0 ||
+ locaTable[i].origOffset > glyfLen) {
+ locaTable[i].origOffset = glyfLen;
+ unsortedLoca = gTrue;
+ }
+ if (i > 0 && locaTable[i].origOffset < locaTable[i-1].origOffset) {
+ unsortedLoca = gTrue;
+ }
+ // glyph descriptions must be at least 12 bytes long (nContours,
+ // xMin, yMin, xMax, yMax, instructionLength - two bytes each);
+ // invalid glyph descriptions (even if they're never used) make
+ // Windows choke, so we work around that problem here (ideally,
+ // this would parse the glyph descriptions in the glyf table and
+ // remove any that were invalid, but this quick test is a decent
+ // start)
+ if (i > 0 &&
+ locaTable[i].origOffset - locaTable[i-1].origOffset > 0 &&
+ locaTable[i].origOffset - locaTable[i-1].origOffset < 12) {
+ locaTable[i-1].origOffset = locaTable[i].origOffset;
+ unsortedLoca = gTrue;
+ }
+ locaTable[i].idx = i;
+ }
+
+ // check for zero-length tables and bogus tags
+ nZeroLengthTables = nBogusTables = 0;
+ for (i = 0; i < nTables; ++i) {
+ if (tables[i].len == 0) {
+ ++nZeroLengthTables;
+ if (tables[i].tag == cmapTag) {
+ missingCmap = gTrue;
+ } else if (tables[i].tag == nameTag) {
+ missingName = gTrue;
+ } else if (tables[i].tag == postTag) {
+ missingPost = gTrue;
+ } else if (tables[i].tag == os2Tag) {
+ missingOS2 = gTrue;
+ }
+ } else if (!(tables[i].tag & 0xe0000000) ||
+ !(tables[i].tag & 0x00e00000) ||
+ !(tables[i].tag & 0x0000e000) ||
+ !(tables[i].tag & 0x000000e0)) {
+ // tags where any of the bytes are < 0x20 are probably bogus
+ // (the TrueType spec uses ASCII sequences for tags) -- this
+ // catches problems where the number of tables given in the
+ // header is too large, and so gibberish data is read at the end
+ // of the table directory
+ ++nBogusTables;
+ }
+ }
+
+ // check for an empty cmap table or an incorrect cmap table length
+ emptyCmap = badCmapLen = gFalse;
+ cmapLen = 0; // make gcc happy
+ if (!missingCmap) {
+ if (nCmaps == 0) {
+ emptyCmap = gTrue;
+ } else {
+ cmapLen = cmaps[0].offset + cmaps[0].len;
+ for (i = 1; i < nCmaps; ++i) {
+ if (cmaps[i].offset + cmaps[i].len > cmapLen) {
+ cmapLen = cmaps[i].offset + cmaps[i].len;
+ }
+ }
+ cmapLen -= tables[cmapIdx].offset;
+ if (cmapLen > tables[cmapIdx].len) {
+ badCmapLen = gTrue;
+ }
+ }
+ }
+
+ // check for an abbreviated hmtx table (this is completely legal,
+ // but confuses the Microsoft PCL5 printer driver, which generates
+ // embedded fonts with the pitch field set to zero)
+ i = seekTable("hhea");
+ nHMetrics = getU16BE(tables[i].offset + 34, &ok);
+ abbrevHMTX = nHMetrics < nGlyphs;
+
+ // if nothing is broken, just write the TTF file as is
+ if (!missingCmap && !missingName && !missingPost && !missingOS2 &&
+ !unsortedLoca && !emptyCmap && !badCmapLen && !abbrevHMTX &&
+ nZeroLengthTables == 0 && nBogusTables == 0 &&
+ !name && !codeToGID && !replacementCmapTable && !isDfont && !isTTC) {
+ (*outputFunc)(outputStream, (char *)file, len);
+ gfree(locaTable);
+ return gFalse;
+ }
+
+ // sort the 'loca' table: some (non-compliant) fonts have
+ // out-of-order loca tables; in order to correctly handle the case
+ // where (compliant) fonts have empty entries in the middle of the
+ // table, cmpTrueTypeLocaOffset uses offset as its primary sort key,
+ // and idx as its secondary key (ensuring that adjacent entries with
+ // the same pos value remain in the same order)
+ glyfLen = 0; // make gcc happy
+ if (unsortedLoca) {
+#if HAVE_STD_SORT
+ std::sort(locaTable, locaTable + nGlyphs + 1,
+ cmpTrueTypeLocaOffsetFunctor());
+#else
+ qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+ &cmpTrueTypeLocaOffset);
+#endif
+ for (i = 0; i < nGlyphs; ++i) {
+ locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset;
+ }
+ locaTable[nGlyphs].len = 0;
+#if HAVE_STD_SORT
+ std::sort(locaTable, locaTable + nGlyphs + 1, cmpTrueTypeLocaIdxFunctor());
+#else
+ qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+ &cmpTrueTypeLocaIdx);
+#endif
+ // if the last entry in the loca is not the max offset (size of
+ // the glyf table), something is wrong -- work around the problem
+ // by forcing the last sorted entry to have a zero length
+ locaTable[nGlyphs].len = 0;
+ pos = 0;
+ for (i = 0; i <= nGlyphs; ++i) {
+ locaTable[i].newOffset = pos;
+ pos += locaTable[i].len;
+ if (pos & 3) {
+ pos += 4 - (pos & 3);
+ }
+ }
+ glyfLen = pos;
+ }
+
+ // compute checksums for the loca and glyf tables
+ locaChecksum = glyfChecksum = 0;
+ if (unsortedLoca) {
+ if (locaFmt) {
+ for (j = 0; j <= nGlyphs; ++j) {
+ locaChecksum += locaTable[j].newOffset;
+ }
+ } else {
+ for (j = 0; j <= nGlyphs; j += 2) {
+ locaChecksum += locaTable[j].newOffset << 16;
+ if (j + 1 <= nGlyphs) {
+ locaChecksum += locaTable[j+1].newOffset;
+ }
+ }
+ }
+ pos = tables[seekTable("glyf")].offset;
+ for (j = 0; j < nGlyphs; ++j) {
+ n = locaTable[j].len;
+ if (n > 0) {
+ k = locaTable[j].origOffset;
+ if (checkRegion(pos + k, n)) {
+ glyfChecksum += computeTableChecksum(file + pos + k, n);
+ }
+ }
+ }
+ }
+
+ // construct the new name table
+ if (name) {
+ n = (int)strlen(name);
+ newNameLen = (6 + 4*12 + 2 * (3*n + 7) + 3) & ~3;
+ newNameTab = (char *)gmalloc(newNameLen);
+ memset(newNameTab, 0, newNameLen);
+ newNameTab[0] = 0; // format selector
+ newNameTab[1] = 0;
+ newNameTab[2] = 0; // number of name records
+ newNameTab[3] = 4;
+ newNameTab[4] = 0; // offset to start of string storage
+ newNameTab[5] = 6 + 4*12;
+ next = 0;
+ for (i = 0; i < 4; ++i) {
+ newNameTab[6 + i*12 + 0] = 0; // platform ID = Microsoft
+ newNameTab[6 + i*12 + 1] = 3;
+ newNameTab[6 + i*12 + 2] = 0; // encoding ID = Unicode
+ newNameTab[6 + i*12 + 3] = 1;
+ newNameTab[6 + i*12 + 4] = 0x04; // language ID = American English
+ newNameTab[6 + i*12 + 5] = 0x09;
+ newNameTab[6 + i*12 + 6] = 0; // name ID
+ newNameTab[6 + i*12 + 7] = (char)(i + 1);
+ // string length
+ newNameTab[6 + i*12 + 8] = (char)(i+1 == 2 ? 0 : ((2*n) >> 8));
+ newNameTab[6 + i*12 + 9] = (char)(i+1 == 2 ? 14 : ((2*n) & 0xff));
+ // string offset
+ newNameTab[6 + i*12 + 10] = (char)(next >> 8);
+ newNameTab[6 + i*12 + 11] = (char)(next & 0xff);
+ if (i+1 == 2) {
+ memcpy(newNameTab + 6 + 4*12 + next, "\0R\0e\0g\0u\0l\0a\0r", 14);
+ next += 14;
+ } else {
+ for (j = 0; j < n; ++j) {
+ newNameTab[6 + 4*12 + next + 2*j] = 0;
+ newNameTab[6 + 4*12 + next + 2*j + 1] = name[j];
+ }
+ next += 2*n;
+ }
+ }
+ } else {
+ newNameLen = 0;
+ newNameTab = NULL;
+ }
+
+ // construct the new cmap table
+ if (codeToGID && !replacementCmapTable) {
+ newCmapLen = 44 + 256 * 2;
+ newCmapTab = (char *)gmalloc(newCmapLen);
+ newCmapTab[0] = 0; // table version number = 0
+ newCmapTab[1] = 0;
+ newCmapTab[2] = 0; // number of encoding tables = 1
+ newCmapTab[3] = 1;
+ newCmapTab[4] = 0; // platform ID = Microsoft
+ newCmapTab[5] = 3;
+ newCmapTab[6] = 0; // encoding ID = Unicode
+ newCmapTab[7] = 1;
+ newCmapTab[8] = 0; // offset of subtable
+ newCmapTab[9] = 0;
+ newCmapTab[10] = 0;
+ newCmapTab[11] = 12;
+ newCmapTab[12] = 0; // subtable format = 4
+ newCmapTab[13] = 4;
+ newCmapTab[14] = 0x02; // subtable length
+ newCmapTab[15] = 0x20;
+ newCmapTab[16] = 0; // subtable version = 0
+ newCmapTab[17] = 0;
+ newCmapTab[18] = 0; // segment count * 2
+ newCmapTab[19] = 4;
+ newCmapTab[20] = 0; // 2 * 2 ^ floor(log2(segCount))
+ newCmapTab[21] = 4;
+ newCmapTab[22] = 0; // floor(log2(segCount))
+ newCmapTab[23] = 1;
+ newCmapTab[24] = 0; // 2*segCount - 2*2^floor(log2(segCount))
+ newCmapTab[25] = 0;
+ newCmapTab[26] = 0x00; // endCount[0]
+ newCmapTab[27] = (char)0xff;
+ newCmapTab[28] = (char)0xff; // endCount[1]
+ newCmapTab[29] = (char)0xff;
+ newCmapTab[30] = 0; // reserved
+ newCmapTab[31] = 0;
+ newCmapTab[32] = 0x00; // startCount[0]
+ newCmapTab[33] = 0x00;
+ newCmapTab[34] = (char)0xff; // startCount[1]
+ newCmapTab[35] = (char)0xff;
+ newCmapTab[36] = 0; // idDelta[0]
+ newCmapTab[37] = 0;
+ newCmapTab[38] = 0; // idDelta[1]
+ newCmapTab[39] = 1;
+ newCmapTab[40] = 0; // idRangeOffset[0]
+ newCmapTab[41] = 4;
+ newCmapTab[42] = 0; // idRangeOffset[1]
+ newCmapTab[43] = 0;
+ for (i = 0; i < 256; ++i) {
+ if (codeToGID[i] < 0) {
+ //~ this may not be correct - we want this character to never be
+ //~ displayed, but mapping it to the notdef glyph may result in
+ //~ little boxes being displayed
+ newCmapTab[44 + 2*i] = 0;
+ newCmapTab[44 + 2*i + 1] = 0;
+ } else {
+ newCmapTab[44 + 2*i] = (char)(codeToGID[i] >> 8);
+ newCmapTab[44 + 2*i + 1] = (char)(codeToGID[i] & 0xff);
+ }
+ }
+ } else {
+ newCmapLen = 0;
+ newCmapTab = NULL;
+ }
+
+ // generate the new hmtx table and the updated hhea table
+ if (abbrevHMTX) {
+ i = seekTable("hhea");
+ pos = tables[i].offset;
+ newHHEALen = 36;
+ newHHEATab = (char *)gmalloc(newHHEALen);
+ for (i = 0; i < newHHEALen; ++i) {
+ newHHEATab[i] = (char)getU8(pos++, &ok);
+ }
+ newHHEATab[34] = (char)(nGlyphs >> 8);
+ newHHEATab[35] = (char)(nGlyphs & 0xff);
+ i = seekTable("hmtx");
+ pos = tables[i].offset;
+ newHMTXLen = 4 * nGlyphs;
+ newHMTXTab = (char *)gmalloc(newHMTXLen);
+ advWidth = 0;
+ for (i = 0; i < nHMetrics; ++i) {
+ advWidth = getU16BE(pos, &ok);
+ lsb = getU16BE(pos + 2, &ok);
+ pos += 4;
+ newHMTXTab[4*i ] = (char)(advWidth >> 8);
+ newHMTXTab[4*i + 1] = (char)(advWidth & 0xff);
+ newHMTXTab[4*i + 2] = (char)(lsb >> 8);
+ newHMTXTab[4*i + 3] = (char)(lsb & 0xff);
+ }
+ for (; i < nGlyphs; ++i) {
+ lsb = getU16BE(pos, &ok);
+ pos += 2;
+ newHMTXTab[4*i ] = (char)(advWidth >> 8);
+ newHMTXTab[4*i + 1] = (char)(advWidth & 0xff);
+ newHMTXTab[4*i + 2] = (char)(lsb >> 8);
+ newHMTXTab[4*i + 3] = (char)(lsb & 0xff);
+ }
+ } else {
+ newHHEATab = newHMTXTab = NULL;
+ newHHEALen = newHMTXLen = 0; // make gcc happy
+ }
+
+ // construct the new table directory:
+ // - keep all original tables with non-zero length
+ // - fix the cmap table's length, if necessary
+ // - add missing tables
+ // - sort the table by tag
+ // - compute new table positions, including 4-byte alignment
+ // - (re)compute table checksums
+ nNewTables = nTables - nZeroLengthTables - nBogusTables +
+ (missingCmap ? 1 : 0) + (missingName ? 1 : 0) +
+ (missingPost ? 1 : 0) + (missingOS2 ? 1 : 0);
+ newTables = (TrueTypeTable *)gmallocn(nNewTables, sizeof(TrueTypeTable));
+ j = 0;
+ for (i = 0; i < nTables; ++i) {
+ if (tables[i].len > 0 &&
+ (tables[i].tag & 0xe0000000) &&
+ (tables[i].tag & 0x00e00000) &&
+ (tables[i].tag & 0x0000e000) &&
+ (tables[i].tag & 0x000000e0)) {
+ newTables[j] = tables[i];
+ newTables[j].origOffset = tables[i].offset;
+ if (checkRegion(tables[i].offset, tables[i].len)) {
+ newTables[j].checksum =
+ computeTableChecksum(file + tables[i].offset, tables[i].len);
+ if (tables[i].tag == headTag) {
+ // don't include the file checksum
+ newTables[j].checksum -= getU32BE(tables[i].offset + 8, &ok);
+ }
+ } else {
+ // we'll write four zero bytes for this table
+ newTables[j].len = 4;
+ }
+ if (newTables[j].tag == cmapTag && replacementCmapTable) {
+ newTables[j].len = replacementCmapTableLen;
+ newTables[j].checksum = computeTableChecksum(replacementCmapTable,
+ replacementCmapTableLen);
+ } else if (newTables[j].tag == cmapTag && codeToGID) {
+ newTables[j].len = newCmapLen;
+ newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab,
+ newCmapLen);
+ } else if (newTables[j].tag == cmapTag && emptyCmap) {
+ newTables[j].checksum = computeTableChecksum((Guchar *)cmapTab,
+ sizeof(cmapTab));
+ newTables[j].len = sizeof(cmapTab);
+ } else if (newTables[j].tag == cmapTag && badCmapLen) {
+ newTables[j].len = cmapLen;
+ } else if (newTables[j].tag == locaTag && unsortedLoca) {
+ newTables[j].len = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+ newTables[j].checksum = locaChecksum;
+ } else if (newTables[j].tag == glyfTag && unsortedLoca) {
+ newTables[j].len = glyfLen;
+ newTables[j].checksum = glyfChecksum;
+ } else if (newTables[j].tag == nameTag && name) {
+ newTables[j].len = newNameLen;
+ newTables[j].checksum = computeTableChecksum((Guchar *)newNameTab,
+ newNameLen);
+ } else if (newTables[j].tag == hheaTag && abbrevHMTX) {
+ newTables[j].len = newHHEALen;
+ newTables[j].checksum = computeTableChecksum((Guchar *)newHHEATab,
+ newHHEALen);
+ } else if (newTables[j].tag == hmtxTag && abbrevHMTX) {
+ newTables[j].len = newHMTXLen;
+ newTables[j].checksum = computeTableChecksum((Guchar *)newHMTXTab,
+ newHMTXLen);
+ }
+ ++j;
+ }
+ }
+ if (missingCmap) {
+ newTables[j].tag = cmapTag;
+ if (replacementCmapTable) {
+ newTables[j].checksum = computeTableChecksum(replacementCmapTable,
+ replacementCmapTableLen);
+ newTables[j].len = replacementCmapTableLen;
+ } else if (codeToGID) {
+ newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab,
+ newCmapLen);
+ newTables[j].len = newCmapLen;
+ } else {
+ newTables[j].checksum = computeTableChecksum((Guchar *)cmapTab,
+ sizeof(cmapTab));
+ newTables[j].len = sizeof(cmapTab);
+ }
+ ++j;
+ }
+ if (missingName) {
+ newTables[j].tag = nameTag;
+ if (name) {
+ newTables[j].checksum = computeTableChecksum((Guchar *)newNameTab,
+ newNameLen);
+ newTables[j].len = newNameLen;
+ } else {
+ newTables[j].checksum = computeTableChecksum((Guchar *)nameTab,
+ sizeof(nameTab));
+ newTables[j].len = sizeof(nameTab);
+ }
+ ++j;
+ }
+ if (missingPost) {
+ newTables[j].tag = postTag;
+ newTables[j].checksum = computeTableChecksum((Guchar *)postTab,
+ sizeof(postTab));
+ newTables[j].len = sizeof(postTab);
+ ++j;
+ }
+ if (missingOS2) {
+ newTables[j].tag = os2Tag;
+ newTables[j].checksum = computeTableChecksum((Guchar *)os2Tab,
+ sizeof(os2Tab));
+ newTables[j].len = sizeof(os2Tab);
+ ++j;
+ }
+#if HAVE_STD_SORT
+ std::sort(newTables, newTables + nNewTables, cmpTrueTypeTableTagFunctor());
+#else
+ qsort(newTables, nNewTables, sizeof(TrueTypeTable),
+ &cmpTrueTypeTableTag);
+#endif
+ pos = 12 + nNewTables * 16;
+ for (i = 0; i < nNewTables; ++i) {
+ newTables[i].offset = pos;
+ pos += newTables[i].len;
+ if (pos & 3) {
+ pos += 4 - (pos & 3);
+ }
+ }
+
+ // write the table directory
+ tableDir = (char *)gmalloc(12 + nNewTables * 16);
+ tableDir[0] = 0x00; // sfnt version
+ tableDir[1] = 0x01;
+ tableDir[2] = 0x00;
+ tableDir[3] = 0x00;
+ tableDir[4] = (char)((nNewTables >> 8) & 0xff); // numTables
+ tableDir[5] = (char)(nNewTables & 0xff);
+ for (i = -1, t = (Guint)nNewTables; t; ++i, t >>= 1) ;
+ t = 1 << (4 + i);
+ tableDir[6] = (char)((t >> 8) & 0xff); // searchRange
+ tableDir[7] = (char)(t & 0xff);
+ tableDir[8] = (char)((i >> 8) & 0xff); // entrySelector
+ tableDir[9] = (char)(i & 0xff);
+ t = nNewTables * 16 - t;
+ tableDir[10] = (char)((t >> 8) & 0xff); // rangeShift
+ tableDir[11] = (char)(t & 0xff);
+ pos = 12;
+ for (i = 0; i < nNewTables; ++i) {
+ tableDir[pos ] = (char)(newTables[i].tag >> 24);
+ tableDir[pos+ 1] = (char)(newTables[i].tag >> 16);
+ tableDir[pos+ 2] = (char)(newTables[i].tag >> 8);
+ tableDir[pos+ 3] = (char) newTables[i].tag;
+ tableDir[pos+ 4] = (char)(newTables[i].checksum >> 24);
+ tableDir[pos+ 5] = (char)(newTables[i].checksum >> 16);
+ tableDir[pos+ 6] = (char)(newTables[i].checksum >> 8);
+ tableDir[pos+ 7] = (char) newTables[i].checksum;
+ tableDir[pos+ 8] = (char)(newTables[i].offset >> 24);
+ tableDir[pos+ 9] = (char)(newTables[i].offset >> 16);
+ tableDir[pos+10] = (char)(newTables[i].offset >> 8);
+ tableDir[pos+11] = (char) newTables[i].offset;
+ tableDir[pos+12] = (char)(newTables[i].len >> 24);
+ tableDir[pos+13] = (char)(newTables[i].len >> 16);
+ tableDir[pos+14] = (char)(newTables[i].len >> 8);
+ tableDir[pos+15] = (char) newTables[i].len;
+ pos += 16;
+ }
+ (*outputFunc)(outputStream, tableDir, 12 + nNewTables * 16);
+
+ // compute the file checksum
+ fileChecksum = computeTableChecksum((Guchar *)tableDir,
+ 12 + nNewTables * 16);
+ for (i = 0; i < nNewTables; ++i) {
+ fileChecksum += newTables[i].checksum;
+ }
+ fileChecksum = 0xb1b0afba - fileChecksum;
+
+ // write the tables
+ for (i = 0; i < nNewTables; ++i) {
+ if (newTables[i].tag == headTag) {
+ if (checkRegion(newTables[i].origOffset, newTables[i].len)) {
+ (*outputFunc)(outputStream, (char *)file + newTables[i].origOffset, 8);
+ checksumBuf[0] = (char)(fileChecksum >> 24);
+ checksumBuf[1] = (char)(fileChecksum >> 16);
+ checksumBuf[2] = (char)(fileChecksum >> 8);
+ checksumBuf[3] = (char)fileChecksum;
+ (*outputFunc)(outputStream, checksumBuf, 4);
+ (*outputFunc)(outputStream,
+ (char *)file + newTables[i].origOffset + 12,
+ newTables[i].len - 12);
+ } else {
+ for (j = 0; j < newTables[i].len; ++j) {
+ (*outputFunc)(outputStream, "\0", 1);
+ }
+ }
+ } else if (newTables[i].tag == cmapTag && replacementCmapTable) {
+ (*outputFunc)(outputStream, (char *)replacementCmapTable,
+ newTables[i].len);
+ } else if (newTables[i].tag == cmapTag && codeToGID) {
+ (*outputFunc)(outputStream, newCmapTab, newTables[i].len);
+ } else if (newTables[i].tag == cmapTag && missingCmap) {
+ (*outputFunc)(outputStream, cmapTab, newTables[i].len);
+ } else if (newTables[i].tag == nameTag && name) {
+ (*outputFunc)(outputStream, newNameTab, newTables[i].len);
+ } else if (newTables[i].tag == nameTag && missingName) {
+ (*outputFunc)(outputStream, nameTab, newTables[i].len);
+ } else if (newTables[i].tag == postTag && missingPost) {
+ (*outputFunc)(outputStream, postTab, newTables[i].len);
+ } else if (newTables[i].tag == os2Tag && missingOS2) {
+ (*outputFunc)(outputStream, os2Tab, newTables[i].len);
+ } else if (newTables[i].tag == hheaTag && abbrevHMTX) {
+ (*outputFunc)(outputStream, newHHEATab, newTables[i].len);
+ } else if (newTables[i].tag == hmtxTag && abbrevHMTX) {
+ (*outputFunc)(outputStream, newHMTXTab, newTables[i].len);
+ } else if (newTables[i].tag == locaTag && unsortedLoca) {
+ for (j = 0; j <= nGlyphs; ++j) {
+ if (locaFmt) {
+ locaBuf[0] = (char)(locaTable[j].newOffset >> 24);
+ locaBuf[1] = (char)(locaTable[j].newOffset >> 16);
+ locaBuf[2] = (char)(locaTable[j].newOffset >> 8);
+ locaBuf[3] = (char) locaTable[j].newOffset;
+ (*outputFunc)(outputStream, locaBuf, 4);
+ } else {
+ locaBuf[0] = (char)(locaTable[j].newOffset >> 9);
+ locaBuf[1] = (char)(locaTable[j].newOffset >> 1);
+ (*outputFunc)(outputStream, locaBuf, 2);
+ }
+ }
+ } else if (newTables[i].tag == glyfTag && unsortedLoca) {
+ pos = tables[seekTable("glyf")].offset;
+ for (j = 0; j < nGlyphs; ++j) {
+ n = locaTable[j].len;
+ if (n > 0) {
+ k = locaTable[j].origOffset;
+ if (checkRegion(pos + k, n)) {
+ (*outputFunc)(outputStream, (char *)file + pos + k, n);
+ } else {
+ for (k = 0; k < n; ++k) {
+ (*outputFunc)(outputStream, "\0", 1);
+ }
+ }
+ if ((k = locaTable[j].len & 3)) {
+ (*outputFunc)(outputStream, "\0\0\0\0", 4 - k);
+ }
+ }
+ }
+ } else {
+ if (checkRegion(newTables[i].origOffset, newTables[i].len)) {
+ (*outputFunc)(outputStream, (char *)file + newTables[i].origOffset,
+ newTables[i].len);
+ } else {
+ for (j = 0; j < newTables[i].len; ++j) {
+ (*outputFunc)(outputStream, "\0", 1);
+ }
+ }
+ }
+ if (newTables[i].len & 3) {
+ (*outputFunc)(outputStream, "\0\0\0", 4 - (newTables[i].len & 3));
+ }
+ }
+
+ gfree(newHMTXTab);
+ gfree(newHHEATab);
+ gfree(newCmapTab);
+ gfree(newNameTab);
+ gfree(tableDir);
+ gfree(newTables);
+ gfree(locaTable);
+ return gTrue;
+}
+
+void FoFiTrueType::cvtEncoding(char **encoding,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ const char *name;
+ GString *buf;
+ int i;
+
+ (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+ if (encoding) {
+ for (i = 0; i < 256; ++i) {
+ if (!(name = encoding[i])) {
+ name = ".notdef";
+ }
+ buf = GString::format("dup {0:d} /", i);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, name, (int)strlen(name));
+ (*outputFunc)(outputStream, " put\n", 5);
+ }
+ } else {
+ for (i = 0; i < 256; ++i) {
+ buf = GString::format("dup {0:d} /c{1:02x} put\n", i, i);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ }
+ (*outputFunc)(outputStream, "readonly def\n", 13);
+}
+
+void FoFiTrueType::cvtCharStrings(char **encoding,
+ int *codeToGID,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ char *name;
+ GString *buf;
+ char buf2[16];
+ int i, k;
+
+ // always define '.notdef'
+ (*outputFunc)(outputStream, "/CharStrings 256 dict dup begin\n", 32);
+ (*outputFunc)(outputStream, "/.notdef 0 def\n", 15);
+
+ // map char name to glyph index:
+ // 1. use encoding to map name to char code
+ // 2. use codeToGID to map char code to glyph index
+ // N.B. We do this in reverse order because font subsets can have
+ // weird encodings that use the same character name twice, and
+ // the first definition is probably the one we want.
+ k = 0; // make gcc happy
+ for (i = 255; i >= 0; --i) {
+ if (encoding) {
+ name = encoding[i];
+ } else {
+ snprintf(buf2, sizeof(buf2), "c%02x", i);
+ name = buf2;
+ }
+ if (name && strcmp(name, ".notdef")) {
+ k = codeToGID[i];
+ // note: Distiller (maybe Adobe's PS interpreter in general)
+ // doesn't like TrueType fonts that have CharStrings entries
+ // which point to nonexistent glyphs, hence the (k < nGlyphs)
+ // test
+ if (k > 0 && k < nGlyphs) {
+ (*outputFunc)(outputStream, "/", 1);
+ (*outputFunc)(outputStream, name, (int)strlen(name));
+ buf = GString::format(" {0:d} def\n", k);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ }
+ }
+
+ (*outputFunc)(outputStream, "end readonly def\n", 17);
+}
+
+void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
+ void *outputStream, GString *name,
+ GBool needVerticalMetrics,
+ int *maxUsedGlyph) {
+ Guchar headData[54];
+ TrueTypeLoca *locaTable;
+ Guchar *locaData;
+ TrueTypeTable newTables[nT42Tables];
+ Guchar tableDir[12 + nT42Tables*16];
+ GBool ok;
+ Guint checksum;
+ int nNewTables;
+ int glyfTableLen, length, pos, glyfPos, i, j, k;
+ Guchar vheaTab[36] = {
+ 0, 1, 0, 0, // table version number
+ 0, 0, // ascent
+ 0, 0, // descent
+ 0, 0, // reserved
+ 0, 0, // max advance height
+ 0, 0, // min top side bearing
+ 0, 0, // min bottom side bearing
+ 0, 0, // y max extent
+ 0, 0, // caret slope rise
+ 0, 1, // caret slope run
+ 0, 0, // caret offset
+ 0, 0, // reserved
+ 0, 0, // reserved
+ 0, 0, // reserved
+ 0, 0, // reserved
+ 0, 0, // metric data format
+ 0, 1 // number of advance heights in vmtx table
+ };
+ Guchar *vmtxTab;
+ GBool needVhea, needVmtx;
+ int advance;
+
+ // construct the 'head' table, zero out the font checksum
+ i = seekTable("head");
+ pos = tables[i].offset;
+ if (!checkRegion(pos, 54)) {
+ return;
+ }
+ memcpy(headData, file + pos, 54);
+ headData[8] = headData[9] = headData[10] = headData[11] = (Guchar)0;
+
+ // check for a bogus loca format field in the 'head' table
+ // (I've encountered fonts with loca format set to 0x0100 instead of 0x0001)
+ if (locaFmt != 0 && locaFmt != 1) {
+ headData[50] = 0;
+ headData[51] = 1;
+ }
+
+ // read the original 'loca' table, pad entries out to 4 bytes, and
+ // sort it into proper order -- some (non-compliant) fonts have
+ // out-of-order loca tables; in order to correctly handle the case
+ // where (compliant) fonts have empty entries in the middle of the
+ // table, cmpTrueTypeLocaOffset uses offset as its primary sort key,
+ // and idx as its secondary key (ensuring that adjacent entries with
+ // the same pos value remain in the same order)
+ //
+ // NB: a glyph description containing 12 zero bytes should be a
+ // valid empty glyph (from my reading of the TrueType spec), but
+ // Acrobat chokes on this (which is an issue when an Xpdf-generated
+ // PS file is converted back to PDF - with Ghostscript or
+ // Distiller), so we drop any glyph descriptions of 12 or fewer
+ // bytes -- an empty glyph description generates an empty glyph with
+ // no errors
+ locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca));
+ i = seekTable("loca");
+ pos = tables[i].offset;
+ i = seekTable("glyf");
+ glyfTableLen = tables[i].len;
+ ok = gTrue;
+ for (i = 0; i <= nGlyphs; ++i) {
+ locaTable[i].idx = i;
+ if (locaFmt) {
+ locaTable[i].origOffset = (int)getU32BE(pos + i*4, &ok);
+ } else {
+ locaTable[i].origOffset = 2 * getU16BE(pos + i*2, &ok);
+ }
+ if (locaTable[i].origOffset < 0 ||
+ locaTable[i].origOffset > glyfTableLen) {
+ locaTable[i].origOffset = glyfTableLen;
+ }
+ }
+#if HAVE_STD_SORT
+ std::sort(locaTable, locaTable + nGlyphs + 1,
+ cmpTrueTypeLocaOffsetFunctor());
+#else
+ qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+ &cmpTrueTypeLocaOffset);
+#endif
+ for (i = 0; i < nGlyphs; ++i) {
+ locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset;
+ }
+ locaTable[nGlyphs].len = 0;
+#if HAVE_STD_SORT
+ std::sort(locaTable, locaTable + nGlyphs + 1, cmpTrueTypeLocaIdxFunctor());
+#else
+ qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+ &cmpTrueTypeLocaIdx);
+#endif
+ pos = 0;
+ *maxUsedGlyph = -1;
+ for (i = 0; i <= nGlyphs; ++i) {
+ locaTable[i].newOffset = pos;
+ if (locaTable[i].len > 12) {
+ pos += locaTable[i].len;
+ if (pos & 3) {
+ pos += 4 - (pos & 3);
+ }
+ *maxUsedGlyph = i;
+ }
+ }
+
+ // construct the new 'loca' table
+ locaData = (Guchar *)gmallocn(nGlyphs + 1, (locaFmt ? 4 : 2));
+ for (i = 0; i <= nGlyphs; ++i) {
+ pos = locaTable[i].newOffset;
+ if (locaFmt) {
+ locaData[4*i ] = (Guchar)(pos >> 24);
+ locaData[4*i+1] = (Guchar)(pos >> 16);
+ locaData[4*i+2] = (Guchar)(pos >> 8);
+ locaData[4*i+3] = (Guchar) pos;
+ } else {
+ locaData[2*i ] = (Guchar)(pos >> 9);
+ locaData[2*i+1] = (Guchar)(pos >> 1);
+ }
+ }
+
+ // count the number of tables
+ nNewTables = 0;
+ for (i = 0; i < nT42Tables; ++i) {
+ if (t42Tables[i].required ||
+ seekTable(t42Tables[i].tag) >= 0) {
+ ++nNewTables;
+ }
+ }
+ vmtxTab = NULL; // make gcc happy
+ advance = 0; // make gcc happy
+ if (needVerticalMetrics) {
+ needVhea = seekTable("vhea") < 0;
+ needVmtx = seekTable("vmtx") < 0;
+ if (needVhea || needVmtx) {
+ i = seekTable("head");
+ advance = getU16BE(tables[i].offset + 18, &ok); // units per em
+ if (needVhea) {
+ ++nNewTables;
+ }
+ if (needVmtx) {
+ ++nNewTables;
+ }
+ }
+ }
+
+ // construct the new table headers, including table checksums
+ // (pad each table out to a multiple of 4 bytes)
+ pos = 12 + nNewTables*16;
+ k = 0;
+ for (i = 0; i < nT42Tables; ++i) {
+ length = -1;
+ checksum = 0; // make gcc happy
+ if (i == t42HeadTable) {
+ length = 54;
+ checksum = computeTableChecksum(headData, 54);
+ } else if (i == t42LocaTable) {
+ length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+ checksum = computeTableChecksum(locaData, length);
+ } else if (i == t42GlyfTable) {
+ length = 0;
+ checksum = 0;
+ glyfPos = tables[seekTable("glyf")].offset;
+ for (j = 0; j < nGlyphs; ++j) {
+ if (locaTable[j].len > 12) {
+ length += locaTable[j].len;
+ if (length & 3) {
+ length += 4 - (length & 3);
+ }
+ if (checkRegion(glyfPos + locaTable[j].origOffset,
+ locaTable[j].len)) {
+ checksum +=
+ computeTableChecksum(file + glyfPos + locaTable[j].origOffset,
+ locaTable[j].len);
+ }
+ }
+ }
+ } else {
+ if ((j = seekTable(t42Tables[i].tag)) >= 0) {
+ length = tables[j].len;
+ if (checkRegion(tables[j].offset, length)) {
+ checksum = computeTableChecksum(file + tables[j].offset, length);
+ }
+ } else if (needVerticalMetrics && i == t42VheaTable) {
+ vheaTab[10] = (Guchar)(advance / 256); // max advance height
+ vheaTab[11] = (Guchar)(advance % 256);
+ length = sizeof(vheaTab);
+ checksum = computeTableChecksum(vheaTab, length);
+ } else if (needVerticalMetrics && i == t42VmtxTable) {
+ length = 4 + (nGlyphs - 1) * 2;
+ vmtxTab = (Guchar *)gmalloc(length);
+ vmtxTab[0] = (Guchar)(advance / 256);
+ vmtxTab[1] = (Guchar)(advance % 256);
+ for (j = 2; j < length; j += 2) {
+ vmtxTab[j] = 0;
+ vmtxTab[j+1] = 0;
+ }
+ checksum = computeTableChecksum(vmtxTab, length);
+ } else if (t42Tables[i].required) {
+ //~ error(-1, "Embedded TrueType font is missing a required table ('%s')",
+ //~ t42Tables[i].tag);
+ length = 0;
+ checksum = 0;
+ }
+ }
+ if (length >= 0) {
+ newTables[k].tag = ((t42Tables[i].tag[0] & 0xff) << 24) |
+ ((t42Tables[i].tag[1] & 0xff) << 16) |
+ ((t42Tables[i].tag[2] & 0xff) << 8) |
+ (t42Tables[i].tag[3] & 0xff);
+ newTables[k].checksum = checksum;
+ newTables[k].offset = pos;
+ newTables[k].len = length;
+ pos += length;
+ if (pos & 3) {
+ pos += 4 - (length & 3);
+ }
+ ++k;
+ }
+ }
+
+ // construct the table directory
+ tableDir[0] = 0x00; // sfnt version
+ tableDir[1] = 0x01;
+ tableDir[2] = 0x00;
+ tableDir[3] = 0x00;
+ tableDir[4] = 0; // numTables
+ tableDir[5] = (Guchar)nNewTables;
+ tableDir[6] = 0; // searchRange
+ tableDir[7] = (Guchar)128;
+ tableDir[8] = 0; // entrySelector
+ tableDir[9] = 3;
+ tableDir[10] = 0; // rangeShift
+ tableDir[11] = (Guchar)(16 * nNewTables - 128);
+ pos = 12;
+ for (i = 0; i < nNewTables; ++i) {
+ tableDir[pos ] = (Guchar)(newTables[i].tag >> 24);
+ tableDir[pos+ 1] = (Guchar)(newTables[i].tag >> 16);
+ tableDir[pos+ 2] = (Guchar)(newTables[i].tag >> 8);
+ tableDir[pos+ 3] = (Guchar) newTables[i].tag;
+ tableDir[pos+ 4] = (Guchar)(newTables[i].checksum >> 24);
+ tableDir[pos+ 5] = (Guchar)(newTables[i].checksum >> 16);
+ tableDir[pos+ 6] = (Guchar)(newTables[i].checksum >> 8);
+ tableDir[pos+ 7] = (Guchar) newTables[i].checksum;
+ tableDir[pos+ 8] = (Guchar)(newTables[i].offset >> 24);
+ tableDir[pos+ 9] = (Guchar)(newTables[i].offset >> 16);
+ tableDir[pos+10] = (Guchar)(newTables[i].offset >> 8);
+ tableDir[pos+11] = (Guchar) newTables[i].offset;
+ tableDir[pos+12] = (Guchar)(newTables[i].len >> 24);
+ tableDir[pos+13] = (Guchar)(newTables[i].len >> 16);
+ tableDir[pos+14] = (Guchar)(newTables[i].len >> 8);
+ tableDir[pos+15] = (Guchar) newTables[i].len;
+ pos += 16;
+ }
+
+ // compute the font checksum and store it in the head table
+ checksum = computeTableChecksum(tableDir, 12 + nNewTables*16);
+ for (i = 0; i < nNewTables; ++i) {
+ checksum += newTables[i].checksum;
+ }
+ checksum = 0xb1b0afba - checksum; // because the TrueType spec says so
+ headData[ 8] = (Guchar)(checksum >> 24);
+ headData[ 9] = (Guchar)(checksum >> 16);
+ headData[10] = (Guchar)(checksum >> 8);
+ headData[11] = (Guchar) checksum;
+
+ // start the sfnts array
+ if (name) {
+ (*outputFunc)(outputStream, "/", 1);
+ (*outputFunc)(outputStream, name->getCString(), name->getLength());
+ (*outputFunc)(outputStream, " [\n", 3);
+ } else {
+ (*outputFunc)(outputStream, "/sfnts [\n", 9);
+ }
+
+ // write the table directory
+ dumpString(tableDir, 12 + nNewTables*16, outputFunc, outputStream);
+
+ // write the tables
+ for (i = 0; i < nNewTables; ++i) {
+ if (i == t42HeadTable) {
+ dumpString(headData, 54, outputFunc, outputStream);
+ } else if (i == t42LocaTable) {
+ length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+ dumpString(locaData, length, outputFunc, outputStream);
+ } else if (i == t42GlyfTable) {
+ glyfPos = tables[seekTable("glyf")].offset;
+ for (j = 0; j < nGlyphs; ++j) {
+ if (locaTable[j].len > 12 &&
+ checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) {
+ dumpString(file + glyfPos + locaTable[j].origOffset,
+ locaTable[j].len, outputFunc, outputStream);
+ }
+ }
+ } else {
+ // length == 0 means the table is missing and the error was
+ // already reported during the construction of the table
+ // headers
+ if ((length = newTables[i].len) > 0) {
+ if ((j = seekTable(t42Tables[i].tag)) >= 0 &&
+ checkRegion(tables[j].offset, tables[j].len)) {
+ dumpString(file + tables[j].offset, tables[j].len,
+ outputFunc, outputStream);
+ } else if (needVerticalMetrics && i == t42VheaTable) {
+ dumpString(vheaTab, length, outputFunc, outputStream);
+ } else if (needVerticalMetrics && i == t42VmtxTable) {
+ dumpString(vmtxTab, length, outputFunc, outputStream);
+ }
+ }
+ }
+ }
+
+ // end the sfnts array
+ (*outputFunc)(outputStream, "] def\n", 6);
+
+ gfree(locaData);
+ gfree(locaTable);
+ if (vmtxTab) {
+ gfree(vmtxTab);
+ }
+}
+
+void FoFiTrueType::dumpString(Guchar *s, int length,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ GString *buf;
+ int pad, i, j;
+
+ (*outputFunc)(outputStream, "<", 1);
+ for (i = 0; i < length; i += 32) {
+ for (j = 0; j < 32 && i+j < length; ++j) {
+ buf = GString::format("{0:02x}", s[i+j] & 0xff);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ if (i % (65536 - 32) == 65536 - 64) {
+ (*outputFunc)(outputStream, ">\n<", 3);
+ } else if (i+32 < length) {
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+ }
+ if (length & 3) {
+ pad = 4 - (length & 3);
+ for (i = 0; i < pad; ++i) {
+ (*outputFunc)(outputStream, "00", 2);
+ }
+ }
+ // add an extra zero byte because the Adobe Type 42 spec says so
+ (*outputFunc)(outputStream, "00>\n", 4);
+}
+
+Guint FoFiTrueType::computeTableChecksum(Guchar *data, int length) {
+ Guint checksum, word;
+ int i;
+
+ checksum = 0;
+ for (i = 0; i+3 < length; i += 4) {
+ word = ((data[i ] & 0xff) << 24) +
+ ((data[i+1] & 0xff) << 16) +
+ ((data[i+2] & 0xff) << 8) +
+ (data[i+3] & 0xff);
+ checksum += word;
+ }
+ if (length & 3) {
+ word = 0;
+ i = length & ~3;
+ switch (length & 3) {
+ case 3:
+ word |= (data[i+2] & 0xff) << 8;
+ case 2:
+ word |= (data[i+1] & 0xff) << 16;
+ case 1:
+ word |= (data[i ] & 0xff) << 24;
+ break;
+ }
+ checksum += word;
+ }
+ return checksum;
+}
+
+void FoFiTrueType::parse(int fontNum, GBool allowHeadlessCFF) {
+ Guint topTag;
+ int offset, pos, ver, i, j, k;
+
+ parsedOk = gTrue;
+
+ // check for a dfont or TrueType collection (TTC)
+ // offset = start of actual TrueType font file (table positions are
+ // relative to this
+ // pos = position of table directory (relative to offset)
+ if (isDfont) {
+ parseDfont(fontNum, &offset, &pos);
+ } else {
+ offset = 0;
+ topTag = getU32BE(0, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (topTag == ttcfTag) {
+ parseTTC(fontNum, &pos);
+ } else {
+ pos = 0;
+ }
+ }
+ if (!parsedOk) {
+ return;
+ }
+
+ // check the sfnt version
+ ver = getU32BE(offset + pos, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ openTypeCFF = ver == 0x4f54544f; // 'OTTO'
+
+ // read the table directory
+ nTables = getU16BE(offset + pos + 4, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ tables = (TrueTypeTable *)gmallocn(nTables, sizeof(TrueTypeTable));
+ pos += 12;
+ j = 0;
+ for (i = 0; i < nTables; ++i) {
+ tables[j].tag = getU32BE(offset + pos, &parsedOk);
+ tables[j].checksum = getU32BE(offset + pos + 4, &parsedOk);
+ tables[j].offset = offset + (int)getU32BE(offset + pos + 8, &parsedOk);
+ tables[j].len = (int)getU32BE(offset + pos + 12, &parsedOk);
+ if (tables[j].offset >= 0 &&
+ tables[j].len >= 0 &&
+ tables[j].offset + tables[j].len >= tables[j].offset &&
+ tables[j].offset + tables[j].len <= len) {
+ // ignore any bogus entries in the table directory
+ ++j;
+ }
+ pos += 16;
+ }
+ nTables = j;
+ if (!parsedOk) {
+ return;
+ }
+
+ // check for the head table; allow for a head-less OpenType CFF font
+ headlessCFF = gFalse;
+ if (seekTable("head") < 0) {
+ if (openTypeCFF && allowHeadlessCFF && seekTable("CFF ") >= 0) {
+ headlessCFF = gTrue;
+ nGlyphs = 0;
+ bbox[0] = bbox[1] = bbox[2] = bbox[3] = 0;
+ locaFmt = 0;
+ return;
+ }
+ parsedOk = gFalse;
+ return;
+ }
+
+ // check for other tables that are required by both the TrueType
+ // spec and the Type 42 spec
+ if (seekTable("hhea") < 0 ||
+ seekTable("maxp") < 0 ||
+ seekTable("hmtx") < 0 ||
+ (!openTypeCFF && seekTable("loca") < 0) ||
+ (!openTypeCFF && seekTable("glyf") < 0) ||
+ (openTypeCFF && seekTable("CFF ") < 0)) {
+ parsedOk = gFalse;
+ return;
+ }
+
+ // read the cmaps
+ if ((i = seekTable("cmap")) >= 0 && tables[i].len >= 4) {
+ pos = tables[i].offset + 2;
+ nCmaps = getU16BE(pos, &parsedOk);
+ pos += 2;
+ if (!parsedOk) {
+ return;
+ }
+ cmaps = (TrueTypeCmap *)gmallocn(nCmaps, sizeof(TrueTypeCmap));
+ k = 0;
+ for (j = 0; j < nCmaps; ++j) {
+ cmaps[k].platform = getU16BE(pos, &parsedOk);
+ cmaps[k].encoding = getU16BE(pos + 2, &parsedOk);
+ cmaps[k].offset = getU32BE(pos + 4, &parsedOk);
+ pos += 8;
+ 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;
+ }
+ }
+
+ // get the number of glyphs from the maxp table
+ i = seekTable("maxp");
+ nGlyphs = getU16BE(tables[i].offset + 4, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+
+ // get the bbox and loca table format from the head table
+ i = seekTable("head");
+ bbox[0] = getS16BE(tables[i].offset + 36, &parsedOk);
+ bbox[1] = getS16BE(tables[i].offset + 38, &parsedOk);
+ bbox[2] = getS16BE(tables[i].offset + 40, &parsedOk);
+ bbox[3] = getS16BE(tables[i].offset + 42, &parsedOk);
+ locaFmt = getS16BE(tables[i].offset + 50, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+
+ // make sure the loca table is sane (correct length)
+ // NB: out-of-bounds entries are handled in writeTTF()
+ if (!openTypeCFF) {
+ i = seekTable("loca");
+ if (tables[i].len < (locaFmt ? 4 : 2)) {
+ parsedOk = gFalse;
+ return;
+ }
+ if (tables[i].len < (nGlyphs + 1) * (locaFmt ? 4 : 2)) {
+ nGlyphs = tables[i].len / (locaFmt ? 4 : 2) - 1;
+ }
+ if (!parsedOk) {
+ return;
+ }
+ }
+
+ // read the post table
+ readPostTable();
+}
+
+// Get the table directory position
+void FoFiTrueType::parseTTC(int fontNum, int *pos) {
+ int nFonts;
+
+ isTTC = gTrue;
+ nFonts = getU32BE(8, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (fontNum < 0 || fontNum >= nFonts) {
+ parsedOk = gFalse;
+ return;
+ }
+ *pos = getU32BE(12 + 4 * fontNum, &parsedOk);
+}
+
+void FoFiTrueType::parseDfont(int fontNum, int *offset, int *startPos) {
+ int resMapOffset, resDataOffset;
+ int resTypeListOffset, nTypes, typeTag;
+ int nFonts, refListOffset, dataOffset;
+ int pos, i;
+
+ resDataOffset = getU32BE(0, &parsedOk);
+ resMapOffset = getU32BE(4, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+
+ resTypeListOffset = getU16BE(resMapOffset + 24, &parsedOk);
+ // resNameListOffset = getU16BE(resMapOffset + 26, &parsedOk);
+ nTypes = getU16BE(resMapOffset + 28, &parsedOk) + 1;
+ if (!parsedOk) {
+ return;
+ }
+
+ pos = 0; // make gcc happy
+ for (i = 0; i < nTypes; ++i) {
+ pos = resMapOffset + resTypeListOffset + 2 + 8*i;
+ typeTag = getU32BE(pos, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (typeTag == 0x73666e74) { // 'sfnt'
+ break;
+ }
+ }
+ if (i >= nTypes) {
+ parsedOk = gFalse;
+ return;
+ }
+ nFonts = getU16BE(pos + 4, &parsedOk) + 1;
+ refListOffset = getU16BE(pos + 6, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (fontNum < 0 || fontNum >= nFonts) {
+ parsedOk = gFalse;
+ return;
+ }
+ pos = resMapOffset + resTypeListOffset + refListOffset + 12 * fontNum;
+ dataOffset = getU32BE(pos + 4, &parsedOk) & 0x00ffffff;
+ if (!parsedOk) {
+ return;
+ }
+ // the data offset points to a 4-byte length field, which we skip over
+ *offset = resDataOffset + dataOffset + 4;
+ *startPos = 0;
+}
+
+void FoFiTrueType::readPostTable() {
+ GString *name;
+ int tablePos, postFmt, stringIdx, stringPos;
+ GBool ok;
+ int i, j, n, m;
+
+ ok = gTrue;
+ if ((i = seekTable("post")) < 0) {
+ return;
+ }
+ tablePos = tables[i].offset;
+ postFmt = getU32BE(tablePos, &ok);
+ if (!ok) {
+ goto err;
+ }
+ if (postFmt == 0x00010000) {
+ nameToGID = new GHash(gTrue);
+ for (i = 0; i < 258; ++i) {
+ nameToGID->add(new GString(macGlyphNames[i]), i);
+ }
+ } else if (postFmt == 0x00020000) {
+ nameToGID = new GHash(gTrue);
+ n = getU16BE(tablePos + 32, &ok);
+ if (!ok) {
+ goto err;
+ }
+ stringIdx = 0;
+ stringPos = tablePos + 34 + 2*n;
+ for (i = 0; i < n && i < nGlyphs; ++i) {
+ ok = gTrue;
+ j = getU16BE(tablePos + 34 + 2*i, &ok);
+ if (j < 258) {
+ nameToGID->removeInt(macGlyphNames[j]);
+ nameToGID->add(new GString(macGlyphNames[j]), i);
+ } else {
+ j -= 258;
+ if (j != stringIdx) {
+ for (stringIdx = 0, stringPos = tablePos + 34 + 2*n;
+ stringIdx < j;
+ ++stringIdx, stringPos += 1 + getU8(stringPos, &ok)) ;
+ if (!ok) {
+ continue;
+ }
+ }
+ m = getU8(stringPos, &ok);
+ if (!ok || !checkRegion(stringPos + 1, m)) {
+ continue;
+ }
+ name = new GString((char *)&file[stringPos + 1], m);
+ nameToGID->removeInt(name);
+ nameToGID->add(name, i);
+ ++stringIdx;
+ stringPos += 1 + m;
+ }
+ }
+ } else if (postFmt == 0x00028000) {
+ nameToGID = new GHash(gTrue);
+ for (i = 0; i < nGlyphs; ++i) {
+ j = getU8(tablePos + 32 + i, &ok);
+ if (!ok) {
+ continue;
+ }
+ if (j < 258) {
+ nameToGID->removeInt(macGlyphNames[j]);
+ nameToGID->add(new GString(macGlyphNames[j]), i);
+ }
+ }
+ }
+
+ return;
+
+ err:
+ if (nameToGID) {
+ delete nameToGID;
+ nameToGID = NULL;
+ }
+}
+
+int FoFiTrueType::seekTable(const char *tag) {
+ Guint tagI;
+ int i;
+
+ tagI = ((tag[0] & 0xff) << 24) |
+ ((tag[1] & 0xff) << 16) |
+ ((tag[2] & 0xff) << 8) |
+ (tag[3] & 0xff);
+ for (i = 0; i < nTables; ++i) {
+ if (tables[i].tag == tagI) {
+ return i;
+ }
+ }
+ return -1;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.h b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.h
new file mode 100644
index 00000000000..619e1bd6676
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiTrueType.h
@@ -0,0 +1,216 @@
+//========================================================================
+//
+// FoFiTrueType.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFITRUETYPE_H
+#define FOFITRUETYPE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "FoFiBase.h"
+
+class GString;
+class GHash;
+struct TrueTypeTable;
+struct TrueTypeCmap;
+
+//------------------------------------------------------------------------
+// FoFiTrueType
+//------------------------------------------------------------------------
+
+class FoFiTrueType: public FoFiBase {
+public:
+
+ // Create a FoFiTrueType object from a memory buffer. If
+ // <allowHeadlessCFF> is true, OpenType CFF fonts without the 'head'
+ // table are permitted -- this is useful when calling the convert*
+ // functions.
+ static FoFiTrueType *make(char *fileA, int lenA, int fontNum,
+ GBool allowHeadlessCFF = gFalse);
+
+ // Create a FoFiTrueType object from a file on disk. If
+ // <allowHeadlessCFF> is true, OpenType CFF fonts without the 'head'
+ // table are permitted -- this is useful when calling the convert*
+ // functions.
+ static FoFiTrueType *load(char *fileName, int fontNum,
+ GBool allowHeadlessCFF = gFalse);
+
+ virtual ~FoFiTrueType();
+
+ // Returns true if this an OpenType font containing CFF data, false
+ // if it's a TrueType font (or OpenType font with TrueType data).
+ GBool isOpenTypeCFF() { return openTypeCFF; }
+
+ // Returns true if this is an OpenType CFF font that is missing the
+ // 'head' table. This is a violation of the OpenType spec, but the
+ // embedded CFF font can be usable for some purposes (e.g., the
+ // convert* functions).
+ GBool isHeadlessCFF() { return headlessCFF; }
+
+ // Return the number of cmaps defined by this font.
+ int getNumCmaps();
+
+ // Return the platform ID of the <i>th cmap.
+ int getCmapPlatform(int i);
+
+ // Return the encoding ID of the <i>th cmap.
+ int getCmapEncoding(int i);
+
+ // Return the index of the cmap for <platform>, <encoding>. Returns
+ // -1 if there is no corresponding cmap.
+ int findCmap(int platform, int encoding);
+
+ // Return the GID corresponding to <c> according to the <i>th cmap.
+ int mapCodeToGID(int i, int c);
+
+ // Returns the GID corresponding to <name> according to the post
+ // table. Returns 0 if there is no mapping for <name> or if the
+ // font does not have a post table.
+ int mapNameToGID(char *name);
+
+ // Return the mapping from CIDs to GIDs, and return the number of
+ // CIDs in *<nCIDs>. This is only useful for CID fonts. (Only
+ // useful for OpenType CFF fonts.)
+ int *getCIDToGIDMap(int *nCIDs);
+
+ // Returns the least restrictive embedding licensing right (as
+ // defined by the TrueType spec):
+ // * 4: OS/2 table is missing or invalid
+ // * 3: installable embedding
+ // * 2: editable embedding
+ // * 1: preview & print embedding
+ // * 0: restricted license embedding
+ int getEmbeddingRights();
+
+ // Return the font matrix as an array of six numbers. (Only useful
+ // for OpenType CFF fonts.)
+ void getFontMatrix(double *mat);
+
+ // Return the number of glyphs in the font.
+ int getNumGlyphs() { return nGlyphs; }
+
+ // Returns true if this looks like a CJK font that uses bytecode
+ // instructions to assemble glyphs.
+ GBool checkForTrickyCJK();
+
+ // Convert to a Type 42 font, suitable for embedding in a PostScript
+ // file. <psName> will be used as the PostScript font name (so we
+ // don't need to depend on the 'name' table in the font). The
+ // <encoding> array specifies the mapping from char codes to names.
+ // If <encoding> is NULL, the encoding is unknown or undefined. The
+ // <codeToGID> array specifies the mapping from char codes to GIDs.
+ // (Not useful for OpenType CFF fonts.)
+ void convertToType42(char *psName, char **encoding,
+ int *codeToGID,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Convert to a Type 1 font, suitable for embedding in a PostScript
+ // file. This is only useful with 8-bit fonts. If <newEncoding> is
+ // not NULL, it will be used in place of the encoding in the Type 1C
+ // font. If <ascii> is true the eexec section will be hex-encoded,
+ // otherwise it will be left as binary data. If <psName> is
+ // non-NULL, it will be used as the PostScript font name. (Only
+ // useful for OpenType CFF fonts.)
+ void convertToType1(char *psName, const char **newEncoding, GBool ascii,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Convert to a Type 2 CIDFont, suitable for embedding in a
+ // PostScript file. <psName> will be used as the PostScript font
+ // name (so we don't need to depend on the 'name' table in the
+ // font). The <cidMap> array maps CIDs to GIDs; it has <nCIDs>
+ // entries. (Not useful for OpenType CFF fonts.)
+ void convertToCIDType2(char *psName, int *cidMap, int nCIDs,
+ GBool needVerticalMetrics,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Convert to a Type 0 CIDFont, suitable for embedding in a
+ // PostScript file. <psName> will be used as the PostScript font
+ // name. (Only useful for OpenType CFF fonts.)
+ void convertToCIDType0(char *psName, int *cidMap, int nCIDs,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Convert to a Type 0 (but non-CID) composite font, suitable for
+ // embedding in a PostScript file. <psName> will be used as the
+ // PostScript font name (so we don't need to depend on the 'name'
+ // table in the font). The <cidMap> array maps CIDs to GIDs; it has
+ // <nCIDs> entries. (Not useful for OpenType CFF fonts.)
+ void convertToType0(char *psName, int *cidMap, int nCIDs,
+ GBool needVerticalMetrics,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Convert to a Type 0 (but non-CID) composite font, suitable for
+ // embedding in a PostScript file. <psName> will be used as the
+ // PostScript font name. (Only useful for OpenType CFF fonts.)
+ void convertToType0(char *psName, int *cidMap, int nCIDs,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Write a clean TTF file, filling in missing tables and correcting
+ // various other errors. If <name> is non-NULL, the font is renamed
+ // to <name>. If <codeToGID> is non-NULL, the font is re-encoded,
+ // using a Windows Unicode cmap. If <name> is NULL and the font is
+ // complete and correct, it will be written unmodified. If
+ // <replacementCmapTable> is non-NULL it will be used as the cmap
+ // table in the written font (overriding any existing cmap table
+ // and/or the codeToGID arg). (Not useful for OpenType CFF fonts.)
+ // Returns true if the font was modified.
+ GBool writeTTF(FoFiOutputFunc outputFunc, void *outputStream,
+ char *name = NULL, int *codeToGID = NULL,
+ Guchar *replacementCmapTable = NULL,
+ int replacementCmapTableLen = 0);
+
+ // Returns a pointer to the CFF font embedded in this OpenType font.
+ // If successful, sets *<start> and *<length>, and returns true.
+ // Otherwise returns false. (Only useful for OpenType CFF fonts).
+ GBool getCFFBlock(char **start, int *length);
+
+private:
+
+ FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA,
+ int fontNum, GBool isDfont, GBool allowHeadlessCFF);
+ void cvtEncoding(char **encoding,
+ FoFiOutputFunc outputFunc,
+ void *outputStream);
+ void cvtCharStrings(char **encoding,
+ int *codeToGID,
+ FoFiOutputFunc outputFunc,
+ void *outputStream);
+ void cvtSfnts(FoFiOutputFunc outputFunc,
+ void *outputStream, GString *name,
+ GBool needVerticalMetrics,
+ int *maxUsedGlyph);
+ void dumpString(Guchar *s, int length,
+ FoFiOutputFunc outputFunc,
+ void *outputStream);
+ Guint computeTableChecksum(Guchar *data, int length);
+ void parse(int fontNum, GBool allowHeadlessCFF);
+ void parseTTC(int fontNum, int *pos);
+ void parseDfont(int fontNum, int *offset, int *pos);
+ void readPostTable();
+ int seekTable(const char *tag);
+
+ TrueTypeTable *tables;
+ int nTables;
+ TrueTypeCmap *cmaps;
+ int nCmaps;
+ int nGlyphs;
+ int locaFmt;
+ int bbox[4];
+ GHash *nameToGID;
+ GBool openTypeCFF;
+ GBool headlessCFF;
+ GBool isDfont;
+ GBool isTTC;
+
+ GBool parsedOk;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc
new file mode 100644
index 00000000000..83ec53ef5e8
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.cc
@@ -0,0 +1,359 @@
+//========================================================================
+//
+// FoFiType1.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "FoFiEncodings.h"
+#include "FoFiType1.h"
+
+//------------------------------------------------------------------------
+// FoFiType1
+//------------------------------------------------------------------------
+
+FoFiType1 *FoFiType1::make(char *fileA, int lenA) {
+ return new FoFiType1(fileA, lenA, gFalse);
+}
+
+FoFiType1 *FoFiType1::load(char *fileName) {
+ char *fileA;
+ int lenA;
+
+ if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
+ return NULL;
+ }
+ return new FoFiType1(fileA, lenA, gTrue);
+}
+
+FoFiType1::FoFiType1(char *fileA, int lenA, GBool freeFileDataA):
+ FoFiBase(fileA, lenA, freeFileDataA)
+{
+ name = NULL;
+ encoding = NULL;
+ fontMatrix[0] = 0.001;
+ fontMatrix[1] = 0;
+ fontMatrix[2] = 0;
+ fontMatrix[3] = 0.001;
+ fontMatrix[4] = 0;
+ fontMatrix[5] = 0;
+ parsed = gFalse;
+ undoPFB();
+}
+
+FoFiType1::~FoFiType1() {
+ int i;
+
+ if (name) {
+ gfree(name);
+ }
+ if (encoding && encoding != (char **)fofiType1StandardEncoding) {
+ for (i = 0; i < 256; ++i) {
+ gfree(encoding[i]);
+ }
+ gfree(encoding);
+ }
+}
+
+char *FoFiType1::getName() {
+ if (!parsed) {
+ parse();
+ }
+ return name;
+}
+
+char **FoFiType1::getEncoding() {
+ if (!parsed) {
+ parse();
+ }
+ return encoding;
+}
+
+void FoFiType1::getFontMatrix(double *mat) {
+ int i;
+
+ if (!parsed) {
+ parse();
+ }
+ for (i = 0; i < 6; ++i) {
+ mat[i] = fontMatrix[i];
+ }
+}
+
+void FoFiType1::writeEncoded(const char **newEncoding,
+ FoFiOutputFunc outputFunc, void *outputStream) {
+ char buf[512];
+ char *line, *line2, *p;
+ int i;
+
+ // copy everything up to the encoding
+ for (line = (char *)file;
+ line && line + 9 <= (char *)file + len && strncmp(line, "/Encoding", 9);
+ line = getNextLine(line)) ;
+ if (!line) {
+ // no encoding - just copy the whole font file
+ (*outputFunc)(outputStream, (char *)file, len);
+ return;
+ }
+ (*outputFunc)(outputStream, (char *)file, (int)(line - (char *)file));
+
+ // write the new encoding
+ (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+ (*outputFunc)(outputStream,
+ "0 1 255 {1 index exch /.notdef put} for\n", 40);
+ for (i = 0; i < 256; ++i) {
+ if (newEncoding[i]) {
+ snprintf(buf, sizeof(buf), "dup %d /%s put\n", i, newEncoding[i]);
+ (*outputFunc)(outputStream, buf, (int)strlen(buf));
+ }
+ }
+ (*outputFunc)(outputStream, "readonly def\n", 13);
+
+ // find the end of the encoding data
+ //~ this ought to parse PostScript tokens
+ if (line + 30 <= (char *)file + len &&
+ !strncmp(line, "/Encoding StandardEncoding def", 30)) {
+ line = getNextLine(line);
+ } else {
+ // skip "/Encoding" + one whitespace char,
+ // then look for 'def' preceded by PostScript whitespace
+ p = line + 10;
+ line = NULL;
+ for (; p < (char *)file + len; ++p) {
+ if ((*p == ' ' || *p == '\t' || *p == '\x0a' ||
+ *p == '\x0d' || *p == '\x0c' || *p == '\0') &&
+ p + 4 <= (char *)file + len &&
+ !strncmp(p + 1, "def", 3)) {
+ line = p + 4;
+ break;
+ }
+ }
+ }
+
+ // some fonts have two /Encoding entries in their dictionary, so we
+ // check for a second one here
+ if (line) {
+ for (line2 = line, i = 0;
+ i < 20 && line2 && line2 + 9 <= (char *)file + len &&
+ strncmp(line2, "/Encoding", 9);
+ line2 = getNextLine(line2), ++i) ;
+ if (i < 20 && line2) {
+ (*outputFunc)(outputStream, line, (int)(line2 - line));
+ if (line2 + 30 <= (char *)file + len &&
+ !strncmp(line2, "/Encoding StandardEncoding def", 30)) {
+ line = getNextLine(line2);
+ } else {
+ // skip "/Encoding",
+ // then look for 'def' preceded by PostScript whitespace
+ p = line2 + 9;
+ line = NULL;
+ for (; p < (char *)file + len; ++p) {
+ if ((*p == ' ' || *p == '\t' || *p == '\x0a' ||
+ *p == '\x0d' || *p == '\x0c' || *p == '\0') &&
+ p + 4 <= (char *)file + len &&
+ !strncmp(p + 1, "def", 3)) {
+ line = p + 4;
+ break;
+ }
+ }
+ }
+ }
+
+ // copy everything after the encoding
+ if (line) {
+ (*outputFunc)(outputStream, line, (int)(((char *)file + len) - line));
+ }
+ }
+}
+
+char *FoFiType1::getNextLine(char *line) {
+ while (line < (char *)file + len && *line != '\x0a' && *line != '\x0d') {
+ ++line;
+ }
+ if (line < (char *)file + len && *line == '\x0d') {
+ ++line;
+ }
+ if (line < (char *)file + len && *line == '\x0a') {
+ ++line;
+ }
+ if (line >= (char *)file + len) {
+ return NULL;
+ }
+ return line;
+}
+
+void FoFiType1::parse() {
+ char *line, *line1, *p, *p2;
+ char buf[256];
+ char c;
+ int n, code, base, i, j;
+ GBool gotMatrix, startsWithDup, endsWithDup;
+
+ gotMatrix = gFalse;
+ for (i = 1, line = (char *)file;
+ i <= 100 && line && (!name || !encoding || !gotMatrix);
+ ++i) {
+
+ // get font name
+ if (!name && line + 9 <= (char *)file + len &&
+ !strncmp(line, "/FontName", 9)) {
+ n = 255;
+ if (line + n > (char *)file + len) {
+ n = (int)(((char *)file + len) - line);
+ }
+ strncpy(buf, line, n);
+ buf[n] = '\0';
+ if ((p = strchr(buf+9, '/')) &&
+ (p = strtok(p+1, " \t\n\r"))) {
+ name = copyString(p);
+ }
+ line = getNextLine(line);
+
+ // get encoding
+ } else if (!encoding && line + 30 <= (char *)file + len &&
+ !strncmp(line, "/Encoding StandardEncoding def", 30)) {
+ encoding = (char **)fofiType1StandardEncoding;
+ } else if (!encoding && line + 19 <= (char *)file + len &&
+ !strncmp(line, "/Encoding 256 array", 19)) {
+ encoding = (char **)gmallocn(256, sizeof(char *));
+ for (j = 0; j < 256; ++j) {
+ encoding[j] = NULL;
+ }
+ for (j = 0, line = getNextLine(line);
+ j < 300 && line && (line1 = getNextLine(line));
+ ++j, line = line1) {
+ if ((n = (int)(line1 - line)) > 255) {
+ n = 255;
+ }
+ strncpy(buf, line, n);
+ buf[n] = '\0';
+ for (p = buf; *p == ' ' || *p == '\t'; ++p) ;
+ endsWithDup = !strncmp(line - 4, "dup\x0a", 4) ||
+ !strncmp(line - 5, "dup\x0d", 4);
+ startsWithDup = !strncmp(p, "dup", 3);
+ if (endsWithDup || startsWithDup) {
+ if (startsWithDup) {
+ p += 3;
+ }
+ while (1) {
+ for (; *p == ' ' || *p == '\t'; ++p) ;
+ code = 0;
+ if (*p == '8' && p[1] == '#') {
+ base = 8;
+ p += 2;
+ } else if (*p >= '0' && *p <= '9') {
+ base = 10;
+ } else {
+ break;
+ }
+ for (; *p >= '0' && *p < '0' + base; ++p) {
+ code = code * base + (*p - '0');
+ }
+ for (; *p == ' ' || *p == '\t'; ++p) ;
+ if (*p != '/') {
+ break;
+ }
+ ++p;
+ for (p2 = p; *p2 && *p2 != ' ' && *p2 != '\t'; ++p2) ;
+ if (code >= 0 && code < 256) {
+ c = *p2;
+ *p2 = '\0';
+ gfree(encoding[code]);
+ encoding[code] = copyString(p);
+ *p2 = c;
+ }
+ for (p = p2; *p == ' ' || *p == '\t'; ++p) ;
+ if (strncmp(p, "put", 3)) {
+ break;
+ }
+ for (p += 3; *p == ' ' || *p == '\t'; ++p) ;
+ if (strncmp(p, "dup", 3)) {
+ break;
+ }
+ p += 3;
+ }
+ } else {
+ if (strtok(buf, " \t") &&
+ (p = strtok(NULL, " \t\n\r")) && !strcmp(p, "def")) {
+ break;
+ }
+ }
+ }
+ //~ check for getinterval/putinterval junk
+
+ } else if (!gotMatrix && line + 11 <= (char *)file + len &&
+ !strncmp(line, "/FontMatrix", 11)) {
+ n = 255;
+ if (line + 11 + n > (char *)file + len) {
+ n = (int)(((char *)file + len) - (line + 11));
+ }
+ strncpy(buf, line + 11, n);
+ buf[n] = '\0';
+ if ((p = strchr(buf, '['))) {
+ ++p;
+ if ((p2 = strchr(p, ']'))) {
+ *p2 = '\0';
+ for (j = 0; j < 6; ++j) {
+ if ((p = strtok(j == 0 ? p : (char *)NULL, " \t\n\r"))) {
+ fontMatrix[j] = atof(p);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ gotMatrix = gTrue;
+
+ } else {
+ line = getNextLine(line);
+ }
+ }
+
+ parsed = gTrue;
+}
+
+// Undo the PFB encoding, i.e., remove the PFB headers.
+void FoFiType1::undoPFB() {
+ GBool ok;
+ Guchar *file2;
+ int pos1, pos2, type;
+ Guint segLen;
+
+ ok = gTrue;
+ if (getU8(0, &ok) != 0x80 || !ok) {
+ return;
+ }
+ file2 = (Guchar *)gmalloc(len);
+ pos1 = pos2 = 0;
+ while (getU8(pos1, &ok) == 0x80 && ok) {
+ type = getU8(pos1 + 1, &ok);
+ if (type < 1 || type > 2 || !ok) {
+ break;
+ }
+ segLen = getU32LE(pos1 + 2, &ok);
+ pos1 += 6;
+ if (!ok || !checkRegion(pos1, segLen)) {
+ break;
+ }
+ memcpy(file2 + pos2, file + pos1, segLen);
+ pos1 += segLen;
+ pos2 += segLen;
+ }
+ if (freeFileData) {
+ gfree(fileData);
+ }
+ file = fileData = file2;
+ freeFileData = gTrue;
+ len = pos2;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.h b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.h
new file mode 100644
index 00000000000..4a64f246dcb
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1.h
@@ -0,0 +1,64 @@
+//========================================================================
+//
+// FoFiType1.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFITYPE1_H
+#define FOFITYPE1_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "FoFiBase.h"
+
+//------------------------------------------------------------------------
+// FoFiType1
+//------------------------------------------------------------------------
+
+class FoFiType1: public FoFiBase {
+public:
+
+ // Create a FoFiType1 object from a memory buffer.
+ static FoFiType1 *make(char *fileA, int lenA);
+
+ // Create a FoFiType1 object from a file on disk.
+ static FoFiType1 *load(char *fileName);
+
+ virtual ~FoFiType1();
+
+ // Return the font name.
+ char *getName();
+
+ // Return the encoding, as an array of 256 names (any of which may
+ // be NULL).
+ char **getEncoding();
+
+ // Return the font matrix as an array of six numbers.
+ void getFontMatrix(double *mat);
+
+ // Write a version of the Type 1 font file with a new encoding.
+ void writeEncoded(const char **newEncoding,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+private:
+
+ FoFiType1(char *fileA, int lenA, GBool freeFileDataA);
+
+ char *getNextLine(char *line);
+ void parse();
+ void undoPFB();
+
+ char *name;
+ char **encoding;
+ double fontMatrix[6];
+ GBool parsed;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc
new file mode 100644
index 00000000000..b4f0a061be9
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.cc
@@ -0,0 +1,3397 @@
+//========================================================================
+//
+// FoFiType1C.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "GHash.h"
+#include "FoFiEncodings.h"
+#include "FoFiType1C.h"
+
+//------------------------------------------------------------------------
+
+static char hexChars[17] = "0123456789ABCDEF";
+
+#define type1cSubrRecursionLimit 20
+
+//------------------------------------------------------------------------
+
+GBool Type1COp::isZero() {
+ switch (kind) {
+ case type1COpInteger: return intgr == 0;
+ case type1COpFloat: return flt == 0;
+ case type1COpRational: return rat.num == 0;
+ default: return gFalse; // shouldn't happen
+ }
+}
+
+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:
+ // shouldn't happen
+ return gFalse;
+ }
+}
+
+int Type1COp::toInt() {
+ switch (kind) {
+ 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:
+ if (rat.den == 0) {
+ return 0;
+ }
+ return (double)rat.num / (double)rat.den;
+ default:
+ // shouldn't happen
+ return 0.0;
+ }
+}
+
+//------------------------------------------------------------------------
+// FoFiType1C
+//------------------------------------------------------------------------
+
+FoFiType1C *FoFiType1C::make(char *fileA, int lenA) {
+ FoFiType1C *ff;
+
+ ff = new FoFiType1C(fileA, lenA, gFalse);
+ if (!ff->parse()) {
+ delete ff;
+ return NULL;
+ }
+ return ff;
+}
+
+FoFiType1C *FoFiType1C::load(char *fileName) {
+ FoFiType1C *ff;
+ char *fileA;
+ int lenA;
+
+ if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
+ return NULL;
+ }
+ ff = new FoFiType1C(fileA, lenA, gTrue);
+ if (!ff->parse()) {
+ delete ff;
+ return NULL;
+ }
+ return ff;
+}
+
+FoFiType1C::FoFiType1C(char *fileA, int lenA, GBool freeFileDataA):
+ FoFiBase(fileA, lenA, freeFileDataA)
+{
+ name = NULL;
+ encoding = NULL;
+ privateDicts = NULL;
+ fdSelect = NULL;
+ charset = NULL;
+}
+
+FoFiType1C::~FoFiType1C() {
+ int i;
+
+ if (name) {
+ delete name;
+ }
+ if (encoding &&
+ encoding != (char **)fofiType1StandardEncoding &&
+ encoding != (char **)fofiType1ExpertEncoding) {
+ for (i = 0; i < 256; ++i) {
+ gfree(encoding[i]);
+ }
+ gfree(encoding);
+ }
+ if (privateDicts) {
+ gfree(privateDicts);
+ }
+ if (fdSelect) {
+ gfree(fdSelect);
+ }
+ if (charset &&
+ charset != fofiType1CISOAdobeCharset &&
+ charset != fofiType1CExpertCharset &&
+ charset != fofiType1CExpertSubsetCharset) {
+ gfree(charset);
+ }
+}
+
+char *FoFiType1C::getName() {
+ return name ? name->getCString() : (char *)NULL;
+}
+
+char **FoFiType1C::getEncoding() {
+ return encoding;
+}
+
+GString *FoFiType1C::getGlyphName(int gid) {
+ char buf[256];
+ GBool ok;
+
+ ok = gTrue;
+ getString(charset[gid], buf, &ok);
+ if (!ok) {
+ return NULL;
+ }
+ return new GString(buf);
+}
+
+GHash *FoFiType1C::getNameToGIDMap() {
+ GHash *map;
+ char name[256];
+ GBool ok;
+ int gid;
+
+ map = new GHash(gTrue);
+ for (gid = 0; gid < nGlyphs; ++gid) {
+ ok = gTrue;
+ getString(charset[gid], name, &ok);
+ if (ok) {
+ map->add(new GString(name), gid);
+ }
+ }
+ return map;
+}
+
+int *FoFiType1C::getCIDToGIDMap(int *nCIDs) {
+ int *map;
+ int n, i;
+
+ // a CID font's top dict has ROS as the first operator
+ if (topDict.firstOp != 0x0c1e) {
+ *nCIDs = 0;
+ return NULL;
+ }
+
+ // in a CID font, the charset data is the GID-to-CID mapping, so all
+ // we have to do is reverse it
+ n = 0;
+ for (i = 0; i < nGlyphs; ++i) {
+ if (charset[i] > n) {
+ n = charset[i];
+ }
+ }
+ ++n;
+ map = (int *)gmallocn(n, sizeof(int));
+ memset(map, 0, n * sizeof(int));
+ for (i = 0; i < nGlyphs; ++i) {
+ map[charset[i]] = i;
+ }
+ *nCIDs = n;
+ return map;
+}
+
+void FoFiType1C::getFontMatrix(double *mat) {
+ int i;
+
+ if (topDict.firstOp == 0x0c1e && privateDicts[0].hasFontMatrix) {
+ if (topDict.hasFontMatrix) {
+ mat[0] = topDict.fontMatrix[0] * privateDicts[0].fontMatrix[0] +
+ topDict.fontMatrix[1] * privateDicts[0].fontMatrix[2];
+ mat[1] = topDict.fontMatrix[0] * privateDicts[0].fontMatrix[1] +
+ topDict.fontMatrix[1] * privateDicts[0].fontMatrix[3];
+ mat[2] = topDict.fontMatrix[2] * privateDicts[0].fontMatrix[0] +
+ topDict.fontMatrix[3] * privateDicts[0].fontMatrix[2];
+ mat[3] = topDict.fontMatrix[2] * privateDicts[0].fontMatrix[1] +
+ topDict.fontMatrix[3] * privateDicts[0].fontMatrix[3];
+ mat[4] = topDict.fontMatrix[4] * privateDicts[0].fontMatrix[0] +
+ topDict.fontMatrix[5] * privateDicts[0].fontMatrix[2];
+ mat[5] = topDict.fontMatrix[4] * privateDicts[0].fontMatrix[1] +
+ topDict.fontMatrix[5] * privateDicts[0].fontMatrix[3];
+ } else {
+ for (i = 0; i < 6; ++i) {
+ mat[i] = privateDicts[0].fontMatrix[i];
+ }
+ }
+ } else {
+ for (i = 0; i < 6; ++i) {
+ mat[i] = topDict.fontMatrix[i];
+ }
+ }
+}
+
+void FoFiType1C::convertToType1(char *psName, const char **newEncoding,
+ GBool ascii, FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ int psNameLen;
+ Type1CEexecBuf eb;
+ Type1CIndex subrIdx;
+ Type1CIndexVal val;
+ GString *buf;
+ char buf2[256];
+ const char **enc;
+ GBool ok;
+ int i;
+
+ if (psName) {
+ psNameLen = (int)strlen(psName);
+ } else {
+ psName = name->getCString();
+ psNameLen = name->getLength();
+ }
+
+ // write header and font dictionary, up to encoding
+ ok = gTrue;
+ (*outputFunc)(outputStream, "%!FontType1-1.0: ", 17);
+ (*outputFunc)(outputStream, psName, psNameLen);
+ if (topDict.versionSID != 0) {
+ getString(topDict.versionSID, buf2, &ok);
+ (*outputFunc)(outputStream, buf2, (int)strlen(buf2));
+ }
+ (*outputFunc)(outputStream, "\n", 1);
+ // the dictionary needs room for 12 entries: the following 9, plus
+ // Private and CharStrings (in the eexec section) and FID (which is
+ // added by definefont)
+ (*outputFunc)(outputStream, "12 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/FontInfo 10 dict dup begin\n", 28);
+ if (topDict.versionSID != 0) {
+ (*outputFunc)(outputStream, "/version ", 9);
+ writePSString(buf2, outputFunc, outputStream);
+ (*outputFunc)(outputStream, " readonly def\n", 14);
+ }
+ if (topDict.noticeSID != 0) {
+ getString(topDict.noticeSID, buf2, &ok);
+ (*outputFunc)(outputStream, "/Notice ", 8);
+ writePSString(buf2, outputFunc, outputStream);
+ (*outputFunc)(outputStream, " readonly def\n", 14);
+ }
+ if (topDict.copyrightSID != 0) {
+ getString(topDict.copyrightSID, buf2, &ok);
+ (*outputFunc)(outputStream, "/Copyright ", 11);
+ writePSString(buf2, outputFunc, outputStream);
+ (*outputFunc)(outputStream, " readonly def\n", 14);
+ }
+ if (topDict.fullNameSID != 0) {
+ getString(topDict.fullNameSID, buf2, &ok);
+ (*outputFunc)(outputStream, "/FullName ", 10);
+ writePSString(buf2, outputFunc, outputStream);
+ (*outputFunc)(outputStream, " readonly def\n", 14);
+ }
+ if (topDict.familyNameSID != 0) {
+ getString(topDict.familyNameSID, buf2, &ok);
+ (*outputFunc)(outputStream, "/FamilyName ", 12);
+ writePSString(buf2, outputFunc, outputStream);
+ (*outputFunc)(outputStream, " readonly def\n", 14);
+ }
+ if (topDict.weightSID != 0) {
+ getString(topDict.weightSID, buf2, &ok);
+ (*outputFunc)(outputStream, "/Weight ", 8);
+ writePSString(buf2, outputFunc, outputStream);
+ (*outputFunc)(outputStream, " readonly def\n", 14);
+ }
+ if (topDict.isFixedPitch) {
+ (*outputFunc)(outputStream, "/isFixedPitch true def\n", 23);
+ } else {
+ (*outputFunc)(outputStream, "/isFixedPitch false def\n", 24);
+ }
+ buf = GString::format("/ItalicAngle {0:.4g} def\n", topDict.italicAngle);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ buf = GString::format("/UnderlinePosition {0:.4g} def\n",
+ topDict.underlinePosition);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ buf = GString::format("/UnderlineThickness {0:.4g} def\n",
+ topDict.underlineThickness);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "end readonly def\n", 17);
+ (*outputFunc)(outputStream, "/FontName /", 11);
+ (*outputFunc)(outputStream, psName, psNameLen);
+ (*outputFunc)(outputStream, " def\n", 5);
+ buf = GString::format("/PaintType {0:d} def\n", topDict.paintType);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
+ buf = GString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] readonly def\n",
+ topDict.fontMatrix[0], topDict.fontMatrix[1],
+ topDict.fontMatrix[2], topDict.fontMatrix[3],
+ topDict.fontMatrix[4], topDict.fontMatrix[5]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ buf = GString::format("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] readonly def\n",
+ topDict.fontBBox[0], topDict.fontBBox[1],
+ topDict.fontBBox[2], topDict.fontBBox[3]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ buf = GString::format("/StrokeWidth {0:.4g} def\n", topDict.strokeWidth);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ if (topDict.uniqueID != 0) {
+ buf = GString::format("/UniqueID {0:d} def\n", topDict.uniqueID);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+
+ // write the encoding
+ (*outputFunc)(outputStream, "/Encoding ", 10);
+ if (!newEncoding && encoding == (char **)fofiType1StandardEncoding) {
+ (*outputFunc)(outputStream, "StandardEncoding def\n", 21);
+ } else {
+ (*outputFunc)(outputStream, "256 array\n", 10);
+ (*outputFunc)(outputStream,
+ "0 1 255 {1 index exch /.notdef put} for\n", 40);
+ enc = newEncoding ? newEncoding : (const char **)encoding;
+ for (i = 0; i < 256; ++i) {
+ if (enc[i]) {
+ buf = GString::format("dup {0:d} /{1:s} put\n", i, enc[i]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ }
+ (*outputFunc)(outputStream, "readonly def\n", 13);
+ }
+ (*outputFunc)(outputStream, "currentdict end\n", 16);
+
+ // start the binary section
+ (*outputFunc)(outputStream, "currentfile eexec\n", 18);
+ eb.outputFunc = outputFunc;
+ eb.outputStream = outputStream;
+ eb.ascii = ascii;
+ eb.r1 = 55665;
+ eb.line = 0;
+
+ // write the private dictionary
+ eexecWrite(&eb, "\x83\xca\x73\xd5");
+ eexecWrite(&eb, "dup /Private 32 dict dup begin\n");
+ eexecWrite(&eb, "/RD {string currentfile exch readstring pop}"
+ " executeonly def\n");
+ eexecWrite(&eb, "/ND {noaccess def} executeonly def\n");
+ eexecWrite(&eb, "/NP {noaccess put} executeonly def\n");
+ eexecWrite(&eb, "/MinFeature {16 16} def\n");
+ eexecWrite(&eb, "/password 5839 def\n");
+ if (privateDicts[0].nBlueValues) {
+ eexecWrite(&eb, "/BlueValues [");
+ for (i = 0; i < privateDicts[0].nBlueValues; ++i) {
+ buf = GString::format("{0:s}{1:d}",
+ i > 0 ? " " : "", privateDicts[0].blueValues[i]);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[0].nOtherBlues) {
+ eexecWrite(&eb, "/OtherBlues [");
+ for (i = 0; i < privateDicts[0].nOtherBlues; ++i) {
+ buf = GString::format("{0:s}{1:d}",
+ i > 0 ? " " : "", privateDicts[0].otherBlues[i]);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[0].nFamilyBlues) {
+ eexecWrite(&eb, "/FamilyBlues [");
+ for (i = 0; i < privateDicts[0].nFamilyBlues; ++i) {
+ buf = GString::format("{0:s}{1:d}",
+ i > 0 ? " " : "", privateDicts[0].familyBlues[i]);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[0].nFamilyOtherBlues) {
+ eexecWrite(&eb, "/FamilyOtherBlues [");
+ for (i = 0; i < privateDicts[0].nFamilyOtherBlues; ++i) {
+ buf = GString::format("{0:s}{1:d}", i > 0 ? " " : "",
+ privateDicts[0].familyOtherBlues[i]);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[0].blueScale != 0.039625) {
+ buf = GString::format("/BlueScale {0:.4g} def\n",
+ privateDicts[0].blueScale);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[0].blueShift != 7) {
+ buf = GString::format("/BlueShift {0:d} def\n", privateDicts[0].blueShift);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[0].blueFuzz != 1) {
+ buf = GString::format("/BlueFuzz {0:d} def\n", privateDicts[0].blueFuzz);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[0].hasStdHW) {
+ buf = GString::format("/StdHW [{0:.4g}] def\n", privateDicts[0].stdHW);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[0].hasStdVW) {
+ buf = GString::format("/StdVW [{0:.4g}] def\n", privateDicts[0].stdVW);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[0].nStemSnapH) {
+ // the StemSnapH array should be unique values in ascending order --
+ // if not, just skip it
+ for (i = 1; i < privateDicts[0].nStemSnapH; ++i) {
+ if (privateDicts[0].stemSnapH[i-1] >= privateDicts[0].stemSnapH[i]) {
+ break;
+ }
+ }
+ if (i == privateDicts[0].nStemSnapH) {
+ eexecWrite(&eb, "/StemSnapH [");
+ for (i = 0; i < privateDicts[0].nStemSnapH; ++i) {
+ buf = GString::format("{0:s}{1:.4g}",
+ i > 0 ? " " : "", privateDicts[0].stemSnapH[i]);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ }
+ if (privateDicts[0].nStemSnapV) {
+ // the StemSnapV array should be unique values in ascending order --
+ // if not, just skip it
+ for (i = 1; i < privateDicts[0].nStemSnapV; ++i) {
+ if (privateDicts[0].stemSnapV[i-1] >= privateDicts[0].stemSnapV[i]) {
+ break;
+ }
+ }
+ if (i == privateDicts[0].nStemSnapV) {
+ eexecWrite(&eb, "/StemSnapV [");
+ for (i = 0; i < privateDicts[0].nStemSnapV; ++i) {
+ buf = GString::format("{0:s}{1:.4g}",
+ i > 0 ? " " : "", privateDicts[0].stemSnapV[i]);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ }
+ if (privateDicts[0].hasForceBold) {
+ buf = GString::format("/ForceBold {0:s} def\n",
+ privateDicts[0].forceBold ? "true" : "false");
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[0].forceBoldThreshold != 0) {
+ buf = GString::format("/ForceBoldThreshold {0:.4g} def\n",
+ privateDicts[0].forceBoldThreshold);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[0].languageGroup != 0) {
+ buf = GString::format("/LanguageGroup {0:d} def\n",
+ privateDicts[0].languageGroup);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[0].expansionFactor != 0.06) {
+ buf = GString::format("/ExpansionFactor {0:.4g} def\n",
+ privateDicts[0].expansionFactor);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+
+ // set up subroutines
+ ok = gTrue;
+ getIndex(privateDicts[0].subrsOffset, &subrIdx, &ok);
+ if (!ok) {
+ subrIdx.pos = -1;
+ }
+
+ // write the CharStrings
+ buf = GString::format("2 index /CharStrings {0:d} dict dup begin\n",
+ nGlyphs);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ for (i = 0; i < nGlyphs; ++i) {
+ ok = gTrue;
+ getIndexVal(&charStringsIdx, i, &val, &ok);
+ if (ok) {
+ getString(charset[i], buf2, &ok);
+ if (ok) {
+ eexecCvtGlyph(&eb, buf2, val.pos, val.len, &subrIdx, &privateDicts[0]);
+ }
+ }
+ }
+ eexecWrite(&eb, "end\n");
+ eexecWrite(&eb, "end\n");
+ eexecWrite(&eb, "readonly put\n");
+ eexecWrite(&eb, "noaccess put\n");
+ eexecWrite(&eb, "dup /FontName get exch definefont pop\n");
+ eexecWrite(&eb, "mark currentfile closefile\n");
+
+ // trailer
+ if (ascii && eb.line > 0) {
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+ for (i = 0; i < 8; ++i) {
+ (*outputFunc)(outputStream, "0000000000000000000000000000000000000000000000000000000000000000\n", 65);
+ }
+ (*outputFunc)(outputStream, "cleartomark\n", 12);
+}
+
+void FoFiType1C::convertToCIDType0(char *psName, int *codeMap, int nCodes,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ int *cidMap;
+ GString *charStrings;
+ int *charStringOffsets;
+ Type1CIndex subrIdx;
+ Type1CIndexVal val;
+ int nCIDs, gdBytes;
+ GString *buf;
+ char buf2[256];
+ GBool ok;
+ int gid, offset, n, i, j, k;
+
+ // compute the CID count and build the CID-to-GID mapping
+ if (codeMap) {
+ nCIDs = nCodes;
+ cidMap = (int *)gmallocn(nCIDs, sizeof(int));
+ for (i = 0; i < nCodes; ++i) {
+ if (codeMap[i] >= 0 && codeMap[i] < nGlyphs) {
+ cidMap[i] = codeMap[i];
+ } else {
+ cidMap[i] = -1;
+ }
+ }
+ } else if (topDict.firstOp == 0x0c1e) {
+ nCIDs = 0;
+ for (i = 0; i < nGlyphs; ++i) {
+ if (charset[i] >= nCIDs) {
+ nCIDs = charset[i] + 1;
+ }
+ }
+ cidMap = (int *)gmallocn(nCIDs, sizeof(int));
+ for (i = 0; i < nCIDs; ++i) {
+ cidMap[i] = -1;
+ }
+ for (i = 0; i < nGlyphs; ++i) {
+ cidMap[charset[i]] = i;
+ }
+ } else {
+ nCIDs = nGlyphs;
+ cidMap = (int *)gmallocn(nCIDs, sizeof(int));
+ for (i = 0; i < nCIDs; ++i) {
+ cidMap[i] = i;
+ }
+ }
+
+ // build the charstrings
+ charStrings = new GString();
+ charStringOffsets = (int *)gmallocn(nCIDs + 1, sizeof(int));
+ for (i = 0; i < nCIDs; ++i) {
+ charStringOffsets[i] = charStrings->getLength();
+ if ((gid = cidMap[i]) >= 0) {
+ ok = gTrue;
+ getIndexVal(&charStringsIdx, gid, &val, &ok);
+ if (ok) {
+ getIndex(privateDicts[fdSelect ? fdSelect[gid] : 0].subrsOffset,
+ &subrIdx, &ok);
+ if (!ok) {
+ subrIdx.pos = -1;
+ }
+ cvtGlyph(val.pos, val.len, charStrings,
+ &subrIdx, &privateDicts[fdSelect ? fdSelect[gid] : 0],
+ gTrue, 0);
+ }
+ }
+ }
+ charStringOffsets[nCIDs] = charStrings->getLength();
+
+ // compute gdBytes = number of bytes needed for charstring offsets
+ // (offset size needs to account for the charstring offset table,
+ // with a worst case of five bytes per entry, plus the charstrings
+ // themselves)
+ i = (nCIDs + 1) * 5 + charStrings->getLength();
+ if (i < 0x100) {
+ gdBytes = 1;
+ } else if (i < 0x10000) {
+ gdBytes = 2;
+ } else if (i < 0x1000000) {
+ gdBytes = 3;
+ } else {
+ gdBytes = 4;
+ }
+
+ // begin the font dictionary
+ (*outputFunc)(outputStream, "/CIDInit /ProcSet findresource begin\n", 37);
+ (*outputFunc)(outputStream, "20 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/CIDFontName /", 14);
+ (*outputFunc)(outputStream, psName, (int)strlen(psName));
+ (*outputFunc)(outputStream, " def\n", 5);
+ (*outputFunc)(outputStream, "/CIDFontType 0 def\n", 19);
+ (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32);
+ if (topDict.registrySID > 0 && topDict.orderingSID > 0) {
+ ok = gTrue;
+ getString(topDict.registrySID, buf2, &ok);
+ if (ok) {
+ (*outputFunc)(outputStream, " /Registry (", 13);
+ (*outputFunc)(outputStream, buf2, (int)strlen(buf2));
+ (*outputFunc)(outputStream, ") def\n", 6);
+ }
+ ok = gTrue;
+ getString(topDict.orderingSID, buf2, &ok);
+ if (ok) {
+ (*outputFunc)(outputStream, " /Ordering (", 13);
+ (*outputFunc)(outputStream, buf2, (int)strlen(buf2));
+ (*outputFunc)(outputStream, ") def\n", 6);
+ }
+ } else {
+ (*outputFunc)(outputStream, " /Registry (Adobe) def\n", 24);
+ (*outputFunc)(outputStream, " /Ordering (Identity) def\n", 27);
+ }
+ buf = GString::format(" /Supplement {0:d} def\n", topDict.supplement);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "end def\n", 8);
+ if (topDict.hasFontMatrix) {
+ buf = GString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n",
+ topDict.fontMatrix[0], topDict.fontMatrix[1],
+ topDict.fontMatrix[2], topDict.fontMatrix[3],
+ topDict.fontMatrix[4], topDict.fontMatrix[5]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ } else if (privateDicts[0].hasFontMatrix) {
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ } else {
+ (*outputFunc)(outputStream,
+ "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38);
+ }
+ buf = GString::format("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] def\n",
+ topDict.fontBBox[0], topDict.fontBBox[1],
+ topDict.fontBBox[2], topDict.fontBBox[3]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "/FontInfo 1 dict dup begin\n", 27);
+ (*outputFunc)(outputStream, " /FSType 8 def\n", 16);
+ (*outputFunc)(outputStream, "end def\n", 8);
+
+ // CIDFont-specific entries
+ buf = GString::format("/CIDCount {0:d} def\n", nCIDs);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "/FDBytes 1 def\n", 15);
+ buf = GString::format("/GDBytes {0:d} def\n", gdBytes);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "/CIDMapOffset 0 def\n", 20);
+ if (topDict.paintType != 0) {
+ buf = GString::format("/PaintType {0:d} def\n", topDict.paintType);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ buf = GString::format("/StrokeWidth {0:.4g} def\n", topDict.strokeWidth);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+
+ // FDArray entry
+ buf = GString::format("/FDArray {0:d} array\n", nFDs);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ for (i = 0; i < nFDs; ++i) {
+ buf = GString::format("dup {0:d} 10 dict begin\n", i);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
+ if (privateDicts[i].hasFontMatrix) {
+ buf = GString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n",
+ privateDicts[i].fontMatrix[0],
+ privateDicts[i].fontMatrix[1],
+ privateDicts[i].fontMatrix[2],
+ privateDicts[i].fontMatrix[3],
+ privateDicts[i].fontMatrix[4],
+ privateDicts[i].fontMatrix[5]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ } else {
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ }
+ buf = GString::format("/PaintType {0:d} def\n", topDict.paintType);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "/Private 32 dict begin\n", 23);
+ if (privateDicts[i].nBlueValues) {
+ (*outputFunc)(outputStream, "/BlueValues [", 13);
+ for (j = 0; j < privateDicts[i].nBlueValues; ++j) {
+ buf = GString::format("{0:s}{1:d}",
+ j > 0 ? " " : "", privateDicts[i].blueValues[j]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ }
+ if (privateDicts[i].nOtherBlues) {
+ (*outputFunc)(outputStream, "/OtherBlues [", 13);
+ for (j = 0; j < privateDicts[i].nOtherBlues; ++j) {
+ buf = GString::format("{0:s}{1:d}",
+ j > 0 ? " " : "", privateDicts[i].otherBlues[j]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ }
+ if (privateDicts[i].nFamilyBlues) {
+ (*outputFunc)(outputStream, "/FamilyBlues [", 14);
+ for (j = 0; j < privateDicts[i].nFamilyBlues; ++j) {
+ buf = GString::format("{0:s}{1:d}",
+ j > 0 ? " " : "",
+ privateDicts[i].familyBlues[j]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ }
+ if (privateDicts[i].nFamilyOtherBlues) {
+ (*outputFunc)(outputStream, "/FamilyOtherBlues [", 19);
+ for (j = 0; j < privateDicts[i].nFamilyOtherBlues; ++j) {
+ buf = GString::format("{0:s}{1:d}", j > 0 ? " " : "",
+ privateDicts[i].familyOtherBlues[j]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ }
+ if (privateDicts[i].blueScale != 0.039625) {
+ buf = GString::format("/BlueScale {0:.4g} def\n",
+ privateDicts[i].blueScale);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ if (privateDicts[i].blueShift != 7) {
+ buf = GString::format("/BlueShift {0:d} def\n",
+ privateDicts[i].blueShift);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ if (privateDicts[i].blueFuzz != 1) {
+ buf = GString::format("/BlueFuzz {0:d} def\n", privateDicts[i].blueFuzz);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ if (privateDicts[i].hasStdHW) {
+ buf = GString::format("/StdHW [{0:.4g}] def\n", privateDicts[i].stdHW);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ if (privateDicts[i].hasStdVW) {
+ buf = GString::format("/StdVW [{0:.4g}] def\n", privateDicts[i].stdVW);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ if (privateDicts[i].nStemSnapH) {
+ // the StemSnapH array should be unique values in ascending order --
+ // if not, just skip it
+ for (j = 1; j < privateDicts[i].nStemSnapH; ++j) {
+ if (privateDicts[i].stemSnapH[j-1] >= privateDicts[i].stemSnapH[j]) {
+ break;
+ }
+ }
+ if (j == privateDicts[i].nStemSnapH) {
+ (*outputFunc)(outputStream, "/StemSnapH [", 12);
+ for (j = 0; j < privateDicts[i].nStemSnapH; ++j) {
+ buf = GString::format("{0:s}{1:.4g}",
+ j > 0 ? " " : "", privateDicts[i].stemSnapH[j]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ }
+ }
+ if (privateDicts[i].nStemSnapV) {
+ // the StemSnapV array should be unique values in ascending order --
+ // if not, just skip it
+ for (j = 1; j < privateDicts[i].nStemSnapV; ++j) {
+ if (privateDicts[i].stemSnapV[j-1] >= privateDicts[i].stemSnapV[j]) {
+ break;
+ }
+ }
+ if (j == privateDicts[i].nStemSnapV) {
+ (*outputFunc)(outputStream, "/StemSnapV [", 12);
+ for (j = 0; j < privateDicts[i].nStemSnapV; ++j) {
+ buf = GString::format("{0:s}{1:.4g}",
+ j > 0 ? " " : "", privateDicts[i].stemSnapV[j]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ }
+ }
+ if (privateDicts[i].hasForceBold) {
+ buf = GString::format("/ForceBold {0:s} def\n",
+ privateDicts[i].forceBold ? "true" : "false");
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ if (privateDicts[i].forceBoldThreshold != 0) {
+ buf = GString::format("/ForceBoldThreshold {0:.4g} def\n",
+ privateDicts[i].forceBoldThreshold);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ if (privateDicts[i].languageGroup != 0) {
+ buf = GString::format("/LanguageGroup {0:d} def\n",
+ privateDicts[i].languageGroup);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ if (privateDicts[i].expansionFactor != 0.06) {
+ buf = GString::format("/ExpansionFactor {0:.4g} def\n",
+ privateDicts[i].expansionFactor);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "currentdict end def\n", 20);
+ (*outputFunc)(outputStream, "currentdict end put\n", 20);
+ }
+ (*outputFunc)(outputStream, "def\n", 4);
+
+ // start the binary section
+ offset = (nCIDs + 1) * (1 + gdBytes);
+ buf = GString::format("(Hex) {0:d} StartData\n",
+ offset + charStrings->getLength());
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+
+ // write the charstring offset (CIDMap) table
+ for (i = 0; i <= nCIDs; i += 6) {
+ for (j = 0; j < 6 && i+j <= nCIDs; ++j) {
+ if (i+j < nCIDs && cidMap[i+j] >= 0 && fdSelect) {
+ buf2[0] = (char)fdSelect[cidMap[i+j]];
+ } else {
+ buf2[0] = (char)0;
+ }
+ n = offset + charStringOffsets[i+j];
+ for (k = gdBytes; k >= 1; --k) {
+ buf2[k] = (char)(n & 0xff);
+ n >>= 8;
+ }
+ for (k = 0; k <= gdBytes; ++k) {
+ buf = GString::format("{0:02x}", buf2[k] & 0xff);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ }
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+
+ // write the charstring data
+ n = charStrings->getLength();
+ for (i = 0; i < n; i += 32) {
+ for (j = 0; j < 32 && i+j < n; ++j) {
+ buf = GString::format("{0:02x}", charStrings->getChar(i+j) & 0xff);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ if (i + 32 >= n) {
+ (*outputFunc)(outputStream, ">", 1);
+ }
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+
+ gfree(charStringOffsets);
+ delete charStrings;
+ gfree(cidMap);
+}
+
+void FoFiType1C::convertToType0(char *psName, int *codeMap, int nCodes,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ int *cidMap;
+ Type1CIndex subrIdx;
+ Type1CIndexVal val;
+ int nCIDs;
+ GString *buf;
+ Type1CEexecBuf eb;
+ GBool ok;
+ int fd, i, j, k;
+
+ // compute the CID count and build the CID-to-GID mapping
+ if (codeMap) {
+ nCIDs = nCodes;
+ cidMap = (int *)gmallocn(nCIDs, sizeof(int));
+ for (i = 0; i < nCodes; ++i) {
+ if (codeMap[i] >= 0 && codeMap[i] < nGlyphs) {
+ cidMap[i] = codeMap[i];
+ } else {
+ cidMap[i] = -1;
+ }
+ }
+ } else if (topDict.firstOp == 0x0c1e) {
+ nCIDs = 0;
+ for (i = 0; i < nGlyphs; ++i) {
+ if (charset[i] >= nCIDs) {
+ nCIDs = charset[i] + 1;
+ }
+ }
+ cidMap = (int *)gmallocn(nCIDs, sizeof(int));
+ for (i = 0; i < nCIDs; ++i) {
+ cidMap[i] = -1;
+ }
+ for (i = 0; i < nGlyphs; ++i) {
+ cidMap[charset[i]] = i;
+ }
+ } else {
+ nCIDs = nGlyphs;
+ cidMap = (int *)gmallocn(nCIDs, sizeof(int));
+ for (i = 0; i < nCIDs; ++i) {
+ cidMap[i] = i;
+ }
+ }
+
+ // write the descendant Type 1 fonts
+ for (i = 0; i < nCIDs; i += 256) {
+
+ //~ this assumes that all CIDs in this block have the same FD --
+ //~ to handle multiple FDs correctly, need to somehow divide the
+ //~ font up by FD; as a kludge we ignore CID 0, which is .notdef
+ fd = 0;
+ // if fdSelect is NULL, we have an 8-bit font, so just leave fd=0
+ if (fdSelect) {
+ for (j = i==0 ? 1 : 0; j < 256 && i+j < nCIDs; ++j) {
+ if (cidMap[i+j] >= 0) {
+ fd = fdSelect[cidMap[i+j]];
+ break;
+ }
+ }
+ }
+ 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);
+ (*outputFunc)(outputStream, "/FontName /", 11);
+ (*outputFunc)(outputStream, psName, (int)strlen(psName));
+ buf = GString::format("_{0:02x} def\n", i >> 8);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
+ if (privateDicts[fd].hasFontMatrix) {
+ buf = GString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n",
+ privateDicts[fd].fontMatrix[0],
+ privateDicts[fd].fontMatrix[1],
+ privateDicts[fd].fontMatrix[2],
+ privateDicts[fd].fontMatrix[3],
+ privateDicts[fd].fontMatrix[4],
+ privateDicts[fd].fontMatrix[5]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ } else if (topDict.hasFontMatrix) {
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ } else {
+ (*outputFunc)(outputStream,
+ "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38);
+ }
+ buf = GString::format("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] def\n",
+ topDict.fontBBox[0], topDict.fontBBox[1],
+ topDict.fontBBox[2], topDict.fontBBox[3]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ buf = GString::format("/PaintType {0:d} def\n", topDict.paintType);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ if (topDict.paintType != 0) {
+ buf = GString::format("/StrokeWidth {0:.4g} def\n", topDict.strokeWidth);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+ for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+ buf = GString::format("dup {0:d} /c{1:02x} put\n", j, j);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ if (j < 256) {
+ buf = GString::format("{0:d} 1 255 {{ 1 index exch /.notdef put }} for\n",
+ j);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "readonly def\n", 13);
+ (*outputFunc)(outputStream, "currentdict end\n", 16);
+
+ // start the binary section
+ (*outputFunc)(outputStream, "currentfile eexec\n", 18);
+ eb.outputFunc = outputFunc;
+ eb.outputStream = outputStream;
+ eb.ascii = gTrue;
+ eb.r1 = 55665;
+ eb.line = 0;
+
+ // start the private dictionary
+ eexecWrite(&eb, "\x83\xca\x73\xd5");
+ eexecWrite(&eb, "dup /Private 32 dict dup begin\n");
+ eexecWrite(&eb, "/RD {string currentfile exch readstring pop}"
+ " executeonly def\n");
+ eexecWrite(&eb, "/ND {noaccess def} executeonly def\n");
+ eexecWrite(&eb, "/NP {noaccess put} executeonly def\n");
+ eexecWrite(&eb, "/MinFeature {16 16} def\n");
+ eexecWrite(&eb, "/password 5839 def\n");
+ if (privateDicts[fd].nBlueValues) {
+ eexecWrite(&eb, "/BlueValues [");
+ for (k = 0; k < privateDicts[fd].nBlueValues; ++k) {
+ buf = GString::format("{0:s}{1:d}",
+ k > 0 ? " " : "",
+ privateDicts[fd].blueValues[k]);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[fd].nOtherBlues) {
+ eexecWrite(&eb, "/OtherBlues [");
+ for (k = 0; k < privateDicts[fd].nOtherBlues; ++k) {
+ buf = GString::format("{0:s}{1:d}",
+ k > 0 ? " " : "",
+ privateDicts[fd].otherBlues[k]);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[fd].nFamilyBlues) {
+ eexecWrite(&eb, "/FamilyBlues [");
+ for (k = 0; k < privateDicts[fd].nFamilyBlues; ++k) {
+ buf = GString::format("{0:s}{1:d}", k > 0 ? " " : "",
+ privateDicts[fd].familyBlues[k]);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[fd].nFamilyOtherBlues) {
+ eexecWrite(&eb, "/FamilyOtherBlues [");
+ for (k = 0; k < privateDicts[fd].nFamilyOtherBlues; ++k) {
+ buf = GString::format("{0:s}{1:d}", k > 0 ? " " : "",
+ privateDicts[fd].familyOtherBlues[k]);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[fd].blueScale != 0.039625) {
+ buf = GString::format("/BlueScale {0:.4g} def\n",
+ privateDicts[fd].blueScale);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[fd].blueShift != 7) {
+ buf = GString::format("/BlueShift {0:d} def\n",
+ privateDicts[fd].blueShift);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[fd].blueFuzz != 1) {
+ buf = GString::format("/BlueFuzz {0:d} def\n",
+ privateDicts[fd].blueFuzz);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[fd].hasStdHW) {
+ buf = GString::format("/StdHW [{0:.4g}] def\n", privateDicts[fd].stdHW);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[fd].hasStdVW) {
+ buf = GString::format("/StdVW [{0:.4g}] def\n", privateDicts[fd].stdVW);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[fd].nStemSnapH) {
+ // the StemSnapH array should be unique values in ascending order --
+ // if not, just skip it
+ for (k = 1; k < privateDicts[fd].nStemSnapH; ++k) {
+ if (privateDicts[fd].stemSnapH[k-1] >= privateDicts[fd].stemSnapH[k]) {
+ break;
+ }
+ }
+ if (k == privateDicts[fd].nStemSnapH) {
+ eexecWrite(&eb, "/StemSnapH [");
+ for (k = 0; k < privateDicts[fd].nStemSnapH; ++k) {
+ buf = GString::format("{0:s}{1:.4g}",
+ k > 0 ? " " : "",
+ privateDicts[fd].stemSnapH[k]);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ }
+ if (privateDicts[fd].nStemSnapV) {
+ // the StemSnapV array should be unique values in ascending order --
+ // if not, just skip it
+ for (k = 1; k < privateDicts[fd].nStemSnapV; ++k) {
+ if (privateDicts[fd].stemSnapV[k-1] >= privateDicts[fd].stemSnapV[k]) {
+ break;
+ }
+ }
+ if (k == privateDicts[fd].nStemSnapV) {
+ eexecWrite(&eb, "/StemSnapV [");
+ for (k = 0; k < privateDicts[fd].nStemSnapV; ++k) {
+ buf = GString::format("{0:s}{1:.4g}",
+ k > 0 ? " " : "",
+ privateDicts[fd].stemSnapV[k]);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ }
+ if (privateDicts[fd].hasForceBold) {
+ buf = GString::format("/ForceBold {0:s} def\n",
+ privateDicts[fd].forceBold ? "true" : "false");
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[fd].forceBoldThreshold != 0) {
+ buf = GString::format("/ForceBoldThreshold {0:.4g} def\n",
+ privateDicts[fd].forceBoldThreshold);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[fd].languageGroup != 0) {
+ buf = GString::format("/LanguageGroup {0:d} def\n",
+ privateDicts[fd].languageGroup);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+ if (privateDicts[fd].expansionFactor != 0.06) {
+ buf = GString::format("/ExpansionFactor {0:.4g} def\n",
+ privateDicts[fd].expansionFactor);
+ eexecWrite(&eb, buf->getCString());
+ delete buf;
+ }
+
+ // set up the subroutines
+ ok = gTrue;
+ getIndex(privateDicts[fd].subrsOffset, &subrIdx, &ok);
+ if (!ok) {
+ subrIdx.pos = -1;
+ }
+
+ // start the CharStrings
+ eexecWrite(&eb, "2 index /CharStrings 256 dict dup begin\n");
+
+ // write the .notdef CharString
+ ok = gTrue;
+ getIndexVal(&charStringsIdx, 0, &val, &ok);
+ if (ok) {
+ eexecCvtGlyph(&eb, ".notdef", val.pos, val.len,
+ &subrIdx, &privateDicts[fd]);
+ }
+
+ // write the CharStrings
+ for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+ if (cidMap[i+j] >= 0) {
+ ok = gTrue;
+ getIndexVal(&charStringsIdx, cidMap[i+j], &val, &ok);
+ if (ok) {
+ buf = GString::format("c{0:02x}", j);
+ eexecCvtGlyph(&eb, buf->getCString(), val.pos, val.len,
+ &subrIdx, &privateDicts[fd]);
+ delete buf;
+ }
+ }
+ }
+ eexecWrite(&eb, "end\n");
+ eexecWrite(&eb, "end\n");
+ eexecWrite(&eb, "readonly put\n");
+ eexecWrite(&eb, "noaccess put\n");
+ eexecWrite(&eb, "dup /FontName get exch definefont pop\n");
+ eexecWrite(&eb, "mark currentfile closefile\n");
+
+ // trailer
+ if (eb.line > 0) {
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+ for (j = 0; j < 8; ++j) {
+ (*outputFunc)(outputStream, "0000000000000000000000000000000000000000000000000000000000000000\n", 65);
+ }
+ (*outputFunc)(outputStream, "cleartomark\n", 12);
+ }
+
+ // write the Type 0 parent font
+ (*outputFunc)(outputStream, "16 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/FontName /", 11);
+ (*outputFunc)(outputStream, psName, (int)strlen(psName));
+ (*outputFunc)(outputStream, " def\n", 5);
+ (*outputFunc)(outputStream, "/FontType 0 def\n", 16);
+ if (topDict.hasFontMatrix) {
+ buf = GString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n",
+ topDict.fontMatrix[0], topDict.fontMatrix[1],
+ topDict.fontMatrix[2], topDict.fontMatrix[3],
+ topDict.fontMatrix[4], topDict.fontMatrix[5]);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ } else {
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ }
+ (*outputFunc)(outputStream, "/FMapType 2 def\n", 16);
+ (*outputFunc)(outputStream, "/Encoding [\n", 12);
+ for (i = 0; i < nCIDs; i += 256) {
+ buf = GString::format("{0:d}\n", i >> 8);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ (*outputFunc)(outputStream, "/FDepVector [\n", 14);
+ for (i = 0; i < nCIDs; i += 256) {
+ (*outputFunc)(outputStream, "/", 1);
+ (*outputFunc)(outputStream, psName, (int)strlen(psName));
+ buf = GString::format("_{0:02x} findfont\n", i >> 8);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
+
+ gfree(cidMap);
+}
+
+void FoFiType1C::eexecCvtGlyph(Type1CEexecBuf *eb, const char *glyphName,
+ int offset, int nBytes,
+ Type1CIndex *subrIdx,
+ Type1CPrivateDict *pDict) {
+ GString *buf;
+ GString *charBuf;
+
+ // generate the charstring
+ charBuf = new GString();
+ cvtGlyph(offset, nBytes, charBuf, subrIdx, pDict, gTrue, 0);
+
+ buf = GString::format("/{0:s} {1:d} RD ", glyphName, charBuf->getLength());
+ eexecWrite(eb, buf->getCString());
+ delete buf;
+ eexecWriteCharstring(eb, (Guchar *)charBuf->getCString(),
+ charBuf->getLength());
+ eexecWrite(eb, " ND\n");
+
+ delete charBuf;
+}
+
+void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
+ Type1CIndex *subrIdx, Type1CPrivateDict *pDict,
+ GBool top, int recursion) {
+ Type1CIndexVal val;
+ Type1COp zero, tmp;
+ GBool ok, dInt;
+ double d, dx, dy;
+ Gushort r2;
+ 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);
+ charBuf->append((char)58);
+ charBuf->append((char)147);
+ charBuf->append((char)134);
+ nOps = 0;
+ nHints = 0;
+ firstOp = gTrue;
+ openPath = gFalse;
+ }
+
+ zero.kind = type1COpInteger;
+ zero.intgr = 0;
+
+ pos = offset;
+ while (pos < offset + nBytes) {
+ ok = gTrue;
+ pos = getOp(pos, gTrue, &ok);
+ if (!ok) {
+ break;
+ }
+ if (ops[nOps - 1].kind == type1COpOperator) {
+ --nOps; // drop the operator
+ switch (ops[nOps].op) {
+ case 0x0001: // hstem
+ if (firstOp) {
+ cvtGlyphWidth(nOps & 1, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (nOps & 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hstem", nOps);
+ }
+ d = 0;
+ dInt = gTrue;
+ for (k = 0; k+1 < nOps; k += 2) {
+ // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints
+ if (ops[k+1].isNegative()) {
+ d += ops[k].toFloat() + ops[k+1].toFloat();
+ dInt &= ops[k].kind == type1COpInteger;
+ dInt &= ops[k+1].kind == type1COpInteger;
+ if (dInt) {
+ tmp.kind = type1COpInteger;
+ tmp.intgr = (int)(d + 0.5);
+ } else {
+ tmp.kind = type1COpFloat;
+ tmp.flt = d;
+ }
+ cvtNum(tmp, charBuf);
+ if (ops[k+1].kind == type1COpInteger) {
+ tmp.kind = type1COpInteger;
+ tmp.intgr = -ops[k+1].intgr;
+ } else if (ops[k+1].kind == type1COpRational) {
+ tmp.kind = type1COpRational;
+ tmp.rat.num = -ops[k+1].rat.num;
+ tmp.rat.den = ops[k+1].rat.den;
+ } else {
+ tmp.kind = type1COpFloat;
+ tmp.flt = -ops[k+1].toFloat();
+ }
+ cvtNum(tmp, charBuf);
+ } else {
+ d += ops[k].toFloat();
+ dInt &= ops[k].kind == type1COpInteger;
+ if (dInt) {
+ tmp.kind = type1COpInteger;
+ tmp.intgr = (int)(d + 0.5);
+ } else {
+ tmp.kind = type1COpFloat;
+ tmp.flt = d;
+ }
+ cvtNum(tmp, charBuf);
+ cvtNum(ops[k+1], charBuf);
+ d += ops[k+1].toFloat();
+ dInt &= ops[k+1].kind == type1COpInteger;
+ }
+ charBuf->append((char)1);
+ }
+ nHints += nOps / 2;
+ nOps = 0;
+ break;
+ case 0x0003: // vstem
+ if (firstOp) {
+ cvtGlyphWidth(nOps & 1, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (nOps & 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 vstem", nOps);
+ }
+ d = 0;
+ dInt = gTrue;
+ for (k = 0; k+1 < nOps; k += 2) {
+ // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints
+ if (ops[k+1].isNegative()) {
+ d += ops[k].toFloat() + ops[k+1].toFloat();
+ dInt &= ops[k].kind == type1COpInteger;
+ dInt &= ops[k+1].kind == type1COpInteger;
+ if (dInt) {
+ tmp.kind = type1COpInteger;
+ tmp.intgr = (int)(d + 0.5);
+ } else {
+ tmp.kind = type1COpFloat;
+ tmp.flt = d;
+ }
+ cvtNum(tmp, charBuf);
+ if (ops[k+1].kind == type1COpInteger) {
+ tmp.kind = type1COpInteger;
+ tmp.intgr = -ops[k+1].intgr;
+ } else if (ops[k+1].kind == type1COpRational) {
+ tmp.kind = type1COpRational;
+ tmp.rat.num = -ops[k+1].rat.num;
+ tmp.rat.den = ops[k+1].rat.den;
+ } else {
+ tmp.kind = type1COpFloat;
+ tmp.flt = -ops[k+1].toFloat();
+ }
+ cvtNum(tmp, charBuf);
+ } else {
+ d += ops[k].toFloat();
+ dInt &= ops[k].kind == type1COpInteger;
+ if (dInt) {
+ tmp.kind = type1COpInteger;
+ tmp.intgr = (int)(d + 0.5);
+ } else {
+ tmp.kind = type1COpFloat;
+ tmp.flt = d;
+ }
+ cvtNum(tmp, charBuf);
+ cvtNum(ops[k+1], charBuf);
+ d += ops[k+1].toFloat();
+ dInt &= ops[k+1].kind == type1COpInteger;
+ }
+ charBuf->append((char)3);
+ }
+ nHints += nOps / 2;
+ nOps = 0;
+ break;
+ case 0x0004: // vmoveto
+ if (firstOp) {
+ cvtGlyphWidth(nOps == 2, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (openPath) {
+ charBuf->append((char)9);
+ openPath = gFalse;
+ }
+ if (nOps != 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps);
+ }
+ cvtNum(ops[0], charBuf);
+ charBuf->append((char)4);
+ nOps = 0;
+ break;
+ case 0x0005: // rlineto
+ if (nOps < 2 || nOps % 2 != 0) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 rlineto", nOps);
+ }
+ for (k = 0; k+1 < nOps; k += 2) {
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ charBuf->append((char)5);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0006: // hlineto
+ if (nOps < 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hlineto", nOps);
+ }
+ for (k = 0; k < nOps; ++k) {
+ cvtNum(ops[k], charBuf);
+ charBuf->append((char)((k & 1) ? 7 : 6));
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0007: // vlineto
+ if (nOps < 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 vlineto", nOps);
+ }
+ for (k = 0; k < nOps; ++k) {
+ cvtNum(ops[k], charBuf);
+ charBuf->append((char)((k & 1) ? 6 : 7));
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0008: // rrcurveto
+ if (nOps < 6 || nOps % 6 != 0) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 rrcurveto", nOps);
+ }
+ for (k = 0; k < nOps; k += 6) {
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(ops[k+3], charBuf);
+ cvtNum(ops[k+4], charBuf);
+ cvtNum(ops[k+5], charBuf);
+ charBuf->append((char)8);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x000a: // callsubr
+ if (nOps >= 1) {
+ subrBias = (subrIdx->len < 1240)
+ ? 107 : (subrIdx->len < 33900) ? 1131 : 32768;
+ k = subrBias + ops[nOps - 1].toInt();
+ --nOps;
+ ok = gTrue;
+ getIndexVal(subrIdx, k, &val, &ok);
+ if (ok) {
+ cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse,
+ recursion + 1);
+ }
+ } else {
+ //~ error(-1, "Too few args to Type 2 callsubr");
+ }
+ // don't clear the stack
+ break;
+ case 0x000b: // return
+ // don't clear the stack
+ break;
+ case 0x000e: // endchar / seac
+ if (firstOp) {
+ cvtGlyphWidth(nOps == 1 || nOps == 5, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (openPath) {
+ charBuf->append((char)9);
+ openPath = gFalse;
+ }
+ if (nOps == 4) {
+ cvtNum(zero, charBuf);
+ cvtNum(ops[0], charBuf);
+ cvtNum(ops[1], charBuf);
+ cvtNum(ops[2], charBuf);
+ cvtNum(ops[3], charBuf);
+ charBuf->append((char)12)->append((char)6);
+ } else if (nOps == 0) {
+ charBuf->append((char)14);
+ } else {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 endchar", nOps);
+ }
+ nOps = 0;
+ break;
+ case 0x000f: // (obsolete)
+ // this op is ignored, but we need the glyph width
+ if (firstOp) {
+ cvtGlyphWidth(nOps > 0, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ nOps = 0;
+ break;
+ case 0x0010: // blend
+ //~ error(-1, "Unimplemented Type 2 charstring op: %d", file[i]);
+ nOps = 0;
+ break;
+ case 0x0012: // hstemhm
+ // ignored
+ if (firstOp) {
+ cvtGlyphWidth(nOps & 1, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (nOps & 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hstemhm", nOps);
+ }
+ nHints += nOps / 2;
+ nOps = 0;
+ break;
+ case 0x0013: // hintmask
+ // ignored
+ if (firstOp) {
+ cvtGlyphWidth(nOps & 1, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (nOps > 0) {
+ if (nOps & 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hintmask/vstemhm",
+ //~ nOps);
+ }
+ nHints += nOps / 2;
+ }
+ pos += (nHints + 7) >> 3;
+ nOps = 0;
+ break;
+ case 0x0014: // cntrmask
+ // ignored
+ if (firstOp) {
+ cvtGlyphWidth(nOps & 1, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (nOps > 0) {
+ if (nOps & 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 cntrmask/vstemhm",
+ //~ nOps);
+ }
+ nHints += nOps / 2;
+ }
+ pos += (nHints + 7) >> 3;
+ nOps = 0;
+ break;
+ case 0x0015: // rmoveto
+ if (firstOp) {
+ cvtGlyphWidth(nOps == 3, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (openPath) {
+ charBuf->append((char)9);
+ openPath = gFalse;
+ }
+ if (nOps != 2) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps);
+ }
+ cvtNum(ops[0], charBuf);
+ cvtNum(ops[1], charBuf);
+ charBuf->append((char)21);
+ nOps = 0;
+ break;
+ case 0x0016: // hmoveto
+ if (firstOp) {
+ cvtGlyphWidth(nOps == 2, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (openPath) {
+ charBuf->append((char)9);
+ openPath = gFalse;
+ }
+ if (nOps != 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps);
+ }
+ cvtNum(ops[0], charBuf);
+ charBuf->append((char)22);
+ nOps = 0;
+ break;
+ case 0x0017: // vstemhm
+ // ignored
+ if (firstOp) {
+ cvtGlyphWidth(nOps & 1, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (nOps & 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 vstemhm", nOps);
+ }
+ nHints += nOps / 2;
+ nOps = 0;
+ break;
+ case 0x0018: // rcurveline
+ if (nOps < 8 || (nOps - 2) % 6 != 0) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 rcurveline", nOps);
+ }
+ for (k = 0; k <= nOps - 8; k += 6) {
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(ops[k+3], charBuf);
+ cvtNum(ops[k+4], charBuf);
+ cvtNum(ops[k+5], charBuf);
+ charBuf->append((char)8);
+ }
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ charBuf->append((char)5);
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0019: // rlinecurve
+ if (nOps < 8 || (nOps - 6) % 2 != 0) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 rlinecurve", nOps);
+ }
+ for (k = 0; k <= nOps - 8; k += 2) {
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ charBuf->append((char)5);
+ }
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(ops[k+3], charBuf);
+ cvtNum(ops[k+4], charBuf);
+ cvtNum(ops[k+5], charBuf);
+ charBuf->append((char)8);
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x001a: // vvcurveto
+ if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 vvcurveto", nOps);
+ }
+ if (nOps % 2 == 1 && nOps >= 5) {
+ cvtNum(ops[0], charBuf);
+ cvtNum(ops[1], charBuf);
+ cvtNum(ops[2], charBuf);
+ cvtNum(ops[3], charBuf);
+ cvtNum(zero, charBuf);
+ cvtNum(ops[4], charBuf);
+ charBuf->append((char)8);
+ k = 5;
+ } else {
+ k = 0;
+ }
+ for (; k+3 < nOps; k += 4) {
+ cvtNum(zero, charBuf);
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(zero, charBuf);
+ cvtNum(ops[k+3], charBuf);
+ charBuf->append((char)8);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x001b: // hhcurveto
+ if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hhcurveto", nOps);
+ }
+ if (nOps % 2 == 1 && nOps >= 5) {
+ cvtNum(ops[1], charBuf);
+ cvtNum(ops[0], charBuf);
+ cvtNum(ops[2], charBuf);
+ cvtNum(ops[3], charBuf);
+ cvtNum(ops[4], charBuf);
+ cvtNum(zero, charBuf);
+ charBuf->append((char)8);
+ k = 5;
+ } else {
+ k = 0;
+ }
+ for (; k+3 < nOps; k += 4) {
+ cvtNum(ops[k], charBuf);
+ cvtNum(zero, charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(ops[k+3], charBuf);
+ cvtNum(zero, charBuf);
+ charBuf->append((char)8);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x001d: // callgsubr
+ if (nOps >= 1) {
+ k = gsubrBias + ops[nOps - 1].toInt();
+ --nOps;
+ ok = gTrue;
+ getIndexVal(&gsubrIdx, k, &val, &ok);
+ if (ok) {
+ cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse,
+ recursion + 1);
+ }
+ } else {
+ //~ error(-1, "Too few args to Type 2 callgsubr");
+ }
+ // don't clear the stack
+ break;
+ case 0x001e: // vhcurveto
+ if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 vhcurveto", nOps);
+ }
+ for (k = 0; k+3 < nOps && k != nOps-5; k += 4) {
+ if (k % 8 == 0) {
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(ops[k+3], charBuf);
+ charBuf->append((char)30);
+ } else {
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(ops[k+3], charBuf);
+ charBuf->append((char)31);
+ }
+ }
+ if (k == nOps-5) {
+ if (k % 8 == 0) {
+ cvtNum(zero, charBuf);
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(ops[k+3], charBuf);
+ cvtNum(ops[k+4], charBuf);
+ } else {
+ cvtNum(ops[k], charBuf);
+ cvtNum(zero, charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(ops[k+4], charBuf);
+ cvtNum(ops[k+3], charBuf);
+ }
+ charBuf->append((char)8);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x001f: // hvcurveto
+ if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hvcurveto", nOps);
+ }
+ for (k = 0; k+3 < nOps && k != nOps-5; k += 4) {
+ if (k % 8 == 0) {
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(ops[k+3], charBuf);
+ charBuf->append((char)31);
+ } else {
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(ops[k+3], charBuf);
+ charBuf->append((char)30);
+ }
+ }
+ if (k == nOps-5) {
+ if (k % 8 == 0) {
+ cvtNum(ops[k], charBuf);
+ cvtNum(zero, charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(ops[k+4], charBuf);
+ cvtNum(ops[k+3], charBuf);
+ } else {
+ cvtNum(zero, charBuf);
+ cvtNum(ops[k], charBuf);
+ cvtNum(ops[k+1], charBuf);
+ cvtNum(ops[k+2], charBuf);
+ cvtNum(ops[k+3], charBuf);
+ cvtNum(ops[k+4], charBuf);
+ }
+ charBuf->append((char)8);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0c00: // dotsection (should be Type 1 only?)
+ // ignored
+ nOps = 0;
+ break;
+ case 0x0c0c: // div
+ if (nOps >= 2) {
+ num = ops[nOps-2].toInt();
+ den = ops[nOps-1].toInt();
+ --nOps;
+ ops[nOps-1].kind = type1COpRational;
+ ops[nOps-1].rat.num = num;
+ ops[nOps-1].rat.den = den;
+ } else {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 div", nOps);
+ }
+ break;
+ case 0x0c03: // and
+ case 0x0c04: // or
+ case 0x0c05: // not
+ case 0x0c08: // store
+ case 0x0c09: // abs
+ case 0x0c0a: // add
+ case 0x0c0b: // sub
+ case 0x0c0d: // load
+ case 0x0c0e: // neg
+ case 0x0c0f: // eq
+ case 0x0c12: // drop
+ case 0x0c14: // put
+ case 0x0c15: // get
+ case 0x0c16: // ifelse
+ case 0x0c17: // random
+ case 0x0c18: // mul
+ case 0x0c1a: // sqrt
+ case 0x0c1b: // dup
+ case 0x0c1c: // exch
+ case 0x0c1d: // index
+ case 0x0c1e: // roll
+ //~ error(-1, "Unimplemented Type 2 charstring op: 12.%d", file[i+1]);
+ nOps = 0;
+ break;
+ case 0x0c22: // hflex
+ if (nOps == 7) {
+ cvtNum(ops[0], charBuf);
+ cvtNum(zero, charBuf);
+ cvtNum(ops[1], charBuf);
+ cvtNum(ops[2], charBuf);
+ cvtNum(ops[3], charBuf);
+ cvtNum(zero, charBuf);
+ charBuf->append((char)8);
+ cvtNum(ops[4], charBuf);
+ cvtNum(zero, charBuf);
+ cvtNum(ops[5], charBuf);
+ if (ops[2].kind == type1COpInteger) {
+ tmp.kind = type1COpInteger;
+ tmp.intgr = -ops[2].intgr;
+ } else if (ops[2].kind == type1COpRational) {
+ tmp.kind = type1COpRational;
+ tmp.rat.num = -ops[2].rat.num;
+ tmp.rat.den = ops[2].rat.den;
+ } else {
+ tmp.kind = type1COpFloat;
+ tmp.flt = -ops[2].toFloat();
+ }
+ cvtNum(tmp, charBuf);
+ cvtNum(ops[6], charBuf);
+ cvtNum(zero, charBuf);
+ charBuf->append((char)8);
+ } else {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hflex", nOps);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0c23: // flex
+ if (nOps == 13) {
+ cvtNum(ops[0], charBuf);
+ cvtNum(ops[1], charBuf);
+ cvtNum(ops[2], charBuf);
+ cvtNum(ops[3], charBuf);
+ cvtNum(ops[4], charBuf);
+ cvtNum(ops[5], charBuf);
+ charBuf->append((char)8);
+ cvtNum(ops[6], charBuf);
+ cvtNum(ops[7], charBuf);
+ cvtNum(ops[8], charBuf);
+ cvtNum(ops[9], charBuf);
+ cvtNum(ops[10], charBuf);
+ cvtNum(ops[11], charBuf);
+ charBuf->append((char)8);
+ } else {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 flex", nOps);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0c24: // hflex1
+ if (nOps == 9) {
+ cvtNum(ops[0], charBuf);
+ cvtNum(ops[1], charBuf);
+ cvtNum(ops[2], charBuf);
+ cvtNum(ops[3], charBuf);
+ cvtNum(ops[4], charBuf);
+ cvtNum(zero, charBuf);
+ charBuf->append((char)8);
+ cvtNum(ops[5], charBuf);
+ cvtNum(zero, charBuf);
+ cvtNum(ops[6], charBuf);
+ cvtNum(ops[7], charBuf);
+ cvtNum(ops[8], charBuf);
+ if (ops[1].kind == type1COpInteger &&
+ ops[3].kind == type1COpInteger &&
+ ops[7].kind == type1COpInteger) {
+ tmp.kind = type1COpInteger;
+ tmp.intgr = -(ops[1].intgr + ops[3].intgr + ops[7].intgr);
+ } else {
+ tmp.kind = type1COpFloat;
+ tmp.flt = -(ops[1].toFloat() + ops[3].toFloat() + ops[7].toFloat());
+ }
+ cvtNum(tmp, charBuf);
+ charBuf->append((char)8);
+ } else {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hflex1", nOps);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0c25: // flex1
+ if (nOps == 11) {
+ cvtNum(ops[0], charBuf);
+ cvtNum(ops[1], charBuf);
+ cvtNum(ops[2], charBuf);
+ cvtNum(ops[3], charBuf);
+ cvtNum(ops[4], charBuf);
+ cvtNum(ops[5], charBuf);
+ charBuf->append((char)8);
+ cvtNum(ops[6], charBuf);
+ cvtNum(ops[7], charBuf);
+ cvtNum(ops[8], charBuf);
+ cvtNum(ops[9], charBuf);
+ dx = ops[0].toFloat() + ops[2].toFloat() + ops[4].toFloat()
+ + ops[6].toFloat() + ops[8].toFloat();
+ dy = ops[1].toFloat() + ops[3].toFloat() + ops[5].toFloat()
+ + ops[7].toFloat() + ops[9].toFloat();
+ if (fabs(dx) > fabs(dy)) {
+ cvtNum(ops[10], charBuf);
+ if (ops[1].kind == type1COpInteger &&
+ ops[3].kind == type1COpInteger &&
+ ops[5].kind == type1COpInteger &&
+ ops[7].kind == type1COpInteger &&
+ ops[9].kind == type1COpInteger) {
+ tmp.kind = type1COpInteger;
+ tmp.intgr = -(int)dy;
+ } else {
+ tmp.kind = type1COpFloat;
+ tmp.flt = -dy;
+ }
+ cvtNum(tmp, charBuf);
+ } else {
+ if (ops[0].kind == type1COpInteger &&
+ ops[2].kind == type1COpInteger &&
+ ops[4].kind == type1COpInteger &&
+ ops[6].kind == type1COpInteger &&
+ ops[8].kind == type1COpInteger) {
+ tmp.kind = type1COpInteger;
+ tmp.intgr = -(int)dx;
+ } else {
+ tmp.kind = type1COpFloat;
+ tmp.flt = -dx;
+ }
+ cvtNum(tmp, charBuf);
+ cvtNum(ops[10], charBuf);
+ }
+ charBuf->append((char)8);
+ } else {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 flex1", nOps);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ default:
+ //~ error(-1, "Illegal Type 2 charstring op: %04x",
+ //~ ops[nOps].op);
+ nOps = 0;
+ break;
+ }
+ }
+ }
+
+ // charstring encryption
+ if (top) {
+ r2 = 4330;
+ for (i = start; i < charBuf->getLength(); ++i) {
+ byte = (Guchar)(charBuf->getChar(i) ^ (r2 >> 8));
+ charBuf->setChar(i, byte);
+ r2 = (Gushort)((byte + r2) * 52845 + 22719);
+ }
+ }
+}
+
+void FoFiType1C::cvtGlyphWidth(GBool useOp, GString *charBuf,
+ Type1CPrivateDict *pDict) {
+ Type1COp zero, w;
+ int i;
+
+ if (useOp) {
+ if (pDict->nominalWidthXInt & (ops[0].kind == type1COpInteger)) {
+ w.kind = type1COpInteger;
+ w.intgr = (int)(pDict->nominalWidthX + ops[0].intgr);
+ } else {
+ w.kind = type1COpFloat;
+ w.flt = pDict->nominalWidthX + ops[0].toFloat();
+ }
+ for (i = 1; i < nOps; ++i) {
+ ops[i-1] = ops[i];
+ }
+ --nOps;
+ } else {
+ if (pDict->defaultWidthXInt) {
+ w.kind = type1COpInteger;
+ w.intgr = (int)pDict->defaultWidthX;
+ } else {
+ w.kind = type1COpFloat;
+ w.flt = pDict->defaultWidthX;
+ }
+ }
+ zero.kind = type1COpInteger;
+ zero.intgr = 0;
+ cvtNum(zero, charBuf);
+ cvtNum(w, charBuf);
+ charBuf->append((char)13);
+}
+
+void FoFiType1C::cvtNum(Type1COp op, GString *charBuf) {
+ Type1COp tmp;
+ Guchar buf[12];
+ int y, n;
+
+ n = 0;
+ switch (op.kind) {
+ case type1COpInteger:
+ y = op.intgr;
+ if (y >= -107 && y <= 107) {
+ buf[0] = (Guchar)(y + 139);
+ n = 1;
+ } else if (y > 107 && y <= 1131) {
+ y -= 108;
+ buf[0] = (Guchar)((y >> 8) + 247);
+ buf[1] = (Guchar)(y & 0xff);
+ n = 2;
+ } else if (y < -107 && y >= -1131) {
+ y = -y - 108;
+ buf[0] = (Guchar)((y >> 8) + 251);
+ buf[1] = (Guchar)(y & 0xff);
+ n = 2;
+ } else {
+ buf[0] = 255;
+ buf[1] = (Guchar)(y >> 24);
+ buf[2] = (Guchar)(y >> 16);
+ buf[3] = (Guchar)(y >> 8);
+ buf[4] = (Guchar)y;
+ n = 5;
+ }
+ break;
+ case type1COpFloat:
+ if (op.flt >= -32768 && op.flt < 32768) {
+ y = (int)(op.flt * 256.0);
+ buf[0] = 255;
+ buf[1] = (Guchar)(y >> 24);
+ buf[2] = (Guchar)(y >> 16);
+ buf[3] = (Guchar)(y >> 8);
+ buf[4] = (Guchar)y;
+ buf[5] = 255;
+ buf[6] = 0;
+ buf[7] = 0;
+ buf[8] = 1;
+ buf[9] = 0;
+ buf[10] = 12;
+ buf[11] = 12;
+ n = 12;
+ } else {
+ //~ error(-1, "Type 2 fixed point constant out of range");
+ }
+ break;
+ case type1COpRational:
+ tmp.kind = type1COpInteger;
+ tmp.intgr = op.rat.num;
+ cvtNum(tmp, charBuf);
+ tmp.intgr = op.rat.den;
+ cvtNum(tmp, charBuf);
+ buf[0] = 0x0c;
+ buf[1] = 0x0c;
+ n = 2;
+ break;
+ default: // shouldn't happen
+ break;
+ }
+ charBuf->append((char *)buf, n);
+}
+
+void FoFiType1C::eexecWrite(Type1CEexecBuf *eb, const char *s) {
+ Guchar *p;
+ Guchar x;
+
+ for (p = (Guchar *)s; *p; ++p) {
+ x = *p ^ (Guchar)(eb->r1 >> 8);
+ eb->r1 = (Gushort)((x + eb->r1) * 52845 + 22719);
+ if (eb->ascii) {
+ (*eb->outputFunc)(eb->outputStream, &hexChars[x >> 4], 1);
+ (*eb->outputFunc)(eb->outputStream, &hexChars[x & 0x0f], 1);
+ eb->line += 2;
+ if (eb->line == 64) {
+ (*eb->outputFunc)(eb->outputStream, "\n", 1);
+ eb->line = 0;
+ }
+ } else {
+ (*eb->outputFunc)(eb->outputStream, (char *)&x, 1);
+ }
+ }
+}
+
+void FoFiType1C::eexecWriteCharstring(Type1CEexecBuf *eb,
+ Guchar *s, int n) {
+ Guchar x;
+ int i;
+
+ // eexec encryption
+ for (i = 0; i < n; ++i) {
+ x = s[i] ^ (Guchar)(eb->r1 >> 8);
+ eb->r1 = (Gushort)((x + eb->r1) * 52845 + 22719);
+ if (eb->ascii) {
+ (*eb->outputFunc)(eb->outputStream, &hexChars[x >> 4], 1);
+ (*eb->outputFunc)(eb->outputStream, &hexChars[x & 0x0f], 1);
+ eb->line += 2;
+ if (eb->line == 64) {
+ (*eb->outputFunc)(eb->outputStream, "\n", 1);
+ eb->line = 0;
+ }
+ } else {
+ (*eb->outputFunc)(eb->outputStream, (char *)&x, 1);
+ }
+ }
+}
+
+void FoFiType1C::writePSString(char *s, FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ char buf[80];
+ char *p;
+ int i, c;
+
+ i = 0;
+ buf[i++] = '(';
+ for (p = s; *p; ++p) {
+ c = *p & 0xff;
+ if (c == '(' || c == ')' || c == '\\') {
+ buf[i++] = '\\';
+ buf[i++] = (char)c;
+ } else if (c < 0x20 || c >= 0x80) {
+ buf[i++] = '\\';
+ buf[i++] = (char)('0' + ((c >> 6) & 7));
+ buf[i++] = (char)('0' + ((c >> 3) & 7));
+ buf[i++] = (char)('0' + (c & 7));
+ } else {
+ buf[i++] = (char)c;
+ }
+ if (i >= 64) {
+ buf[i++] = '\\';
+ buf[i++] = '\n';
+ (*outputFunc)(outputStream, buf, i);
+ i = 0;
+ }
+ }
+ buf[i++] = ')';
+ (*outputFunc)(outputStream, buf, i);
+}
+
+void FoFiType1C::convertToOpenType(FoFiOutputFunc outputFunc,
+ void *outputStream,
+ int nWidths, Gushort *widths,
+ Guchar *cmapTable, int cmapTableLen) {
+ // dummy OS/2 table (taken from FoFiTrueType::writeTTF)
+ static Guchar os2Tab[86] = {
+ 0, 1, // version
+ 0, 1, // xAvgCharWidth
+ 0x01, 0x90, // usWeightClass
+ 0, 5, // usWidthClass
+ 0, 0, // fsType
+ 0, 0, // ySubscriptXSize
+ 0, 0, // ySubscriptYSize
+ 0, 0, // ySubscriptXOffset
+ 0, 0, // ySubscriptYOffset
+ 0, 0, // ySuperscriptXSize
+ 0, 0, // ySuperscriptYSize
+ 0, 0, // ySuperscriptXOffset
+ 0, 0, // ySuperscriptYOffset
+ 0, 0, // yStrikeoutSize
+ 0, 0, // yStrikeoutPosition
+ 0, 0, // sFamilyClass
+ 0, 0, 0, 0, 0, // panose
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, // ulUnicodeRange1
+ 0, 0, 0, 0, // ulUnicodeRange2
+ 0, 0, 0, 0, // ulUnicodeRange3
+ 0, 0, 0, 0, // ulUnicodeRange4
+ 0, 0, 0, 0, // achVendID
+ 0, 0, // fsSelection
+ 0, 0, // usFirstCharIndex
+ 0, 0, // usLastCharIndex
+ 0, 0, // sTypoAscender
+ 0, 0, // sTypoDescender
+ 0, 0, // sTypoLineGap
+ 0x20, 0x00, // usWinAscent
+ 0x20, 0x00, // usWinDescent
+ 0, 0, 0, 1, // ulCodePageRange1
+ 0, 0, 0, 0 // ulCodePageRange2
+ };
+ Guchar headTable[54], hheaTable[36], maxpTable[6];
+ Guchar nameTable[26], postTable[32];
+ Guchar *hmtxTable;
+ static const char *tableTag[9] = {
+ "CFF ",
+ "OS/2",
+ "cmap",
+ "head",
+ "hhea",
+ "hmtx",
+ "maxp",
+ "name",
+ "post"
+ };
+ Guchar *tableData[9];
+ int tableLength[9];
+ Guchar header[12 + 9*16];
+ double mat[6];
+ Gushort maxWidth;
+ Guint checksum, fileChecksum;
+ int unitsPerEm, xMin, yMin, xMax, yMax, offset, i;
+
+ //--- CFF_ table
+ tableData[0] = file;
+ tableLength[0] = len;
+
+ //--- OS/2 table
+ tableData[1] = os2Tab;
+ tableLength[1] = 86;
+
+ //--- cmap table
+ tableData[2] = cmapTable;
+ tableLength[2] = cmapTableLen;
+
+ //--- head table
+ getFontMatrix(mat);
+ if (mat[0] == 0) {
+ unitsPerEm = 1000;
+ } else {
+ unitsPerEm = (int)(1 / mat[0] + 0.5);
+ }
+ xMin = (int)(topDict.fontBBox[0] + 0.5);
+ yMin = (int)(topDict.fontBBox[1] + 0.5);
+ xMax = (int)(topDict.fontBBox[2] + 0.5);
+ yMax = (int)(topDict.fontBBox[3] + 0.5);
+ headTable[ 0] = 0x00; // version
+ headTable[ 1] = 0x01;
+ headTable[ 2] = 0x00;
+ headTable[ 3] = 0x00;
+ headTable[ 4] = 0x00; // revision
+ headTable[ 5] = 0x00;
+ headTable[ 6] = 0x00;
+ headTable[ 7] = 0x00;
+ headTable[ 8] = 0x00; // checksumAdjustment
+ headTable[ 9] = 0x00; // (set later)
+ headTable[10] = 0x00;
+ headTable[11] = 0x00;
+ headTable[12] = 0x5f; // magicNumber
+ headTable[13] = 0x0f;
+ headTable[14] = 0x3c;
+ headTable[15] = 0xf5;
+ headTable[16] = 0x00; // flags
+ headTable[17] = 0x03;
+ headTable[18] = (Guchar)(unitsPerEm >> 8); // unitsPerEm
+ headTable[19] = (Guchar)unitsPerEm;
+ headTable[20] = 0x00; // created
+ headTable[21] = 0x00;
+ headTable[22] = 0x00;
+ headTable[23] = 0x00;
+ headTable[24] = 0x00;
+ headTable[25] = 0x00;
+ headTable[26] = 0x00;
+ headTable[27] = 0x00;
+ headTable[28] = 0x00; // modified
+ headTable[29] = 0x00;
+ headTable[30] = 0x00;
+ headTable[31] = 0x00;
+ headTable[32] = 0x00;
+ headTable[33] = 0x00;
+ headTable[34] = 0x00;
+ headTable[35] = 0x00;
+ headTable[36] = (Guchar)(xMin >> 8); // xMin
+ headTable[37] = (Guchar)xMin;
+ headTable[38] = (Guchar)(yMin >> 8); // yMin
+ headTable[39] = (Guchar)yMin;
+ headTable[40] = (Guchar)(xMax >> 8); // xMax
+ headTable[41] = (Guchar)xMax;
+ headTable[42] = (Guchar)(yMax >> 8); // yMax
+ headTable[43] = (Guchar)yMax;
+ headTable[44] = 0; // macStyle
+ headTable[45] = 0;
+ headTable[46] = 0; // lowestRecPPEM
+ headTable[47] = 3;
+ headTable[48] = 0; // fontDirectionHint
+ headTable[49] = 2; // (deprecated)
+ headTable[50] = 0; // indexToLocFormat
+ headTable[51] = 0; // (n/a to CFF fonts)
+ headTable[52] = 0; // glyphDataFormat
+ headTable[53] = 0; // (n/a to CFF fonts)
+ tableData[3] = headTable;
+ tableLength[3] = 54;
+
+ //--- hhea table
+ maxWidth = widths[0];
+ for (i = 1; i < nWidths; ++i) {
+ if (widths[i] > maxWidth) {
+ maxWidth = widths[i];
+ }
+ }
+ hheaTable[ 0] = 0x00; // version
+ hheaTable[ 1] = 0x01;
+ hheaTable[ 2] = 0x00;
+ hheaTable[ 3] = 0x00;
+ hheaTable[ 4] = (Guchar)(yMax >> 8); // ascender
+ hheaTable[ 5] = (Guchar)yMax;
+ hheaTable[ 6] = (Guchar)(yMin >> 8); // descender
+ hheaTable[ 7] = (Guchar)yMin;
+ hheaTable[ 8] = 0; // lineGap
+ hheaTable[ 9] = 0;
+ hheaTable[10] = (Guchar)(maxWidth >> 8); // advanceWidthMax
+ hheaTable[11] = (Guchar)maxWidth;
+ hheaTable[12] = 0; // minLeftSideBearing
+ hheaTable[13] = 0;
+ hheaTable[14] = 0; // minRightSideBearing
+ hheaTable[15] = 0;
+ hheaTable[16] = (Guchar)(maxWidth >> 8); // xMaxExtent
+ hheaTable[17] = (Guchar)maxWidth;
+ hheaTable[18] = 0; // caretSlopeRise
+ hheaTable[19] = 1;
+ hheaTable[20] = 0; // caretSlopeRun
+ hheaTable[21] = 0;
+ hheaTable[22] = 0; // caretOffset
+ hheaTable[23] = 0;
+ hheaTable[24] = 0; // reserved
+ hheaTable[25] = 0;
+ hheaTable[26] = 0; // reserved
+ hheaTable[27] = 0;
+ hheaTable[28] = 0; // reserved
+ hheaTable[29] = 0;
+ hheaTable[30] = 0; // reserved
+ hheaTable[31] = 0;
+ hheaTable[32] = 0; // metricDataFormat
+ hheaTable[33] = 0;
+ hheaTable[34] = (Guchar)(nWidths >> 8); // numberOfHMetrics
+ hheaTable[35] = (Guchar)nWidths;
+ tableData[4] = hheaTable;
+ tableLength[4] = 36;
+
+ //--- hmtx table
+ //~ this currently sets LSB to 0 for all glyphs
+ hmtxTable = (Guchar *)gmallocn(nWidths, 4);
+ for (i = 0; i < nWidths; ++i) {
+ hmtxTable[4*i ] = (Guchar)(widths[i] >> 8);
+ hmtxTable[4*i+1] = (Guchar)widths[i];
+ hmtxTable[4*i+2] = 0;
+ hmtxTable[4*i+3] = 0;
+ }
+ tableData[5] = hmtxTable;
+ tableLength[5] = 4 * nWidths;
+
+ //--- maxp table
+ maxpTable[0] = 0x00; // version = 0.5
+ maxpTable[1] = 0x00;
+ maxpTable[2] = 0x50;
+ maxpTable[3] = 0x00;
+ maxpTable[4] = (Guchar)(nGlyphs >> 8); // numGlyphs
+ maxpTable[5] = (Guchar)nGlyphs;
+ tableData[6] = maxpTable;
+ tableLength[6] = 6;
+
+ //--- name table
+ nameTable[ 0] = 0x00; // format
+ nameTable[ 1] = 0x00;
+ nameTable[ 2] = 0x00; // count
+ nameTable[ 3] = 0x01;
+ nameTable[ 4] = 0x00; // stringOffset
+ nameTable[ 5] = 0x12;
+ nameTable[ 6] = 0x00; // platformID
+ nameTable[ 7] = 0x00;
+ nameTable[ 8] = 0x00; // encodingID
+ nameTable[ 9] = 0x03;
+ nameTable[10] = 0x00; // languageID
+ nameTable[11] = 0x00;
+ nameTable[12] = 0x00; // nameID
+ nameTable[13] = 0x00;
+ nameTable[14] = 0x00; // length
+ nameTable[15] = 0x08;
+ nameTable[16] = 0x00; // offset
+ nameTable[17] = 0x00;
+ nameTable[18] = 0x00; // string data
+ nameTable[19] = (Guchar)'n';
+ nameTable[20] = 0x00;
+ nameTable[21] = (Guchar)'o';
+ nameTable[22] = 0x00;
+ nameTable[23] = (Guchar)'n';
+ nameTable[24] = 0x00;
+ nameTable[25] = (Guchar)'e';
+ tableData[7] = nameTable;
+ tableLength[7] = 26;
+
+ //--- post table
+ postTable[ 0] = 0x00; // version = 3.0
+ postTable[ 1] = 0x03;
+ postTable[ 2] = 0x00;
+ postTable[ 3] = 0x00;
+ postTable[ 4] = 0x00; // italicAngle
+ postTable[ 5] = 0x00;
+ postTable[ 6] = 0x00;
+ postTable[ 7] = 0x00;
+ postTable[ 8] = 0x00; // underlinePosition
+ postTable[ 9] = 0x00;
+ postTable[10] = 0x00; // underlineThickness
+ postTable[11] = 0x00;
+ postTable[12] = 0x00; // isFixedPitch
+ postTable[13] = 0x00;
+ postTable[14] = 0x00;
+ postTable[15] = 0x00;
+ postTable[16] = 0x00; // minMemType42
+ postTable[17] = 0x00;
+ postTable[18] = 0x00;
+ postTable[19] = 0x00;
+ postTable[20] = 0x00; // maxMemType42
+ postTable[21] = 0x00;
+ postTable[22] = 0x00;
+ postTable[23] = 0x00;
+ postTable[24] = 0x00; // minMemType1
+ postTable[25] = 0x00;
+ postTable[26] = 0x00;
+ postTable[27] = 0x00;
+ postTable[28] = 0x00; // maxMemType1
+ postTable[29] = 0x00;
+ postTable[30] = 0x00;
+ postTable[31] = 0x00;
+ tableData[8] = postTable;
+ tableLength[8] = 32;
+
+ //--- header and table directory
+ header[ 0] = 'O'; // sfnt version
+ header[ 1] = 'T';
+ header[ 2] = 'T';
+ header[ 3] = 'O';
+ header[ 4] = 0x00; // numTables
+ header[ 5] = 0x09;
+ header[ 6] = 0x00; // searchRange
+ header[ 7] = 0x80;
+ header[ 8] = 0x00; // entrySelector
+ header[ 9] = 0x03;
+ header[10] = 0x00; // rangeShift
+ header[11] = 0x10;
+ offset = 12 + 9*16;
+ fileChecksum = 0;
+ for (i = 0; i < 9; ++i) {
+ header[12 + i*16 + 0] = tableTag[i][0];
+ header[12 + i*16 + 1] = tableTag[i][1];
+ header[12 + i*16 + 2] = tableTag[i][2];
+ header[12 + i*16 + 3] = tableTag[i][3];
+ checksum = computeOpenTypeTableChecksum(tableData[i], tableLength[i]);
+ fileChecksum += checksum;
+ header[12 + i*16 + 4] = (Guchar)(checksum >> 24);
+ header[12 + i*16 + 5] = (Guchar)(checksum >> 16);
+ header[12 + i*16 + 6] = (Guchar)(checksum >> 8);
+ header[12 + i*16 + 7] = (Guchar)checksum;
+ header[12 + i*16 + 8] = (Guchar)(offset >> 24);
+ header[12 + i*16 + 9] = (Guchar)(offset >> 16);
+ header[12 + i*16 + 10] = (Guchar)(offset >> 8);
+ header[12 + i*16 + 11] = (Guchar)offset;
+ header[12 + i*16 + 12] = (Guchar)(tableLength[i] >> 24);
+ header[12 + i*16 + 13] = (Guchar)(tableLength[i] >> 16);
+ header[12 + i*16 + 14] = (Guchar)(tableLength[i] >> 8);
+ header[12 + i*16 + 15] = (Guchar)tableLength[i];
+ offset += tableLength[i];
+ if (tableLength[i] & 3) {
+ offset += 4 - (tableLength[i] & 3);
+ }
+ }
+
+ //--- file checksum
+ fileChecksum += computeOpenTypeTableChecksum(header, 12 + 9*16);
+ fileChecksum = 0xb1b0afba - fileChecksum;
+ headTable[ 8] = (Guchar)(fileChecksum >> 24);
+ headTable[ 9] = (Guchar)(fileChecksum >> 16);
+ headTable[10] = (Guchar)(fileChecksum >> 8);
+ headTable[11] = (Guchar)fileChecksum;
+
+ //--- write the OpenType font
+ (*outputFunc)(outputStream, (char *)header, 12 + 9*16);
+ for (i = 0; i < 9; ++i) {
+ (*outputFunc)(outputStream, (char *)tableData[i], tableLength[i]);
+ if (tableLength[i] & 3) {
+ (*outputFunc)(outputStream, "\0\0\0", 4 - (tableLength[i] & 3));
+ }
+ }
+
+ gfree(hmtxTable);
+}
+
+Guint FoFiType1C::computeOpenTypeTableChecksum(Guchar *data, int length) {
+ Guint checksum, word;
+ int i;
+
+ checksum = 0;
+ for (i = 0; i+3 < length; i += 4) {
+ word = ((data[i ] & 0xff) << 24) +
+ ((data[i+1] & 0xff) << 16) +
+ ((data[i+2] & 0xff) << 8) +
+ (data[i+3] & 0xff);
+ checksum += word;
+ }
+ if (length & 3) {
+ word = 0;
+ i = length & ~3;
+ switch (length & 3) {
+ case 3:
+ word |= (data[i+2] & 0xff) << 8;
+ case 2:
+ word |= (data[i+1] & 0xff) << 16;
+ case 1:
+ word |= (data[i ] & 0xff) << 24;
+ break;
+ }
+ checksum += word;
+ }
+ return checksum;
+}
+
+GBool FoFiType1C::parse() {
+ Type1CIndex fdIdx;
+ Type1CIndexVal val;
+ int i;
+
+ parsedOk = gTrue;
+
+ // some tools embed Type 1C fonts with an extra whitespace char at
+ // the beginning
+ if (len > 0 && file[0] != '\x01') {
+ ++file;
+ --len;
+ }
+
+ // find the indexes
+ getIndex(getU8(2, &parsedOk), &nameIdx, &parsedOk);
+ getIndex(nameIdx.endPos, &topDictIdx, &parsedOk);
+ getIndex(topDictIdx.endPos, &stringIdx, &parsedOk);
+ getIndex(stringIdx.endPos, &gsubrIdx, &parsedOk);
+ if (!parsedOk) {
+ return gFalse;
+ }
+ gsubrBias = (gsubrIdx.len < 1240) ? 107
+ : (gsubrIdx.len < 33900) ? 1131 : 32768;
+
+ // read the first font name
+ getIndexVal(&nameIdx, 0, &val, &parsedOk);
+ if (!parsedOk) {
+ return gFalse;
+ }
+ name = new GString((char *)&file[val.pos], val.len);
+
+ // read the top dict for the first font
+ readTopDict();
+
+ // for CID fonts: read the FDArray dicts and private dicts
+ if (topDict.firstOp == 0x0c1e) {
+ if (topDict.fdArrayOffset == 0) {
+ nFDs = 1;
+ privateDicts = (Type1CPrivateDict *)gmalloc(sizeof(Type1CPrivateDict));
+ readPrivateDict(0, 0, &privateDicts[0]);
+ } else {
+ getIndex(topDict.fdArrayOffset, &fdIdx, &parsedOk);
+ if (!parsedOk) {
+ return gFalse;
+ }
+ nFDs = fdIdx.len;
+ privateDicts = (Type1CPrivateDict *)
+ gmallocn(nFDs, sizeof(Type1CPrivateDict));
+ for (i = 0; i < nFDs; ++i) {
+ getIndexVal(&fdIdx, i, &val, &parsedOk);
+ if (!parsedOk) {
+ return gFalse;
+ }
+ readFD(val.pos, val.len, &privateDicts[i]);
+ }
+ }
+
+ // for 8-bit fonts: read the private dict
+ } else {
+ nFDs = 1;
+ privateDicts = (Type1CPrivateDict *)gmalloc(sizeof(Type1CPrivateDict));
+ readPrivateDict(topDict.privateOffset, topDict.privateSize,
+ &privateDicts[0]);
+ }
+
+ // check for parse errors in the private dict(s)
+ if (!parsedOk) {
+ return gFalse;
+ }
+
+ // get the charstrings index
+ if (topDict.charStringsOffset <= 0) {
+ parsedOk = gFalse;
+ return gFalse;
+ }
+ getIndex(topDict.charStringsOffset, &charStringsIdx, &parsedOk);
+ if (!parsedOk) {
+ return gFalse;
+ }
+ nGlyphs = charStringsIdx.len;
+
+ // for CID fonts: read the FDSelect table
+ if (topDict.firstOp == 0x0c1e) {
+ readFDSelect();
+ if (!parsedOk) {
+ return gFalse;
+ }
+ }
+
+ // read the charset
+ if (!readCharset()) {
+ parsedOk = gFalse;
+ return gFalse;
+ }
+
+ // for 8-bit fonts: build the encoding
+ if (topDict.firstOp != 0x0c14 && topDict.firstOp != 0x0c1e) {
+ buildEncoding();
+ if (!parsedOk) {
+ return gFalse;
+ }
+ }
+
+ return parsedOk;
+}
+
+void FoFiType1C::readTopDict() {
+ Type1CIndexVal topDictPtr;
+ int pos;
+
+ topDict.firstOp = -1;
+ topDict.versionSID = 0;
+ topDict.noticeSID = 0;
+ topDict.copyrightSID = 0;
+ topDict.fullNameSID = 0;
+ topDict.familyNameSID = 0;
+ topDict.weightSID = 0;
+ topDict.isFixedPitch = 0;
+ topDict.italicAngle = 0;
+ topDict.underlinePosition = -100;
+ topDict.underlineThickness = 50;
+ topDict.paintType = 0;
+ topDict.charstringType = 2;
+ topDict.fontMatrix[0] = 0.001;
+ topDict.fontMatrix[1] = 0;
+ topDict.fontMatrix[2] = 0;
+ topDict.fontMatrix[3] = 0.001;
+ topDict.fontMatrix[4] = 0;
+ topDict.fontMatrix[5] = 0;
+ topDict.hasFontMatrix = gFalse;
+ topDict.uniqueID = 0;
+ topDict.fontBBox[0] = 0;
+ topDict.fontBBox[1] = 0;
+ topDict.fontBBox[2] = 0;
+ topDict.fontBBox[3] = 0;
+ topDict.strokeWidth = 0;
+ topDict.charsetOffset = 0;
+ topDict.encodingOffset = 0;
+ topDict.charStringsOffset = 0;
+ topDict.privateSize = 0;
+ topDict.privateOffset = 0;
+ topDict.registrySID = 0;
+ topDict.orderingSID = 0;
+ topDict.supplement = 0;
+ topDict.fdArrayOffset = 0;
+ topDict.fdSelectOffset = 0;
+
+ getIndexVal(&topDictIdx, 0, &topDictPtr, &parsedOk);
+ pos = topDictPtr.pos;
+ nOps = 0;
+ while (pos < topDictPtr.pos + topDictPtr.len) {
+ pos = getOp(pos, gFalse, &parsedOk);
+ if (!parsedOk) {
+ break;
+ }
+ if (ops[nOps - 1].kind == type1COpOperator) {
+ --nOps; // drop the operator
+ if (topDict.firstOp < 0) {
+ topDict.firstOp = ops[nOps].op;
+ }
+ switch (ops[nOps].op) {
+ case 0x0000: topDict.versionSID = ops[0].toInt(); break;
+ case 0x0001: topDict.noticeSID = ops[0].toInt(); break;
+ case 0x0c00: topDict.copyrightSID = ops[0].toInt(); break;
+ case 0x0002: topDict.fullNameSID = ops[0].toInt(); break;
+ case 0x0003: topDict.familyNameSID = ops[0].toInt(); break;
+ case 0x0004: topDict.weightSID = ops[0].toInt(); break;
+ case 0x0c01: topDict.isFixedPitch = ops[0].toInt(); break;
+ case 0x0c02: topDict.italicAngle = ops[0].toFloat(); break;
+ case 0x0c03: topDict.underlinePosition = ops[0].toFloat(); break;
+ case 0x0c04: topDict.underlineThickness = ops[0].toFloat(); break;
+ case 0x0c05: topDict.paintType = ops[0].toInt(); break;
+ case 0x0c06: topDict.charstringType = ops[0].toInt(); break;
+ case 0x0c07: topDict.fontMatrix[0] = ops[0].toFloat();
+ topDict.fontMatrix[1] = ops[1].toFloat();
+ topDict.fontMatrix[2] = ops[2].toFloat();
+ topDict.fontMatrix[3] = ops[3].toFloat();
+ topDict.fontMatrix[4] = ops[4].toFloat();
+ topDict.fontMatrix[5] = ops[5].toFloat();
+ topDict.hasFontMatrix = gTrue;
+ break;
+ case 0x000d: topDict.uniqueID = ops[0].toInt(); break;
+ case 0x0005: topDict.fontBBox[0] = ops[0].toFloat();
+ topDict.fontBBox[1] = ops[1].toFloat();
+ topDict.fontBBox[2] = ops[2].toFloat();
+ topDict.fontBBox[3] = ops[3].toFloat();
+ break;
+ case 0x0c08: topDict.strokeWidth = ops[0].toFloat(); break;
+ case 0x000f: topDict.charsetOffset = ops[0].toInt(); break;
+ case 0x0010: topDict.encodingOffset = ops[0].toInt(); break;
+ case 0x0011: topDict.charStringsOffset = ops[0].toInt(); break;
+ case 0x0012: topDict.privateSize = ops[0].toInt();
+ topDict.privateOffset = ops[1].toInt();
+ break;
+ case 0x0c1e: topDict.registrySID = ops[0].toInt();
+ topDict.orderingSID = ops[1].toInt();
+ topDict.supplement = ops[2].toInt();
+ break;
+ case 0x0c24: topDict.fdArrayOffset = ops[0].toInt(); break;
+ case 0x0c25: topDict.fdSelectOffset = ops[0].toInt(); break;
+ }
+ nOps = 0;
+ }
+ }
+}
+
+// Read a CID font dict (FD) - this pulls out the private dict
+// pointer, and reads the private dict. It also pulls the FontMatrix
+// (if any) out of the FD.
+void FoFiType1C::readFD(int offset, int length, Type1CPrivateDict *pDict) {
+ int pos, pSize, pOffset;
+ double fontMatrix[6];
+ GBool hasFontMatrix;
+
+ hasFontMatrix = gFalse;
+ fontMatrix[0] = fontMatrix[1] = fontMatrix[2] = 0; // make gcc happy
+ fontMatrix[3] = fontMatrix[4] = fontMatrix[5] = 0;
+ pSize = pOffset = 0;
+ pos = offset;
+ nOps = 0;
+ while (pos < offset + length) {
+ pos = getOp(pos, gFalse, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (ops[nOps - 1].kind == type1COpOperator) {
+ if (ops[nOps - 1].op == 0x0012) {
+ if (nOps < 3) {
+ parsedOk = gFalse;
+ return;
+ }
+ pSize = ops[0].toInt();
+ pOffset = ops[1].toInt();
+ break;
+ } else if (ops[nOps - 1].op == 0x0c07) {
+ fontMatrix[0] = ops[0].toFloat();
+ fontMatrix[1] = ops[1].toFloat();
+ fontMatrix[2] = ops[2].toFloat();
+ fontMatrix[3] = ops[3].toFloat();
+ fontMatrix[4] = ops[4].toFloat();
+ fontMatrix[5] = ops[5].toFloat();
+ hasFontMatrix = gTrue;
+ }
+ nOps = 0;
+ }
+ }
+ readPrivateDict(pOffset, pSize, pDict);
+ if (hasFontMatrix) {
+ pDict->fontMatrix[0] = fontMatrix[0];
+ pDict->fontMatrix[1] = fontMatrix[1];
+ pDict->fontMatrix[2] = fontMatrix[2];
+ pDict->fontMatrix[3] = fontMatrix[3];
+ pDict->fontMatrix[4] = fontMatrix[4];
+ pDict->fontMatrix[5] = fontMatrix[5];
+ pDict->hasFontMatrix = gTrue;
+ }
+}
+
+void FoFiType1C::readPrivateDict(int offset, int length,
+ Type1CPrivateDict *pDict) {
+ int pos;
+
+ pDict->hasFontMatrix = gFalse;
+ pDict->nBlueValues = 0;
+ pDict->nOtherBlues = 0;
+ pDict->nFamilyBlues = 0;
+ pDict->nFamilyOtherBlues = 0;
+ pDict->blueScale = 0.039625;
+ pDict->blueShift = 7;
+ pDict->blueFuzz = 1;
+ pDict->hasStdHW = gFalse;
+ pDict->hasStdVW = gFalse;
+ pDict->nStemSnapH = 0;
+ pDict->nStemSnapV = 0;
+ pDict->hasForceBold = gFalse;
+ pDict->forceBoldThreshold = 0;
+ pDict->languageGroup = 0;
+ pDict->expansionFactor = 0.06;
+ pDict->initialRandomSeed = 0;
+ pDict->subrsOffset = 0;
+ pDict->defaultWidthX = 0;
+ pDict->defaultWidthXInt = gTrue;
+ pDict->nominalWidthX = 0;
+ pDict->nominalWidthXInt = gTrue;
+
+ // no dictionary
+ if (offset == 0 || length == 0) {
+ return;
+ }
+
+ pos = offset;
+ nOps = 0;
+ while (pos < offset + length) {
+ pos = getOp(pos, gFalse, &parsedOk);
+ if (!parsedOk) {
+ break;
+ }
+ if (ops[nOps - 1].kind == type1COpOperator) {
+ --nOps; // drop the operator
+ switch (ops[nOps].op) {
+ case 0x0006:
+ pDict->nBlueValues = getDeltaIntArray(pDict->blueValues,
+ type1CMaxBlueValues);
+ break;
+ case 0x0007:
+ pDict->nOtherBlues = getDeltaIntArray(pDict->otherBlues,
+ type1CMaxOtherBlues);
+ break;
+ case 0x0008:
+ pDict->nFamilyBlues = getDeltaIntArray(pDict->familyBlues,
+ type1CMaxBlueValues);
+ break;
+ case 0x0009:
+ pDict->nFamilyOtherBlues = getDeltaIntArray(pDict->familyOtherBlues,
+ type1CMaxOtherBlues);
+ break;
+ case 0x0c09:
+ pDict->blueScale = ops[0].toFloat();
+ break;
+ case 0x0c0a:
+ pDict->blueShift = ops[0].toInt();
+ break;
+ case 0x0c0b:
+ pDict->blueFuzz = ops[0].toInt();
+ break;
+ case 0x000a:
+ pDict->stdHW = ops[0].toFloat();
+ pDict->hasStdHW = gTrue;
+ break;
+ case 0x000b:
+ pDict->stdVW = ops[0].toFloat();
+ pDict->hasStdVW = gTrue;
+ break;
+ case 0x0c0c:
+ pDict->nStemSnapH = getDeltaFPArray(pDict->stemSnapH,
+ type1CMaxStemSnap);
+ break;
+ case 0x0c0d:
+ pDict->nStemSnapV = getDeltaFPArray(pDict->stemSnapV,
+ type1CMaxStemSnap);
+ break;
+ case 0x0c0e:
+ pDict->forceBold = !ops[0].isZero();
+ pDict->hasForceBold = gTrue;
+ break;
+ case 0x0c0f:
+ pDict->forceBoldThreshold = ops[0].toFloat();
+ break;
+ case 0x0c11:
+ pDict->languageGroup = ops[0].toInt();
+ break;
+ case 0x0c12:
+ pDict->expansionFactor = ops[0].toFloat();
+ break;
+ case 0x0c13:
+ pDict->initialRandomSeed = ops[0].toInt();
+ break;
+ case 0x0013:
+ pDict->subrsOffset = offset + ops[0].toInt();
+ break;
+ case 0x0014:
+ pDict->defaultWidthX = ops[0].toFloat();
+ pDict->defaultWidthXInt = ops[0].kind == type1COpInteger;
+ break;
+ case 0x0015:
+ pDict->nominalWidthX = ops[0].toFloat();
+ pDict->nominalWidthXInt = ops[0].kind == type1COpInteger;
+ break;
+ }
+ nOps = 0;
+ }
+ }
+}
+
+void FoFiType1C::readFDSelect() {
+ int fdSelectFmt, pos, nRanges, gid0, gid1, fd, i, j;
+
+ fdSelect = (Guchar *)gmalloc(nGlyphs);
+ for (i = 0; i < nGlyphs; ++i) {
+ fdSelect[i] = 0;
+ }
+ if (topDict.fdSelectOffset != 0) {
+ pos = topDict.fdSelectOffset;
+ fdSelectFmt = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (fdSelectFmt == 0) {
+ if (!checkRegion(pos, nGlyphs)) {
+ parsedOk = gFalse;
+ return;
+ }
+ for (gid0 = 0; gid0 < nGlyphs; ++gid0) {
+ if (file[pos + gid0] >= nFDs) {
+ //~ error(-1, "Bad FDSelect table in CID font");
+ parsedOk = gFalse;
+ return;
+ }
+ fdSelect[gid0] = file[pos + gid0];
+ }
+ } else if (fdSelectFmt == 3) {
+ nRanges = getU16BE(pos, &parsedOk);
+ pos += 2;
+ gid0 = getU16BE(pos, &parsedOk);
+ pos += 2;
+ for (i = 1; i <= nRanges; ++i) {
+ fd = getU8(pos++, &parsedOk);
+ gid1 = getU16BE(pos, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ pos += 2;
+ if (gid0 > gid1 || gid1 > nGlyphs || fd >= nFDs) {
+ //~ error(-1, "Bad FDSelect table in CID font");
+ parsedOk = gFalse;
+ return;
+ }
+ for (j = gid0; j < gid1; ++j) {
+ fdSelect[j] = (Guchar)fd;
+ }
+ gid0 = gid1;
+ }
+ } else {
+ //~ error(-1, "Unknown FDSelect table format in CID font");
+ }
+ }
+}
+
+void FoFiType1C::buildEncoding() {
+ char buf[256];
+ int nCodes, nRanges, encFormat;
+ int pos, c, sid, nLeft, nSups, i, j;
+
+ if (topDict.encodingOffset == 0) {
+ encoding = (char **)fofiType1StandardEncoding;
+
+ } else if (topDict.encodingOffset == 1) {
+ encoding = (char **)fofiType1ExpertEncoding;
+
+ } else {
+ encoding = (char **)gmallocn(256, sizeof(char *));
+ for (i = 0; i < 256; ++i) {
+ encoding[i] = NULL;
+ }
+ pos = topDict.encodingOffset;
+ encFormat = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if ((encFormat & 0x7f) == 0) {
+ nCodes = 1 + getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (nCodes > nGlyphs) {
+ nCodes = nGlyphs;
+ }
+ for (i = 1; i < nCodes; ++i) {
+ c = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (encoding[c]) {
+ gfree(encoding[c]);
+ }
+ encoding[c] = copyString(getString(charset[i], buf, &parsedOk));
+ }
+ } else if ((encFormat & 0x7f) == 1) {
+ nRanges = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ nCodes = 1;
+ for (i = 0; i < nRanges; ++i) {
+ c = getU8(pos++, &parsedOk);
+ nLeft = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) {
+ if (c < 256) {
+ if (encoding[c]) {
+ gfree(encoding[c]);
+ }
+ encoding[c] = copyString(getString(charset[nCodes], buf,
+ &parsedOk));
+ }
+ ++nCodes;
+ ++c;
+ }
+ }
+ }
+ if (encFormat & 0x80) {
+ nSups = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ for (i = 0; i < nSups; ++i) {
+ c = getU8(pos++, &parsedOk);;
+ if (!parsedOk) {
+ return;;
+ }
+ sid = getU16BE(pos, &parsedOk);
+ pos += 2;
+ if (!parsedOk) {
+ return;
+ }
+ if (encoding[c]) {
+ gfree(encoding[c]);
+ }
+ encoding[c] = copyString(getString(sid, buf, &parsedOk));
+ }
+ }
+ }
+}
+
+GBool FoFiType1C::readCharset() {
+ int charsetFormat, c, pos;
+ int nLeft, i, j;
+
+ if (topDict.charsetOffset == 0) {
+ charset = fofiType1CISOAdobeCharset;
+ if (nGlyphs > 229) {
+ nGlyphs = 229;
+ }
+ } else if (topDict.charsetOffset == 1) {
+ charset = fofiType1CExpertCharset;
+ if (nGlyphs > 166) {
+ nGlyphs = 166;
+ }
+ } else if (topDict.charsetOffset == 2) {
+ charset = fofiType1CExpertSubsetCharset;
+ if (nGlyphs > 87) {
+ nGlyphs = 87;
+ }
+ } else {
+ charset = (Gushort *)gmallocn(nGlyphs, sizeof(Gushort));
+ for (i = 0; i < nGlyphs; ++i) {
+ charset[i] = 0;
+ }
+ pos = topDict.charsetOffset;
+ charsetFormat = getU8(pos++, &parsedOk);
+ if (charsetFormat == 0) {
+ for (i = 1; i < nGlyphs; ++i) {
+ charset[i] = (Gushort)getU16BE(pos, &parsedOk);
+ pos += 2;
+ if (!parsedOk) {
+ break;
+ }
+ }
+ } else if (charsetFormat == 1) {
+ i = 1;
+ while (i < nGlyphs) {
+ c = getU16BE(pos, &parsedOk);
+ pos += 2;
+ nLeft = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ break;
+ }
+ for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
+ charset[i++] = (Gushort)c++;
+ }
+ }
+ } else if (charsetFormat == 2) {
+ i = 1;
+ while (i < nGlyphs) {
+ c = getU16BE(pos, &parsedOk);
+ pos += 2;
+ nLeft = getU16BE(pos, &parsedOk);
+ pos += 2;
+ if (!parsedOk) {
+ break;
+ }
+ for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
+ charset[i++] = (Gushort)c++;
+ }
+ }
+ }
+ if (!parsedOk) {
+ gfree(charset);
+ charset = NULL;
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
+int FoFiType1C::getOp(int pos, GBool charstring, GBool *ok) {
+ static char nybChars[16] = "0123456789.ee -";
+ Type1COp op;
+ char buf[65];
+ int b0, b1, nyb0, nyb1, x, i;
+
+ b0 = getU8(pos++, ok);
+
+ if (b0 == 28) {
+ x = getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ if (x & 0x8000) {
+ x |= ~0xffff;
+ }
+ op.kind = type1COpInteger;
+ op.intgr = x;
+
+ } else if (!charstring && b0 == 29) {
+ x = getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ if (x & 0x80000000) {
+ x |= ~0xffffffff;
+ }
+ op.kind = type1COpInteger;
+ op.intgr = x;
+
+ } else if (!charstring && b0 == 30) {
+ i = 0;
+ do {
+ b1 = getU8(pos++, ok);
+ nyb0 = b1 >> 4;
+ nyb1 = b1 & 0x0f;
+ if (nyb0 == 0xf) {
+ break;
+ }
+ buf[i++] = nybChars[nyb0];
+ if (i == 64) {
+ break;
+ }
+ if (nyb0 == 0xc) {
+ buf[i++] = '-';
+ }
+ if (i == 64) {
+ break;
+ }
+ if (nyb1 == 0xf) {
+ break;
+ }
+ buf[i++] = nybChars[nyb1];
+ if (i == 64) {
+ break;
+ }
+ if (nyb1 == 0xc) {
+ buf[i++] = '-';
+ }
+ } while (i < 64);
+ buf[i] = '\0';
+ op.kind = type1COpFloat;
+ op.flt = atof(buf);
+
+ } else if (b0 >= 32 && b0 <= 246) {
+ op.kind = type1COpInteger;
+ op.intgr = b0 - 139;
+
+ } else if (b0 >= 247 && b0 <= 250) {
+ op.kind = type1COpInteger;
+ op.intgr = ((b0 - 247) << 8) + getU8(pos++, ok) + 108;
+
+ } else if (b0 >= 251 && b0 <= 254) {
+ op.kind = type1COpInteger;
+ op.intgr = -((b0 - 251) << 8) - getU8(pos++, ok) - 108;
+
+ } else if (charstring && b0 == 255) {
+ x = getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ if (x & 0x80000000) {
+ x |= ~0xffffffff;
+ }
+ op.kind = type1COpFloat;
+ op.flt = (double)x / 65536.0;
+
+ } else if (b0 == 12) {
+ op.kind = type1COpOperator;
+ op.op = 0x0c00 + getU8(pos++, ok);
+
+ } else {
+ op.kind = type1COpOperator;
+ op.op = b0;
+ }
+
+ if (nOps < 49) {
+ ops[nOps++] = op;
+ }
+
+ return pos;
+}
+
+// Convert the delta-encoded ops array to an array of ints.
+int FoFiType1C::getDeltaIntArray(int *arr, int maxLen) {
+ int x;
+ int n, i;
+
+ if ((n = nOps) > maxLen) {
+ n = maxLen;
+ }
+ x = 0;
+ for (i = 0; i < n; ++i) {
+ x += ops[i].toInt();
+ arr[i] = x;
+ }
+ return n;
+}
+
+// Convert the delta-encoded ops array to an array of doubles.
+int FoFiType1C::getDeltaFPArray(double *arr, int maxLen) {
+ double x;
+ int n, i;
+
+ if ((n = nOps) > maxLen) {
+ n = maxLen;
+ }
+ x = 0;
+ for (i = 0; i < n; ++i) {
+ x += ops[i].toFloat();
+ arr[i] = x;
+ }
+ return n;
+}
+
+void FoFiType1C::getIndex(int pos, Type1CIndex *idx, GBool *ok) {
+ idx->pos = pos;
+ idx->len = getU16BE(pos, ok);
+ if (idx->len == 0) {
+ // empty indexes are legal and contain just the length field
+ idx->offSize = 0;
+ idx->startPos = idx->endPos = pos + 2;
+ } else {
+ idx->offSize = getU8(pos + 2, ok);
+ if (idx->offSize < 1 || idx->offSize > 4) {
+ *ok = gFalse;
+ }
+ idx->startPos = pos + 3 + (idx->len + 1) * idx->offSize - 1;
+ if (idx->startPos < 0 || idx->startPos >= len) {
+ *ok = gFalse;
+ }
+ idx->endPos = idx->startPos + getUVarBE(pos + 3 + idx->len * idx->offSize,
+ idx->offSize, ok);
+ if (idx->endPos < idx->startPos || idx->endPos > len) {
+ *ok = gFalse;
+ }
+ }
+}
+
+void FoFiType1C::getIndexVal(Type1CIndex *idx, int i,
+ Type1CIndexVal *val, GBool *ok) {
+ int pos0, pos1;
+
+ if (i < 0 || i >= idx->len) {
+ val->pos = 0;
+ val->len = 0;
+ *ok = gFalse;
+ return;
+ }
+ pos0 = idx->startPos + getUVarBE(idx->pos + 3 + i * idx->offSize,
+ idx->offSize, ok);
+ pos1 = idx->startPos + getUVarBE(idx->pos + 3 + (i + 1) * idx->offSize,
+ idx->offSize, ok);
+ if (pos0 < idx->startPos || pos0 > idx->endPos ||
+ pos1 <= idx->startPos || pos1 > idx->endPos ||
+ pos1 < pos0) {
+ *ok = gFalse;
+ }
+ val->pos = pos0;
+ val->len = pos1 - pos0;
+}
+
+char *FoFiType1C::getString(int sid, char *buf, GBool *ok) {
+ Type1CIndexVal val;
+ int n;
+
+ if (sid < 0) {
+ buf[0] = '\0';
+ } else if (sid < 391) {
+ strcpy(buf, fofiType1CStdStrings[sid]);
+ } else {
+ sid -= 391;
+ getIndexVal(&stringIdx, sid, &val, ok);
+ if (*ok) {
+ if ((n = val.len) > 255) {
+ n = 255;
+ }
+ strncpy(buf, (char *)&file[val.pos], n);
+ buf[n] = '\0';
+ } else {
+ buf[0] = '\0';
+ }
+ }
+ return buf;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h
new file mode 100644
index 00000000000..15a823cffe6
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/fofi/FoFiType1C.h
@@ -0,0 +1,281 @@
+//========================================================================
+//
+// FoFiType1C.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFITYPE1C_H
+#define FOFITYPE1C_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "FoFiBase.h"
+
+class GHash;
+class GString;
+
+//------------------------------------------------------------------------
+
+struct Type1CIndex {
+ int pos; // absolute position in file
+ int len; // length (number of entries)
+ int offSize; // offset size
+ int startPos; // position of start of index data - 1
+ int endPos; // position one byte past end of the index
+};
+
+struct Type1CIndexVal {
+ int pos; // absolute position in file
+ int len; // length, in bytes
+};
+
+struct Type1CTopDict {
+ int firstOp;
+
+ int versionSID;
+ int noticeSID;
+ int copyrightSID;
+ int fullNameSID;
+ int familyNameSID;
+ int weightSID;
+ int isFixedPitch;
+ double italicAngle;
+ double underlinePosition;
+ double underlineThickness;
+ int paintType;
+ int charstringType;
+ double fontMatrix[6];
+ GBool hasFontMatrix; // CID fonts are allowed to put their
+ // FontMatrix in the FD instead of the
+ // top dict
+ int uniqueID;
+ double fontBBox[4];
+ double strokeWidth;
+ int charsetOffset;
+ int encodingOffset;
+ int charStringsOffset;
+ int privateSize;
+ int privateOffset;
+
+ // CIDFont entries
+ int registrySID;
+ int orderingSID;
+ int supplement;
+ int fdArrayOffset;
+ int fdSelectOffset;
+};
+
+#define type1CMaxBlueValues 14
+#define type1CMaxOtherBlues 10
+#define type1CMaxStemSnap 12
+
+struct Type1CPrivateDict {
+ double fontMatrix[6];
+ GBool hasFontMatrix;
+ int blueValues[type1CMaxBlueValues];
+ int nBlueValues;
+ int otherBlues[type1CMaxOtherBlues];
+ int nOtherBlues;
+ int familyBlues[type1CMaxBlueValues];
+ int nFamilyBlues;
+ int familyOtherBlues[type1CMaxOtherBlues];
+ int nFamilyOtherBlues;
+ double blueScale;
+ int blueShift;
+ int blueFuzz;
+ double stdHW;
+ GBool hasStdHW;
+ double stdVW;
+ GBool hasStdVW;
+ double stemSnapH[type1CMaxStemSnap];
+ int nStemSnapH;
+ double stemSnapV[type1CMaxStemSnap];
+ int nStemSnapV;
+ GBool forceBold;
+ GBool hasForceBold;
+ double forceBoldThreshold;
+ int languageGroup;
+ double expansionFactor;
+ int initialRandomSeed;
+ int subrsOffset;
+ double defaultWidthX;
+ GBool defaultWidthXInt;
+ double nominalWidthX;
+ GBool nominalWidthXInt;
+};
+
+// operand kind
+enum Type1COpKind {
+ type1COpOperator,
+ type1COpInteger,
+ type1COpFloat,
+ type1COpRational
+};
+
+// operand
+struct Type1COp {
+ Type1COpKind kind;
+ union {
+ int op; // type1COpOperator
+ int intgr; // type1COpInteger
+ double flt; // type1COpFloat
+ struct {
+ int num, den; // type1COpRational
+ } rat;
+ };
+ GBool isZero();
+ GBool isNegative();
+ int toInt();
+ double toFloat();
+};
+
+struct Type1CEexecBuf {
+ FoFiOutputFunc outputFunc;
+ void *outputStream;
+ GBool ascii; // ASCII encoding?
+ Gushort r1; // eexec encryption key
+ int line; // number of eexec chars left on current line
+};
+
+//------------------------------------------------------------------------
+// FoFiType1C
+//------------------------------------------------------------------------
+
+class FoFiType1C: public FoFiBase {
+public:
+
+ // Create a FoFiType1C object from a memory buffer.
+ static FoFiType1C *make(char *fileA, int lenA);
+
+ // Create a FoFiType1C object from a file on disk.
+ static FoFiType1C *load(char *fileName);
+
+ virtual ~FoFiType1C();
+
+ // Return the font name.
+ char *getName();
+
+ // Return the encoding, as an array of 256 names (any of which may
+ // be NULL). This is only useful with 8-bit fonts.
+ char **getEncoding();
+
+ // Get the glyph names.
+ int getNumGlyphs() { return nGlyphs; }
+ GString *getGlyphName(int gid);
+
+ // Returns a hash mapping glyph names to GIDs. This is only useful
+ // with 8-bit fonts.
+ GHash *getNameToGIDMap();
+
+ // Return the mapping from CIDs to GIDs, and return the number of
+ // CIDs in *<nCIDs>. This is only useful for CID fonts.
+ int *getCIDToGIDMap(int *nCIDs);
+
+ // Return the font matrix as an array of six numbers.
+ void getFontMatrix(double *mat);
+
+ // Convert to a Type 1 font, suitable for embedding in a PostScript
+ // file. This is only useful with 8-bit fonts. If <newEncoding> is
+ // not NULL, it will be used in place of the encoding in the Type 1C
+ // font. If <ascii> is true the eexec section will be hex-encoded,
+ // otherwise it will be left as binary data. If <psName> is non-NULL,
+ // it will be used as the PostScript font name.
+ void convertToType1(char *psName, const char **newEncoding, GBool ascii,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Convert to a Type 0 CIDFont, suitable for embedding in a
+ // PostScript file. <psName> will be used as the PostScript font
+ // name. There are three cases for the CID-to-GID mapping:
+ // (1) if <codeMap> is non-NULL, then it is the CID-to-GID mapping
+ // (2) if <codeMap> is NULL and this is a CID CFF font, then the
+ // font's internal CID-to-GID mapping is used
+ // (3) is <codeMap> is NULL and this is an 8-bit CFF font, then
+ // the identity CID-to-GID mapping is used
+ void convertToCIDType0(char *psName, int *codeMap, int nCodes,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Convert to a Type 0 (but non-CID) composite font, suitable for
+ // embedding in a PostScript file. <psName> will be used as the
+ // PostScript font name. There are three cases for the CID-to-GID
+ // mapping:
+ // (1) if <codeMap> is non-NULL, then it is the CID-to-GID mapping
+ // (2) if <codeMap> is NULL and this is a CID CFF font, then the
+ // font's internal CID-to-GID mapping is used
+ // (3) is <codeMap> is NULL and this is an 8-bit CFF font, then
+ // the identity CID-to-GID mapping is used
+ void convertToType0(char *psName, int *codeMap, int nCodes,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Write an OpenType file, encapsulating the CFF font. <widths>
+ // provides the glyph widths (in design units) for <nWidths> glyphs.
+ // The cmap table must be supplied by the caller.
+ void convertToOpenType(FoFiOutputFunc outputFunc, void *outputStream,
+ int nWidths, Gushort *widths,
+ Guchar *cmapTable, int cmapTableLen);
+
+private:
+
+ FoFiType1C(char *fileA, int lenA, GBool freeFileDataA);
+ void eexecCvtGlyph(Type1CEexecBuf *eb, const char *glyphName,
+ int offset, int nBytes,
+ Type1CIndex *subrIdx,
+ Type1CPrivateDict *pDict);
+ void cvtGlyph(int offset, int nBytes, GString *charBuf,
+ Type1CIndex *subrIdx, Type1CPrivateDict *pDict,
+ GBool top, int recursion);
+ void cvtGlyphWidth(GBool useOp, GString *charBuf,
+ Type1CPrivateDict *pDict);
+ void cvtNum(Type1COp op, GString *charBuf);
+ void eexecWrite(Type1CEexecBuf *eb, const char *s);
+ void eexecWriteCharstring(Type1CEexecBuf *eb, Guchar *s, int n);
+ void writePSString(char *s, FoFiOutputFunc outputFunc, void *outputStream);
+ Guint computeOpenTypeTableChecksum(Guchar *data, int length);
+ GBool parse();
+ void readTopDict();
+ void readFD(int offset, int length, Type1CPrivateDict *pDict);
+ void readPrivateDict(int offset, int length, Type1CPrivateDict *pDict);
+ void readFDSelect();
+ void buildEncoding();
+ GBool readCharset();
+ int getOp(int pos, GBool charstring, GBool *ok);
+ int getDeltaIntArray(int *arr, int maxLen);
+ int getDeltaFPArray(double *arr, int maxLen);
+ void getIndex(int pos, Type1CIndex *idx, GBool *ok);
+ void getIndexVal(Type1CIndex *idx, int i, Type1CIndexVal *val, GBool *ok);
+ char *getString(int sid, char *buf, GBool *ok);
+
+ GString *name;
+ char **encoding;
+
+ Type1CIndex nameIdx;
+ Type1CIndex topDictIdx;
+ Type1CIndex stringIdx;
+ Type1CIndex gsubrIdx;
+ Type1CIndex charStringsIdx;
+
+ Type1CTopDict topDict;
+ Type1CPrivateDict *privateDicts;
+
+ int nGlyphs;
+ int nFDs;
+ Guchar *fdSelect;
+ Gushort *charset;
+ int gsubrBias;
+
+ GBool parsedOk;
+
+ Type1COp ops[49]; // operands and operator
+ int nOps; // number of operands
+ int nHints; // number of hints for the current glyph
+ GBool firstOp; // true if we haven't hit the first op yet
+ GBool openPath; // true if there is an unclosed path
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt b/Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt
new file mode 100644
index 00000000000..0a1c56b0ebd
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/CMakeLists.txt
@@ -0,0 +1,28 @@
+#========================================================================
+#
+# goo/CMakeLists.txt
+#
+# CMake script for the goo library.
+#
+# Copyright 2015 Glyph & Cog, LLC
+#
+#========================================================================
+
+include_directories("${PROJECT_SOURCE_DIR}")
+include_directories("${PROJECT_BINARY_DIR}")
+
+add_library(goo_objs OBJECT
+ FixedPoint.cc
+ GHash.cc
+ GList.cc
+ GString.cc
+ gfile.cc
+ gmem.cc
+ gmempp.cc
+ parseargs.c
+ Trace.cc
+)
+
+add_library(goo
+ $<TARGET_OBJECTS:goo_objs>
+)
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.cc b/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.cc
new file mode 100644
index 00000000000..503a06aa9c7
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.cc
@@ -0,0 +1,134 @@
+//========================================================================
+//
+// FixedPoint.cc
+//
+// Fixed point type, with C++ operators.
+//
+// Copyright 2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#if USE_FIXEDPOINT
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmempp.h"
+#include "FixedPoint.h"
+
+#define ln2 ((FixedPoint)0.69314718)
+
+FixedPoint FixedPoint::sqrt(FixedPoint x) {
+ FixedPoint y0, y1, z;
+
+ if (x.val <= 0) {
+ y1.val = 0;
+ } else {
+ y1.val = x.val == 1 ? 2 : x.val >> 1;
+ do {
+ y0.val = y1.val;
+ z = x / y0;
+ y1.val = (y0.val + z.val) >> 1;
+ } while (::abs(y0.val - y1.val) > 1);
+ }
+ return y1;
+}
+
+FixedPoint FixedPoint::pow(FixedPoint x, FixedPoint y) {
+ FixedPoint t, t2, lnx0, lnx, z0, z;
+ int d, n, i;
+
+ if (y.val <= 0) {
+ z.val = 0;
+ } else {
+ // y * ln(x)
+ t = (x - 1) / (x + 1);
+ t2 = t * t;
+ d = 1;
+ lnx = 0;
+ do {
+ lnx0 = lnx;
+ lnx += t / d;
+ t *= t2;
+ d += 2;
+ } while (::abs(lnx.val - lnx0.val) > 2);
+ lnx.val <<= 1;
+ t = y * lnx;
+ // exp(y * ln(x))
+ n = floor(t / ln2);
+ t -= ln2 * n;
+ t2 = t;
+ d = 1;
+ i = 1;
+ z = 1;
+ do {
+ z0 = z;
+ z += t2 / d;
+ t2 *= t;
+ ++i;
+ d *= i;
+ } while (::abs(z.val - z0.val) > 2 && d < (1 << fixptShift));
+ if (n >= 0) {
+ z.val <<= n;
+ } else if (n < 0) {
+ z.val >>= -n;
+ }
+ }
+ return z;
+}
+
+int FixedPoint::mul(int x, int y) {
+ FixPtInt64 z;
+
+ z = ((FixPtInt64)x * y) >> fixptShift;
+ if (z > 0x7fffffffLL) {
+ return 0x7fffffff;
+ } else if (z < -0x80000000LL) {
+ return 0x80000000;
+ } else {
+ return (int)z;
+ }
+}
+
+int FixedPoint::div(int x, int y) {
+ FixPtInt64 z;
+
+ z = ((FixPtInt64)x << fixptShift) / y;
+ if (z > 0x7fffffffLL) {
+ return 0x7fffffff;
+ } else if (z < -0x80000000LL) {
+ return 0x80000000;
+ } else {
+ return (int)z;
+ }
+}
+
+GBool FixedPoint::divCheck(FixedPoint x, FixedPoint y, FixedPoint *result) {
+ FixPtInt64 z;
+
+ z = ((FixPtInt64)x.val << fixptShift) / y.val;
+ if ((z == 0 && x != 0) ||
+ z >= ((FixPtInt64)1 << 31) || z < -((FixPtInt64)1 << 31)) {
+ return gFalse;
+ }
+ result->val = (int)z;
+ return gTrue;
+}
+
+GBool FixedPoint::checkDet(FixedPoint m11, FixedPoint m12,
+ FixedPoint m21, FixedPoint m22,
+ FixedPoint epsilon) {
+ FixPtInt64 det, e;
+
+ det = (FixPtInt64)m11.val * (FixPtInt64)m22.val
+ - (FixPtInt64)m12.val * (FixPtInt64)m21.val;
+ e = (FixPtInt64)epsilon.val << fixptShift;
+ // NB: this comparison has to be >= not > because epsilon can be
+ // truncated to zero as a fixed point value.
+ return det >= e || det <= -e;
+}
+
+#endif // USE_FIXEDPOINT
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h b/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h
new file mode 100644
index 00000000000..9f0c3240627
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/FixedPoint.h
@@ -0,0 +1,180 @@
+//========================================================================
+//
+// FixedPoint.h
+//
+// Fixed point type, with C++ operators.
+//
+// Copyright 2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FIXEDPOINT_H
+#define FIXEDPOINT_H
+
+#include <aconf.h>
+
+#if USE_FIXEDPOINT
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "gtypes.h"
+
+#define fixptShift 16
+#define fixptMaskL ((1 << fixptShift) - 1)
+#define fixptMaskH (~fixptMaskL)
+
+typedef long long FixPtInt64;
+
+class FixedPoint {
+public:
+
+ FixedPoint() { val = 0; }
+ 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()
+ { return (float) val * ((float)1 / (float)(1 << fixptShift)); }
+ operator double()
+ { return (double) val * (1.0 / (double)(1 << fixptShift)); }
+ operator int()
+ { return val >> fixptShift; }
+
+ int get16Dot16() { return val; }
+
+ FixedPoint operator =(FixedPoint x) { val = x.val; return *this; }
+
+ 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); }
+
+ 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)); }
+
+ static int floor(FixedPoint x) { return x.val >> fixptShift; }
+
+ static int ceil(FixedPoint x)
+ { return (x.val & fixptMaskL) ? ((x.val >> fixptShift) + 1)
+ : (x.val >> fixptShift); }
+
+ static int round(FixedPoint x)
+ { return (x.val + (1 << (fixptShift - 1))) >> fixptShift; }
+
+ // Computes (x+y)/2 avoiding overflow and LSbit accuracy issues.
+ static FixedPoint avg(FixedPoint x, FixedPoint y)
+ { return make((x.val >> 1) + (y.val >> 1) + ((x.val | y.val) & 1)); }
+
+ static FixedPoint sqrt(FixedPoint x);
+
+ static FixedPoint pow(FixedPoint x, FixedPoint y);
+
+ // Compute *result = x/y; return false if there is an underflow or
+ // overflow.
+ static GBool divCheck(FixedPoint x, FixedPoint y, FixedPoint *result);
+
+ // Compute abs(m11*m22 - m12*m21) >= epsilon, handling the case
+ // where the multiplications overflow.
+ static GBool checkDet(FixedPoint m11, FixedPoint m12,
+ FixedPoint m21, FixedPoint m22,
+ FixedPoint epsilon);
+
+private:
+
+ static FixedPoint make(int valA) { FixedPoint x; x.val = valA; return x; }
+
+ static int mul(int x, int y);
+ static int div(int x, int y);
+
+ int val; // fixed point: (n-fixptShift).(fixptShift)
+};
+
+#endif // USE_FIXEDPOINT
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/GHash.cc b/Build/source/libs/xpdf/xpdf-src/goo/GHash.cc
new file mode 100644
index 00000000000..e6b32440fb9
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/GHash.cc
@@ -0,0 +1,385 @@
+//========================================================================
+//
+// GHash.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "GHash.h"
+
+//------------------------------------------------------------------------
+
+struct GHashBucket {
+ GString *key;
+ union {
+ void *p;
+ int i;
+ } val;
+ GHashBucket *next;
+};
+
+struct GHashIter {
+ int h;
+ GHashBucket *p;
+};
+
+//------------------------------------------------------------------------
+
+GHash::GHash(GBool deleteKeysA) {
+ int h;
+
+ deleteKeys = deleteKeysA;
+ size = 7;
+ tab = (GHashBucket **)gmallocn(size, sizeof(GHashBucket *));
+ for (h = 0; h < size; ++h) {
+ tab[h] = NULL;
+ }
+ len = 0;
+}
+
+GHash::~GHash() {
+ GHashBucket *p;
+ int h;
+
+ for (h = 0; h < size; ++h) {
+ while (tab[h]) {
+ p = tab[h];
+ tab[h] = p->next;
+ if (deleteKeys) {
+ delete p->key;
+ }
+ delete p;
+ }
+ }
+ gfree(tab);
+}
+
+void GHash::add(GString *key, void *val) {
+ GHashBucket *p;
+ int h;
+
+ // expand the table if necessary
+ if (len >= size) {
+ expand();
+ }
+
+ // add the new symbol
+ p = new GHashBucket;
+ p->key = key;
+ p->val.p = val;
+ h = hash(key);
+ p->next = tab[h];
+ tab[h] = p;
+ ++len;
+}
+
+void GHash::add(GString *key, int val) {
+ GHashBucket *p;
+ int h;
+
+ // expand the table if necessary
+ if (len >= size) {
+ expand();
+ }
+
+ // add the new symbol
+ p = new GHashBucket;
+ p->key = key;
+ p->val.i = val;
+ h = hash(key);
+ p->next = tab[h];
+ tab[h] = p;
+ ++len;
+}
+
+void GHash::replace(GString *key, void *val) {
+ GHashBucket *p;
+ int h;
+
+ if ((p = find(key, &h))) {
+ p->val.p = val;
+ if (deleteKeys) {
+ delete key;
+ }
+ } else {
+ add(key, val);
+ }
+}
+
+void GHash::replace(GString *key, int val) {
+ GHashBucket *p;
+ int h;
+
+ if ((p = find(key, &h))) {
+ p->val.i = val;
+ if (deleteKeys) {
+ delete key;
+ }
+ } else {
+ add(key, val);
+ }
+}
+
+void *GHash::lookup(GString *key) {
+ GHashBucket *p;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return NULL;
+ }
+ return p->val.p;
+}
+
+int GHash::lookupInt(GString *key) {
+ GHashBucket *p;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return 0;
+ }
+ return p->val.i;
+}
+
+void *GHash::lookup(const char *key) {
+ GHashBucket *p;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return NULL;
+ }
+ return p->val.p;
+}
+
+int GHash::lookupInt(const char *key) {
+ GHashBucket *p;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return 0;
+ }
+ return p->val.i;
+}
+
+void *GHash::remove(GString *key) {
+ GHashBucket *p;
+ GHashBucket **q;
+ void *val;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return NULL;
+ }
+ q = &tab[h];
+ while (*q != p) {
+ q = &((*q)->next);
+ }
+ *q = p->next;
+ if (deleteKeys) {
+ delete p->key;
+ }
+ val = p->val.p;
+ delete p;
+ --len;
+ return val;
+}
+
+int GHash::removeInt(GString *key) {
+ GHashBucket *p;
+ GHashBucket **q;
+ int val;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return 0;
+ }
+ q = &tab[h];
+ while (*q != p) {
+ q = &((*q)->next);
+ }
+ *q = p->next;
+ if (deleteKeys) {
+ delete p->key;
+ }
+ val = p->val.i;
+ delete p;
+ --len;
+ return val;
+}
+
+void *GHash::remove(const char *key) {
+ GHashBucket *p;
+ GHashBucket **q;
+ void *val;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return NULL;
+ }
+ q = &tab[h];
+ while (*q != p) {
+ q = &((*q)->next);
+ }
+ *q = p->next;
+ if (deleteKeys) {
+ delete p->key;
+ }
+ val = p->val.p;
+ delete p;
+ --len;
+ return val;
+}
+
+int GHash::removeInt(const char *key) {
+ GHashBucket *p;
+ GHashBucket **q;
+ int val;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return 0;
+ }
+ q = &tab[h];
+ while (*q != p) {
+ q = &((*q)->next);
+ }
+ *q = p->next;
+ if (deleteKeys) {
+ delete p->key;
+ }
+ val = p->val.i;
+ delete p;
+ --len;
+ return val;
+}
+
+void GHash::startIter(GHashIter **iter) {
+ *iter = new GHashIter;
+ (*iter)->h = -1;
+ (*iter)->p = NULL;
+}
+
+GBool GHash::getNext(GHashIter **iter, GString **key, void **val) {
+ if (!*iter) {
+ return gFalse;
+ }
+ if ((*iter)->p) {
+ (*iter)->p = (*iter)->p->next;
+ }
+ while (!(*iter)->p) {
+ if (++(*iter)->h == size) {
+ delete *iter;
+ *iter = NULL;
+ return gFalse;
+ }
+ (*iter)->p = tab[(*iter)->h];
+ }
+ *key = (*iter)->p->key;
+ *val = (*iter)->p->val.p;
+ return gTrue;
+}
+
+GBool GHash::getNext(GHashIter **iter, GString **key, int *val) {
+ if (!*iter) {
+ return gFalse;
+ }
+ if ((*iter)->p) {
+ (*iter)->p = (*iter)->p->next;
+ }
+ while (!(*iter)->p) {
+ if (++(*iter)->h == size) {
+ delete *iter;
+ *iter = NULL;
+ return gFalse;
+ }
+ (*iter)->p = tab[(*iter)->h];
+ }
+ *key = (*iter)->p->key;
+ *val = (*iter)->p->val.i;
+ return gTrue;
+}
+
+void GHash::killIter(GHashIter **iter) {
+ delete *iter;
+ *iter = NULL;
+}
+
+void GHash::expand() {
+ GHashBucket **oldTab;
+ GHashBucket *p;
+ int oldSize, h, i;
+
+ oldSize = size;
+ oldTab = tab;
+ size = 2*size + 1;
+ tab = (GHashBucket **)gmallocn(size, sizeof(GHashBucket *));
+ for (h = 0; h < size; ++h) {
+ tab[h] = NULL;
+ }
+ for (i = 0; i < oldSize; ++i) {
+ while (oldTab[i]) {
+ p = oldTab[i];
+ oldTab[i] = oldTab[i]->next;
+ h = hash(p->key);
+ p->next = tab[h];
+ tab[h] = p;
+ }
+ }
+ gfree(oldTab);
+}
+
+GHashBucket *GHash::find(GString *key, int *h) {
+ GHashBucket *p;
+
+ *h = hash(key);
+ for (p = tab[*h]; p; p = p->next) {
+ if (!p->key->cmp(key)) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+GHashBucket *GHash::find(const char *key, int *h) {
+ GHashBucket *p;
+
+ *h = hash(key);
+ for (p = tab[*h]; p; p = p->next) {
+ if (!p->key->cmp(key)) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+int GHash::hash(GString *key) {
+ const char *p;
+ unsigned int h;
+ int i;
+
+ h = 0;
+ for (p = key->getCString(), i = 0; i < key->getLength(); ++p, ++i) {
+ h = 17 * h + (int)(*p & 0xff);
+ }
+ return (int)(h % size);
+}
+
+int GHash::hash(const char *key) {
+ const char *p;
+ unsigned int h;
+
+ h = 0;
+ for (p = key; *p; ++p) {
+ h = 17 * h + (int)(*p & 0xff);
+ }
+ return (int)(h % size);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/GHash.h b/Build/source/libs/xpdf/xpdf-src/goo/GHash.h
new file mode 100644
index 00000000000..179753a50b4
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/GHash.h
@@ -0,0 +1,78 @@
+//========================================================================
+//
+// GHash.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GHASH_H
+#define GHASH_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class GString;
+struct GHashBucket;
+struct GHashIter;
+
+//------------------------------------------------------------------------
+
+class GHash {
+public:
+
+ GHash(GBool deleteKeysA = gFalse);
+ ~GHash();
+ void add(GString *key, void *val);
+ void add(GString *key, int val);
+ void replace(GString *key, void *val);
+ void replace(GString *key, int val);
+ void *lookup(GString *key);
+ int lookupInt(GString *key);
+ void *lookup(const char *key);
+ int lookupInt(const char *key);
+ void *remove(GString *key);
+ int removeInt(GString *key);
+ void *remove(const char *key);
+ int removeInt(const char *key);
+ int getLength() { return len; }
+ void startIter(GHashIter **iter);
+ GBool getNext(GHashIter **iter, GString **key, void **val);
+ GBool getNext(GHashIter **iter, GString **key, int *val);
+ void killIter(GHashIter **iter);
+
+private:
+
+ void expand();
+ GHashBucket *find(GString *key, int *h);
+ GHashBucket *find(const char *key, int *h);
+ int hash(GString *key);
+ int hash(const char *key);
+
+ GBool deleteKeys; // set if key strings should be deleted
+ int size; // number of buckets
+ int len; // number of entries
+ GHashBucket **tab;
+};
+
+#define deleteGHash(hash, T) \
+ do { \
+ GHash *_hash = (hash); \
+ { \
+ GHashIter *_iter; \
+ GString *_key; \
+ void *_p; \
+ _hash->startIter(&_iter); \
+ while (_hash->getNext(&_iter, &_key, &_p)) { \
+ delete (T*)_p; \
+ } \
+ delete _hash; \
+ } \
+ } while(0)
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/GList.cc b/Build/source/libs/xpdf/xpdf-src/goo/GList.cc
new file mode 100644
index 00000000000..c194818a743
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/GList.cc
@@ -0,0 +1,123 @@
+//========================================================================
+//
+// GList.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GList.h"
+
+//------------------------------------------------------------------------
+// GList
+//------------------------------------------------------------------------
+
+GList::GList() {
+ size = 8;
+ data = (void **)gmallocn(size, sizeof(void*));
+ length = 0;
+ inc = 0;
+}
+
+GList::GList(int sizeA) {
+ size = sizeA ? sizeA : 8;
+ data = (void **)gmallocn(size, sizeof(void*));
+ length = 0;
+ inc = 0;
+}
+
+GList::~GList() {
+ gfree(data);
+}
+
+GList *GList::copy() {
+ GList *ret;
+
+ ret = new GList(length);
+ ret->length = length;
+ memcpy(ret->data, data, length * sizeof(void *));
+ ret->inc = inc;
+ return ret;
+}
+
+void GList::append(void *p) {
+ if (length >= size) {
+ expand();
+ }
+ data[length++] = p;
+}
+
+void GList::append(GList *list) {
+ int i;
+
+ while (length + list->length > size) {
+ expand();
+ }
+ for (i = 0; i < list->length; ++i) {
+ data[length++] = list->data[i];
+ }
+}
+
+void GList::insert(int i, void *p) {
+ if (length >= size) {
+ expand();
+ }
+ if (i < 0) {
+ i = 0;
+ }
+ if (i < length) {
+ memmove(data+i+1, data+i, (length - i) * sizeof(void *));
+ }
+ data[i] = p;
+ ++length;
+}
+
+void *GList::del(int i) {
+ void *p;
+
+ p = data[i];
+ if (i < length - 1) {
+ memmove(data+i, data+i+1, (length - i - 1) * sizeof(void *));
+ }
+ --length;
+ if (size - length >= ((inc > 0) ? inc : size/2)) {
+ shrink();
+ }
+ return p;
+}
+
+void GList::sort(int (*cmp)(const void *obj1, const void *obj2)) {
+ qsort(data, length, sizeof(void *), cmp);
+}
+
+void GList::reverse() {
+ void *t;
+ int n, i;
+
+ n = length / 2;
+ for (i = 0; i < n; ++i) {
+ t = data[i];
+ data[i] = data[length - 1 - i];
+ data[length - 1 - i] = t;
+ }
+}
+
+void GList::expand() {
+ size += (inc > 0) ? inc : size;
+ data = (void **)greallocn(data, size, sizeof(void*));
+}
+
+void GList::shrink() {
+ size -= (inc > 0) ? inc : size/2;
+ data = (void **)greallocn(data, size, sizeof(void*));
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/GList.h b/Build/source/libs/xpdf/xpdf-src/goo/GList.h
new file mode 100644
index 00000000000..005043726a5
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/GList.h
@@ -0,0 +1,106 @@
+//========================================================================
+//
+// GList.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GLIST_H
+#define GLIST_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+// GList
+//------------------------------------------------------------------------
+
+class GList {
+public:
+
+ // Create an empty list.
+ GList();
+
+ // Create an empty list with space for <size1> elements.
+ GList(int sizeA);
+
+ // Destructor - does not free pointed-to objects.
+ ~GList();
+
+ //----- general
+
+ // Get the number of elements.
+ int getLength() { return length; }
+
+ // Returns a (shallow) copy of this list.
+ GList *copy();
+
+ //----- ordered list support
+
+ // Return the <i>th element.
+ // Assumes 0 <= i < length.
+ void *get(int i) { return data[i]; }
+
+ // Replace the <i>th element.
+ // Assumes 0 <= i < length.
+ void put(int i, void *p) { data[i] = p; }
+
+ // Append an element to the end of the list.
+ void append(void *p);
+
+ // Append another list to the end of this one.
+ void append(GList *list);
+
+ // Insert an element at index <i>.
+ // Assumes 0 <= i <= length.
+ void insert(int i, void *p);
+
+ // Deletes and returns the element at index <i>.
+ // Assumes 0 <= i < length.
+ void *del(int i);
+
+ // Sort the list accoring to the given comparison function.
+ // NB: this sorts an array of pointers, so the pointer args need to
+ // be double-dereferenced.
+ void sort(int (*cmp)(const void *ptr1, const void *ptr2));
+
+ // Reverse the list.
+ void reverse();
+
+ //----- control
+
+ // Set allocation increment to <inc>. If inc > 0, that many
+ // elements will be allocated every time the list is expanded.
+ // If inc <= 0, the list will be doubled in size.
+ void setAllocIncr(int incA) { inc = incA; }
+
+private:
+
+ void expand();
+ void shrink();
+
+ void **data; // the list elements
+ int size; // size of data array
+ int length; // number of elements on list
+ int inc; // allocation increment
+};
+
+#define deleteGList(list, T) \
+ do { \
+ GList *_list = (list); \
+ { \
+ int _i; \
+ for (_i = 0; _i < _list->getLength(); ++_i) { \
+ delete (T*)_list->get(_i); \
+ } \
+ delete _list; \
+ } \
+ } while (0)
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/GMutex.h b/Build/source/libs/xpdf/xpdf-src/goo/GMutex.h
new file mode 100644
index 00000000000..aef4aec4859
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/GMutex.h
@@ -0,0 +1,101 @@
+//========================================================================
+//
+// GMutex.h
+//
+// Portable mutex macros.
+// Portable atomic increment/decrement.
+//
+// Copyright 2002-2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GMUTEX_H
+#define GMUTEX_H
+
+#include <aconf.h>
+#ifdef _WIN32
+# include <windows.h>
+# include <intrin.h>
+#else
+# include <pthread.h>
+#endif
+
+//------------------------------------------------------------------------
+// GMutex
+//------------------------------------------------------------------------
+
+// Usage:
+//
+// GMutex m;
+// gInitMutex(&m);
+// ...
+// gLockMutex(&m);
+// ... critical section ...
+// gUnlockMutex(&m);
+// ...
+// gDestroyMutex(&m);
+
+#ifdef _WIN32
+
+typedef CRITICAL_SECTION GMutex;
+
+#define gInitMutex(m) InitializeCriticalSection(m)
+#define gDestroyMutex(m) DeleteCriticalSection(m)
+#define gLockMutex(m) EnterCriticalSection(m)
+#define gUnlockMutex(m) LeaveCriticalSection(m)
+
+#else // assume pthreads
+
+typedef pthread_mutex_t GMutex;
+
+#define gInitMutex(m) pthread_mutex_init(m, NULL)
+#define gDestroyMutex(m) pthread_mutex_destroy(m)
+#define gLockMutex(m) pthread_mutex_lock(m)
+#define gUnlockMutex(m) pthread_mutex_unlock(m)
+
+#endif
+
+//------------------------------------------------------------------------
+// atomic increment/decrement
+//------------------------------------------------------------------------
+
+// NB: this must be "long" to work on Windows
+typedef long GAtomicCounter;
+
+// Increment *counter by one and return the final value (after the
+// increment).
+static inline GAtomicCounter gAtomicIncrement(GAtomicCounter *counter) {
+ GAtomicCounter newVal;
+
+#if defined(_WIN32)
+ newVal = _InterlockedIncrement(counter);
+#elif defined(__GNUC__) || defined(__xlC__)
+ // __GNUC__ also covers LLVM/clang
+ newVal = __sync_add_and_fetch(counter, 1);
+#elif defined(__SUNPRO_CC)
+ newVal = atomic_inc_ulong_nv((ulong_t *)counter);
+#else
+# error "gAtomicIncrement is not defined for this compiler/platform"
+#endif
+ return newVal;
+}
+
+// Decrement *counter by one and return the final value (after the
+// decrement).
+static inline GAtomicCounter gAtomicDecrement(GAtomicCounter *counter) {
+ GAtomicCounter newVal;
+
+#if defined(_WIN32)
+ newVal = _InterlockedDecrement(counter);
+#elif defined(__GNUC__) || defined(__xlC__)
+ // __GNUC__ also covers LLVM/clang
+ newVal = __sync_sub_and_fetch(counter, 1);
+#elif defined(__SUNPRO_CC)
+ newVal = atomic_dec_ulong_nv((ulong_t *)counter);
+#else
+# error "gAtomicDecrement is not defined for this compiler/platform"
+#endif
+ return newVal;
+}
+
+#endif // GMUTEX_H
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/GString.cc b/Build/source/libs/xpdf/xpdf-src/goo/GString.cc
new file mode 100644
index 00000000000..54de35b0a75
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/GString.cc
@@ -0,0 +1,853 @@
+//========================================================================
+//
+// GString.cc
+//
+// Simple variable-length string type.
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <limits.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+
+//------------------------------------------------------------------------
+
+union GStringFormatArg {
+ int i;
+ Guint ui;
+ long l;
+ Gulong ul;
+#ifdef LLONG_MAX
+ long long ll;
+#endif
+#ifdef ULLONG_MAX
+ unsigned long long ull;
+#endif
+ double f;
+ char c;
+ char *s;
+ GString *gs;
+};
+
+enum GStringFormatType {
+ fmtIntDecimal,
+ fmtIntHex,
+ fmtIntOctal,
+ fmtIntBinary,
+ fmtUIntDecimal,
+ fmtUIntHex,
+ fmtUIntOctal,
+ fmtUIntBinary,
+ fmtLongDecimal,
+ fmtLongHex,
+ fmtLongOctal,
+ fmtLongBinary,
+ fmtULongDecimal,
+ fmtULongHex,
+ fmtULongOctal,
+ fmtULongBinary,
+#ifdef LLONG_MAX
+ fmtLongLongDecimal,
+ fmtLongLongHex,
+ fmtLongLongOctal,
+ fmtLongLongBinary,
+#endif
+#ifdef ULLONG_MAX
+ fmtULongLongDecimal,
+ fmtULongLongHex,
+ fmtULongLongOctal,
+ fmtULongLongBinary,
+#endif
+ fmtDouble,
+ fmtDoubleTrim,
+ fmtChar,
+ fmtString,
+ fmtGString,
+ fmtSpace
+};
+
+static const char *formatStrings[] = {
+ "d", "x", "o", "b", "ud", "ux", "uo", "ub",
+ "ld", "lx", "lo", "lb", "uld", "ulx", "ulo", "ulb",
+#ifdef LLONG_MAX
+ "lld", "llx", "llo", "llb",
+#endif
+#ifdef ULLONG_MAX
+ "ulld", "ullx", "ullo", "ullb",
+#endif
+ "f", "g",
+ "c",
+ "s",
+ "t",
+ "w",
+ NULL
+};
+
+//------------------------------------------------------------------------
+
+static inline int size(int len) {
+ int delta;
+ for (delta = 8; delta < len && delta < 0x100000; delta <<= 1) ;
+ if (len > INT_MAX - delta) {
+ gMemError("Integer overflow in GString::size()");
+ }
+ // this is ((len + 1) + (delta - 1)) & ~(delta - 1)
+ return (len + delta) & ~(delta - 1);
+}
+
+inline void GString::resize(int length1) {
+ char *s1;
+
+ if (length1 < 0) {
+ gMemError("GString::resize() with negative length");
+ }
+ if (!s) {
+ s = new char[size(length1)];
+ } else if (size(length1) != size(length)) {
+ s1 = new char[size(length1)];
+ if (length1 < length) {
+ memcpy(s1, s, length1);
+ s1[length1] = '\0';
+ } else {
+ memcpy(s1, s, length + 1);
+ }
+ delete[] s;
+ s = s1;
+ }
+}
+
+GString::GString() {
+ s = NULL;
+ resize(length = 0);
+ s[0] = '\0';
+}
+
+GString::GString(const char *sA) {
+ int n = (int)strlen(sA);
+
+ s = NULL;
+ resize(length = n);
+ memcpy(s, sA, n + 1);
+}
+
+GString::GString(const char *sA, int lengthA) {
+ s = NULL;
+ resize(length = lengthA);
+ memcpy(s, sA, length * sizeof(char));
+ s[length] = '\0';
+}
+
+GString::GString(GString *str, int idx, int lengthA) {
+ s = NULL;
+ resize(length = lengthA);
+ memcpy(s, str->getCString() + idx, length);
+ s[length] = '\0';
+}
+
+GString::GString(GString *str) {
+ s = NULL;
+ resize(length = str->getLength());
+ memcpy(s, str->getCString(), length + 1);
+}
+
+GString::GString(GString *str1, GString *str2) {
+ int n1 = str1->getLength();
+ int n2 = str2->getLength();
+
+ s = NULL;
+ if (n1 > INT_MAX - n2) {
+ gMemError("Integer overflow in GString::GString()");
+ }
+ resize(length = n1 + n2);
+ memcpy(s, str1->getCString(), n1);
+ memcpy(s + n1, str2->getCString(), n2 + 1);
+}
+
+GString *GString::fromInt(int x) {
+ char buf[24]; // enough space for 64-bit ints plus a little extra
+ const char *p;
+ int len;
+
+ formatInt(x, buf, sizeof(buf), gFalse, 0, 10, &p, &len);
+ return new GString(p, len);
+}
+
+GString *GString::format(const char *fmt, ...) {
+ va_list argList;
+ GString *s;
+
+ s = new GString();
+ va_start(argList, fmt);
+ s->appendfv(fmt, argList);
+ va_end(argList);
+ return s;
+}
+
+GString *GString::formatv(const char *fmt, va_list argList) {
+ GString *s;
+
+ s = new GString();
+ s->appendfv(fmt, argList);
+ return s;
+}
+
+GString::~GString() {
+ delete[] s;
+}
+
+GString *GString::clear() {
+ s[length = 0] = '\0';
+ resize(0);
+ return this;
+}
+
+GString *GString::append(char c) {
+ if (length > INT_MAX - 1) {
+ gMemError("Integer overflow in GString::append()");
+ }
+ resize(length + 1);
+ s[length++] = c;
+ s[length] = '\0';
+ return this;
+}
+
+GString *GString::append(GString *str) {
+ int n = str->getLength();
+
+ if (length > INT_MAX - n) {
+ gMemError("Integer overflow in GString::append()");
+ }
+ resize(length + n);
+ memcpy(s + length, str->getCString(), n + 1);
+ length += n;
+ return this;
+}
+
+GString *GString::append(const char *str) {
+ int n = (int)strlen(str);
+
+ if (length > INT_MAX - n) {
+ gMemError("Integer overflow in GString::append()");
+ }
+ resize(length + n);
+ memcpy(s + length, str, n + 1);
+ length += n;
+ return this;
+}
+
+GString *GString::append(const char *str, int lengthA) {
+ if (lengthA < 0 || length > INT_MAX - lengthA) {
+ gMemError("Integer overflow in GString::append()");
+ }
+ resize(length + lengthA);
+ memcpy(s + length, str, lengthA);
+ length += lengthA;
+ s[length] = '\0';
+ return this;
+}
+
+GString *GString::appendf(const char *fmt, ...) {
+ va_list argList;
+
+ va_start(argList, fmt);
+ appendfv(fmt, argList);
+ va_end(argList);
+ return this;
+}
+
+GString *GString::appendfv(const char *fmt, va_list argList) {
+ GStringFormatArg *args;
+ int argsLen, argsSize;
+ GStringFormatArg arg;
+ int idx, width, prec;
+ GBool reverseAlign, zeroFill;
+ GStringFormatType ft;
+ char buf[65];
+ int len, i;
+ const char *p0, *p1;
+ const char *str;
+
+ argsLen = 0;
+ argsSize = 8;
+ args = (GStringFormatArg *)gmallocn(argsSize, sizeof(GStringFormatArg));
+
+ p0 = fmt;
+ while (*p0) {
+ if (*p0 == '{') {
+ ++p0;
+ if (*p0 == '{') {
+ ++p0;
+ append('{');
+ } else {
+
+ // parse the format string
+ if (!(*p0 >= '0' && *p0 <= '9')) {
+ break;
+ }
+ idx = *p0 - '0';
+ for (++p0; *p0 >= '0' && *p0 <= '9'; ++p0) {
+ idx = 10 * idx + (*p0 - '0');
+ }
+ if (*p0 != ':') {
+ break;
+ }
+ ++p0;
+ if (*p0 == '-') {
+ reverseAlign = gTrue;
+ ++p0;
+ } else {
+ reverseAlign = gFalse;
+ }
+ width = 0;
+ zeroFill = *p0 == '0';
+ for (; *p0 >= '0' && *p0 <= '9'; ++p0) {
+ width = 10 * width + (*p0 - '0');
+ }
+ if (width < 0) {
+ width = 0;
+ }
+ if (*p0 == '.') {
+ ++p0;
+ prec = 0;
+ for (; *p0 >= '0' && *p0 <= '9'; ++p0) {
+ prec = 10 * prec + (*p0 - '0');
+ }
+ } else {
+ prec = 0;
+ }
+ for (ft = (GStringFormatType)0;
+ formatStrings[ft];
+ ft = (GStringFormatType)(ft + 1)) {
+ if (!strncmp(p0, formatStrings[ft], strlen(formatStrings[ft]))) {
+ break;
+ }
+ }
+ if (!formatStrings[ft]) {
+ break;
+ }
+ p0 += strlen(formatStrings[ft]);
+ if (*p0 != '}') {
+ break;
+ }
+ ++p0;
+
+ // fetch the argument
+ if (idx > argsLen) {
+ break;
+ }
+ if (idx == argsLen) {
+ if (argsLen == argsSize) {
+ argsSize *= 2;
+ args = (GStringFormatArg *)greallocn(args, argsSize,
+ sizeof(GStringFormatArg));
+ }
+ switch (ft) {
+ case fmtIntDecimal:
+ case fmtIntHex:
+ case fmtIntOctal:
+ case fmtIntBinary:
+ case fmtSpace:
+ args[argsLen].i = va_arg(argList, int);
+ break;
+ case fmtUIntDecimal:
+ case fmtUIntHex:
+ case fmtUIntOctal:
+ case fmtUIntBinary:
+ args[argsLen].ui = va_arg(argList, Guint);
+ break;
+ case fmtLongDecimal:
+ case fmtLongHex:
+ case fmtLongOctal:
+ case fmtLongBinary:
+ args[argsLen].l = va_arg(argList, long);
+ break;
+ case fmtULongDecimal:
+ case fmtULongHex:
+ case fmtULongOctal:
+ case fmtULongBinary:
+ args[argsLen].ul = va_arg(argList, Gulong);
+ break;
+#ifdef LLONG_MAX
+ case fmtLongLongDecimal:
+ case fmtLongLongHex:
+ case fmtLongLongOctal:
+ case fmtLongLongBinary:
+ args[argsLen].ll = va_arg(argList, long long);
+ break;
+#endif
+#ifdef ULLONG_MAX
+ case fmtULongLongDecimal:
+ case fmtULongLongHex:
+ case fmtULongLongOctal:
+ case fmtULongLongBinary:
+ args[argsLen].ull = va_arg(argList, unsigned long long);
+ break;
+#endif
+ case fmtDouble:
+ case fmtDoubleTrim:
+ args[argsLen].f = va_arg(argList, double);
+ break;
+ case fmtChar:
+ args[argsLen].c = (char)va_arg(argList, int);
+ break;
+ case fmtString:
+ args[argsLen].s = va_arg(argList, char *);
+ break;
+ case fmtGString:
+ args[argsLen].gs = va_arg(argList, GString *);
+ break;
+ }
+ ++argsLen;
+ }
+
+ // format the argument
+ arg = args[idx];
+ switch (ft) {
+ case fmtIntDecimal:
+ formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 10, &str, &len);
+ break;
+ case fmtIntHex:
+ formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 16, &str, &len);
+ break;
+ case fmtIntOctal:
+ formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
+ break;
+ case fmtIntBinary:
+ formatInt(arg.i, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
+ break;
+ case fmtUIntDecimal:
+ formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 10,
+ &str, &len);
+ break;
+ case fmtUIntHex:
+ formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 16,
+ &str, &len);
+ break;
+ case fmtUIntOctal:
+ formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
+ break;
+ case fmtUIntBinary:
+ formatUInt(arg.ui, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
+ break;
+ case fmtLongDecimal:
+ formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 10, &str, &len);
+ break;
+ case fmtLongHex:
+ formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 16, &str, &len);
+ break;
+ case fmtLongOctal:
+ formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
+ break;
+ case fmtLongBinary:
+ formatInt(arg.l, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
+ break;
+ case fmtULongDecimal:
+ formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 10,
+ &str, &len);
+ break;
+ case fmtULongHex:
+ formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 16,
+ &str, &len);
+ break;
+ case fmtULongOctal:
+ formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
+ break;
+ case fmtULongBinary:
+ formatUInt(arg.ul, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
+ break;
+#ifdef LLONG_MAX
+ case fmtLongLongDecimal:
+ formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 10, &str, &len);
+ break;
+ case fmtLongLongHex:
+ formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 16, &str, &len);
+ break;
+ case fmtLongLongOctal:
+ formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 8, &str, &len);
+ break;
+ case fmtLongLongBinary:
+ formatInt(arg.ll, buf, sizeof(buf), zeroFill, width, 2, &str, &len);
+ break;
+#endif
+#ifdef ULLONG_MAX
+ case fmtULongLongDecimal:
+ formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 10,
+ &str, &len);
+ break;
+ case fmtULongLongHex:
+ formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 16,
+ &str, &len);
+ break;
+ case fmtULongLongOctal:
+ formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 8,
+ &str, &len);
+ break;
+ case fmtULongLongBinary:
+ formatUInt(arg.ull, buf, sizeof(buf), zeroFill, width, 2,
+ &str, &len);
+ break;
+#endif
+ case fmtDouble:
+ formatDouble(arg.f, buf, sizeof(buf), prec, gFalse, &str, &len);
+ break;
+ case fmtDoubleTrim:
+ formatDouble(arg.f, buf, sizeof(buf), prec, gTrue, &str, &len);
+ break;
+ case fmtChar:
+ buf[0] = arg.c;
+ str = buf;
+ len = 1;
+ reverseAlign = !reverseAlign;
+ break;
+ case fmtString:
+ if (arg.s) {
+ str = arg.s;
+ len = (int)strlen(str);
+ } else {
+ str = "(null)";
+ len = 6;
+ }
+ reverseAlign = !reverseAlign;
+ break;
+ case fmtGString:
+ if (arg.gs) {
+ str = arg.gs->getCString();
+ len = arg.gs->getLength();
+ } else {
+ str = "(null)";
+ len = 6;
+ }
+ reverseAlign = !reverseAlign;
+ break;
+ case fmtSpace:
+ str = buf;
+ len = 0;
+ width = arg.i;
+ break;
+ }
+
+ // append the formatted arg, handling width and alignment
+ if (!reverseAlign && len < width) {
+ for (i = len; i < width; ++i) {
+ append(' ');
+ }
+ }
+ append(str, len);
+ if (reverseAlign && len < width) {
+ for (i = len; i < width; ++i) {
+ append(' ');
+ }
+ }
+ }
+
+ } else if (*p0 == '}') {
+ ++p0;
+ if (*p0 == '}') {
+ ++p0;
+ }
+ append('}');
+
+ } else {
+ for (p1 = p0 + 1; *p1 && *p1 != '{' && *p1 != '}'; ++p1) ;
+ append(p0, (int)(p1 - p0));
+ p0 = p1;
+ }
+ }
+
+ gfree(args);
+ return this;
+}
+
+#ifdef LLONG_MAX
+void GString::formatInt(long long x, char *buf, int bufSize,
+ GBool zeroFill, int width, int base,
+ const char **p, int *len) {
+#else
+void GString::formatInt(long x, char *buf, int bufSize,
+ GBool zeroFill, int width, int base,
+ const char **p, int *len) {
+#endif
+ static char vals[17] = "0123456789abcdef";
+ GBool neg;
+ int start, i, j;
+
+ i = bufSize;
+ if ((neg = x < 0)) {
+ x = -x;
+ }
+ start = neg ? 1 : 0;
+ if (x == 0) {
+ buf[--i] = '0';
+ } else {
+ while (i > start && x) {
+ buf[--i] = vals[x % base];
+ x /= base;
+ }
+ }
+ if (zeroFill) {
+ for (j = bufSize - i; i > start && j < width - start; ++j) {
+ buf[--i] = '0';
+ }
+ }
+ if (neg) {
+ buf[--i] = '-';
+ }
+ *p = buf + i;
+ *len = bufSize - i;
+}
+
+#ifdef ULLONG_MAX
+void GString::formatUInt(unsigned long long x, char *buf, int bufSize,
+ GBool zeroFill, int width, int base,
+ const char **p, int *len) {
+#else
+void GString::formatUInt(Gulong x, char *buf, int bufSize,
+ GBool zeroFill, int width, int base,
+ const char **p, int *len) {
+#endif
+ static char vals[17] = "0123456789abcdef";
+ int i, j;
+
+ i = bufSize;
+ if (x == 0) {
+ buf[--i] = '0';
+ } else {
+ while (i > 0 && x) {
+ buf[--i] = vals[x % base];
+ x /= base;
+ }
+ }
+ if (zeroFill) {
+ for (j = bufSize - i; i > 0 && j < width; ++j) {
+ buf[--i] = '0';
+ }
+ }
+ *p = buf + i;
+ *len = bufSize - i;
+}
+
+void GString::formatDouble(double x, char *buf, int bufSize, int prec,
+ GBool trim, const char **p, int *len) {
+ GBool neg, started;
+ double x2;
+ int d, i, j;
+
+ if ((neg = x < 0)) {
+ x = -x;
+ }
+ x = floor(x * pow(10.0, prec) + 0.5);
+ i = bufSize;
+ started = !trim;
+ for (j = 0; j < prec && i > 1; ++j) {
+ x2 = floor(0.1 * (x + 0.5));
+ d = (int)floor(x - 10 * x2 + 0.5);
+ if (started || d != 0) {
+ buf[--i] = (char)('0' + d);
+ started = gTrue;
+ }
+ x = x2;
+ }
+ if (i > 1 && started) {
+ buf[--i] = '.';
+ }
+ if (i > 1) {
+ do {
+ x2 = floor(0.1 * (x + 0.5));
+ d = (int)floor(x - 10 * x2 + 0.5);
+ buf[--i] = (char)('0' + d);
+ x = x2;
+ } while (i > 1 && x);
+ }
+ if (neg) {
+ buf[--i] = '-';
+ }
+ *p = buf + i;
+ *len = bufSize - i;
+}
+
+GString *GString::insert(int i, char c) {
+ int j;
+
+ if (length > INT_MAX - 1) {
+ gMemError("Integer overflow in GString::insert()");
+ }
+ resize(length + 1);
+ for (j = length + 1; j > i; --j)
+ s[j] = s[j-1];
+ s[i] = c;
+ ++length;
+ return this;
+}
+
+GString *GString::insert(int i, GString *str) {
+ int n = str->getLength();
+ int j;
+
+ if (length > INT_MAX - n) {
+ gMemError("Integer overflow in GString::insert()");
+ }
+ resize(length + n);
+ for (j = length; j >= i; --j)
+ s[j+n] = s[j];
+ memcpy(s+i, str->getCString(), n);
+ length += n;
+ return this;
+}
+
+GString *GString::insert(int i, const char *str) {
+ int n = (int)strlen(str);
+ int j;
+
+ if (length > INT_MAX - n) {
+ gMemError("Integer overflow in GString::insert()");
+ }
+ resize(length + n);
+ for (j = length; j >= i; --j)
+ s[j+n] = s[j];
+ memcpy(s+i, str, n);
+ length += n;
+ return this;
+}
+
+GString *GString::insert(int i, const char *str, int lengthA) {
+ int j;
+
+ if (lengthA < 0 || length > INT_MAX - lengthA) {
+ gMemError("Integer overflow in GString::insert()");
+ }
+ resize(length + lengthA);
+ for (j = length; j >= i; --j)
+ s[j+lengthA] = s[j];
+ memcpy(s+i, str, lengthA);
+ length += lengthA;
+ return this;
+}
+
+GString *GString::del(int i, int n) {
+ int j;
+
+ if (i >= 0 && n > 0 && i <= INT_MAX - n) {
+ if (i + n > length) {
+ n = length - i;
+ }
+ for (j = i; j <= length - n; ++j) {
+ s[j] = s[j + n];
+ }
+ resize(length -= n);
+ }
+ return this;
+}
+
+GString *GString::upperCase() {
+ int i;
+
+ for (i = 0; i < length; ++i) {
+ if (islower(s[i] & 0xff)) {
+ s[i] = (char)toupper(s[i] & 0xff);
+ }
+ }
+ return this;
+}
+
+GString *GString::lowerCase() {
+ int i;
+
+ for (i = 0; i < length; ++i) {
+ if (isupper(s[i] & 0xff)) {
+ s[i] = (char)tolower(s[i] & 0xff);
+ }
+ }
+ return this;
+}
+
+int GString::cmp(GString *str) {
+ int n1, n2, i, x;
+ char *p1, *p2;
+
+ n1 = length;
+ n2 = str->length;
+ for (i = 0, p1 = s, p2 = str->s; i < n1 && i < n2; ++i, ++p1, ++p2) {
+ x = (*p1 & 0xff) - (*p2 & 0xff);
+ if (x != 0) {
+ return x;
+ }
+ }
+ return n1 - n2;
+}
+
+int GString::cmpN(GString *str, int n) {
+ int n1, n2, i, x;
+ char *p1, *p2;
+
+ n1 = length;
+ n2 = str->length;
+ for (i = 0, p1 = s, p2 = str->s;
+ i < n1 && i < n2 && i < n;
+ ++i, ++p1, ++p2) {
+ x = (*p1 & 0xff) - (*p2 & 0xff);
+ if (x != 0) {
+ return x;
+ }
+ }
+ if (i == n) {
+ return 0;
+ }
+ return n1 - n2;
+}
+
+int GString::cmp(const char *sA) {
+ int n1, i, x;
+ const char *p1, *p2;
+
+ n1 = length;
+ for (i = 0, p1 = s, p2 = sA; i < n1 && *p2; ++i, ++p1, ++p2) {
+ x = (*p1 & 0xff) - (*p2 & 0xff);
+ if (x != 0) {
+ return x;
+ }
+ }
+ if (i < n1) {
+ return 1;
+ }
+ if (*p2) {
+ return -1;
+ }
+ return 0;
+}
+
+int GString::cmpN(const char *sA, int n) {
+ int n1, i, x;
+ const char *p1, *p2;
+
+ n1 = length;
+ for (i = 0, p1 = s, p2 = sA; i < n1 && *p2 && i < n; ++i, ++p1, ++p2) {
+ x = (*p1 & 0xff) - (*p2 & 0xff);
+ if (x != 0) {
+ return x;
+ }
+ }
+ if (i == n) {
+ return 0;
+ }
+ if (i < n1) {
+ return 1;
+ }
+ if (*p2) {
+ return -1;
+ }
+ return 0;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/GString.h b/Build/source/libs/xpdf/xpdf-src/goo/GString.h
new file mode 100644
index 00000000000..6b342f85ec7
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/GString.h
@@ -0,0 +1,151 @@
+//========================================================================
+//
+// GString.h
+//
+// Simple variable-length string type.
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GSTRING_H
+#define GSTRING_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <limits.h> // for LLONG_MAX and ULLONG_MAX
+#include <stdarg.h>
+#include "gtypes.h"
+
+class GString {
+public:
+
+ // Create an empty string.
+ GString();
+
+ // Create a string from a C string.
+ GString(const char *sA);
+
+ // Create a string from <lengthA> chars at <sA>. This string
+ // can contain null characters.
+ GString(const char *sA, int lengthA);
+
+ // Create a string from <lengthA> chars at <idx> in <str>.
+ GString(GString *str, int idx, int lengthA);
+
+ // Copy a string.
+ GString(GString *str);
+ GString *copy() { return new GString(this); }
+
+ // Concatenate two strings.
+ GString(GString *str1, GString *str2);
+
+ // Convert an integer to a string.
+ static GString *fromInt(int x);
+
+ // Create a formatted string. Similar to printf, but without the
+ // string overflow issues. Formatting elements consist of:
+ // {<arg>:[<width>][.<precision>]<type>}
+ // where:
+ // - <arg> is the argument number (arg 0 is the first argument
+ // following the format string) -- NB: args must be first used in
+ // order; they can be reused in any order
+ // - <width> is the field width -- negative to reverse the alignment;
+ // starting with a leading zero to zero-fill (for integers)
+ // - <precision> is the number of digits to the right of the decimal
+ // point (for floating point numbers)
+ // - <type> is one of:
+ // d, x, o, b -- int in decimal, hex, octal, binary
+ // ud, ux, uo, ub -- unsigned int
+ // ld, lx, lo, lb, uld, ulx, ulo, ulb -- long, unsigned long
+ // lld, llx, llo, llb, ulld, ullx, ullo, ullb
+ // -- long long, unsigned long long
+ // f, g -- double
+ // c -- char
+ // s -- string (char *)
+ // t -- GString *
+ // w -- blank space; arg determines width
+ // To get literal curly braces, use {{ or }}.
+ static GString *format(const char *fmt, ...);
+ static GString *formatv(const char *fmt, va_list argList);
+
+ // Destructor.
+ ~GString();
+
+ // Get length.
+ int getLength() { return length; }
+
+ // Get C string.
+ char *getCString() { return s; }
+
+ // Get <i>th character.
+ char getChar(int i) { return s[i]; }
+
+ // Change <i>th character.
+ void setChar(int i, char c) { s[i] = c; }
+
+ // Clear string to zero length.
+ GString *clear();
+
+ // Append a character or string.
+ GString *append(char c);
+ GString *append(GString *str);
+ GString *append(const char *str);
+ GString *append(const char *str, int lengthA);
+
+ // Append a formatted string.
+ GString *appendf(const char *fmt, ...);
+ GString *appendfv(const char *fmt, va_list argList);
+
+ // Insert a character or string.
+ GString *insert(int i, char c);
+ GString *insert(int i, GString *str);
+ GString *insert(int i, const char *str);
+ GString *insert(int i, const char *str, int lengthA);
+
+ // Delete a character or range of characters.
+ GString *del(int i, int n = 1);
+
+ // Convert string to all-upper/all-lower case.
+ GString *upperCase();
+ GString *lowerCase();
+
+ // Compare two strings: -1:< 0:= +1:>
+ int cmp(GString *str);
+ int cmpN(GString *str, int n);
+ int cmp(const char *sA);
+ int cmpN(const char *sA, int n);
+
+private:
+
+ int length;
+ char *s;
+
+ void resize(int length1);
+#ifdef LLONG_MAX
+ static void formatInt(long long x, char *buf, int bufSize,
+ GBool zeroFill, int width, int base,
+ const char **p, int *len);
+#else
+ static void formatInt(long x, char *buf, int bufSize,
+ GBool zeroFill, int width, int base,
+ const char **p, int *len);
+#endif
+#ifdef ULLONG_MAX
+ static void formatUInt(unsigned long long x, char *buf, int bufSize,
+ GBool zeroFill, int width, int base,
+ const char **p, int *len);
+#else
+ static void formatUInt(Gulong x, char *buf, int bufSize,
+ GBool zeroFill, int width, int base,
+ const char **p, int *len);
+#endif
+ static void formatDouble(double x, char *buf, int bufSize, int prec,
+ GBool trim, const char **p, int *len);
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/Trace.cc b/Build/source/libs/xpdf/xpdf-src/goo/Trace.cc
new file mode 100644
index 00000000000..4b730e5b97d
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/Trace.cc
@@ -0,0 +1,114 @@
+//========================================================================
+//
+// Trace.cc
+//
+// Nested tracing.
+//
+// Copyright 2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#if ENABLE_TRACING
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include "GString.h"
+#include "Trace.h"
+
+// NB: This module is NOT thread-safe.
+
+static bool traceInitialized = false;
+static FILE *traceOut = NULL;
+
+static void traceInit() {
+ if (traceInitialized) {
+ return;
+ }
+ //~ this could read an env var to set up an output file
+ GString *fileName = GString::format("/tmp/trace.{0:d}", (int)getpid());
+ traceOut = fopen(fileName->getCString(), "w");
+ delete fileName;
+ traceInitialized = true;
+}
+
+static void traceHeader(char flag, void *handle) {
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ if (handle) {
+ fprintf(traceOut, "%c %ld %06ld %p ", flag, tv.tv_sec, tv.tv_usec, handle);
+ } else {
+ fprintf(traceOut, "%c %ld %06ld 0x0 ", flag, tv.tv_sec, tv.tv_usec);
+ }
+}
+
+void traceBegin(void *nestHandle, const char *fmt, ...) {
+ traceInit();
+ if (!traceOut) {
+ return;
+ }
+ traceHeader('B', nestHandle);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(traceOut, fmt, args);
+ va_end(args);
+ fprintf(traceOut, "\n");
+}
+
+void traceEnd(void *nestHandle, const char *fmt, ...) {
+ traceInit();
+ if (!traceOut) {
+ return;
+ }
+ traceHeader('E', nestHandle);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(traceOut, fmt, args);
+ va_end(args);
+ fprintf(traceOut, "\n");
+}
+
+void traceAlloc(void *resourceHandle, const char *fmt, ...) {
+ traceInit();
+ if (!traceOut) {
+ return;
+ }
+ traceHeader('A', resourceHandle);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(traceOut, fmt, args);
+ va_end(args);
+ fprintf(traceOut, "\n");
+}
+
+void traceFree(void *resourceHandle, const char *fmt, ...) {
+ traceInit();
+ if (!traceOut) {
+ return;
+ }
+ traceHeader('F', resourceHandle);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(traceOut, fmt, args);
+ va_end(args);
+ fprintf(traceOut, "\n");
+}
+
+void traceMessage(const char *fmt, ...) {
+ traceInit();
+ if (!traceOut) {
+ return;
+ }
+ traceHeader('M', NULL);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(traceOut, fmt, args);
+ va_end(args);
+ fprintf(traceOut, "\n");
+}
+
+#endif // ENABLE_TRACING
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/Trace.h b/Build/source/libs/xpdf/xpdf-src/goo/Trace.h
new file mode 100644
index 00000000000..9f53ac5ef68
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/Trace.h
@@ -0,0 +1,43 @@
+//========================================================================
+//
+// Trace.h
+//
+// Nested tracing.
+//
+// Copyright 2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef TRACING_H
+#define TRACING_H
+
+#include <aconf.h>
+
+#if ENABLE_TRACING
+
+// Enter a nesting level.
+extern void traceBegin(void *nestHandle, const char *fmt, ...);
+
+// Exit a nesting level.
+extern void traceEnd(void *nestHandle, const char *fmt, ...);
+
+// Mark a resource as allocated.
+extern void traceAlloc(void *resourceHandle, const char *fmt, ...);
+
+// Mark a resource as freed.
+extern void traceFree(void *resourceHandle, const char *fmt, ...);
+
+// Misc message.
+extern void traceMessage(const char *fmt, ...);
+
+#else // ENABLE_TRACING
+
+static inline void traceBegin(void *nestHandle, const char *fmt, ...) {}
+static inline void traceEnd(void *nestHandle, const char *fmt, ...) {}
+static inline void traceAlloc(void *resourceHandle, const char *fmt, ...) {}
+static inline void traceFree(void *nestHandle, const char *fmt, ...) {}
+static inline void traceMessage(const char *fmt, ...) {}
+
+#endif // ENABLE_TRACING
+
+#endif // TRACING_H
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/gfile.cc b/Build/source/libs/xpdf/xpdf-src/goo/gfile.cc
new file mode 100644
index 00000000000..570f4e7ab13
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/gfile.cc
@@ -0,0 +1,770 @@
+//========================================================================
+//
+// gfile.cc
+//
+// Miscellaneous file and directory name manipulation.
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+// Modified for TeX Live by Peter Breitenlohner <tex-live@tug.org>
+// See top-level ChangeLog for a list of all modifications
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef _WIN32
+# undef WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# include <time.h>
+# include <direct.h>
+# include <shobjidl.h>
+# include <shlguid.h>
+#else
+# if !defined(ACORN)
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+# endif
+# include <time.h>
+# include <limits.h>
+# include <string.h>
+# if !defined(VMS) && !defined(ACORN)
+# include <pwd.h>
+# endif
+# if defined(VMS) && (__DECCXX_VER < 50200000)
+# include <unixlib.h>
+# endif
+#endif // _WIN32
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "gfile.h"
+
+// Some systems don't define this, so just make it something reasonably
+// large.
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+//------------------------------------------------------------------------
+
+GString *getHomeDir() {
+#ifdef VMS
+ //---------- VMS ----------
+ return new GString("SYS$LOGIN:");
+
+#elif defined(__EMX__) || defined(_WIN32)
+ //---------- OS/2+EMX and Win32 ----------
+ char *s;
+ GString *ret;
+
+#ifdef _WIN32
+ if ((s = getenv("USERPROFILE")))
+#else
+ if ((s = getenv("HOME")))
+#endif
+ ret = new GString(s);
+ else
+ ret = new GString(".");
+ return ret;
+
+#elif defined(ACORN)
+ //---------- RISCOS ----------
+ return new GString("@");
+
+#else
+ //---------- Unix ----------
+ char *s;
+ struct passwd *pw;
+ GString *ret;
+
+ if ((s = getenv("HOME"))) {
+ ret = new GString(s);
+ } else {
+ if ((s = getenv("USER")))
+ pw = getpwnam(s);
+ else
+ pw = getpwuid(getuid());
+ if (pw)
+ ret = new GString(pw->pw_dir);
+ else
+ ret = new GString(".");
+ }
+ return ret;
+#endif
+}
+
+GString *getCurrentDir() {
+ char buf[PATH_MAX+1];
+
+#if defined(__EMX__)
+ if (_getcwd2(buf, sizeof(buf)))
+#elif defined(_WIN32)
+ if (GetCurrentDirectoryA(sizeof(buf), buf))
+#elif defined(ACORN)
+ if (strcpy(buf, "@"))
+#else
+ if (getcwd(buf, sizeof(buf)))
+#endif
+ return new GString(buf);
+ return new GString();
+}
+
+GString *appendToPath(GString *path, const char *fileName) {
+#if defined(VMS)
+ //---------- VMS ----------
+ //~ this should handle everything necessary for file
+ //~ requesters, but it's certainly not complete
+ char *p0, *p1, *p2;
+ char *q1;
+
+ p0 = path->getCString();
+ p1 = p0 + path->getLength() - 1;
+ if (!strcmp(fileName, "-")) {
+ if (*p1 == ']') {
+ for (p2 = p1; p2 > p0 && *p2 != '.' && *p2 != '['; --p2) ;
+ if (*p2 == '[')
+ ++p2;
+ path->del(p2 - p0, p1 - p2);
+ } else if (*p1 == ':') {
+ path->append("[-]");
+ } else {
+ path->clear();
+ path->append("[-]");
+ }
+ } else if ((q1 = strrchr(fileName, '.')) && !strncmp(q1, ".DIR;", 5)) {
+ if (*p1 == ']') {
+ path->insert(p1 - p0, '.');
+ path->insert(p1 - p0 + 1, fileName, q1 - fileName);
+ } else if (*p1 == ':') {
+ path->append('[');
+ path->append(']');
+ path->append(fileName, q1 - fileName);
+ } else {
+ path->clear();
+ path->append(fileName, q1 - fileName);
+ }
+ } else {
+ if (*p1 != ']' && *p1 != ':')
+ path->clear();
+ path->append(fileName);
+ }
+ return path;
+
+#elif defined(_WIN32)
+ //---------- Win32 ----------
+ GString *tmp;
+ char buf[256];
+ char *fp;
+
+ tmp = new GString(path);
+ tmp->append('/');
+ tmp->append(fileName);
+ GetFullPathNameA(tmp->getCString(), sizeof(buf), buf, &fp);
+ delete tmp;
+ path->clear();
+ path->append(buf);
+ return path;
+
+#elif defined(ACORN)
+ //---------- RISCOS ----------
+ char *p;
+ int i;
+
+ path->append(".");
+ i = path->getLength();
+ path->append(fileName);
+ for (p = path->getCString() + i; *p; ++p) {
+ if (*p == '/') {
+ *p = '.';
+ } else if (*p == '.') {
+ *p = '/';
+ }
+ }
+ return path;
+
+#elif defined(__EMX__)
+ //---------- OS/2+EMX ----------
+ int i;
+
+ // appending "." does nothing
+ if (!strcmp(fileName, "."))
+ return path;
+
+ // appending ".." goes up one directory
+ if (!strcmp(fileName, "..")) {
+ for (i = path->getLength() - 2; i >= 0; --i) {
+ if (path->getChar(i) == '/' || path->getChar(i) == '\\' ||
+ path->getChar(i) == ':')
+ break;
+ }
+ if (i <= 0) {
+ if (path->getChar(0) == '/' || path->getChar(0) == '\\') {
+ path->del(1, path->getLength() - 1);
+ } else if (path->getLength() >= 2 && path->getChar(1) == ':') {
+ path->del(2, path->getLength() - 2);
+ } else {
+ path->clear();
+ path->append("..");
+ }
+ } else {
+ if (path->getChar(i-1) == ':')
+ ++i;
+ path->del(i, path->getLength() - i);
+ }
+ return path;
+ }
+
+ // otherwise, append "/" and new path component
+ if (path->getLength() > 0 &&
+ path->getChar(path->getLength() - 1) != '/' &&
+ path->getChar(path->getLength() - 1) != '\\')
+ path->append('/');
+ path->append(fileName);
+ return path;
+
+#else
+ //---------- Unix ----------
+ int i;
+
+ // appending "." does nothing
+ if (!strcmp(fileName, "."))
+ return path;
+
+ // appending ".." goes up one directory
+ if (!strcmp(fileName, "..")) {
+ for (i = path->getLength() - 2; i >= 0; --i) {
+ if (path->getChar(i) == '/')
+ break;
+ }
+ if (i <= 0) {
+ if (path->getChar(0) == '/') {
+ path->del(1, path->getLength() - 1);
+ } else {
+ path->clear();
+ path->append("..");
+ }
+ } else {
+ path->del(i, path->getLength() - i);
+ }
+ return path;
+ }
+
+ // otherwise, append "/" and new path component
+ if (path->getLength() > 0 &&
+ path->getChar(path->getLength() - 1) != '/')
+ path->append('/');
+ path->append(fileName);
+ return path;
+#endif
+}
+
+GString *grabPath(char *fileName) {
+#ifdef VMS
+ //---------- VMS ----------
+ char *p;
+
+ if ((p = strrchr(fileName, ']')))
+ return new GString(fileName, p + 1 - fileName);
+ if ((p = strrchr(fileName, ':')))
+ return new GString(fileName, p + 1 - fileName);
+ return new GString();
+
+#elif defined(__EMX__) || defined(_WIN32)
+ //---------- OS/2+EMX and Win32 ----------
+ char *p;
+
+ if ((p = strrchr(fileName, '/')))
+ return new GString(fileName, (int)(p - fileName));
+ if ((p = strrchr(fileName, '\\')))
+ return new GString(fileName, (int)(p - fileName));
+ if ((p = strrchr(fileName, ':')))
+ return new GString(fileName, (int)(p + 1 - fileName));
+ return new GString();
+
+#elif defined(ACORN)
+ //---------- RISCOS ----------
+ char *p;
+
+ if ((p = strrchr(fileName, '.')))
+ return new GString(fileName, p - fileName);
+ return new GString();
+
+#else
+ //---------- Unix ----------
+ char *p;
+
+ if ((p = strrchr(fileName, '/')))
+ return new GString(fileName, (int)(p - fileName));
+ return new GString();
+#endif
+}
+
+GBool isAbsolutePath(char *path) {
+#ifdef VMS
+ //---------- VMS ----------
+ return strchr(path, ':') ||
+ (path[0] == '[' && path[1] != '.' && path[1] != '-');
+
+#elif defined(__EMX__) || defined(_WIN32)
+ //---------- OS/2+EMX and Win32 ----------
+ return path[0] == '/' || path[0] == '\\' || path[1] == ':';
+
+#elif defined(ACORN)
+ //---------- RISCOS ----------
+ return path[0] == '$';
+
+#else
+ //---------- Unix ----------
+ return path[0] == '/';
+#endif
+}
+
+GString *makePathAbsolute(GString *path) {
+#ifdef VMS
+ //---------- VMS ----------
+ char buf[PATH_MAX+1];
+
+ if (!isAbsolutePath(path->getCString())) {
+ if (getcwd(buf, sizeof(buf))) {
+ path->insert(0, buf);
+ }
+ }
+ return path;
+
+#elif defined(_WIN32)
+ //---------- Win32 ----------
+ char buf[_MAX_PATH];
+ char *fp;
+
+ buf[0] = '\0';
+ if (!GetFullPathNameA(path->getCString(), _MAX_PATH, buf, &fp)) {
+ path->clear();
+ return path;
+ }
+ path->clear();
+ path->append(buf);
+ return path;
+
+#elif defined(ACORN)
+ //---------- RISCOS ----------
+ path->insert(0, '@');
+ return path;
+
+#else
+ //---------- Unix and OS/2+EMX ----------
+ struct passwd *pw;
+ char buf[PATH_MAX+1];
+ GString *s;
+ char *p1, *p2;
+ int n;
+
+ if (path->getChar(0) == '~') {
+ if (path->getChar(1) == '/' ||
+#ifdef __EMX__
+ path->getChar(1) == '\\' ||
+#endif
+ path->getLength() == 1) {
+ path->del(0, 1);
+ s = getHomeDir();
+ path->insert(0, s);
+ delete s;
+ } else {
+ p1 = path->getCString() + 1;
+#ifdef __EMX__
+ for (p2 = p1; *p2 && *p2 != '/' && *p2 != '\\'; ++p2) ;
+#else
+ for (p2 = p1; *p2 && *p2 != '/'; ++p2) ;
+#endif
+ if ((n = (int)(p2 - p1)) > PATH_MAX)
+ n = PATH_MAX;
+ strncpy(buf, p1, n);
+ buf[n] = '\0';
+ if ((pw = getpwnam(buf))) {
+ path->del(0, (int)(p2 - p1 + 1));
+ path->insert(0, pw->pw_dir);
+ }
+ }
+ } else if (!isAbsolutePath(path->getCString())) {
+ if (getcwd(buf, sizeof(buf))) {
+#ifndef __EMX__
+ path->insert(0, '/');
+#endif
+ path->insert(0, buf);
+ }
+ }
+ return path;
+#endif
+}
+
+time_t getModTime(char *fileName) {
+#ifdef _WIN32
+ //~ should implement this, but it's (currently) only used in xpdf
+ return 0;
+#else
+ struct stat statBuf;
+
+ if (stat(fileName, &statBuf)) {
+ return 0;
+ }
+ return statBuf.st_mtime;
+#endif
+}
+
+#ifndef PDF_PARSER_ONLY
+GBool openTempFile(GString **name, FILE **f,
+ const char *mode, const char *ext) {
+#if defined(_WIN32)
+ //---------- Win32 ----------
+ char tempPath[MAX_PATH + 1];
+ GString *s, *s2;
+ FILE *f2;
+ DWORD n;
+ int t, i;
+
+ // this has the standard race condition problem, but I haven't found
+ // a better way to generate temp file names with extensions on
+ // Windows
+ n = GetTempPathA(sizeof(tempPath), tempPath);
+ if (n > 0 && n <= sizeof(tempPath)) {
+ s = new GString(tempPath);
+ if (tempPath[n-1] != '\\') {
+ s->append('\\');
+ }
+ } else {
+ s = new GString(".\\");
+ }
+ s->appendf("xpdf_{0:d}_{1:d}_",
+ (int)GetCurrentProcessId(), (int)GetCurrentThreadId());
+ t = (int)time(NULL);
+ for (i = 0; i < 1000; ++i) {
+ s2 = GString::format("{0:t}{1:d}", s, t + i);
+ if (ext) {
+ s2->append(ext);
+ }
+ if (!(f2 = fopen(s2->getCString(), "r"))) {
+ if (!(f2 = fopen(s2->getCString(), mode))) {
+ delete s2;
+ delete s;
+ return gFalse;
+ }
+ *name = s2;
+ *f = f2;
+ delete s;
+ return gTrue;
+ }
+ fclose(f2);
+ delete s2;
+ }
+ delete s;
+ return gFalse;
+#elif defined(VMS) || defined(__EMX__) || defined(ACORN)
+ //---------- non-Unix ----------
+ char *s;
+
+ // There is a security hole here: an attacker can create a symlink
+ // with this file name after the tmpnam call and before the fopen
+ // call. I will happily accept fixes to this function for non-Unix
+ // OSs.
+ if (!(s = tmpnam(NULL))) {
+ return gFalse;
+ }
+ *name = new GString(s);
+ if (ext) {
+ (*name)->append(ext);
+ }
+ if (!(*f = fopen((*name)->getCString(), mode))) {
+ delete (*name);
+ *name = NULL;
+ return gFalse;
+ }
+ return gTrue;
+#else
+ //---------- Unix ----------
+ char *s;
+ int fd;
+
+ if (ext) {
+#if HAVE_MKSTEMPS
+ if ((s = getenv("TMPDIR"))) {
+ *name = new GString(s);
+ } else {
+ *name = new GString("/tmp");
+ }
+ (*name)->append("/XXXXXX")->append(ext);
+ fd = mkstemps((*name)->getCString(), (int)strlen(ext));
+#else
+ if (!(s = tmpnam(NULL))) {
+ return gFalse;
+ }
+ *name = new GString(s);
+ (*name)->append(ext);
+ fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
+#endif
+ } else {
+#if HAVE_MKSTEMP
+ if ((s = getenv("TMPDIR"))) {
+ *name = new GString(s);
+ } else {
+ *name = new GString("/tmp");
+ }
+ (*name)->append("/XXXXXX");
+ fd = mkstemp((*name)->getCString());
+#else // HAVE_MKSTEMP
+ if (!(s = tmpnam(NULL))) {
+ return gFalse;
+ }
+ *name = new GString(s);
+ fd = open((*name)->getCString(), O_WRONLY | O_CREAT | O_EXCL, 0600);
+#endif // HAVE_MKSTEMP
+ }
+ if (fd < 0 || !(*f = fdopen(fd, mode))) {
+ delete *name;
+ *name = NULL;
+ return gFalse;
+ }
+ return gTrue;
+#endif
+}
+#endif /* !PDF_PARSER_ONLY */
+
+GBool createDir(char *path, int mode) {
+#ifdef _WIN32
+ return !_mkdir(path);
+#else
+ return !mkdir(path, mode);
+#endif
+}
+
+GBool executeCommand(char *cmd) {
+#ifdef VMS
+ return system(cmd) ? gTrue : gFalse;
+#else
+ return system(cmd) ? gFalse : gTrue;
+#endif
+}
+
+#ifdef _WIN32
+GString *fileNameToUTF8(char *path) {
+ GString *s;
+ char *p;
+
+ s = new GString();
+ for (p = path; *p; ++p) {
+ if (*p & 0x80) {
+ s->append((char)(0xc0 | ((*p >> 6) & 0x03)));
+ s->append((char)(0x80 | (*p & 0x3f)));
+ } else {
+ s->append(*p);
+ }
+ }
+ return s;
+}
+
+GString *fileNameToUTF8(wchar_t *path) {
+ GString *s;
+ wchar_t *p;
+
+ s = new GString();
+ for (p = path; *p; ++p) {
+ if (*p < 0x80) {
+ s->append((char)*p);
+ } else if (*p < 0x800) {
+ s->append((char)(0xc0 | ((*p >> 6) & 0x1f)));
+ s->append((char)(0x80 | (*p & 0x3f)));
+ } else {
+ s->append((char)(0xe0 | ((*p >> 12) & 0x0f)));
+ s->append((char)(0x80 | ((*p >> 6) & 0x3f)));
+ s->append((char)(0x80 | (*p & 0x3f)));
+ }
+ }
+ return s;
+}
+
+wchar_t *fileNameToUCS2(const char *path, wchar_t *out, size_t outSize) {
+ const char *p;
+ size_t i;
+
+ for (p = path, i = 0; *p && i < outSize - 1; ++i) {
+ if ((p[0] & 0xe0) == 0xc0 &&
+ p[1] && (p[1] & 0xc0) == 0x80) {
+ out[i] = (wchar_t)(((p[0] & 0x1f) << 6) |
+ (p[1] & 0x3f));
+ p += 2;
+ } else if ((p[0] & 0xf0) == 0xe0 &&
+ (p[1] & 0xc0) == 0x80 &&
+ (p[2] & 0xc0) == 0x80) {
+ out[i] = (wchar_t)(((p[0] & 0x0f) << 12) |
+ ((p[1] & 0x3f) << 6) |
+ (p[2] & 0x3f));
+ p += 3;
+ } else {
+ out[i] = (wchar_t)(p[0] & 0xff);
+ p += 1;
+ }
+ }
+ out[i] = (wchar_t)0;
+ return out;
+}
+#endif
+
+FILE *openFile(const char *path, const char *mode) {
+#if defined(_WIN32)
+ return fopen(path, mode);
+#if 0
+ wchar_t wPath[_MAX_PATH + 1];
+ wchar_t wMode[8];
+ int i;
+
+ 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");
+#else
+ return fopen(path, mode);
+#endif
+}
+
+#if 0
+#ifdef _WIN32
+void readWindowsShortcut(wchar_t *wPath, size_t wPathSize) {
+ size_t n = wcslen(wPath);
+ if (n < 4 || wcscmp(wPath + n - 4, L".lnk")) {
+ return;
+ }
+ IShellLinkW *shellLink;
+ HRESULT hres;
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ IID_IShellLinkW, (LPVOID *)&shellLink);
+ bool needCoUninit = false;
+ if (hres == CO_E_NOTINITIALIZED) {
+ CoInitialize(NULL);
+ needCoUninit = true;
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ IID_IShellLinkW, (LPVOID *)&shellLink);
+ }
+ if (FAILED(hres)) {
+ return;
+ }
+ IPersistFile *persistFile;
+ hres = shellLink->QueryInterface(IID_IPersistFile, (LPVOID *)&persistFile);
+ if (FAILED(hres)) {
+ return;
+ }
+ hres = persistFile->Load(wPath, STGM_READ);
+ if (FAILED(hres)) {
+ fprintf(stderr, "IPersistFile.Load failed: 0x%08x\n", hres);
+ exit(1);
+ }
+ wchar_t target[_MAX_PATH + 1];
+ hres = shellLink->GetPath(target, _MAX_PATH + 1, NULL, 0);
+ if (FAILED(hres)) {
+ return;
+ }
+ shellLink->Release();
+ if (needCoUninit) {
+ CoUninitialize();
+ }
+ if (wcslen(target) > wPathSize - 1) {
+ return;
+ }
+ wcscpy(wPath, target);
+}
+#endif
+#endif /* 0 */
+
+int makeDir(const char *path, int mode) {
+#ifdef _WIN32
+ return _mkdir(path);
+#else
+ return mkdir(path, (mode_t)mode);
+#endif
+}
+
+char *getLine(char *buf, int size, FILE *f) {
+ int c, i;
+
+ i = 0;
+ while (i < size - 1) {
+ if ((c = fgetc(f)) == EOF) {
+ break;
+ }
+ buf[i++] = (char)c;
+ if (c == '\x0a') {
+ break;
+ }
+ if (c == '\x0d') {
+ c = fgetc(f);
+ if (c == '\x0a' && i < size - 1) {
+ buf[i++] = (char)c;
+ } else if (c != EOF) {
+ ungetc(c, f);
+ }
+ break;
+ }
+ }
+ buf[i] = '\0';
+ if (i == 0) {
+ return NULL;
+ }
+ return buf;
+}
+
+int gfseek(FILE *f, GFileOffset offset, int whence) {
+#if HAVE_FSEEKO
+ return fseeko(f, offset, whence);
+#elif HAVE_FSEEK64
+ return fseek64(f, offset, whence);
+#elif HAVE_FSEEKI64
+ return _fseeki64(f, offset, whence);
+#else
+ return fseek(f, offset, whence);
+#endif
+}
+
+GFileOffset gftell(FILE *f) {
+#if HAVE_FSEEKO
+ return ftello(f);
+#elif HAVE_FSEEK64
+ return ftell64(f);
+#elif HAVE_FSEEKI64
+ return _ftelli64(f);
+#else
+ return ftell(f);
+#endif
+}
+
+#ifndef PDF_PARSER_ONLY
+void fixCommandLine(int *argc, char **argv[]) {
+#ifdef _WIN32
+ int argcw;
+ wchar_t **argvw;
+ GString *arg;
+ int i;
+
+ argvw = CommandLineToArgvW(GetCommandLineW(), &argcw);
+ if (!argvw || argcw < 0) {
+ return;
+ }
+
+ *argc = argcw;
+
+ *argv = (char **)gmallocn(argcw + 1, sizeof(char *));
+ for (i = 0; i < argcw; ++i) {
+ arg = fileNameToUTF8(argvw[i]);
+ (*argv)[i] = copyString(arg->getCString());
+ delete arg;
+ }
+ (*argv)[argcw] = NULL;
+
+ LocalFree(argvw);
+#endif
+}
+#endif /* !PDF_PARSER_ONLY */
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/gfile.h b/Build/source/libs/xpdf/xpdf-src/goo/gfile.h
new file mode 100644
index 00000000000..2075d731a6c
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/gfile.h
@@ -0,0 +1,131 @@
+//========================================================================
+//
+// gfile.h
+//
+// Miscellaneous file and directory name manipulation.
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GFILE_H
+#define GFILE_H
+
+#include <aconf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#if defined(_WIN32)
+# include <sys/stat.h>
+# ifdef FPTEX
+# include <win32lib.h>
+# else
+# include <windows.h>
+# endif
+#elif defined(ACORN)
+#elif defined(ANDROID)
+#else
+# include <unistd.h>
+# include <sys/types.h>
+#endif
+#include "gtypes.h"
+
+class GString;
+
+//------------------------------------------------------------------------
+
+// Get home directory path.
+extern GString *getHomeDir();
+
+// Get current directory.
+extern GString *getCurrentDir();
+
+// Append a file name to a path string. <path> may be an empty
+// string, denoting the current directory). Returns <path>.
+extern GString *appendToPath(GString *path, const char *fileName);
+
+// Grab the path from the front of the file name. If there is no
+// directory component in <fileName>, returns an empty string.
+extern GString *grabPath(char *fileName);
+
+// Is this an absolute path or file name?
+extern GBool isAbsolutePath(char *path);
+
+// Make this path absolute by prepending current directory (if path is
+// relative) or prepending user's directory (if path starts with '~').
+extern GString *makePathAbsolute(GString *path);
+
+// Get the modification time for <fileName>. Returns 0 if there is an
+// error.
+extern time_t getModTime(char *fileName);
+
+// Create a temporary file and open it for writing. If <ext> is not
+// NULL, it will be used as the file name extension. Returns both the
+// name and the file pointer. For security reasons, all writing
+// should be done to the returned file pointer; the file may be
+// reopened later for reading, but not for writing. The <mode> string
+// should be "w" or "wb". Returns true on success.
+extern GBool openTempFile(GString **name, FILE **f,
+ const char *mode, const char *ext);
+
+// Create a directory. Returns true on success.
+extern GBool createDir(char *path, int mode);
+
+// Execute <command>. Returns true on success.
+extern GBool executeCommand(char *cmd);
+
+#ifdef _WIN32
+// Convert a file name from Latin-1 to UTF-8.
+extern GString *fileNameToUTF8(char *path);
+
+// Convert a file name from UCS-2 to UTF-8.
+extern GString *fileNameToUTF8(wchar_t *path);
+
+// Convert a file name from UTF-8 to UCS-2. [out] has space for
+// [outSize] wchar_t elements (including the trailing zero). Returns
+// [out].
+extern wchar_t *fileNameToUCS2(const char *path, wchar_t *out, size_t outSize);
+#endif
+
+// Open a file. On Windows, this converts the path from UTF-8 to
+// UCS-2 and calls _wfopen(). 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);
+
+// Type used by gfseek/gftell for file offsets. This will be 64 bits
+// on systems that support it.
+#if HAVE_FSEEKO
+typedef off_t GFileOffset;
+#define GFILEOFFSET_MAX 0x7fffffffffffffffLL
+#elif HAVE_FSEEK64
+typedef long long GFileOffset;
+#define GFILEOFFSET_MAX 0x7fffffffffffffffLL
+#elif HAVE_FSEEKI64
+typedef __int64 GFileOffset;
+#define GFILEOFFSET_MAX 0x7fffffffffffffffLL
+#else
+typedef long GFileOffset;
+#define GFILEOFFSET_MAX LONG_MAX
+#endif
+
+// Like fseek, but uses a 64-bit file offset if available.
+extern int gfseek(FILE *f, GFileOffset offset, int whence);
+
+// Like ftell, but returns a 64-bit file offset if available.
+extern GFileOffset gftell(FILE *f);
+
+// 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
+extern void fixCommandLine(int *argc, char **argv[]);
+#endif /* !PDF_PARSER_ONLY */
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/gmem.cc b/Build/source/libs/xpdf/xpdf-src/goo/gmem.cc
new file mode 100644
index 00000000000..7291f2099bb
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/gmem.cc
@@ -0,0 +1,396 @@
+/*
+ * gmem.c
+ *
+ * Memory routines with out-of-memory checking.
+ *
+ * Copyright 1996-2003 Glyph & Cog, LLC
+ */
+
+#include <aconf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.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 <string.h>
+#include <limits.h>
+#if MULTITHREADED && defined(_WIN32)
+# include <windows.h>
+#endif
+#include "gmem.h"
+
+#ifdef DEBUG_MEM
+
+typedef struct _GMemHdr {
+ unsigned int magic;
+ int index;
+ size_t size;
+ struct _GMemHdr *next, *prev;
+} GMemHdr;
+
+#define gMemHdrSize ((sizeof(GMemHdr) + 7) & ~7)
+#define gMemTrlSize (sizeof(long))
+
+#define gMemMagic 0xabcd9999
+
+#if ULONG_MAX > 0xffffffffL
+#define gMemDeadVal 0xdeadbeefdeadbeefUL
+#else
+#define gMemDeadVal 0xdeadbeefUL
+#endif
+
+/* round data size so trailer will be aligned */
+#define gMemDataSize(size) \
+ (int)(((((size) + gMemTrlSize - 1) / gMemTrlSize) * gMemTrlSize))
+
+#define gMemDataSize64(size) \
+ (size_t)(((((size) + gMemTrlSize - 1) / gMemTrlSize) * gMemTrlSize))
+
+static GMemHdr *gMemHead = NULL;
+static GMemHdr *gMemTail = NULL;
+
+static int gMemIndex = 0;
+static int gMemAlloc = 0;
+static size_t gMemInUse = 0;
+static size_t gMaxMemInUse = 0;
+
+#if MULTITHREADED
+# ifdef _WIN32
+ static CRITICAL_SECTION gMemMutex;
+ static INIT_ONCE gMemMutexInitStruct = INIT_ONCE_STATIC_INIT;
+ static BOOL CALLBACK gMemMutexInitFunc(PINIT_ONCE initOnce, PVOID param,
+ PVOID *context) {
+ InitializeCriticalSection(&gMemMutex);
+ return TRUE;
+ }
+# define gMemInitMutex InitOnceExecuteOnce(&gMemMutexInitStruct, \
+ &gMemMutexInitFunc, NULL, NULL)
+# define gMemLock EnterCriticalSection(&gMemMutex);
+# define gMemUnlock LeaveCriticalSection(&gMemMutex);
+# else
+# include <pthread.h>
+ static pthread_mutex_t gMemMutex = PTHREAD_MUTEX_INITIALIZER;
+# define gMemInitMutex
+# define gMemLock pthread_mutex_lock(&gMemMutex)
+# define gMemUnlock pthread_mutex_unlock(&gMemMutex)
+# endif
+#else
+# define gMemInitMutex
+# define gMemLock
+# define gMemUnlock
+#endif
+
+#endif /* DEBUG_MEM */
+
+#ifdef DEBUG_MEM
+void *gmalloc(int size, int ignore) GMEM_EXCEP {
+ int size1;
+ char *mem;
+ GMemHdr *hdr;
+ void *data;
+ unsigned long *trl, *p;
+
+ gMemInitMutex;
+ if (size < 0) {
+ gMemError("Invalid memory allocation size");
+ }
+ if (size == 0) {
+ return NULL;
+ }
+ size1 = gMemDataSize(size);
+ if (!(mem = (char *)malloc(size1 + gMemHdrSize + gMemTrlSize))) {
+ gMemError("Out of memory");
+ }
+ hdr = (GMemHdr *)mem;
+ data = (void *)(mem + gMemHdrSize);
+ trl = (unsigned long *)(mem + gMemHdrSize + size1);
+ hdr->magic = gMemMagic;
+ hdr->size = size;
+ gMemLock;
+ if (ignore) {
+ hdr->index = -1;
+ } else {
+ hdr->index = gMemIndex++;
+ }
+ if (gMemTail) {
+ gMemTail->next = hdr;
+ hdr->prev = gMemTail;
+ gMemTail = hdr;
+ } else {
+ hdr->prev = NULL;
+ gMemHead = gMemTail = hdr;
+ }
+ hdr->next = NULL;
+ ++gMemAlloc;
+ gMemInUse += size;
+ if (gMemInUse > gMaxMemInUse) {
+ gMaxMemInUse = gMemInUse;
+ }
+ gMemUnlock;
+ for (p = (unsigned long *)data; p <= trl; ++p) {
+ *p = gMemDeadVal;
+ }
+ return data;
+}
+#else
+void *gmalloc(int size) GMEM_EXCEP {
+ void *p;
+
+ if (size < 0) {
+ gMemError("Invalid memory allocation size");
+ }
+ if (size == 0) {
+ return NULL;
+ }
+ if (!(p = malloc(size))) {
+ gMemError("Out of memory");
+ }
+ return p;
+}
+#endif
+
+void *grealloc(void *p, int size) GMEM_EXCEP {
+#ifdef DEBUG_MEM
+ GMemHdr *hdr;
+ void *q;
+ int oldSize;
+
+ if (size < 0) {
+ gMemError("Invalid memory allocation size");
+ }
+ if (size == 0) {
+ if (p) {
+ gfree(p);
+ }
+ return NULL;
+ }
+ if (p) {
+ hdr = (GMemHdr *)((char *)p - gMemHdrSize);
+ oldSize = (int)hdr->size;
+ q = gmalloc(size);
+ memcpy(q, p, size < oldSize ? size : oldSize);
+ gfree(p);
+ } else {
+ q = gmalloc(size);
+ }
+ return q;
+#else
+ void *q;
+
+ if (size < 0) {
+ gMemError("Invalid memory allocation size");
+ }
+ if (size == 0) {
+ if (p) {
+ free(p);
+ }
+ return NULL;
+ }
+ if (p) {
+ q = realloc(p, size);
+ } else {
+ q = malloc(size);
+ }
+ if (!q) {
+ gMemError("Out of memory");
+ }
+ return q;
+#endif
+}
+
+void *gmallocn(int nObjs, int objSize) GMEM_EXCEP {
+ int n;
+
+ if (nObjs == 0) {
+ return NULL;
+ }
+ n = nObjs * objSize;
+ if (objSize <= 0 || nObjs < 0 || nObjs >= INT_MAX / objSize) {
+ gMemError("Bogus memory allocation size");
+ }
+ return gmalloc(n);
+}
+
+#ifdef DEBUG_MEM
+void *gmalloc64(size_t size, int ignore) GMEM_EXCEP {
+ size_t size1;
+ char *mem;
+ GMemHdr *hdr;
+ void *data;
+ unsigned long *trl, *p;
+
+ gMemInitMutex;
+ if (size == 0) {
+ return NULL;
+ }
+ size1 = gMemDataSize64(size);
+ if (!(mem = (char *)malloc(size1 + gMemHdrSize + gMemTrlSize))) {
+ gMemError("Out of memory");
+ }
+ hdr = (GMemHdr *)mem;
+ data = (void *)(mem + gMemHdrSize);
+ trl = (unsigned long *)(mem + gMemHdrSize + size1);
+ hdr->magic = gMemMagic;
+ hdr->size = size;
+ gMemLock;
+ if (ignore) {
+ hdr->index = -1;
+ } else {
+ hdr->index = gMemIndex++;
+ }
+ if (gMemTail) {
+ gMemTail->next = hdr;
+ hdr->prev = gMemTail;
+ gMemTail = hdr;
+ } else {
+ hdr->prev = NULL;
+ gMemHead = gMemTail = hdr;
+ }
+ hdr->next = NULL;
+ ++gMemAlloc;
+ gMemInUse += size;
+ if (gMemInUse > gMaxMemInUse) {
+ gMaxMemInUse = gMemInUse;
+ }
+ gMemUnlock;
+ for (p = (unsigned long *)data; p <= trl; ++p) {
+ *p = gMemDeadVal;
+ }
+ return data;
+}
+#else
+void *gmalloc64(size_t size) GMEM_EXCEP {
+ void *p;
+
+ if (size == 0) {
+ return NULL;
+ }
+ if (!(p = malloc(size))) {
+ gMemError("Out of memory");
+ }
+ return p;
+}
+#endif
+
+void *gmallocn64(int nObjs, size_t objSize) GMEM_EXCEP {
+ size_t n;
+
+ if (nObjs == 0) {
+ return NULL;
+ }
+ n = nObjs * objSize;
+ if (nObjs < 0 || (size_t)nObjs >= SIZE_MAX / objSize) {
+ gMemError("Bogus memory allocation size");
+ }
+ return gmalloc64(n);
+}
+
+void *greallocn(void *p, int nObjs, int objSize) GMEM_EXCEP {
+ int n;
+
+ if (nObjs == 0) {
+ if (p) {
+ gfree(p);
+ }
+ return NULL;
+ }
+ n = nObjs * objSize;
+ if (objSize <= 0 || nObjs < 0 || nObjs >= INT_MAX / objSize) {
+ gMemError("Bogus memory allocation size");
+ }
+ return grealloc(p, n);
+}
+
+void gfree(void *p) {
+#ifdef DEBUG_MEM
+ size_t size;
+ GMemHdr *hdr;
+ unsigned long *trl, *clr;
+
+ if (p) {
+ hdr = (GMemHdr *)((char *)p - gMemHdrSize);
+ gMemLock;
+ if (hdr->magic == gMemMagic &&
+ ((hdr->prev == NULL) == (hdr == gMemHead)) &&
+ ((hdr->next == NULL) == (hdr == gMemTail))) {
+ if (hdr->prev) {
+ hdr->prev->next = hdr->next;
+ } else {
+ gMemHead = hdr->next;
+ }
+ if (hdr->next) {
+ hdr->next->prev = hdr->prev;
+ } else {
+ gMemTail = hdr->prev;
+ }
+ --gMemAlloc;
+ gMemInUse -= hdr->size;
+ gMemUnlock;
+ size = gMemDataSize64(hdr->size);
+ trl = (unsigned long *)((char *)hdr + gMemHdrSize + size);
+ if (*trl != gMemDeadVal) {
+ fprintf(stderr, "Overwrite past end of block %d at address %p\n",
+ hdr->index, p);
+ }
+ for (clr = (unsigned long *)hdr; clr <= trl; ++clr) {
+ *clr = gMemDeadVal;
+ }
+ free(hdr);
+ } else {
+ gMemUnlock;
+ fprintf(stderr, "Attempted to free bad address %p\n", p);
+ }
+ }
+#else
+ if (p) {
+ free(p);
+ }
+#endif
+}
+
+void gMemError(const char *msg) GMEM_EXCEP {
+#if USE_EXCEPTIONS
+ throw GMemException();
+#else
+ fprintf(stderr, "%s\n", msg);
+ exit(1);
+#endif
+}
+
+#ifdef DEBUG_MEM
+void gMemReport(FILE *f) {
+ GMemHdr *p;
+ int left;
+
+ fprintf(f, "%d memory allocations in all\n", gMemIndex);
+ fprintf(f, "maximum memory in use: %zd bytes\n", gMaxMemInUse);
+ left = 0;
+ if (gMemAlloc > 0) {
+ for (p = gMemHead; p; p = p->next) {
+ if (p->index >= 0) {
+ if (!left) {
+ fprintf(f, "%d memory blocks left allocated:\n", gMemAlloc);
+ fprintf(f, " index size\n");
+ fprintf(f, "-------- --------\n");
+ left = 1;
+ }
+ fprintf(f, "%8d %8zd\n", p->index, p->size);
+ }
+ }
+ }
+ if (!left) {
+ fprintf(f, "No memory blocks left allocated\n");
+ }
+}
+#endif
+
+char *copyString(const char *s) {
+ char *s1;
+
+ s1 = (char *)gmalloc((int)strlen(s) + 1);
+ strcpy(s1, s);
+ return s1;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/gmem.h b/Build/source/libs/xpdf/xpdf-src/goo/gmem.h
new file mode 100644
index 00000000000..f131c176f8b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/gmem.h
@@ -0,0 +1,95 @@
+/*
+ * gmem.h
+ *
+ * Memory routines with out-of-memory checking.
+ *
+ * Copyright 1996-2003 Glyph & Cog, LLC
+ */
+
+#ifndef GMEM_H
+#define GMEM_H
+
+#include <stdio.h>
+#include <aconf.h>
+
+#if USE_EXCEPTIONS
+
+class GMemException {
+public:
+ GMemException() {}
+ ~GMemException() {}
+};
+
+// This used to be:
+// #define GMEM_EXCEP throw(GMemException)
+// but the throw decl was never really very useful, and is deprecated
+// as of C++11 and illegal as of C++17.
+#define GMEM_EXCEP
+
+#else // USE_EXCEPTIONS
+
+#define GMEM_EXCEP
+
+#endif // USE_EXCEPTIONS
+
+/*
+ * Same as malloc, but prints error message and exits if malloc()
+ * returns NULL.
+ */
+#ifdef DEBUG_MEM
+extern void *gmalloc(int size, int ignore = 0) GMEM_EXCEP;
+#else
+extern void *gmalloc(int size) GMEM_EXCEP;
+#endif
+
+/*
+ * Same as realloc, but prints error message and exits if realloc()
+ * returns NULL. If <p> is NULL, calls malloc instead of realloc().
+ */
+extern void *grealloc(void *p, int size) GMEM_EXCEP;
+
+/*
+ * These are similar to gmalloc and grealloc, but take an object count
+ * and size. The result is similar to allocating nObjs * objSize
+ * bytes, but there is an additional error check that the total size
+ * doesn't overflow an int.
+ */
+extern void *gmallocn(int nObjs, int objSize) GMEM_EXCEP;
+extern void *greallocn(void *p, int nObjs, int objSize) GMEM_EXCEP;
+
+/*
+ * Same as gmalloc and gmallocn, but allow a 64-bit size on 64-bit
+ * systems.
+ */
+#ifdef DEBUG_MEM
+extern void *gmalloc64(size_t size, int ignore = 0) GMEM_EXCEP;
+#else
+extern void *gmalloc64(size_t size) GMEM_EXCEP;
+#endif
+extern void *gmallocn64(int nObjs, size_t objSize) GMEM_EXCEP;
+
+/*
+ * Same as free, but checks for and ignores NULL pointers.
+ */
+extern void gfree(void *p);
+
+/*
+ * Report a memory error.
+ */
+extern void gMemError(const char *msg) GMEM_EXCEP;
+
+#ifdef DEBUG_MEM
+/*
+ * Report on unfreed memory.
+ */
+extern void gMemReport(FILE *f);
+#else
+#define gMemReport(f)
+#endif
+
+/*
+ * Allocate memory and copy a string into it.
+ */
+extern char *copyString(const char *s);
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/gmempp.cc b/Build/source/libs/xpdf/xpdf-src/goo/gmempp.cc
new file mode 100644
index 00000000000..fe5790b1f58
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/gmempp.cc
@@ -0,0 +1,51 @@
+//========================================================================
+//
+// gmempp.cc
+//
+// Use gmalloc/gfree for C++ new/delete operators.
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include "gmem.h"
+// NB: do not include gmempp.h here.
+
+#ifdef DEBUG_MEM
+
+void *operator new(size_t size) {
+ return gmalloc((int)size, 1);
+}
+
+void *operator new(size_t size, int dummy) {
+ return gmalloc((int)size, 0);
+}
+
+void *operator new[](size_t size) {
+ return gmalloc((int)size, 1);
+}
+
+void *operator new[](size_t size, int dummy) {
+ return gmalloc((int)size, 0);
+}
+
+void operator delete(void *p) {
+ gfree(p);
+}
+
+// This is only called if a constructor throws an exception.
+void operator delete(void *p, int dummy) {
+ gfree(p);
+}
+
+void operator delete[](void *p) {
+ gfree(p);
+}
+
+// This is only called if a constructor throws an exception.
+void operator delete[](void *p, int dummy) {
+ gfree(p);
+}
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/gmempp.h b/Build/source/libs/xpdf/xpdf-src/goo/gmempp.h
new file mode 100644
index 00000000000..27f7fe786c5
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/gmempp.h
@@ -0,0 +1,48 @@
+//========================================================================
+//
+// gmempp.h
+//
+// Redefine the new and delete operators to call the debugging malloc.
+//
+// This is a hack: some libraries (Qt) allocate global objects and
+// never free them, so this file #define's new to have an argument, so
+// that we can differentiate our new calls from external library new
+// calls. Calls from external libraries will not be counted when
+// un-freed memory is listed. There's no guarantee that this will
+// work in all cases: if some external library also defines a new
+// operator that takes the same extra argument, things will break
+// horribly.
+//
+// This entire .h file is wrapped in '#ifdef DEBUG_MEM'. I.e., if you
+// don't define DEBUG_MEM, this hack goes away and can't cause any
+// problems. Do not define DEBUG_MEM in production code.
+//
+// To use this .h file, it must be #included *after* all system
+// includes. Calls to new in any modules that do not include this .h
+// file will not be counted as un-freed memory.
+//
+//========================================================================
+
+#ifndef GMEMPP_H
+#define GMEMPP_H
+
+#ifdef DEBUG_MEM
+
+#include <stdlib.h>
+
+extern void *operator new(size_t size, int dummy);
+extern void *operator new[](size_t size, int dummy);
+
+// These have to be defined (and declared) to avoid a compiler warning
+// with Visual Studio.
+extern void operator delete(void *p, int dummy);
+extern void operator delete[](void *p, int dummy);
+
+// This transforms 'new Foo(...)' into 'new (1) Foo(...)', which
+// forces a call to the operator new variant with the 'int dummy' arg.
+#define debug_new new (1)
+#define new debug_new
+
+#endif // DEBUG_MEM
+
+#endif // GMEMPP_H
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/gtypes.h b/Build/source/libs/xpdf/xpdf-src/goo/gtypes.h
new file mode 100644
index 00000000000..9f64f57d4a1
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/gtypes.h
@@ -0,0 +1,29 @@
+/*
+ * gtypes.h
+ *
+ * Some useful simple types.
+ *
+ * Copyright 1996-2003 Glyph & Cog, LLC
+ */
+
+#ifndef GTYPES_H
+#define GTYPES_H
+
+/*
+ * These have stupid names to avoid conflicts with some (but not all)
+ * C++ compilers which define them.
+ */
+typedef int GBool;
+#define gTrue 1
+#define gFalse 0
+
+/*
+ * These have stupid names to avoid conflicts with <sys/types.h>,
+ * which on various systems defines some random subset of these.
+ */
+typedef unsigned char Guchar;
+typedef unsigned short Gushort;
+typedef unsigned int Guint;
+typedef unsigned long Gulong;
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/parseargs.c b/Build/source/libs/xpdf/xpdf-src/goo/parseargs.c
new file mode 100644
index 00000000000..2f5844f06d4
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/parseargs.c
@@ -0,0 +1,195 @@
+/*
+ * parseargs.h
+ *
+ * Command line argument parser.
+ *
+ * Copyright 1996-2003 Glyph & Cog, LLC
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "parseargs.h"
+
+static ArgDesc *findArg(ArgDesc *args, char *arg);
+static GBool grabArg(ArgDesc *arg, int i, int *argc, char *argv[]);
+
+GBool parseArgs(ArgDesc *args, int *argc, char *argv[]) {
+ ArgDesc *arg;
+ int i, j;
+ GBool ok;
+
+ ok = gTrue;
+ i = 1;
+ while (i < *argc) {
+ if (!strcmp(argv[i], "--")) {
+ --*argc;
+ for (j = i; j < *argc; ++j) {
+ argv[j] = argv[j+1];
+ }
+ break;
+ } else if ((arg = findArg(args, argv[i]))) {
+ if (!grabArg(arg, i, argc, argv)) {
+ ok = gFalse;
+ }
+ } else if (argv[i][0] == '-') {
+ ok = gFalse;
+ break;
+ } else {
+ break;
+ }
+ }
+ return ok;
+}
+
+void printUsage(const char *program, const char *otherArgs, ArgDesc *args) {
+ ArgDesc *arg;
+ char *typ;
+ int w, w1;
+
+ w = 0;
+ for (arg = args; arg->arg; ++arg) {
+ if ((w1 = (int)strlen(arg->arg)) > w)
+ w = w1;
+ }
+
+ fprintf(stderr, "Usage: %s [options]", program);
+ if (otherArgs)
+ fprintf(stderr, " %s", otherArgs);
+ fprintf(stderr, "\n");
+
+ for (arg = args; arg->arg; ++arg) {
+ fprintf(stderr, " %s", arg->arg);
+ w1 = 9 + w - (int)strlen(arg->arg);
+ switch (arg->kind) {
+ case argInt:
+ case argIntDummy:
+ typ = " <int>";
+ break;
+ case argFP:
+ case argFPDummy:
+ typ = " <number>";
+ break;
+ case argString:
+ case argStringDummy:
+ typ = " <string>";
+ break;
+ case argFlag:
+ case argFlagDummy:
+ default:
+ typ = "";
+ break;
+ }
+ fprintf(stderr, "%-*s", w1, typ);
+ if (arg->usage)
+ fprintf(stderr, ": %s", arg->usage);
+ fprintf(stderr, "\n");
+ }
+}
+
+static ArgDesc *findArg(ArgDesc *args, char *arg) {
+ ArgDesc *p;
+
+ for (p = args; p->arg; ++p) {
+ if (p->kind < argFlagDummy && !strcmp(p->arg, arg))
+ return p;
+ }
+ return NULL;
+}
+
+static GBool grabArg(ArgDesc *arg, int i, int *argc, char *argv[]) {
+ int n;
+ int j;
+ GBool ok;
+
+ ok = gTrue;
+ n = 0;
+ switch (arg->kind) {
+ case argFlag:
+ *(GBool *)arg->val = gTrue;
+ n = 1;
+ break;
+ case argInt:
+ if (i + 1 < *argc && isInt(argv[i+1])) {
+ *(int *)arg->val = atoi(argv[i+1]);
+ n = 2;
+ } else {
+ ok = gFalse;
+ n = 1;
+ }
+ break;
+ case argFP:
+ if (i + 1 < *argc && isFP(argv[i+1])) {
+ *(double *)arg->val = atof(argv[i+1]);
+ n = 2;
+ } else {
+ ok = gFalse;
+ n = 1;
+ }
+ break;
+ case argString:
+ if (i + 1 < *argc) {
+ strncpy((char *)arg->val, argv[i+1], arg->size - 1);
+ ((char *)arg->val)[arg->size - 1] = '\0';
+ n = 2;
+ } else {
+ ok = gFalse;
+ n = 1;
+ }
+ break;
+ default:
+ fprintf(stderr, "Internal error in arg table\n");
+ n = 1;
+ break;
+ }
+ if (n > 0) {
+ *argc -= n;
+ for (j = i; j < *argc; ++j)
+ argv[j] = argv[j+n];
+ }
+ return ok;
+}
+
+GBool isInt(char *s) {
+ if (*s == '-' || *s == '+')
+ ++s;
+ while (isdigit(*s & 0xff))
+ ++s;
+ if (*s)
+ return gFalse;
+ return gTrue;
+}
+
+GBool isFP(char *s) {
+ int n;
+
+ if (*s == '-' || *s == '+')
+ ++s;
+ n = 0;
+ while (isdigit(*s & 0xff)) {
+ ++s;
+ ++n;
+ }
+ if (*s == '.')
+ ++s;
+ while (isdigit(*s & 0xff)) {
+ ++s;
+ ++n;
+ }
+ if (n > 0 && (*s == 'e' || *s == 'E')) {
+ ++s;
+ if (*s == '-' || *s == '+')
+ ++s;
+ n = 0;
+ if (!isdigit(*s & 0xff))
+ return gFalse;
+ do {
+ ++s;
+ } while (isdigit(*s & 0xff));
+ }
+ if (*s)
+ return gFalse;
+ return gTrue;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/goo/parseargs.h b/Build/source/libs/xpdf/xpdf-src/goo/parseargs.h
new file mode 100644
index 00000000000..b4daa3f29fc
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/goo/parseargs.h
@@ -0,0 +1,72 @@
+/*
+ * parseargs.h
+ *
+ * Command line argument parser.
+ *
+ * Copyright 1996-2003 Glyph & Cog, LLC
+ */
+
+#ifndef PARSEARGS_H
+#define PARSEARGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gtypes.h"
+
+/*
+ * Argument kinds.
+ */
+typedef enum {
+ argFlag, /* flag (present / not-present) */
+ /* [val: GBool *] */
+ argInt, /* integer arg */
+ /* [val: int *] */
+ argFP, /* floating point arg */
+ /* [val: double *] */
+ argString, /* string arg */
+ /* [val: char *] */
+ /* dummy entries -- these show up in the usage listing only; */
+ /* useful for X args, for example */
+ argFlagDummy,
+ argIntDummy,
+ argFPDummy,
+ argStringDummy
+} ArgKind;
+
+/*
+ * Argument descriptor.
+ */
+typedef struct {
+ const char *arg; /* the command line switch */
+ ArgKind kind; /* kind of arg */
+ void *val; /* place to store value */
+ int size; /* for argString: size of string */
+ const char *usage; /* usage string */
+} ArgDesc;
+
+/*
+ * Parse command line. Removes all args which are found in the arg
+ * descriptor list <args>. Stops parsing if "--" is found (and removes
+ * it). Returns gFalse if there was an error.
+ */
+extern GBool parseArgs(ArgDesc *args, int *argc, char *argv[]);
+
+/*
+ * Print usage message, based on arg descriptor list.
+ */
+extern void printUsage(const char *program, const char *otherArgs,
+ ArgDesc *args);
+
+/*
+ * Check if a string is a valid integer or floating point number.
+ */
+extern GBool isInt(char *s);
+extern GBool isFP(char *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/CMakeLists.txt b/Build/source/libs/xpdf/xpdf-src/splash/CMakeLists.txt
new file mode 100644
index 00000000000..f84968cfb2a
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/CMakeLists.txt
@@ -0,0 +1,47 @@
+#========================================================================
+#
+# splash/CMakeLists.txt
+#
+# CMake script for the splash library.
+#
+# Copyright 2015 Glyph & Cog, LLC
+#
+#========================================================================
+
+if (HAVE_SPLASH)
+ include_directories("${PROJECT_SOURCE_DIR}")
+ include_directories("${PROJECT_BINARY_DIR}")
+ include_directories("${PROJECT_SOURCE_DIR}/goo")
+ include_directories("${PROJECT_SOURCE_DIR}/fofi")
+ include_directories("${FREETYPE_INCLUDE_DIRS}")
+
+ if (HAVE_DTYPE4_H)
+ include_directories("${DTYPE_INCLUDE_DIR}")
+ set(DTYPE_SRCS
+ SplashDT4Font.cc SplashDT4FontEngine.cc SplashDT4FontFile.cc)
+ endif ()
+
+ add_library(splash_objs OBJECT
+ Splash.cc
+ SplashBitmap.cc
+ SplashClip.cc
+ SplashFTFont.cc
+ SplashFTFontEngine.cc
+ SplashFTFontFile.cc
+ SplashFont.cc
+ SplashFontEngine.cc
+ SplashFontFile.cc
+ SplashFontFileID.cc
+ SplashPath.cc
+ SplashPattern.cc
+ SplashScreen.cc
+ SplashState.cc
+ SplashXPath.cc
+ SplashXPathScanner.cc
+ ${DTYPE_SRCS}
+ )
+
+ add_library(splash
+ $<TARGET_OBJECTS:splash_objs>
+ )
+endif ()
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/Splash.cc b/Build/source/libs/xpdf/xpdf-src/splash/Splash.cc
new file mode 100644
index 00000000000..343db796fb2
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/Splash.cc
@@ -0,0 +1,8210 @@
+//========================================================================
+//
+// Splash.cc
+//
+// Copyright 2003-2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <math.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "SplashErrorCodes.h"
+#include "SplashMath.h"
+#include "SplashBitmap.h"
+#include "SplashState.h"
+#include "SplashPath.h"
+#include "SplashXPath.h"
+#include "SplashXPathScanner.h"
+#include "SplashPattern.h"
+#include "SplashScreen.h"
+#include "SplashFont.h"
+#include "SplashGlyphBitmap.h"
+#include "Splash.h"
+
+// the MSVC math.h doesn't define this
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+//------------------------------------------------------------------------
+
+// distance of Bezier control point from center for circle approximation
+// = (4 * (sqrt(2) - 1) / 3) * r
+#define bezierCircle ((SplashCoord)0.55228475)
+#define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475))
+
+// 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;
+}
+
+// Used by drawImage and fillImageMask to divide the target
+// quadrilateral into sections.
+struct ImageSection {
+ int y0, y1; // actual y range
+ int ia0, ia1; // vertex indices for edge A
+ int ib0, ib1; // vertex indices for edge B
+ SplashCoord xa0, ya0, xa1, ya1; // edge A
+ SplashCoord dxdya; // slope of edge A
+ SplashCoord xb0, yb0, xb1, yb1; // edge B
+ SplashCoord dxdyb; // slope of edge B
+};
+
+//------------------------------------------------------------------------
+// SplashPipe
+//------------------------------------------------------------------------
+
+#define splashPipeMaxStages 9
+
+struct SplashPipe {
+ // source pattern
+ SplashPattern *pattern;
+
+ // source alpha and color
+ 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;
+ SplashPipeResultColorCtrl resultColorCtrl;
+
+ // non-isolated group correction
+ // (this is only used when Splash::composite() is called to composite
+ // a non-isolated group onto the backdrop)
+ GBool nonIsolatedGroup;
+
+ // the "run" function
+ void (Splash::*run)(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+};
+
+SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
+ splashPipeResultColorNoAlphaBlendMono,
+ splashPipeResultColorNoAlphaBlendMono,
+ splashPipeResultColorNoAlphaBlendRGB,
+ splashPipeResultColorNoAlphaBlendRGB
+#if SPLASH_CMYK
+ ,
+ splashPipeResultColorNoAlphaBlendCMYK
+#endif
+};
+
+SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = {
+ splashPipeResultColorAlphaNoBlendMono,
+ splashPipeResultColorAlphaNoBlendMono,
+ splashPipeResultColorAlphaNoBlendRGB,
+ splashPipeResultColorAlphaNoBlendRGB
+#if SPLASH_CMYK
+ ,
+ splashPipeResultColorAlphaNoBlendCMYK
+#endif
+};
+
+SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = {
+ splashPipeResultColorAlphaBlendMono,
+ splashPipeResultColorAlphaBlendMono,
+ splashPipeResultColorAlphaBlendRGB,
+ splashPipeResultColorAlphaBlendRGB
+#if SPLASH_CMYK
+ ,
+ splashPipeResultColorAlphaBlendCMYK
+#endif
+};
+
+//------------------------------------------------------------------------
+// modified region
+//------------------------------------------------------------------------
+
+void Splash::clearModRegion() {
+ modXMin = bitmap->width;
+ modYMin = bitmap->height;
+ modXMax = -1;
+ modYMax = -1;
+}
+
+inline void Splash::updateModX(int x) {
+ if (x < modXMin) {
+ modXMin = x;
+ }
+ if (x > modXMax) {
+ modXMax = x;
+ }
+}
+
+inline void Splash::updateModY(int y) {
+ if (y < modYMin) {
+ modYMin = y;
+ }
+ if (y > modYMax) {
+ modYMax = y;
+ }
+}
+
+//------------------------------------------------------------------------
+// pipeline
+//------------------------------------------------------------------------
+
+inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern,
+ Guchar aInput, GBool usesShape,
+ GBool nonIsolatedGroup, GBool usesSrcOverprint) {
+ SplashColorMode mode;
+
+ mode = bitmap->mode;
+
+ pipe->pattern = NULL;
+
+ // source color
+ if (pattern && pattern->isStatic()) {
+ pattern->getColor(0, 0, pipe->cSrcVal);
+ pipe->pattern = NULL;
+ } else {
+ pipe->pattern = pattern;
+ }
+
+ // source alpha
+ pipe->aInput = aInput;
+
+ // source overprint mask
+ pipe->srcOverprintMaskPtr = NULL;
+
+ // special cases
+ pipe->noTransparency = aInput == 255 &&
+ !state->softMask &&
+ !usesShape &&
+ !state->inNonIsolatedGroup &&
+ !state->inKnockoutGroup &&
+ !nonIsolatedGroup &&
+ state->overprintMask == 0xffffffff;
+ pipe->shapeOnly = aInput == 255 &&
+ !state->softMask &&
+ usesShape &&
+ !state->inNonIsolatedGroup &&
+ !state->inKnockoutGroup &&
+ !nonIsolatedGroup &&
+ state->overprintMask == 0xffffffff;
+
+ // result color
+ if (pipe->noTransparency) {
+ // the !state->blendFunc case is handled separately in pipeRun
+ pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[mode];
+ } else if (!state->blendFunc) {
+ pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[mode];
+ } else {
+ pipe->resultColorCtrl = pipeResultColorAlphaBlend[mode];
+ }
+
+ // non-isolated group correction
+ pipe->nonIsolatedGroup = nonIsolatedGroup;
+
+ // select the 'run' function
+ pipe->run = &Splash::pipeRun;
+ 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) {
+ pipe->run = &Splash::pipeRunSimpleMono8;
+ } else if (mode == splashModeRGB8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSimpleRGB8;
+ } else if (mode == splashModeBGR8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSimpleBGR8;
+#if SPLASH_CMYK
+ } else if (mode == splashModeCMYK8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunSimpleCMYK8;
+#endif
+ }
+ } else if (!pipe->pattern && pipe->shapeOnly && !state->blendFunc) {
+ if (mode == splashModeMono1 && !bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeMono1;
+ } else if (mode == splashModeMono8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeMono8;
+ } else if (mode == splashModeRGB8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeRGB8;
+ } else if (mode == splashModeBGR8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunShapeBGR8;
+#if SPLASH_CMYK
+ } 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 &&
+ !(state->inNonIsolatedGroup && groupBackBitmap->alpha) &&
+ !state->inKnockoutGroup &&
+ !state->blendFunc && !pipe->nonIsolatedGroup) {
+ if (mode == splashModeMono1 && !bitmap->alpha) {
+ pipe->run = &Splash::pipeRunAAMono1;
+ } else if (mode == splashModeMono8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunAAMono8;
+ } else if (mode == splashModeRGB8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunAARGB8;
+ } else if (mode == splashModeBGR8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunAABGR8;
+#if SPLASH_CMYK
+ } else if (mode == splashModeCMYK8 && bitmap->alpha) {
+ 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 &&
+ !state->inKnockoutGroup &&
+ !state->blendFunc && !pipe->nonIsolatedGroup) {
+ if (mode == splashModeMono8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunNonIsoMono8;
+ } else if (mode == splashModeRGB8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunNonIsoRGB8;
+ } else if (mode == splashModeBGR8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunNonIsoBGR8;
+#if SPLASH_CMYK
+ } else if (mode == splashModeCMYK8 && bitmap->alpha) {
+ pipe->run = &Splash::pipeRunNonIsoCMYK8;
+#endif
+ }
+ }
+}
+
+// general case
+void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar *shapePtr2;
+ Guchar shape, aSrc, aDest, alphaI, alphaIm1, alpha0, aResult;
+ SplashColor cSrc, cDest, cBlend;
+ Guchar shapeVal, cResult0, cResult1, cResult2, cResult3;
+ int cSrcStride, shapeStride, x, lastX, t;
+ SplashColorPtr destColorPtr;
+ Guchar destColorMask;
+ Guchar *destAlphaPtr;
+ SplashColorPtr color0Ptr;
+ Guchar color0Mask;
+ Guchar *alpha0Ptr;
+ SplashColorPtr softMaskPtr;
+ Guint overprintMask;
+ Guint *overprintMaskPtr;
+#if SPLASH_CMYK
+ Guchar aPrev;
+ SplashColor cSrc2, cDest2;
+#endif
+
+ if (cSrcPtr && !pipe->pattern) {
+ cSrcStride = bitmapComps;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+
+ if (shapePtr) {
+ shapePtr2 = shapePtr;
+ shapeStride = 1;
+ for (; x0 <= x1; ++x0) {
+ if (*shapePtr2) {
+ break;
+ }
+ cSrcPtr += cSrcStride;
+ ++shapePtr2;
+ if (pipe->srcOverprintMaskPtr) {
+ ++pipe->srcOverprintMaskPtr;
+ }
+ }
+ } else {
+ shapeVal = 0xff;
+ shapePtr2 = &shapeVal;
+ shapeStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModY(y);
+ lastX = x0;
+
+ useDestRow(y);
+
+ if (bitmap->mode == splashModeMono1) {
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
+ destColorMask = (Guchar)(0x80 >> (x0 & 7));
+ } else {
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * bitmapComps];
+ destColorMask = 0; // make gcc happy
+ }
+ if (bitmap->alpha) {
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+ } else {
+ destAlphaPtr = NULL;
+ }
+ if (state->softMask) {
+ softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0];
+ } else {
+ softMaskPtr = NULL;
+ }
+ if (state->inKnockoutGroup) {
+ if (bitmap->mode == splashModeMono1) {
+ color0Ptr =
+ &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize +
+ ((groupBackX + x0) >> 3)];
+ color0Mask = (Guchar)(0x80 >> ((groupBackX + x0) & 7));
+ } else {
+ color0Ptr =
+ &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize +
+ (groupBackX + x0) * bitmapComps];
+ color0Mask = 0; // make gcc happy
+ }
+ } else {
+ color0Ptr = NULL;
+ color0Mask = 0; // make gcc happy
+ }
+ if (state->inNonIsolatedGroup && groupBackBitmap->alpha) {
+ alpha0Ptr =
+ &groupBackBitmap->alpha[(groupBackY + y)
+ * groupBackBitmap->alphaRowSize +
+ (groupBackX + x0)];
+ } else {
+ alpha0Ptr = NULL;
+ }
+ if (overprintMaskBitmap) {
+ overprintMaskPtr = overprintMaskBitmap + y * bitmap->width + x0;
+ } else {
+ overprintMaskPtr = NULL;
+ }
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+
+ shape = *shapePtr2;
+ if (!shape) {
+ if (bitmap->mode == splashModeMono1) {
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+ } else {
+ destColorPtr += bitmapComps;
+ }
+ if (destAlphaPtr) {
+ ++destAlphaPtr;
+ }
+ if (softMaskPtr) {
+ ++softMaskPtr;
+ }
+ if (color0Ptr) {
+ if (bitmap->mode == splashModeMono1) {
+ color0Ptr += color0Mask & 1;
+ color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1));
+ } else {
+ color0Ptr += bitmapComps;
+ }
+ }
+ if (alpha0Ptr) {
+ ++alpha0Ptr;
+ }
+ cSrcPtr += cSrcStride;
+ shapePtr2 += shapeStride;
+ if (pipe->srcOverprintMaskPtr) {
+ ++pipe->srcOverprintMaskPtr;
+ }
+ if (overprintMaskPtr) {
+ ++overprintMaskPtr;
+ }
+ continue;
+ }
+ lastX = x;
+
+ //----- source color
+
+ // static pattern: handled in pipeInit
+ // fixed color: handled in pipeInit
+
+ // dynamic pattern
+ if (pipe->pattern) {
+ pipe->pattern->getColor(x, y, pipe->cSrcVal);
+ }
+
+ cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
+
+ if (pipe->noTransparency && !state->blendFunc) {
+
+ //----- result color
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ cResult0 = state->grayTransfer[cSrcPtr[0]];
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ cResult0 = state->rgbTransferR[cSrcPtr[0]];
+ cResult1 = state->rgbTransferG[cSrcPtr[1]];
+ cResult2 = state->rgbTransferB[cSrcPtr[2]];
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ cResult0 = state->cmykTransferC[cSrcPtr[0]];
+ cResult1 = state->cmykTransferM[cSrcPtr[1]];
+ cResult2 = state->cmykTransferY[cSrcPtr[2]];
+ cResult3 = state->cmykTransferK[cSrcPtr[3]];
+ break;
+#endif
+ }
+ aResult = 255;
+
+ } else { // if (noTransparency && !blendFunc)
+
+ //----- read destination pixel
+ // (or backdrop color, for knockout groups)
+
+ if (color0Ptr) {
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ cDest[0] = (*color0Ptr & color0Mask) ? 0xff : 0x00;
+ color0Ptr += color0Mask & 1;
+ color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1));
+ break;
+ case splashModeMono8:
+ cDest[0] = *color0Ptr++;
+ break;
+ case splashModeRGB8:
+ cDest[0] = color0Ptr[0];
+ cDest[1] = color0Ptr[1];
+ cDest[2] = color0Ptr[2];
+ color0Ptr += 3;
+ break;
+ case splashModeBGR8:
+ cDest[2] = color0Ptr[0];
+ cDest[1] = color0Ptr[1];
+ cDest[0] = color0Ptr[2];
+ color0Ptr += 3;
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ cDest[0] = color0Ptr[0];
+ cDest[1] = color0Ptr[1];
+ cDest[2] = color0Ptr[2];
+ cDest[3] = color0Ptr[3];
+ color0Ptr += 4;
+ break;
+#endif
+ }
+
+ } else {
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ cDest[0] = (*destColorPtr & destColorMask) ? 0xff : 0x00;
+ break;
+ case splashModeMono8:
+ cDest[0] = *destColorPtr;
+ break;
+ case splashModeRGB8:
+ cDest[0] = destColorPtr[0];
+ cDest[1] = destColorPtr[1];
+ cDest[2] = destColorPtr[2];
+ break;
+ case splashModeBGR8:
+ cDest[0] = destColorPtr[2];
+ cDest[1] = destColorPtr[1];
+ cDest[2] = destColorPtr[0];
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ cDest[0] = destColorPtr[0];
+ cDest[1] = destColorPtr[1];
+ cDest[2] = destColorPtr[2];
+ cDest[3] = destColorPtr[3];
+ break;
+#endif
+ }
+
+ }
+
+ if (destAlphaPtr) {
+ aDest = *destAlphaPtr;
+ } else {
+ aDest = 0xff;
+ }
+
+ //----- 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:
+ cSrc[0] = state->grayTransfer[cSrcPtr[0]];
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ cSrc[0] = state->rgbTransferR[cSrcPtr[0]];
+ cSrc[1] = state->rgbTransferG[cSrcPtr[1]];
+ cSrc[2] = state->rgbTransferB[cSrcPtr[2]];
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ if (alpha0Ptr) { // non-isolated group
+ if (color0Ptr) { // non-isolated, knockout group
+ aPrev = *alpha0Ptr;
+ } else { // non-isolated, non-knockout group
+ aPrev = (Guchar)(*alpha0Ptr + aDest - div255(*alpha0Ptr * aDest));
+ }
+ } else { // isolated group
+ if (color0Ptr) { // isolated, knockout group
+ aPrev = 0;
+ } else { // isolated, non-knockout group
+ aPrev = aDest;
+ }
+ }
+ if (overprintMask & 0x01) {
+ cSrc[0] = state->cmykTransferC[cSrcPtr[0]];
+ } else {
+ cSrc[0] = div255(aPrev * cDest[0]);
+ }
+ if (overprintMask & 0x02) {
+ cSrc[1] = state->cmykTransferM[cSrcPtr[1]];
+ } else {
+ cSrc[1] = div255(aPrev * cDest[1]);
+ }
+ if (overprintMask & 0x04) {
+ cSrc[2] = state->cmykTransferY[cSrcPtr[2]];
+ } else {
+ cSrc[2] = div255(aPrev * cDest[2]);
+ }
+ if (overprintMask & 0x08) {
+ cSrc[3] = state->cmykTransferK[cSrcPtr[3]];
+ } else {
+ cSrc[3] = div255(aPrev * cDest[3]);
+ }
+ break;
+#endif
+ }
+
+ //----- source alpha
+
+ if (softMaskPtr) {
+ if (shapePtr) {
+ aSrc = div255(div255(pipe->aInput * *softMaskPtr++) * shape);
+ } else {
+ aSrc = div255(pipe->aInput * *softMaskPtr++);
+ }
+ } else if (shapePtr) {
+ aSrc = div255(pipe->aInput * shape);
+ } else {
+ aSrc = pipe->aInput;
+ }
+
+ //----- non-isolated group correction
+
+ if (pipe->nonIsolatedGroup) {
+ // This path is only used when Splash::composite() is called to
+ // composite a non-isolated group onto the backdrop. In this
+ // case, shape is the source (group) alpha.
+ t = (aDest * 255) / shape - aDest;
+ switch (bitmap->mode) {
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ cSrc[3] = clip255(cSrc[3] + ((cSrc[3] - cDest[3]) * t) / 255);
+#endif
+ case splashModeRGB8:
+ case splashModeBGR8:
+ cSrc[2] = clip255(cSrc[2] + ((cSrc[2] - cDest[2]) * t) / 255);
+ cSrc[1] = clip255(cSrc[1] + ((cSrc[1] - cDest[1]) * t) / 255);
+ case splashModeMono1:
+ case splashModeMono8:
+ cSrc[0] = clip255(cSrc[0] + ((cSrc[0] - cDest[0]) * t) / 255);
+ break;
+ }
+ }
+
+ //----- blend function
+
+ if (state->blendFunc) {
+#if SPLASH_CMYK
+ if (bitmap->mode == splashModeCMYK8) {
+ // convert colors to additive
+ cSrc2[0] = (Guchar)(0xff - cSrc[0]);
+ cSrc2[1] = (Guchar)(0xff - cSrc[1]);
+ cSrc2[2] = (Guchar)(0xff - cSrc[2]);
+ cSrc2[3] = (Guchar)(0xff - cSrc[3]);
+ cDest2[0] = (Guchar)(0xff - cDest[0]);
+ cDest2[1] = (Guchar)(0xff - cDest[1]);
+ cDest2[2] = (Guchar)(0xff - cDest[2]);
+ cDest2[3] = (Guchar)(0xff - cDest[3]);
+ (*state->blendFunc)(cSrc2, cDest2, cBlend, bitmap->mode);
+ // convert result back to subtractive
+ cBlend[0] = (Guchar)(0xff - cBlend[0]);
+ cBlend[1] = (Guchar)(0xff - cBlend[1]);
+ cBlend[2] = (Guchar)(0xff - cBlend[2]);
+ cBlend[3] = (Guchar)(0xff - cBlend[3]);
+ } else
+#endif
+ (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode);
+ }
+
+ //----- result alpha and non-isolated group element correction
+
+ // alphaI = alpha_i
+ // alphaIm1 = alpha_(i-1)
+
+ if (pipe->noTransparency) {
+ alphaI = alphaIm1 = aResult = 255;
+ } else if (alpha0Ptr) {
+ if (color0Ptr) {
+ // non-isolated, knockout
+ aResult = aSrc;
+ alpha0 = *alpha0Ptr++;
+ alphaI = (Guchar)(aSrc + alpha0 - div255(aSrc * alpha0));
+ alphaIm1 = alpha0;
+ } else {
+ // non-isolated, non-knockout
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alpha0 = *alpha0Ptr++;
+ alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
+ alphaIm1 = (Guchar)(alpha0 + aDest - div255(alpha0 * aDest));
+ }
+ } else {
+ if (color0Ptr) {
+ // isolated, knockout
+ aResult = aSrc;
+ alphaI = aSrc;
+ alphaIm1 = 0;
+ } else {
+ // isolated, non-knockout
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+ alphaIm1 = aDest;
+ }
+ }
+
+ //----- result color
+
+ switch (pipe->resultColorCtrl) {
+
+ case splashPipeResultColorNoAlphaBlendMono:
+ cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]);
+ break;
+ case splashPipeResultColorNoAlphaBlendRGB:
+ cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]);
+ cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]);
+ cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]);
+ break;
+#if SPLASH_CMYK
+ case splashPipeResultColorNoAlphaBlendCMYK:
+ cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]);
+ cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]);
+ cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]);
+ cResult3 = div255((255 - aDest) * cSrc[3] + aDest * cBlend[3]);
+ break;
+#endif
+
+ case splashPipeResultColorAlphaNoBlendMono:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0])
+ / alphaI);
+ }
+ break;
+ case splashPipeResultColorAlphaNoBlendRGB:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0])
+ / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1])
+ / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2])
+ / alphaI);
+ }
+ break;
+#if SPLASH_CMYK
+ case splashPipeResultColorAlphaNoBlendCMYK:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ cResult3 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0])
+ / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1])
+ / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2])
+ / alphaI);
+ cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] + aSrc * cSrc[3])
+ / alphaI);
+ }
+ break;
+#endif
+
+ case splashPipeResultColorAlphaBlendMono:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] +
+ aSrc * ((255 - alphaIm1) * cSrc[0] +
+ alphaIm1 * cBlend[0]) / 255)
+ / alphaI);
+ }
+ break;
+ case splashPipeResultColorAlphaBlendRGB:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] +
+ aSrc * ((255 - alphaIm1) * cSrc[0] +
+ alphaIm1 * cBlend[0]) / 255)
+ / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] +
+ aSrc * ((255 - alphaIm1) * cSrc[1] +
+ alphaIm1 * cBlend[1]) / 255)
+ / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] +
+ aSrc * ((255 - alphaIm1) * cSrc[2] +
+ alphaIm1 * cBlend[2]) / 255)
+ / alphaI);
+ }
+ break;
+#if SPLASH_CMYK
+ case splashPipeResultColorAlphaBlendCMYK:
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ cResult3 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] +
+ aSrc * ((255 - alphaIm1) * cSrc[0] +
+ alphaIm1 * cBlend[0]) / 255)
+ / alphaI);
+ cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] +
+ aSrc * ((255 - alphaIm1) * cSrc[1] +
+ alphaIm1 * cBlend[1]) / 255)
+ / alphaI);
+ cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] +
+ aSrc * ((255 - alphaIm1) * cSrc[2] +
+ alphaIm1 * cBlend[2]) / 255)
+ / alphaI);
+ cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] +
+ aSrc * ((255 - alphaIm1) * cSrc[3] +
+ alphaIm1 * cBlend[3]) / 255)
+ / alphaI);
+ }
+ break;
+#endif
+ }
+
+ } // if (noTransparency && !blendFunc)
+
+ //----- write destination pixel
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ if (state->screen->test(x, y, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= (Guchar)~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+ break;
+ case splashModeMono8:
+ *destColorPtr++ = cResult0;
+ break;
+ case splashModeRGB8:
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr += 3;
+ break;
+ case splashModeBGR8:
+ destColorPtr[0] = cResult2;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult0;
+ destColorPtr += 3;
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ destColorPtr[0] = cResult0;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult2;
+ destColorPtr[3] = cResult3;
+ destColorPtr += 4;
+ break;
+#endif
+ }
+ if (destAlphaPtr) {
+ *destAlphaPtr++ = aResult;
+ }
+
+ cSrcPtr += cSrcStride;
+ shapePtr2 += shapeStride;
+ } // for (x ...)
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
+// bitmap->mode == splashModeMono1 && !bitmap->alpha) {
+void Splash::pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar destColorMask;
+ SplashScreenCursor screenCursor;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)];
+ destColorMask = (Guchar)(0x80 >> (x0 & 7));
+
+ screenCursor = state->screen->getTestCursor(y);
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ cResult0 = state->grayTransfer[cSrcPtr[0]];
+ if (state->screen->testWithCursor(screenCursor, x, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= (Guchar)~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+
+ cSrcPtr += cSrcStride;
+ }
+}
+
+// special case:
+// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
+// bitmap->mode == splashModeMono8 && bitmap->alpha) {
+void Splash::pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 1;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ *destColorPtr++ = state->grayTransfer[cSrcPtr[0]];
+ *destAlphaPtr++ = 255;
+
+ cSrcPtr += cSrcStride;
+ }
+}
+
+// special case:
+// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
+// bitmap->mode == splashModeRGB8 && bitmap->alpha) {
+void Splash::pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]];
+ destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]];
+ destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]];
+ destColorPtr += 3;
+ *destAlphaPtr++ = 255;
+
+ cSrcPtr += cSrcStride;
+ }
+}
+
+// special case:
+// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
+// bitmap->mode == splashModeBGR8 && bitmap->alpha) {
+void Splash::pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 3;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]];
+ destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]];
+ destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]];
+ destColorPtr += 3;
+ *destAlphaPtr++ = 255;
+
+ cSrcPtr += cSrcStride;
+ }
+}
+
+#if SPLASH_CMYK
+// special case:
+// !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
+// bitmap->mode == splashModeCMYK8 && bitmap->alpha) {
+void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ int cSrcStride, x;
+
+ if (cSrcPtr) {
+ cSrcStride = 4;
+ } else {
+ cSrcPtr = pipe->cSrcVal;
+ cSrcStride = 0;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ updateModX(x0);
+ updateModX(x1);
+ updateModY(y);
+
+ useDestRow(y);
+
+ destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- write destination pixel
+ destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]];
+ destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]];
+ destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]];
+ destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]];
+ destColorPtr += 4;
+ *destAlphaPtr++ = 255;
+
+ cSrcPtr += cSrcStride;
+ }
+}
+#endif
+
+
+// special case:
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeMono1 && !bitmap->alpha
+void Splash::pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar destColorMask;
+ SplashScreenCursor screenCursor;
+ 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 >> 3)];
+ destColorMask = (Guchar)(0x80 >> (x0 & 7));
+
+ screenCursor = state->screen->getTestCursor(y);
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+ 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 & destColorMask) ? 0xff : 0x00;
+
+ //----- result color
+ // note: aDest = alphaI = aResult = 0xff
+ cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0);
+ }
+
+ //----- write destination pixel
+ if (state->screen->testWithCursor(screenCursor, x, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= (Guchar)~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeMono8 && bitmap->alpha
+void Splash::pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ 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];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ ++destColorPtr;
+ ++destAlphaPtr;
+ 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) {
+ 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;
+
+ //----- 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++ = cResult0;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeRGB8 && bitmap->alpha
+void Splash::pipeRunShapeRGB8(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;
+ 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 + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = 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 && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeBGR8 && bitmap->alpha
+void Splash::pipeRunShapeBGR8(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;
+ 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 + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = 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[0] = cResult2;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult0;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+#if SPLASH_CMYK
+// special case:
+// !pipe->pattern && pipe->shapeOnly && !state->blendFunc &&
+// bitmap->mode == splashModeCMYK8 && bitmap->alpha
+void Splash::pipeRunShapeCMYK8(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;
+ 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 + 4 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 4;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ cDest3 = destColorPtr[3];
+ 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 = 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
+
+
+// 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 &&
+// bitmap->mode == splashModeMono1 && !bitmap->alpha
+void Splash::pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar destColorMask;
+ SplashScreenCursor screenCursor;
+ 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 >> 3)];
+ destColorMask = (Guchar)(0x80 >> (x0 & 7));
+
+ screenCursor = state->screen->getTestCursor(y);
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00;
+
+ //----- source color
+ cSrc0 = state->grayTransfer[cSrcPtr[0]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result color
+ // note: aDest = alphaI = aResult = 0xff
+ cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0);
+
+ //----- write destination pixel
+ if (state->screen->testWithCursor(screenCursor, x, cResult0)) {
+ *destColorPtr |= destColorMask;
+ } else {
+ *destColorPtr &= (Guchar)~destColorMask;
+ }
+ destColorPtr += destColorMask & 1;
+ destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1));
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeMono8 && bitmap->alpha
+void Splash::pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ 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];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ ++destColorPtr;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = *destColorPtr;
+ aDest = *destAlphaPtr;
+
+ //----- source color
+ cSrc0 = state->grayTransfer[cSrcPtr[0]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ }
+
+ //----- write destination pixel
+ *destColorPtr++ = cResult0;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeRGB8 && bitmap->alpha
+void Splash::pipeRunAARGB8(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;
+ 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 + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ aDest = *destAlphaPtr;
+
+ //----- source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ 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 && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeBGR8 && bitmap->alpha
+void Splash::pipeRunAABGR8(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;
+ 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 + 3 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[2];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[0];
+ aDest = *destAlphaPtr;
+
+ //----- source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ 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] = cResult2;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult0;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ }
+
+ updateModX(lastX);
+}
+
+#if SPLASH_CMYK
+// special case:
+// !pipe->pattern && !pipe->noTransparency && !state->softMask &&
+// pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
+// !pipe->nonIsolatedGroup &&
+// bitmap->mode == splashModeCMYK8 && bitmap->alpha
+void Splash::pipeRunAACMYK8(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;
+ 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 + 4 * x0];
+ destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 4;
+ ++destAlphaPtr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ cDest3 = destColorPtr[3];
+ 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);
+ }
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alphaI = aResult;
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ cResult3 = 0;
+ } else {
+ 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
+
+
+// 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;
+ Guchar cSrc0, cDest0, cResult0;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ Guchar *alpha0Ptr;
+ 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];
+ alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
+ * groupBackBitmap->alphaRowSize +
+ (groupBackX + x0)];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 1;
+ ++destAlphaPtr;
+ ++alpha0Ptr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ aDest = *destAlphaPtr;
+
+ //----- source color
+ cSrc0 = state->grayTransfer[cSrcPtr[0]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alpha0 = *alpha0Ptr++;
+ alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ } else {
+ cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI);
+ }
+
+ //----- write destination pixel
+ *destColorPtr++ = cResult0;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ } // for (x ...)
+
+ updateModX(lastX);
+}
+
+void Splash::pipeRunNonIsoRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, alpha0, aResult;
+ Guchar cSrc0, cSrc1, cSrc2;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ Guchar *alpha0Ptr;
+ 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];
+ alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
+ * groupBackBitmap->alphaRowSize +
+ (groupBackX + x0)];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ ++alpha0Ptr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ aDest = *destAlphaPtr;
+
+ //----- source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alpha0 = *alpha0Ptr++;
+ alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ 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;
+ } // for (x ...)
+
+ updateModX(lastX);
+}
+
+void Splash::pipeRunNonIsoBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, alpha0, aResult;
+ Guchar cSrc0, cSrc1, cSrc2;
+ Guchar cDest0, cDest1, cDest2;
+ Guchar cResult0, cResult1, cResult2;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ Guchar *alpha0Ptr;
+ 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];
+ alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
+ * groupBackBitmap->alphaRowSize +
+ (groupBackX + x0)];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 3;
+ ++destAlphaPtr;
+ ++alpha0Ptr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[2];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[0];
+ aDest = *destAlphaPtr;
+
+ //----- source color
+ cSrc0 = state->rgbTransferR[cSrcPtr[0]];
+ cSrc1 = state->rgbTransferG[cSrcPtr[1]];
+ cSrc2 = state->rgbTransferB[cSrcPtr[2]];
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alpha0 = *alpha0Ptr++;
+ alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ } else {
+ 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] = cResult2;
+ destColorPtr[1] = cResult1;
+ destColorPtr[2] = cResult0;
+ destColorPtr += 3;
+ *destAlphaPtr++ = aResult;
+
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ } // for (x ...)
+
+ updateModX(lastX);
+}
+
+#if SPLASH_CMYK
+void Splash::pipeRunNonIsoCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr) {
+ Guchar shape, aSrc, aDest, alphaI, alpha0, aResult, aPrev;
+ Guchar cSrc0, cSrc1, cSrc2, cSrc3;
+ Guchar cDest0, cDest1, cDest2, cDest3;
+ Guchar cResult0, cResult1, cResult2, cResult3;
+ SplashColorPtr destColorPtr;
+ Guchar *destAlphaPtr;
+ Guchar *alpha0Ptr;
+ 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];
+ alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y)
+ * groupBackBitmap->alphaRowSize +
+ (groupBackX + x0)];
+
+ for (x = x0; x <= x1; ++x) {
+
+ //----- shape
+ shape = *shapePtr;
+ if (!shape) {
+ destColorPtr += 4;
+ ++destAlphaPtr;
+ ++alpha0Ptr;
+ cSrcPtr += cSrcStride;
+ ++shapePtr;
+ continue;
+ }
+ lastX = x;
+
+ //----- read destination pixel
+ cDest0 = destColorPtr[0];
+ cDest1 = destColorPtr[1];
+ cDest2 = destColorPtr[2];
+ cDest3 = destColorPtr[3];
+ aDest = *destAlphaPtr;
+
+ //----- overprint
+ aPrev = (Guchar)(*alpha0Ptr + aDest - div255(*alpha0Ptr * aDest));
+ if (state->overprintMask & 0x01) {
+ cSrc0 = state->cmykTransferC[cSrcPtr[0]];
+ } else {
+ cSrc0 = div255(aPrev * cDest0);
+ }
+ if (state->overprintMask & 0x02) {
+ cSrc1 = state->cmykTransferM[cSrcPtr[1]];
+ } else {
+ cSrc1 = div255(aPrev * cDest1);
+ }
+ if (state->overprintMask & 0x04) {
+ cSrc2 = state->cmykTransferY[cSrcPtr[2]];
+ } else {
+ cSrc2 = div255(aPrev * cDest2);
+ }
+ if (state->overprintMask & 0x08) {
+ cSrc3 = state->cmykTransferK[cSrcPtr[3]];
+ } else {
+ cSrc3 = div255(aPrev * cDest3);
+ }
+
+ //----- source alpha
+ aSrc = div255(pipe->aInput * shape);
+
+ //----- result alpha and non-isolated group element correction
+ aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest));
+ alpha0 = *alpha0Ptr++;
+ alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0));
+
+ //----- result color
+ if (alphaI == 0) {
+ cResult0 = 0;
+ cResult1 = 0;
+ cResult2 = 0;
+ cResult3 = 0;
+ } else {
+ 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;
+ } // for (x ...)
+
+ updateModX(lastX);
+}
+#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.
+inline void Splash::transform(SplashCoord *matrix,
+ SplashCoord xi, SplashCoord yi,
+ SplashCoord *xo, SplashCoord *yo) {
+ // [ m[0] m[1] 0 ]
+ // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ]
+ // [ m[4] m[5] 1 ]
+ *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
+ *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
+}
+
+//------------------------------------------------------------------------
+// SplashImageCache
+//------------------------------------------------------------------------
+
+SplashImageCache::SplashImageCache() {
+ tag = NULL;
+ width = 0;
+ height = 0;
+ mode = splashModeRGB8;
+ alpha = gFalse;
+ interpolate = gFalse;
+ colorData = NULL;
+ alphaData = NULL;
+ refCount = 1;
+}
+
+SplashImageCache::~SplashImageCache() {
+ if (tag) {
+ delete tag;
+ }
+ 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() {
+ ++refCount;
+}
+
+void SplashImageCache::decRefCount() {
+ --refCount;
+ if (refCount == 0) {
+ delete this;
+ }
+}
+
+//------------------------------------------------------------------------
+// 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
+//------------------------------------------------------------------------
+
+Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
+ SplashImageCache *imageCacheA,
+ SplashScreenParams *screenParams) {
+ bitmap = bitmapA;
+ bitmapComps = splashColorModeNComps[bitmap->mode];
+ vectorAntialias = vectorAntialiasA;
+ inShading = gFalse;
+ state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
+ screenParams);
+ scanBuf = (Guchar *)gmalloc(bitmap->width);
+ if (bitmap->mode == splashModeMono1) {
+ scanBuf2 = (Guchar *)gmalloc(bitmap->width);
+ } else {
+ scanBuf2 = NULL;
+ }
+ groupBackBitmap = NULL;
+ groupDestInitMode = splashGroupDestPreInit;
+ overprintMaskBitmap = NULL;
+ minLineWidth = 0;
+ clearModRegion();
+ debugMode = gFalse;
+
+ if (imageCacheA) {
+ imageCache = imageCacheA;
+ imageCache->incRefCount();
+ } else {
+ imageCache = new SplashImageCache();
+ }
+}
+
+Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
+ SplashImageCache *imageCacheA, SplashScreen *screenA) {
+ bitmap = bitmapA;
+ bitmapComps = splashColorModeNComps[bitmap->mode];
+ vectorAntialias = vectorAntialiasA;
+ inShading = gFalse;
+ state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
+ screenA);
+ scanBuf = (Guchar *)gmalloc(bitmap->width);
+ if (bitmap->mode == splashModeMono1) {
+ scanBuf2 = (Guchar *)gmalloc(bitmap->width);
+ } else {
+ scanBuf2 = NULL;
+ }
+ groupBackBitmap = NULL;
+ groupDestInitMode = splashGroupDestPreInit;
+ overprintMaskBitmap = NULL;
+ minLineWidth = 0;
+ clearModRegion();
+ debugMode = gFalse;
+
+ if (imageCacheA) {
+ imageCache = imageCacheA;
+ imageCache->incRefCount();
+ } else {
+ imageCache = new SplashImageCache();
+ }
+}
+
+Splash::~Splash() {
+ imageCache->decRefCount();
+
+ while (state->next) {
+ restoreState();
+ }
+ delete state;
+ gfree(scanBuf);
+ gfree(scanBuf2);
+}
+
+//------------------------------------------------------------------------
+// state read
+//------------------------------------------------------------------------
+
+SplashCoord *Splash::getMatrix() {
+ return state->matrix;
+}
+
+SplashPattern *Splash::getStrokePattern() {
+ return state->strokePattern;
+}
+
+SplashPattern *Splash::getFillPattern() {
+ return state->fillPattern;
+}
+
+SplashScreen *Splash::getScreen() {
+ return state->screen;
+}
+
+SplashBlendFunc Splash::getBlendFunc() {
+ return state->blendFunc;
+}
+
+SplashCoord Splash::getStrokeAlpha() {
+ return state->strokeAlpha;
+}
+
+SplashCoord Splash::getFillAlpha() {
+ return state->fillAlpha;
+}
+
+SplashCoord Splash::getLineWidth() {
+ return state->lineWidth;
+}
+
+int Splash::getLineCap() {
+ return state->lineCap;
+}
+
+int Splash::getLineJoin() {
+ return state->lineJoin;
+}
+
+SplashCoord Splash::getMiterLimit() {
+ return state->miterLimit;
+}
+
+SplashCoord Splash::getFlatness() {
+ return state->flatness;
+}
+
+SplashCoord *Splash::getLineDash() {
+ return state->lineDash;
+}
+
+int Splash::getLineDashLength() {
+ return state->lineDashLength;
+}
+
+SplashCoord Splash::getLineDashPhase() {
+ return state->lineDashPhase;
+}
+
+SplashStrokeAdjustMode Splash::getStrokeAdjust() {
+ return state->strokeAdjust;
+}
+
+SplashClip *Splash::getClip() {
+ return state->clip;
+}
+
+SplashBitmap *Splash::getSoftMask() {
+ return state->softMask;
+}
+
+GBool Splash::getInNonIsolatedGroup() {
+ return state->inNonIsolatedGroup;
+}
+
+GBool Splash::getInKnockoutGroup() {
+ return state->inKnockoutGroup;
+}
+
+//------------------------------------------------------------------------
+// state write
+//------------------------------------------------------------------------
+
+void Splash::setMatrix(SplashCoord *matrix) {
+ memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
+}
+
+void Splash::setStrokePattern(SplashPattern *strokePattern) {
+ state->setStrokePattern(strokePattern);
+}
+
+void Splash::setFillPattern(SplashPattern *fillPattern) {
+ state->setFillPattern(fillPattern);
+}
+
+void Splash::setScreen(SplashScreen *screen) {
+ state->setScreen(screen);
+}
+
+void Splash::setBlendFunc(SplashBlendFunc func) {
+ state->blendFunc = func;
+}
+
+void Splash::setStrokeAlpha(SplashCoord alpha) {
+ state->strokeAlpha = alpha;
+}
+
+void Splash::setFillAlpha(SplashCoord alpha) {
+ state->fillAlpha = alpha;
+}
+
+void Splash::setLineWidth(SplashCoord lineWidth) {
+ state->lineWidth = lineWidth;
+}
+
+void Splash::setLineCap(int lineCap) {
+ if (lineCap >= 0 && lineCap <= 2) {
+ state->lineCap = lineCap;
+ } else {
+ state->lineCap = 0;
+ }
+}
+
+void Splash::setLineJoin(int lineJoin) {
+ if (lineJoin >= 0 && lineJoin <= 2) {
+ state->lineJoin = lineJoin;
+ } else {
+ state->lineJoin = 0;
+ }
+}
+
+void Splash::setMiterLimit(SplashCoord miterLimit) {
+ state->miterLimit = miterLimit;
+}
+
+void Splash::setFlatness(SplashCoord flatness) {
+ if (flatness < 1) {
+ state->flatness = 1;
+ } else {
+ state->flatness = flatness;
+ }
+}
+
+void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
+ SplashCoord lineDashPhase) {
+ state->setLineDash(lineDash, lineDashLength, lineDashPhase);
+}
+
+void Splash::setStrokeAdjust(SplashStrokeAdjustMode strokeAdjust) {
+ state->strokeAdjust = strokeAdjust;
+}
+
+void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ state->clipResetToRect(x0, y0, x1, y1);
+}
+
+SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ return state->clipToRect(x0, y0, x1, y1);
+}
+
+SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
+ return state->clipToPath(path, eo);
+}
+
+void Splash::setSoftMask(SplashBitmap *softMask) {
+ state->setSoftMask(softMask);
+}
+
+void Splash::setInTransparencyGroup(SplashBitmap *groupBackBitmapA,
+ int groupBackXA, int groupBackYA,
+ SplashGroupDestInitMode groupDestInitModeA,
+ GBool nonIsolated, GBool knockout) {
+ groupBackBitmap = groupBackBitmapA;
+ groupBackX = groupBackXA;
+ groupBackY = groupBackYA;
+ groupDestInitMode = groupDestInitModeA;
+ groupDestInitYMin = 1;
+ groupDestInitYMax = 0;
+ state->inNonIsolatedGroup = nonIsolated;
+ state->inKnockoutGroup = knockout;
+}
+
+void Splash::forceDeferredInit(int y, int h) {
+ useDestRow(y);
+ useDestRow(y + h - 1);
+}
+
+void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue,
+ Guchar *gray) {
+ state->setTransfer(red, green, blue, gray);
+}
+
+void Splash::setOverprintMask(Guint overprintMask) {
+ state->overprintMask = overprintMask;
+}
+
+
+void Splash::setEnablePathSimplification(GBool en) {
+ state->enablePathSimplification = en;
+}
+
+//------------------------------------------------------------------------
+// state save/restore
+//------------------------------------------------------------------------
+
+void Splash::saveState() {
+ SplashState *newState;
+
+ newState = state->copy();
+ newState->next = state;
+ state = newState;
+}
+
+SplashError Splash::restoreState() {
+ SplashState *oldState;
+
+ if (!state->next) {
+ return splashErrNoSave;
+ }
+ oldState = state;
+ state = state->next;
+ delete oldState;
+ return splashOk;
+}
+
+//------------------------------------------------------------------------
+// drawing operations
+//------------------------------------------------------------------------
+
+void Splash::clear(SplashColorPtr color, Guchar alpha) {
+ SplashColorPtr row, p;
+ Guchar mono;
+ int x, y;
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ mono = (color[0] & 0x80) ? 0xff : 0x00;
+ if (bitmap->rowSize < 0) {
+ memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
+ mono, -bitmap->rowSize * bitmap->height);
+ } else {
+ memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
+ }
+ break;
+ case splashModeMono8:
+ if (bitmap->rowSize < 0) {
+ memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
+ color[0], -bitmap->rowSize * bitmap->height);
+ } else {
+ memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
+ }
+ break;
+ case splashModeRGB8:
+ if (color[0] == color[1] && color[1] == color[2]) {
+ if (bitmap->rowSize < 0) {
+ memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
+ color[0], -bitmap->rowSize * bitmap->height);
+ } else {
+ memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
+ }
+ } else {
+ row = bitmap->data;
+ for (y = 0; y < bitmap->height; ++y) {
+ p = row;
+ for (x = 0; x < bitmap->width; ++x) {
+ *p++ = color[0];
+ *p++ = color[1];
+ *p++ = color[2];
+ }
+ row += bitmap->rowSize;
+ }
+ }
+ break;
+ case splashModeBGR8:
+ if (color[0] == color[1] && color[1] == color[2]) {
+ if (bitmap->rowSize < 0) {
+ memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
+ color[0], -bitmap->rowSize * bitmap->height);
+ } else {
+ memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
+ }
+ } else {
+ row = bitmap->data;
+ for (y = 0; y < bitmap->height; ++y) {
+ p = row;
+ for (x = 0; x < bitmap->width; ++x) {
+ *p++ = color[2];
+ *p++ = color[1];
+ *p++ = color[0];
+ }
+ row += bitmap->rowSize;
+ }
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
+ if (bitmap->rowSize < 0) {
+ memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
+ color[0], -bitmap->rowSize * bitmap->height);
+ } else {
+ memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
+ }
+ } else {
+ row = bitmap->data;
+ for (y = 0; y < bitmap->height; ++y) {
+ p = row;
+ for (x = 0; x < bitmap->width; ++x) {
+ *p++ = color[0];
+ *p++ = color[1];
+ *p++ = color[2];
+ *p++ = color[3];
+ }
+ row += bitmap->rowSize;
+ }
+ }
+ break;
+#endif
+ }
+
+ if (bitmap->alpha) {
+ memset(bitmap->alpha, alpha, bitmap->alphaRowSize * bitmap->height);
+ }
+
+ updateModX(0);
+ updateModY(0);
+ updateModX(bitmap->width - 1);
+ updateModY(bitmap->height - 1);
+}
+
+SplashError Splash::stroke(SplashPath *path) {
+ SplashPath *path2, *dPath;
+ SplashCoord t0, t1, t2, t3, w, w2, lineDashMax, lineDashTotal;
+ int lineCap, lineJoin, i;
+
+ if (debugMode) {
+ printf("stroke [dash:%d] [width:%.2f]:\n",
+ state->lineDashLength, (double)state->lineWidth);
+ dumpPath(path);
+ }
+ opClipRes = splashClipAllOutside;
+ if (path->length == 0) {
+ return splashErrEmptyPath;
+ }
+ path2 = flattenPath(path, state->matrix, state->flatness);
+
+ // Compute an approximation of the transformed line width.
+ // Given a CTM of [m0 m1],
+ // [m2 m3]
+ // if |m0|*|m3| >= |m1|*|m2| then use min{|m0|,|m3|}, else
+ // use min{|m1|,|m2|}.
+ // This handles the common cases -- [s 0 ] and [0 s] --
+ // [0 +/-s] [+/-s 0]
+ // well, and still does something reasonable for the uncommon
+ // case transforms.
+ t0 = splashAbs(state->matrix[0]);
+ t1 = splashAbs(state->matrix[1]);
+ t2 = splashAbs(state->matrix[2]);
+ t3 = splashAbs(state->matrix[3]);
+ if (t0 * t3 >= t1 * t2) {
+ w = (t0 < t3) ? t0 : t3;
+ } else {
+ w = (t1 < t2) ? t1 : t2;
+ }
+ w2 = w * state->lineWidth;
+
+ // construct the dashed path
+ if (state->lineDashLength > 0) {
+
+ // check the maximum transformed dash element length (using the
+ // same approximation as for line width) -- if it's less than 0.1
+ // pixel, don't apply the dash pattern; this avoids a huge
+ // performance/memory hit with PDF files that use absurd dash
+ // patterns like [0.0007 0.0003]
+ lineDashTotal = 0;
+ lineDashMax = 0;
+ for (i = 0; i < state->lineDashLength; ++i) {
+ lineDashTotal += state->lineDash[i];
+ if (state->lineDash[i] > lineDashMax) {
+ lineDashMax = state->lineDash[i];
+ }
+ }
+ // Acrobat simply draws nothing if the dash array is [0]
+ if (lineDashTotal == 0) {
+ delete path2;
+ return splashOk;
+ }
+ if (w * lineDashMax > 0.1) {
+
+ dPath = makeDashedPath(path2);
+ delete path2;
+ path2 = dPath;
+ if (path2->length == 0) {
+ delete path2;
+ return splashErrEmptyPath;
+ }
+ }
+ }
+
+ // round caps on narrow lines look bad, and can't be
+ // stroke-adjusted, so use projecting caps instead (but we can't do
+ // this if there are zero-length dashes or segments, because those
+ // turn into round dots)
+ lineCap = state->lineCap;
+ lineJoin = state->lineJoin;
+ if (state->strokeAdjust == splashStrokeAdjustCAD &&
+ w2 < 3.5) {
+ if (lineCap == splashLineCapRound &&
+ !state->lineDashContainsZeroLengthDashes() &&
+ !path->containsZeroLengthSubpaths()) {
+ lineCap = splashLineCapProjecting;
+ }
+ if (lineJoin == splashLineJoinRound) {
+ lineJoin = splashLineJoinBevel;
+ }
+ }
+
+ // if there is a min line width set, and the transformed line width
+ // is smaller, use the min line width
+ if (w > 0 && w2 < minLineWidth) {
+ strokeWide(path2, minLineWidth / w, splashLineCapButt, splashLineJoinBevel);
+ } else if (bitmap->mode == splashModeMono1 || !vectorAntialias) {
+ // in monochrome mode or if antialiasing is disabled, use 0-width
+ // lines for any transformed line width <= 1 -- lines less than 1
+ // pixel wide look too fat without antialiasing
+ if (w2 < 1.001) {
+ strokeNarrow(path2);
+ } else {
+ strokeWide(path2, state->lineWidth, lineCap, lineJoin);
+ }
+ } else {
+ // in gray and color modes, only use 0-width lines if the line
+ // width is explicitly set to 0
+ if (state->lineWidth == 0) {
+ strokeNarrow(path2);
+ } else {
+ strokeWide(path2, state->lineWidth, lineCap, lineJoin);
+ }
+ }
+
+ delete path2;
+ return splashOk;
+}
+
+void Splash::strokeNarrow(SplashPath *path) {
+ SplashPipe pipe;
+ SplashXPath *xPath;
+ SplashXPathSeg *seg;
+ int x0, x1, y0, y1, xa, xb, y;
+ SplashCoord dxdy;
+ SplashClipResult clipRes;
+ int nClipRes[3];
+ int i;
+
+ nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
+
+ xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse,
+ state->enablePathSimplification,
+ state->strokeAdjust);
+
+ pipeInit(&pipe, state->strokePattern,
+ (Guchar)splashRound(state->strokeAlpha * 255),
+ gTrue, gFalse);
+
+ for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
+ if (seg->y0 <= seg->y1) {
+ y0 = splashFloor(seg->y0);
+ y1 = splashFloor(seg->y1);
+ x0 = splashFloor(seg->x0);
+ x1 = splashFloor(seg->x1);
+ } else {
+ y0 = splashFloor(seg->y1);
+ y1 = splashFloor(seg->y0);
+ x0 = splashFloor(seg->x1);
+ x1 = splashFloor(seg->x0);
+ }
+ if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
+ x0 <= x1 ? x1 : x0, y1,
+ state->strokeAdjust))
+ != splashClipAllOutside) {
+ if (y0 == y1) {
+ if (x0 <= x1) {
+ drawStrokeSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
+ } else {
+ drawStrokeSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside);
+ }
+ } else {
+ dxdy = seg->dxdy;
+ y = state->clip->getYMinI(state->strokeAdjust);
+ if (y0 < y) {
+ y0 = y;
+ x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy);
+ }
+ y = state->clip->getYMaxI(state->strokeAdjust);
+ if (y1 > y) {
+ y1 = y;
+ x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy);
+ }
+ if (x0 <= x1) {
+ xa = x0;
+ for (y = y0; y <= y1; ++y) {
+ if (y < y1) {
+ xb = splashFloor(seg->x0 +
+ ((SplashCoord)y + 1 - seg->y0) * dxdy);
+ } else {
+ xb = x1 + 1;
+ }
+ if (xa == xb) {
+ drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside);
+ } else {
+ drawStrokeSpan(&pipe, xa, xb - 1, y,
+ clipRes == splashClipAllInside);
+ }
+ xa = xb;
+ }
+ } else {
+ xa = x0;
+ for (y = y0; y <= y1; ++y) {
+ if (y < y1) {
+ xb = splashFloor(seg->x0 +
+ ((SplashCoord)y + 1 - seg->y0) * dxdy);
+ } else {
+ xb = x1 - 1;
+ }
+ if (xa == xb) {
+ drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside);
+ } else {
+ drawStrokeSpan(&pipe, xb + 1, xa, y,
+ clipRes == splashClipAllInside);
+ }
+ xa = xb;
+ }
+ }
+ }
+ }
+ ++nClipRes[clipRes];
+ }
+ if (nClipRes[splashClipPartial] ||
+ (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
+ opClipRes = splashClipPartial;
+ } else if (nClipRes[splashClipAllInside]) {
+ opClipRes = splashClipAllInside;
+ } else {
+ opClipRes = splashClipAllOutside;
+ }
+
+ delete xPath;
+}
+
+void Splash::drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y,
+ GBool noClip) {
+ int x;
+
+ x = state->clip->getXMinI(state->strokeAdjust);
+ if (x > x0) {
+ x0 = x;
+ }
+ x = state->clip->getXMaxI(state->strokeAdjust);
+ if (x < x1) {
+ x1 = x;
+ }
+ if (x0 > x1) {
+ return;
+ }
+ for (x = x0; x <= x1; ++x) {
+ scanBuf[x] = 0xff;
+ }
+ if (!noClip) {
+ if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1, state->strokeAdjust)) {
+ return;
+ }
+ }
+ (this->*pipe->run)(pipe, x0, x1, y, scanBuf + x0, NULL);
+}
+
+void Splash::strokeWide(SplashPath *path, SplashCoord w,
+ int lineCap, int lineJoin) {
+ SplashPath *path2;
+
+ path2 = makeStrokePath(path, w, lineCap, lineJoin, gFalse);
+ fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha);
+ delete path2;
+}
+
+SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
+ SplashCoord flatness) {
+ SplashPath *fPath;
+ SplashCoord flatness2;
+ Guchar flag;
+ int i;
+
+ fPath = new SplashPath();
+#if USE_FIXEDPOINT
+ flatness2 = flatness;
+#else
+ flatness2 = flatness * flatness;
+#endif
+ i = 0;
+ while (i < path->length) {
+ flag = path->flags[i];
+ if (flag & splashPathFirst) {
+ fPath->moveTo(path->pts[i].x, path->pts[i].y);
+ ++i;
+ } else {
+ if (flag & splashPathCurve) {
+ flattenCurve(path->pts[i-1].x, path->pts[i-1].y,
+ path->pts[i ].x, path->pts[i ].y,
+ path->pts[i+1].x, path->pts[i+1].y,
+ path->pts[i+2].x, path->pts[i+2].y,
+ matrix, flatness2, fPath);
+ i += 3;
+ } else {
+ fPath->lineTo(path->pts[i].x, path->pts[i].y);
+ ++i;
+ }
+ if (path->flags[i-1] & splashPathClosed) {
+ fPath->close();
+ }
+ }
+ }
+ return fPath;
+}
+
+void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1,
+ SplashCoord x2, SplashCoord y2,
+ SplashCoord x3, SplashCoord y3,
+ SplashCoord *matrix, SplashCoord flatness2,
+ SplashPath *fPath) {
+ SplashCoord cx[splashMaxCurveSplits + 1][3];
+ SplashCoord cy[splashMaxCurveSplits + 1][3];
+ int cNext[splashMaxCurveSplits + 1];
+ SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
+ SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
+ SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
+ int p1, p2, p3;
+
+ // initial segment
+ p1 = 0;
+ p2 = splashMaxCurveSplits;
+ cx[p1][0] = x0; cy[p1][0] = y0;
+ cx[p1][1] = x1; cy[p1][1] = y1;
+ cx[p1][2] = x2; cy[p1][2] = y2;
+ cx[p2][0] = x3; cy[p2][0] = y3;
+ cNext[p1] = p2;
+
+ while (p1 < splashMaxCurveSplits) {
+
+ // get the next segment
+ xl0 = cx[p1][0]; yl0 = cy[p1][0];
+ xx1 = cx[p1][1]; yy1 = cy[p1][1];
+ xx2 = cx[p1][2]; yy2 = cy[p1][2];
+ p2 = cNext[p1];
+ xr3 = cx[p2][0]; yr3 = cy[p2][0];
+
+ // compute the distances (in device space) from the control points
+ // to the midpoint of the straight line (this is a bit of a hack,
+ // but it's much faster than computing the actual distances to the
+ // line)
+ transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
+ transform(matrix, xx1, yy1, &tx, &ty);
+#if USE_FIXEDPOINT
+ d1 = splashDist(tx, ty, mx, my);
+#else
+ dx = tx - mx;
+ dy = ty - my;
+ d1 = dx*dx + dy*dy;
+#endif
+ transform(matrix, xx2, yy2, &tx, &ty);
+#if USE_FIXEDPOINT
+ d2 = splashDist(tx, ty, mx, my);
+#else
+ dx = tx - mx;
+ dy = ty - my;
+ d2 = dx*dx + dy*dy;
+#endif
+
+ // if the curve is flat enough, or no more subdivisions are
+ // allowed, add the straight line segment
+ if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
+ fPath->lineTo(xr3, yr3);
+ p1 = p2;
+
+ // otherwise, subdivide the curve
+ } else {
+ xl1 = splashAvg(xl0, xx1);
+ yl1 = splashAvg(yl0, yy1);
+ xh = splashAvg(xx1, xx2);
+ yh = splashAvg(yy1, yy2);
+ xl2 = splashAvg(xl1, xh);
+ yl2 = splashAvg(yl1, yh);
+ xr2 = splashAvg(xx2, xr3);
+ yr2 = splashAvg(yy2, yr3);
+ xr1 = splashAvg(xh, xr2);
+ yr1 = splashAvg(yh, yr2);
+ xr0 = splashAvg(xl2, xr1);
+ yr0 = splashAvg(yl2, yr1);
+ // add the new subdivision points
+ p3 = (p1 + p2) / 2;
+ cx[p1][1] = xl1; cy[p1][1] = yl1;
+ cx[p1][2] = xl2; cy[p1][2] = yl2;
+ cNext[p1] = p3;
+ cx[p3][0] = xr0; cy[p3][0] = yr0;
+ cx[p3][1] = xr1; cy[p3][1] = yr1;
+ cx[p3][2] = xr2; cy[p3][2] = yr2;
+ cNext[p3] = p2;
+ }
+ }
+}
+
+SplashPath *Splash::makeDashedPath(SplashPath *path) {
+ SplashPath *dPath;
+ SplashCoord lineDashTotal;
+ SplashCoord lineDashStartPhase, lineDashDist, segLen;
+ SplashCoord x0, y0, x1, y1, xa, ya;
+ GBool lineDashStartOn, lineDashEndOn, lineDashOn, newPath;
+ int lineDashStartIdx, lineDashIdx, subpathStart, nDashes;
+ int i, j, k;
+
+ lineDashTotal = 0;
+ for (i = 0; i < state->lineDashLength; ++i) {
+ lineDashTotal += state->lineDash[i];
+ }
+ // Acrobat simply draws nothing if the dash array is [0]
+ if (lineDashTotal == 0) {
+ return new SplashPath();
+ }
+ lineDashStartPhase = state->lineDashPhase;
+ if (lineDashStartPhase > lineDashTotal * 2) {
+ i = splashFloor(lineDashStartPhase / (lineDashTotal * 2));
+ lineDashStartPhase -= lineDashTotal * i * 2;
+ } else if (lineDashStartPhase < 0) {
+ i = splashCeil(-lineDashStartPhase / (lineDashTotal * 2));
+ lineDashStartPhase += lineDashTotal * i * 2;
+ }
+ i = splashFloor(lineDashStartPhase / lineDashTotal);
+ lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
+ lineDashStartOn = gTrue;
+ lineDashStartIdx = 0;
+ if (lineDashStartPhase > 0) {
+ while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
+ lineDashStartOn = !lineDashStartOn;
+ lineDashStartPhase -= state->lineDash[lineDashStartIdx];
+ if (++lineDashStartIdx == state->lineDashLength) {
+ lineDashStartIdx = 0;
+ }
+ }
+ }
+
+ dPath = new SplashPath();
+
+ // process each subpath
+ i = 0;
+ while (i < path->length) {
+
+ // find the end of the subpath
+ for (j = i;
+ j < path->length - 1 && !(path->flags[j] & splashPathLast);
+ ++j) ;
+
+ // initialize the dash parameters
+ lineDashOn = lineDashStartOn;
+ lineDashEndOn = lineDashStartOn;
+ lineDashIdx = lineDashStartIdx;
+ lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
+ subpathStart = dPath->length;
+ nDashes = 0;
+
+ // process each segment of the subpath
+ newPath = gTrue;
+ for (k = i; k < j; ++k) {
+
+ // grab the segment
+ x0 = path->pts[k].x;
+ y0 = path->pts[k].y;
+ x1 = path->pts[k+1].x;
+ y1 = path->pts[k+1].y;
+ segLen = splashDist(x0, y0, x1, y1);
+
+ // process the segment
+ while (segLen > 0) {
+
+ // Special case for zero-length dash segments: draw a very
+ // short -- but not zero-length -- segment. This ensures that
+ // we get the correct behavior with butt and projecting line
+ // caps. The PS/PDF specs imply that zero-length segments are
+ // not drawn unless the line cap is round, but Acrobat and
+ // Ghostscript both draw very short segments (for butt caps)
+ // and squares (for projecting caps).
+ if (lineDashDist == 0) {
+ if (lineDashOn) {
+ if (newPath) {
+ dPath->moveTo(x0, y0);
+ newPath = gFalse;
+ ++nDashes;
+ }
+ xa = x0 + ((SplashCoord)0.001 / segLen) * (x1 - x0);
+ ya = y0 + ((SplashCoord)0.001 / segLen) * (y1 - y0);
+ dPath->lineTo(xa, ya);
+ }
+
+ } else if (lineDashDist >= segLen) {
+ if (lineDashOn) {
+ if (newPath) {
+ dPath->moveTo(x0, y0);
+ newPath = gFalse;
+ ++nDashes;
+ }
+ dPath->lineTo(x1, y1);
+ }
+ lineDashDist -= segLen;
+ segLen = 0;
+
+ } else {
+ xa = x0 + (lineDashDist / segLen) * (x1 - x0);
+ ya = y0 + (lineDashDist / segLen) * (y1 - y0);
+ if (lineDashOn) {
+ if (newPath) {
+ dPath->moveTo(x0, y0);
+ newPath = gFalse;
+ ++nDashes;
+ }
+ dPath->lineTo(xa, ya);
+ }
+ x0 = xa;
+ y0 = ya;
+ segLen -= lineDashDist;
+ lineDashDist = 0;
+ }
+
+ lineDashEndOn = lineDashOn;
+
+ // get the next entry in the dash array
+ if (lineDashDist <= 0) {
+ lineDashOn = !lineDashOn;
+ if (++lineDashIdx == state->lineDashLength) {
+ lineDashIdx = 0;
+ }
+ lineDashDist = state->lineDash[lineDashIdx];
+ newPath = gTrue;
+ }
+ }
+ }
+
+ // in a closed subpath, where the dash pattern is "on" at both the
+ // start and end of the subpath, we need to merge the start and
+ // end to get a proper line join
+ if ((path->flags[j] & splashPathClosed) &&
+ lineDashStartOn &&
+ lineDashEndOn) {
+ if (nDashes == 1) {
+ dPath->close();
+ } else if (nDashes > 1) {
+ k = subpathStart;
+ do {
+ ++k;
+ dPath->lineTo(dPath->pts[k].x, dPath->pts[k].y);
+ } while (!(dPath->flags[k] & splashPathLast));
+ ++k;
+ memmove(&dPath->pts[subpathStart], &dPath->pts[k],
+ (dPath->length - k) * sizeof(SplashPathPoint));
+ memmove(&dPath->flags[subpathStart], &dPath->flags[k],
+ (dPath->length - k) * sizeof(Guchar));
+ dPath->length -= k - subpathStart;
+ dPath->curSubpath -= k - subpathStart;
+ }
+ }
+
+ i = j + 1;
+ }
+
+ return dPath;
+}
+
+SplashError Splash::fill(SplashPath *path, GBool eo) {
+ if (debugMode) {
+ printf("fill [eo:%d]:\n", eo);
+ dumpPath(path);
+ }
+ return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
+}
+
+SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
+ SplashPattern *pattern,
+ SplashCoord alpha) {
+ SplashPipe pipe;
+ SplashPath *path2;
+ SplashXPath *xPath;
+ SplashXPathScanner *scanner;
+ int xMin, yMin, xMax, xMin2, xMax2, yMax, y, t;
+ SplashClipResult clipRes;
+
+ if (path->length == 0) {
+ return splashErrEmptyPath;
+ }
+ if (pathAllOutside(path)) {
+ opClipRes = splashClipAllOutside;
+ return splashOk;
+ }
+
+ path2 = tweakFillPath(path);
+
+ xPath = new SplashXPath(path2, state->matrix, state->flatness, gTrue,
+ state->enablePathSimplification,
+ state->strokeAdjust);
+ if (path2 != path) {
+ delete path2;
+ }
+ xMin = xPath->getXMin();
+ yMin = xPath->getYMin();
+ xMax = xPath->getXMax();
+ yMax = xPath->getYMax();
+ if (xMin > xMax || yMin > yMax) {
+ delete xPath;
+ return splashOk;
+ }
+ scanner = new SplashXPathScanner(xPath, eo, yMin, yMax);
+
+ // check clipping
+ if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax,
+ state->strokeAdjust))
+ != splashClipAllOutside) {
+
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) {
+ xMin = t;
+ }
+ if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) {
+ xMax = t;
+ }
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) {
+ yMin = t;
+ }
+ if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) {
+ yMax = t;
+ }
+ if (xMin > xMax || yMin > yMax) {
+ delete scanner;
+ delete xPath;
+ return splashOk;
+ }
+
+ pipeInit(&pipe, pattern, (Guchar)splashRound(alpha * 255),
+ gTrue, gFalse);
+
+ // draw the spans
+ if (vectorAntialias && !inShading) {
+ for (y = yMin; y <= yMax; ++y) {
+ scanner->getSpan(scanBuf, y, xMin, xMax, &xMin2, &xMax2);
+ if (xMin2 <= xMax2) {
+ if (clipRes != splashClipAllInside) {
+ state->clip->clipSpan(scanBuf, y, xMin2, xMax2,
+ state->strokeAdjust);
+ }
+ (this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL);
+ }
+ }
+ } else {
+ for (y = yMin; y <= yMax; ++y) {
+ scanner->getSpanBinary(scanBuf, y, xMin, xMax, &xMin2, &xMax2);
+ if (xMin2 <= xMax2) {
+ if (clipRes != splashClipAllInside) {
+ state->clip->clipSpanBinary(scanBuf, y, xMin2, xMax2,
+ state->strokeAdjust);
+ }
+ (this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL);
+ }
+ }
+ }
+ }
+ opClipRes = clipRes;
+
+ delete scanner;
+ delete xPath;
+ return splashOk;
+}
+
+// Applies various tweaks to a fill path:
+// (1) add stroke adjust hints to a filled rectangle
+// (2) applies a minimum width to a zero-width filled rectangle (so
+// stroke adjustment works correctly
+// (3) convert a degenerate fill ('moveto lineto fill' and 'moveto
+// lineto closepath fill') to a minimum-width filled rectangle
+//
+// These tweaks only apply to paths with a single subpath.
+//
+// Returns either the unchanged input path or a new path (in which
+// case the returned path must be deleted by the caller).
+SplashPath *Splash::tweakFillPath(SplashPath *path) {
+ SplashPath *path2;
+ SplashCoord xx0, yy0, xx1, yy1, dx, dy, d, wx, wy, w;
+ int n;
+
+ if (state->strokeAdjust == splashStrokeAdjustOff || path->hints) {
+ return path;
+ }
+
+ n = path->getLength();
+ if (!((n == 2) ||
+ (n == 3 &&
+ path->flags[1] == 0) ||
+ (n == 4 &&
+ path->flags[1] == 0 &&
+ path->flags[2] == 0) ||
+ (n == 5 &&
+ path->flags[1] == 0 &&
+ path->flags[2] == 0 &&
+ path->flags[3] == 0))) {
+ return path;
+ }
+
+ path2 = path;
+
+ // degenerate fill (2 or 3 points) or rectangle of (nearly) zero
+ // width --> replace with a min-width rectangle and hint
+ if (n == 2 ||
+ (n == 3 && (path->flags[0] & splashPathClosed)) ||
+ (n == 3 && (splashAbs(path->pts[0].x - path->pts[2].x) < 0.001 &&
+ splashAbs(path->pts[0].y - path->pts[2].y) < 0.001)) ||
+ ((n == 4 ||
+ (n == 5 && (path->flags[0] & splashPathClosed))) &&
+ ((splashAbs(path->pts[0].x - path->pts[1].x) < 0.001 &&
+ splashAbs(path->pts[0].y - path->pts[1].y) < 0.001 &&
+ splashAbs(path->pts[2].x - path->pts[3].x) < 0.001 &&
+ splashAbs(path->pts[2].y - path->pts[3].y) < 0.001) ||
+ (splashAbs(path->pts[0].x - path->pts[3].x) < 0.001 &&
+ splashAbs(path->pts[0].y - path->pts[3].y) < 0.001 &&
+ splashAbs(path->pts[1].x - path->pts[2].x) < 0.001 &&
+ splashAbs(path->pts[1].y - path->pts[2].y) < 0.001)))) {
+ wx = state->matrix[0] + state->matrix[2];
+ wy = state->matrix[1] + state->matrix[3];
+ w = splashSqrt(wx*wx + wy*wy);
+ if (w < 0.001) {
+ w = 0;
+ } else {
+ // min width is 0.1 -- this constant is minWidth * sqrt(2)
+ w = (SplashCoord)0.1414 / w;
+ }
+ xx0 = path->pts[0].x;
+ yy0 = path->pts[0].y;
+ if (n <= 3) {
+ xx1 = path->pts[1].x;
+ yy1 = path->pts[1].y;
+ } else {
+ xx1 = path->pts[2].x;
+ yy1 = path->pts[2].y;
+ }
+ dx = xx1 - xx0;
+ dy = yy1 - yy0;
+ d = splashSqrt(dx * dx + dy * dy);
+ if (d < 0.001) {
+ d = 0;
+ } else {
+ d = w / d;
+ }
+ dx *= d;
+ dy *= d;
+ path2 = new SplashPath();
+ path2->moveTo(xx0 + dy, yy0 - dx);
+ path2->lineTo(xx1 + dy, yy1 - dx);
+ path2->lineTo(xx1 - dy, yy1 + dx);
+ path2->lineTo(xx0 - dy, yy0 + dx);
+ path2->close(gTrue);
+ path2->addStrokeAdjustHint(0, 2, 0, 4);
+ path2->addStrokeAdjustHint(1, 3, 0, 4);
+
+ // unclosed rectangle --> close and hint
+ } else if (n == 4 && !(path->flags[0] & splashPathClosed)) {
+ path2->close(gTrue);
+ path2->addStrokeAdjustHint(0, 2, 0, 4);
+ path2->addStrokeAdjustHint(1, 3, 0, 4);
+
+ // closed rectangle --> hint
+ } else if (n == 5 && (path->flags[0] & splashPathClosed)) {
+ path2->addStrokeAdjustHint(0, 2, 0, 4);
+ path2->addStrokeAdjustHint(1, 3, 0, 4);
+ }
+
+ return path2;
+}
+
+GBool Splash::pathAllOutside(SplashPath *path) {
+ SplashCoord xMin1, yMin1, xMax1, yMax1;
+ SplashCoord xMin2, yMin2, xMax2, yMax2;
+ SplashCoord x, y;
+ int xMinI, yMinI, xMaxI, yMaxI;
+ int i;
+
+ xMin1 = xMax1 = path->pts[0].x;
+ yMin1 = yMax1 = path->pts[0].y;
+ for (i = 1; i < path->length; ++i) {
+ if (path->pts[i].x < xMin1) {
+ xMin1 = path->pts[i].x;
+ } else if (path->pts[i].x > xMax1) {
+ xMax1 = path->pts[i].x;
+ }
+ if (path->pts[i].y < yMin1) {
+ yMin1 = path->pts[i].y;
+ } else if (path->pts[i].y > yMax1) {
+ yMax1 = path->pts[i].y;
+ }
+ }
+
+ transform(state->matrix, xMin1, yMin1, &x, &y);
+ xMin2 = xMax2 = x;
+ yMin2 = yMax2 = y;
+ transform(state->matrix, xMin1, yMax1, &x, &y);
+ if (x < xMin2) {
+ xMin2 = x;
+ } else if (x > xMax2) {
+ xMax2 = x;
+ }
+ if (y < yMin2) {
+ yMin2 = y;
+ } else if (y > yMax2) {
+ yMax2 = y;
+ }
+ transform(state->matrix, xMax1, yMin1, &x, &y);
+ if (x < xMin2) {
+ xMin2 = x;
+ } else if (x > xMax2) {
+ xMax2 = x;
+ }
+ if (y < yMin2) {
+ yMin2 = y;
+ } else if (y > yMax2) {
+ yMax2 = y;
+ }
+ transform(state->matrix, xMax1, yMax1, &x, &y);
+ if (x < xMin2) {
+ xMin2 = x;
+ } else if (x > xMax2) {
+ xMax2 = x;
+ }
+ if (y < yMin2) {
+ yMin2 = y;
+ } else if (y > yMax2) {
+ yMax2 = y;
+ }
+ // sanity-check the coordinates - xMinI/yMinI/xMaxI/yMaxI are
+ // 32-bit integers, so coords need to be < 2^31
+ SplashXPath::clampCoords(&xMin2, &yMin2);
+ SplashXPath::clampCoords(&xMax2, &yMax2);
+ xMinI = splashFloor(xMin2);
+ yMinI = splashFloor(yMin2);
+ xMaxI = splashFloor(xMax2);
+ yMaxI = splashFloor(yMax2);
+
+ return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI,
+ state->strokeAdjust) ==
+ splashClipAllOutside;
+}
+
+SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
+ int c, SplashFont *font) {
+ SplashGlyphBitmap glyph;
+ SplashCoord xt, yt;
+ int x0, y0, xFrac, yFrac;
+ SplashError err;
+
+ if (debugMode) {
+ printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
+ (double)x, (double)y, c, c, c);
+ }
+ transform(state->matrix, x, y, &xt, &yt);
+ x0 = splashFloor(xt);
+ xFrac = splashFloor((xt - x0) * splashFontFraction);
+ y0 = splashFloor(yt);
+ yFrac = splashFloor((yt - y0) * splashFontFraction);
+ if (!font->getGlyph(c, xFrac, yFrac, &glyph)) {
+ return splashErrNoGlyph;
+ }
+ err = fillGlyph2(x0, y0, &glyph);
+ if (glyph.freeData) {
+ gfree(glyph.data);
+ }
+ return err;
+}
+
+SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y,
+ SplashGlyphBitmap *glyph) {
+ SplashCoord xt, yt;
+ int x0, y0;
+
+ transform(state->matrix, x, y, &xt, &yt);
+ x0 = splashFloor(xt);
+ y0 = splashFloor(yt);
+ return fillGlyph2(x0, y0, glyph);
+}
+
+SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) {
+ SplashPipe pipe;
+ SplashClipResult clipRes;
+ Guchar alpha;
+ Guchar *p;
+ int xMin, yMin, xMax, yMax;
+ int x, y, xg, yg, xx, t;
+
+ xg = x0 - glyph->x;
+ yg = y0 - glyph->y;
+ xMin = xg;
+ xMax = xg + glyph->w - 1;
+ yMin = yg;
+ yMax = yg + glyph->h - 1;
+ if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax,
+ state->strokeAdjust))
+ != splashClipAllOutside) {
+ pipeInit(&pipe, state->fillPattern,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ gTrue, gFalse);
+ if (clipRes == splashClipAllInside) {
+ if (glyph->aa) {
+ p = glyph->data;
+ for (y = yMin; y <= yMax; ++y) {
+ (this->*pipe.run)(&pipe, xMin, xMax, y,
+ glyph->data + (y - yMin) * glyph->w, NULL);
+ }
+ } else {
+ p = glyph->data;
+ for (y = yMin; y <= yMax; ++y) {
+ for (x = xMin; x <= xMax; x += 8) {
+ alpha = *p++;
+ for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) {
+ scanBuf[x + xx] = (alpha & 0x80) ? 0xff : 0x00;
+ alpha = (Guchar)(alpha << 1);
+ }
+ }
+ (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
+ }
+ }
+ } else {
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) {
+ xMin = t;
+ }
+ if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) {
+ xMax = t;
+ }
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) {
+ yMin = t;
+ }
+ if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) {
+ yMax = t;
+ }
+ if (xMin <= xMax && yMin <= yMax) {
+ if (glyph->aa) {
+ for (y = yMin; y <= yMax; ++y) {
+ p = glyph->data + (y - yg) * glyph->w + (xMin - xg);
+ memcpy(scanBuf + xMin, p, xMax - xMin + 1);
+ state->clip->clipSpan(scanBuf, y, xMin, xMax,
+ state->strokeAdjust);
+ (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
+ }
+ } else {
+ for (y = yMin; y <= yMax; ++y) {
+ p = glyph->data + (y - yg) * ((glyph->w + 7) >> 3)
+ + ((xMin - xg) >> 3);
+ alpha = *p++;
+ xx = (xMin - xg) & 7;
+ alpha = (Guchar)(alpha << xx);
+ for (x = xMin; xx < 8 && x <= xMax; ++x, ++xx) {
+ scanBuf[x] = (alpha & 0x80) ? 255 : 0;
+ alpha = (Guchar)(alpha << 1);
+ }
+ for (; x <= xMax; x += 8) {
+ alpha = *p++;
+ for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) {
+ scanBuf[x + xx] = (alpha & 0x80) ? 255 : 0;
+ alpha = (Guchar)(alpha << 1);
+ }
+ }
+ state->clip->clipSpanBinary(scanBuf, y, xMin, xMax,
+ state->strokeAdjust);
+ (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL);
+ }
+ }
+ }
+ }
+ }
+ opClipRes = clipRes;
+
+ return splashOk;
+}
+
+void Splash::getImageBounds(SplashCoord xyMin, SplashCoord xyMax,
+ int *xyMinI, int *xyMaxI) {
+ if (state->strokeAdjust == splashStrokeAdjustOff) {
+ // make sure the coords fit in 32-bit ints
+#if USE_FIXEDPOINT
+ if (xyMin < -32767) {
+ xyMin = -32767;
+ } else if (xyMin > 32767) {
+ xyMin = 32767;
+ }
+ if (xyMax < -32767) {
+ xyMax = -32767;
+ } else if (xyMax > 32767) {
+ xyMax = 32767;
+ }
+#else
+ if (xyMin < -1e9) {
+ xyMin = -1e9;
+ } else if (xyMin > 1e9) {
+ xyMin = 1e9;
+ }
+ if (xyMax < -1e9) {
+ xyMax = -1e9;
+ } else if (xyMax > 1e9) {
+ xyMax = 1e9;
+ }
+#endif
+ *xyMinI = splashFloor(xyMin);
+ *xyMaxI = splashFloor(xyMax);
+ if (*xyMaxI <= *xyMinI) {
+ *xyMaxI = *xyMinI + 1;
+ }
+ } else {
+ splashStrokeAdjust(xyMin, xyMax, xyMinI, xyMaxI, state->strokeAdjust);
+ }
+}
+
+struct SplashDrawImageMaskRowData {
+ SplashPipe pipe;
+};
+
+// The glyphMode flag is not currently used, but may be useful if the
+// stroke adjustment behavior is changed.
+SplashError Splash::fillImageMask(GString *imageTag,
+ SplashImageMaskSource src, void *srcData,
+ int w, int h, SplashCoord *mat,
+ GBool glyphMode, GBool interpolate) {
+ if (debugMode) {
+ printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
+ w, h, (double)mat[0], (double)mat[1], (double)mat[2],
+ (double)mat[3], (double)mat[4], (double)mat[5]);
+ }
+
+ //--- check for singular matrix
+ if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
+ return splashErrSingularMatrix;
+ }
+
+ //--- 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);
+ }
+ } 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);
+ }
+ }
+ } else {
+ int xx = splashRound(mat[4]); // (0,0)
+ int yy = splashRound(mat[5]);
+ xMin = xMax = xx;
+ yMin = yMax = yy;
+ xx = splashRound(mat[0] + mat[4]); // (1,0)
+ yy = splashRound(mat[1] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ xx = splashRound(mat[2] + mat[4]); // (0,1)
+ yy = splashRound(mat[3] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ xx = splashRound(mat[0] + mat[2] + mat[4]); // (1,1)
+ yy = splashRound(mat[1] + mat[3] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ if (xMax <= xMin) {
+ xMax = xMin + 1;
+ }
+ if (yMax <= yMin) {
+ yMax = yMin + 1;
+ }
+ }
+ SplashClipResult clipRes =
+ state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
+ state->strokeAdjust);
+ // If the scaled mask is much wider and/or taller than the clip
+ // region, we use the "arbitrary" scaling path, to avoid a
+ // potentially very slow loop in the flips-only path (which scans
+ // the full width and height of the scaled mask, regardless of the
+ // clip region).
+ int clipW = state->clip->getXMaxI(state->strokeAdjust)
+ - state->clip->getXMinI(state->strokeAdjust);
+ int clipH = state->clip->getYMaxI(state->strokeAdjust)
+ - state->clip->getYMinI(state->strokeAdjust);
+ GBool veryLarge = ((xMax - xMin) / 8 > clipW && xMax - xMin > 1000) ||
+ ((yMax - yMin) / 8 > clipH && yMax - yMin > 1000);
+
+ //--- set up the SplashDrawImageMaskRowData object and the pipes
+ SplashDrawImageMaskRowData dd;
+ pipeInit(&dd.pipe, state->fillPattern,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ gTrue, gFalse);
+
+ //--- choose the drawRow function
+ SplashDrawImageMaskRowFunc drawRowFunc;
+ if (clipRes == splashClipAllInside) {
+ drawRowFunc = &Splash::drawImageMaskRowNoClip;
+ } else {
+ if (vectorAntialias) {
+ drawRowFunc = &Splash::drawImageMaskRowClipAA;
+ } else {
+ drawRowFunc = &Splash::drawImageMaskRowClipNoAA;
+ }
+ }
+
+ //--- 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);
+ }
+ } else { // bottom-up
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ (this->*drawRowFunc)(&dd, scaler.data(),
+ xMin, yMax - 1 - y, scaledWidth);
+ }
+ }
+ } else {
+ if (horizFlip) { // top-down, mirrored
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ mirrorImageMaskRow(scaler.data(), tmpLine, scaledWidth);
+ (this->*drawRowFunc)(&dd, tmpLine,
+ xMin, yMin + y, scaledWidth);
+ }
+ } else { // top-down
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ (this->*drawRowFunc)(&dd, scaler.data(),
+ xMin, yMin + y, scaledWidth);
+ }
+ }
+ }
+ gfree(tmpLine);
+ }
+
+ //--- arbitrary transform
+ } else {
+ // estimate of size of scaled image
+ int scaledWidth = splashRound(splashSqrt(mat[0] * mat[0]
+ + mat[1] * mat[1]));
+ int scaledHeight = splashRound(splashSqrt(mat[2] * mat[2]
+ + mat[3] * mat[3]));
+ if (scaledWidth < 1) {
+ scaledWidth = 1;
+ }
+ if (scaledHeight < 1) {
+ scaledHeight = 1;
+ }
+ GBool downscaling = gTrue;
+ if (veryLarge || (scaledWidth >= w && scaledHeight >= h)) {
+ downscaling = gFalse;
+ scaledWidth = w;
+ scaledHeight = h;
+ }
+
+ // compute mapping from device space to scaled image space
+ SplashCoord mat1[6];
+ mat1[0] = mat[0] / scaledWidth;
+ mat1[1] = mat[1] / scaledWidth;
+ mat1[2] = mat[2] / scaledHeight;
+ mat1[3] = mat[3] / scaledHeight;
+ mat1[4] = mat[4];
+ mat1[5] = mat[5];
+ SplashCoord det = mat1[0] * mat1[3] - mat1[1] * mat1[2];
+ if (splashAbs(det) < 1e-6) {
+ // this should be caught by the singular matrix check in drawImage
+ return splashErrSingularMatrix;
+ }
+ SplashCoord invMat[6];
+ invMat[0] = mat1[3] / det;
+ invMat[1] = -mat1[1] / det;
+ invMat[2] = -mat1[2] / det;
+ invMat[3] = mat1[0] / det;
+ // the extra "+ 0.5 * (...)" terms are here because the
+ // drawImageArbitrary(No)Interp functions multiply by pixel
+ // centers, (x + 0.5, y + 0.5)
+ invMat[4] = (mat1[2] * mat1[5] - mat1[3] * mat1[4]) / det
+ + (invMat[0] + invMat[2]) * 0.5;
+ invMat[5] = (mat1[1] * mat1[4] - mat1[0] * mat1[5]) / det
+ + (invMat[1] + invMat[3]) * 0.5;
+
+ // if downscaling: store the downscaled image mask
+ // if upscaling: store the unscaled image mask
+ Guchar *scaledMask = (Guchar *)gmallocn(scaledHeight, scaledWidth);
+ if (downscaling) {
+ ImageMaskScaler scaler(src, srcData, w, h,
+ scaledWidth, scaledHeight, interpolate);
+ Guchar *ptr = scaledMask;
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ memcpy(ptr, scaler.data(), scaledWidth);
+ ptr += scaledWidth;
+ }
+ } else {
+ Guchar *ptr = scaledMask;
+ for (int y = 0; y < scaledHeight; ++y) {
+ (*src)(srcData, ptr);
+ for (int x = 0; x < scaledWidth; ++x) {
+ *ptr = (Guchar)(*ptr * 255);
+ ++ptr;
+ }
+ }
+ }
+
+ // draw it
+ if (interpolate) {
+ drawImageMaskArbitraryInterp(scaledMask,
+ &dd, drawRowFunc, invMat,
+ scaledWidth, scaledHeight,
+ xMin, yMin, xMax, yMax);
+ } else {
+ drawImageMaskArbitraryNoInterp(scaledMask,
+ &dd, drawRowFunc, invMat,
+ scaledWidth, scaledHeight,
+ xMin, yMin, xMax, yMax);
+ }
+
+ // free the downscaled/unscaled image
+ gfree(scaledMask);
+ }
+
+ return splashOk;
+}
+
+void Splash::drawImageMaskArbitraryNoInterp(
+ Guchar *scaledMask,
+ SplashDrawImageMaskRowData *dd,
+ SplashDrawImageMaskRowFunc drawRowFunc,
+ SplashCoord *invMat,
+ int scaledWidth, int scaledHeight,
+ int xMin, int yMin, int xMax, int yMax) {
+ int tt = state->clip->getXMinI(state->strokeAdjust);
+ if (tt > xMin) {
+ xMin = tt;
+ }
+ tt = state->clip->getXMaxI(state->strokeAdjust) + 1;
+ if (tt < xMax) {
+ xMax = tt;
+ }
+ tt = state->clip->getYMinI(state->strokeAdjust);
+ if (tt > yMin) {
+ yMin = tt;
+ }
+ tt = state->clip->getYMaxI(state->strokeAdjust) + 1;
+ if (tt < yMax) {
+ yMax = tt;
+ }
+ if (xMax <= xMin || yMax <= yMin) {
+ return;
+ }
+
+ Guchar *buf = (Guchar *)gmalloc(xMax - xMin);
+
+ for (int y = yMin; y < yMax; ++y) {
+ int rowMin = xMax;
+ int rowMax = 0;
+ for (int x = xMin; x < xMax; ++x) {
+ // note: invMat includes a "+0.5" factor so that this is really
+ // a multiply by (x+0.5, y+0.5)
+ int xx = splashFloor((SplashCoord)x * invMat[0]
+ + (SplashCoord)y * invMat[2] + invMat[4]);
+ int yy = splashFloor((SplashCoord)x * invMat[1]
+ + (SplashCoord)y * invMat[3] + invMat[5]);
+ if (xx >= 0 && xx < scaledWidth &&
+ yy >= 0 && yy < scaledHeight) {
+ Guchar *p = scaledMask + (yy * scaledWidth + xx);
+ Guchar *q = buf + (x - xMin);
+ *q = *p;
+ if (x < rowMin) {
+ rowMin = x;
+ }
+ rowMax = x + 1;
+ }
+ }
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd, buf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
+ }
+ }
+
+ gfree(buf);
+}
+
+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;
+ }
+
+ Guchar *buf = (Guchar *)gmalloc(xMax - xMin);
+
+ for (int y = yMin; y < yMax; ++y) {
+ int rowMin = xMax;
+ int rowMax = 0;
+ for (int x = xMin; x < xMax; ++x) {
+ // note: invMat includes a "+0.5" factor so that this is really
+ // a multiply by (x+0.5, y+0.5)
+ SplashCoord xs = (SplashCoord)x * invMat[0]
+ + (SplashCoord)y * invMat[2] + invMat[4];
+ SplashCoord ys = (SplashCoord)x * invMat[1]
+ + (SplashCoord)y * invMat[3] + invMat[5];
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ if (x1 >= 0 && x0 < scaledWidth && y1 >= 0 && y0 < scaledHeight) {
+ SplashCoord sx0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord sx1 = (SplashCoord)1 - sx0;
+ SplashCoord sy0 = (SplashCoord)y1 + 0.5 - ys;
+ SplashCoord sy1 = (SplashCoord)1 - sy0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (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;
+ }
+ }
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd, buf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
+ }
+ }
+
+ gfree(buf);
+}
+
+void Splash::mirrorImageMaskRow(Guchar *maskIn, Guchar *maskOut, int width) {
+ Guchar *p, *q;
+
+ p = maskIn;
+ q = maskOut + (width - 1);
+ for (int i = 0; i < width; ++i) {
+ *q = *p;
+ ++p;
+ --q;
+ }
+}
+
+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::drawImageMaskRowClipNoAA(SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
+ }
+ 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::drawImageMaskRowClipAA(SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
+ }
+ if (x < 0) {
+ maskData -= x;
+ width += x;
+ x = 0;
+ }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
+ }
+ if (width <= 0) {
+ return;
+ }
+ memcpy(scanBuf + x, maskData, width);
+ state->clip->clipSpan(scanBuf, y, x, x + width - 1, state->strokeAdjust);
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y,
+ scanBuf + x, NULL);
+}
+
+struct SplashDrawImageRowData {
+ int nComps;
+ GBool srcAlpha;
+ SplashPipe pipe;
+};
+
+SplashError Splash::drawImage(GString *imageTag,
+ SplashImageSource src, void *srcData,
+ SplashColorMode srcMode, GBool srcAlpha,
+ int w, int h, SplashCoord *mat,
+ GBool interpolate) {
+ if (debugMode) {
+ printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
+ srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],
+ (double)mat[3], (double)mat[4], (double)mat[5]);
+ }
+
+ //--- check color modes
+ GBool ok = gFalse;
+ int nComps = 0;
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ ok = srcMode == splashModeMono8;
+ nComps = 1;
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ ok = srcMode == splashModeRGB8;
+ nComps = 3;
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ ok = srcMode == splashModeCMYK8;
+ nComps = 4;
+ break;
+#endif
+ default:
+ ok = gFalse;
+ break;
+ }
+ if (!ok) {
+ return splashErrModeMismatch;
+ }
+
+ //--- check for singular matrix
+ if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
+ return splashErrSingularMatrix;
+ }
+
+ //--- 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);
+ }
+ } 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);
+ }
+ }
+ } else {
+ int xx = splashRound(mat[4]); // (0,0)
+ int yy = splashRound(mat[5]);
+ xMin = xMax = xx;
+ yMin = yMax = yy;
+ xx = splashRound(mat[0] + mat[4]); // (1,0)
+ yy = splashRound(mat[1] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ xx = splashRound(mat[2] + mat[4]); // (0,1)
+ yy = splashRound(mat[3] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ xx = splashRound(mat[0] + mat[2] + mat[4]); // (1,1)
+ yy = splashRound(mat[1] + mat[3] + mat[5]);
+ if (xx < xMin) {
+ xMin = xx;
+ } else if (xx > xMax) {
+ xMax = xx;
+ }
+ if (yy < yMin) {
+ yMin = yy;
+ } else if (yy > yMax) {
+ yMax = yy;
+ }
+ if (xMax <= xMin) {
+ xMax = xMin + 1;
+ }
+ if (yMax <= yMin) {
+ yMax = yMin + 1;
+ }
+ }
+ SplashClipResult clipRes =
+ state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1,
+ state->strokeAdjust);
+ // If the scaled image is much wider and/or taller than the clip
+ // region, we use the arbitrary transform path, to avoid a
+ // potentially very slow loop in the flips-only path (which scans
+ // the full width and height of the scaled image, regardless of the
+ // clip region).
+ int clipW = state->clip->getXMaxI(state->strokeAdjust)
+ - state->clip->getXMinI(state->strokeAdjust);
+ int clipH = state->clip->getYMaxI(state->strokeAdjust)
+ - state->clip->getYMinI(state->strokeAdjust);
+ GBool veryLarge = ((xMax - xMin) / 8 > clipW && xMax - xMin > 1000) ||
+ ((yMax - yMin) / 8 > clipH && yMax - yMin > 1000);
+
+ //--- set up the SplashDrawImageRowData object and the pipes
+ SplashDrawImageRowData dd;
+ dd.nComps = nComps;
+ dd.srcAlpha = srcAlpha;
+ pipeInit(&dd.pipe, NULL,
+ (Guchar)splashRound(state->fillAlpha * 255),
+ clipRes != splashClipAllInside || srcAlpha,
+ gFalse);
+
+ //--- choose the drawRow function
+ SplashDrawImageRowFunc drawRowFunc;
+ if (clipRes == splashClipAllInside) {
+ if (srcAlpha) {
+ drawRowFunc = &Splash::drawImageRowNoClipAlpha;
+ } else {
+ drawRowFunc = &Splash::drawImageRowNoClipNoAlpha;
+ }
+ } else {
+ if (srcAlpha) {
+ if (vectorAntialias) {
+ drawRowFunc = &Splash::drawImageRowClipAlphaAA;
+ } else {
+ drawRowFunc = &Splash::drawImageRowClipAlphaNoAA;
+ }
+ } else {
+ if (vectorAntialias) {
+ drawRowFunc = &Splash::drawImageRowClipNoAlphaAA;
+ } else {
+ drawRowFunc = &Splash::drawImageRowClipNoAlphaNoAA;
+ }
+ }
+ }
+
+ //--- 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);
+ }
+ }
+ if (vertFlip) {
+ if (horizFlip) { // bottom-up, mirrored
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler->nextLine();
+ mirrorImageRow(scaler->colorData(), scaler->alphaData(),
+ tmpLine, tmpAlphaLine,
+ scaledWidth, nComps, srcAlpha);
+ (this->*drawRowFunc)(&dd, tmpLine, tmpAlphaLine,
+ xMin, yMax - 1 - y, scaledWidth);
+ }
+ } else { // bottom-up
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler->nextLine();
+ (this->*drawRowFunc)(&dd, scaler->colorData(), scaler->alphaData(),
+ xMin, yMax - 1 - y, scaledWidth);
+ }
+ }
+ } else {
+ if (horizFlip) { // top-down, mirrored
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler->nextLine();
+ mirrorImageRow(scaler->colorData(), scaler->alphaData(),
+ tmpLine, tmpAlphaLine,
+ scaledWidth, nComps, srcAlpha);
+ (this->*drawRowFunc)(&dd, tmpLine, tmpAlphaLine,
+ xMin, yMin + y, scaledWidth);
+ }
+ } else { // top-down
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler->nextLine();
+ (this->*drawRowFunc)(&dd, scaler->colorData(), scaler->alphaData(),
+ xMin, yMin + y, scaledWidth);
+ }
+ }
+ }
+ gfree(tmpLine);
+ gfree(tmpAlphaLine);
+ delete scaler;
+ }
+
+ //--- arbitrary transform
+ } else {
+ // estimate of size of scaled image
+ int scaledWidth = splashRound(splashSqrt(mat[0] * mat[0]
+ + mat[1] * mat[1]));
+ int scaledHeight = splashRound(splashSqrt(mat[2] * mat[2]
+ + mat[3] * mat[3]));
+ if (scaledWidth < 1) {
+ scaledWidth = 1;
+ }
+ if (scaledHeight < 1) {
+ scaledHeight = 1;
+ }
+ if (veryLarge || (scaledWidth >= w && scaledHeight >= h)) {
+ scaledWidth = w;
+ scaledHeight = h;
+ }
+
+ // compute mapping from device space to scaled image space
+ SplashCoord mat1[6];
+ mat1[0] = mat[0] / scaledWidth;
+ mat1[1] = mat[1] / scaledWidth;
+ mat1[2] = mat[2] / scaledHeight;
+ mat1[3] = mat[3] / scaledHeight;
+ mat1[4] = mat[4];
+ mat1[5] = mat[5];
+ SplashCoord det = mat1[0] * mat1[3] - mat1[1] * mat1[2];
+ if (splashAbs(det) < 1e-6) {
+ // this should be caught by the singular matrix check in drawImage
+ return splashErrSingularMatrix;
+ }
+ SplashCoord invMat[6];
+ invMat[0] = mat1[3] / det;
+ invMat[1] = -mat1[1] / det;
+ invMat[2] = -mat1[2] / det;
+ invMat[3] = mat1[0] / det;
+ // the extra "+ 0.5 * (...)" terms are here because the
+ // drawImageArbitrary(No)Interp functions multiply by pixel
+ // centers, (x + 0.5, y + 0.5)
+ invMat[4] = (mat1[2] * mat1[5] - mat1[3] * mat1[4]) / det
+ + (invMat[0] + invMat[2]) * 0.5;
+ invMat[5] = (mat1[1] * mat1[4] - mat1[0] * mat1[5]) / det
+ + (invMat[1] + invMat[3]) * 0.5;
+
+ Guchar *scaledColor, *scaledAlpha;
+ GBool freeScaledImage;
+ getScaledImage(imageTag, src, srcData, w, h, nComps,
+ scaledWidth, scaledHeight, srcMode, srcAlpha, interpolate,
+ &scaledColor, &scaledAlpha, &freeScaledImage);
+
+ // draw it
+ if (interpolate) {
+ drawImageArbitraryInterp(scaledColor, scaledAlpha,
+ &dd, drawRowFunc, invMat,
+ scaledWidth, scaledHeight,
+ xMin, yMin, xMax, yMax,
+ nComps, srcAlpha);
+ } else {
+ drawImageArbitraryNoInterp(scaledColor, scaledAlpha,
+ &dd, drawRowFunc, invMat,
+ scaledWidth, scaledHeight,
+ xMin, yMin, xMax, yMax,
+ nComps, srcAlpha);
+ }
+
+ // free the downscaled/unscaled image
+ if (freeScaledImage) {
+ gfree(scaledColor);
+ gfree(scaledAlpha);
+ }
+ }
+
+ return splashOk;
+}
+
+ImageScaler *Splash::getImageScaler(GString *imageTag,
+ SplashImageSource src, void *srcData,
+ int w, int h, int nComps,
+ int scaledWidth, int scaledHeight,
+ SplashColorMode srcMode,
+ GBool srcAlpha, GBool interpolate) {
+ // Notes:
+ //
+ // * If the scaled image width or height is greater than 2000, we
+ // don't cache it.
+ //
+ // * Caching is done on the third consecutive use (second
+ // consecutive reuse) of an image; this avoids overhead on the
+ // common case of single-use images.
+
+ if (scaledWidth < 2000 && scaledHeight < 2000 &&
+ imageCache->match(imageTag, scaledWidth, scaledHeight,
+ srcMode, srcAlpha, interpolate)) {
+ if (imageCache->colorData) {
+ return new ReplayImageScaler(nComps, srcAlpha, scaledWidth,
+ imageCache->colorData,
+ imageCache->alphaData);
+ } else {
+ int lineSize;
+ if (scaledWidth < INT_MAX / nComps) {
+ lineSize = scaledWidth * nComps;
+ } else {
+ lineSize = -1;
+ }
+ imageCache->colorData = (Guchar *)gmallocn(scaledHeight, lineSize);
+ if (srcAlpha) {
+ 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);
+ }
+}
+
+void Splash::getScaledImage(GString *imageTag,
+ SplashImageSource src, void *srcData,
+ int w, int h, int nComps,
+ int scaledWidth, int scaledHeight,
+ SplashColorMode srcMode,
+ GBool srcAlpha, GBool interpolate,
+ Guchar **scaledColor, Guchar **scaledAlpha,
+ GBool *freeScaledImage) {
+ // Notes:
+ //
+ // * If the scaled image width or height is greater than 2000, we
+ // don't cache it.
+ //
+ // * This buffers the whole image anyway, so there's no reason to
+ // skip caching on the first reuse.
+
+ if (scaledWidth >= 2000 || scaledHeight >= 2000) {
+ int lineSize;
+ if (scaledWidth < INT_MAX / nComps) {
+ lineSize = scaledWidth * nComps;
+ } else {
+ lineSize = -1;
+ }
+ *scaledColor = (Guchar *)gmallocn(scaledHeight, lineSize);
+ if (srcAlpha) {
+ *scaledAlpha = (Guchar *)gmallocn(scaledHeight, scaledWidth);
+ } else {
+ *scaledAlpha = NULL;
+ }
+ *freeScaledImage = gTrue;
+ if (scaledWidth == w && scaledHeight == h) {
+ Guchar *colorPtr = *scaledColor;
+ Guchar *alphaPtr = *scaledAlpha;
+ for (int y = 0; y < scaledHeight; ++y) {
+ (*src)(srcData, colorPtr, alphaPtr);
+ colorPtr += scaledWidth * nComps;
+ if (srcAlpha) {
+ alphaPtr += scaledWidth;
+ }
+ }
+ } 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;
+ }
+ }
+ }
+ } 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 {
+ lineSize = -1;
+ }
+ imageCache->colorData = (Guchar *)gmallocn(scaledHeight, lineSize);
+ if (srcAlpha) {
+ imageCache->alphaData = (Guchar *)gmallocn(scaledHeight, scaledWidth);
+ }
+ if (scaledWidth == w && scaledHeight == h) {
+ Guchar *colorPtr = imageCache->colorData;
+ Guchar *alphaPtr = imageCache->alphaData;
+ for (int y = 0; y < scaledHeight; ++y) {
+ (*src)(srcData, colorPtr, alphaPtr);
+ colorPtr += scaledWidth * nComps;
+ if (srcAlpha) {
+ alphaPtr += scaledWidth;
+ }
+ }
+ } else {
+ SavingImageScaler scaler(src, srcData, w, h, nComps, srcAlpha,
+ scaledWidth, scaledHeight, interpolate,
+ imageCache->colorData, imageCache->alphaData);
+ Guchar *colorPtr = imageCache->colorData;
+ Guchar *alphaPtr = imageCache->alphaData;
+ for (int y = 0; y < scaledHeight; ++y) {
+ scaler.nextLine();
+ memcpy(colorPtr, scaler.colorData(), scaledWidth * nComps);
+ colorPtr += scaledWidth * nComps;
+ if (srcAlpha) {
+ memcpy(alphaPtr, scaler.alphaData(), scaledWidth);
+ alphaPtr += scaledWidth;
+ }
+ }
+ }
+ }
+ *scaledColor = imageCache->colorData;
+ *scaledAlpha = imageCache->alphaData;
+ *freeScaledImage = gFalse;
+ }
+}
+
+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;
+ }
+
+ Guchar *colorBuf = (Guchar *)gmallocn(xMax - xMin, nComps);
+ Guchar *alphaBuf = NULL;
+ if (srcAlpha) {
+ alphaBuf = (Guchar *)gmalloc(xMax - xMin);
+ }
+
+ for (int y = yMin; y < yMax; ++y) {
+ int rowMin = xMax;
+ int rowMax = 0;
+ for (int x = xMin; x < xMax; ++x) {
+ // note: invMat includes a "+0.5" factor so that this is really
+ // a multiply by (x+0.5, y+0.5)
+ int xx = splashFloor((SplashCoord)x * invMat[0]
+ + (SplashCoord)y * invMat[2] + invMat[4]);
+ int yy = splashFloor((SplashCoord)x * invMat[1]
+ + (SplashCoord)y * invMat[3] + invMat[5]);
+ if (xx >= 0 && xx < scaledWidth &&
+ yy >= 0 && yy < scaledHeight) {
+ Guchar *p = scaledColor + (yy * scaledWidth + xx) * nComps;
+ Guchar *q = colorBuf + (x - xMin) * nComps;
+ for (int i = 0; i < nComps; ++i) {
+ *q++ = *p++;
+ }
+ if (srcAlpha) {
+ alphaBuf[x - xMin] = scaledAlpha[yy * scaledWidth + xx];
+ }
+ if (x < rowMin) {
+ rowMin = x;
+ }
+ rowMax = x + 1;
+ }
+ }
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd,
+ colorBuf + (rowMin - xMin) * nComps,
+ alphaBuf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
+ }
+ }
+
+ gfree(colorBuf);
+ gfree(alphaBuf);
+}
+
+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;
+ }
+
+ Guchar *colorBuf = (Guchar *)gmallocn(xMax - xMin, nComps);
+ Guchar *alphaBuf = NULL;
+ if (srcAlpha) {
+ alphaBuf = (Guchar *)gmalloc(xMax - xMin);
+ }
+
+ for (int y = yMin; y < yMax; ++y) {
+ int rowMin = xMax;
+ int rowMax = 0;
+ for (int x = xMin; x < xMax; ++x) {
+ // note: invMat includes a "+0.5" factor so that this is really
+ // a multiply by (x+0.5, y+0.5)
+ SplashCoord xs = (SplashCoord)x * invMat[0]
+ + (SplashCoord)y * invMat[2] + invMat[4];
+ SplashCoord ys = (SplashCoord)x * invMat[1]
+ + (SplashCoord)y * invMat[3] + invMat[5];
+ int x0 = splashFloor(xs - 0.5);
+ int x1 = x0 + 1;
+ int y0 = splashFloor(ys - 0.5);
+ int y1 = y0 + 1;
+ if (x1 >= 0 && x0 < scaledWidth && y1 >= 0 && y0 < scaledHeight) {
+ SplashCoord sx0 = (SplashCoord)x1 + 0.5 - xs;
+ SplashCoord sx1 = (SplashCoord)1 - sx0;
+ SplashCoord sy0 = (SplashCoord)y1 + 0.5 - ys;
+ SplashCoord sy1 = (SplashCoord)1 - sy0;
+ if (x0 < 0) {
+ x0 = 0;
+ }
+ if (x1 >= scaledWidth) {
+ x1 = scaledWidth - 1;
+ }
+ if (y0 < 0) {
+ y0 = 0;
+ }
+ 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;
+ }
+ }
+ if (rowMin < rowMax) {
+ (this->*drawRowFunc)(dd,
+ colorBuf + (rowMin - xMin) * nComps,
+ alphaBuf + (rowMin - xMin),
+ rowMin, y, rowMax - rowMin);
+ }
+ }
+
+ gfree(colorBuf);
+ gfree(alphaBuf);
+}
+
+void Splash::mirrorImageRow(Guchar *colorIn, Guchar *alphaIn,
+ Guchar *colorOut, Guchar *alphaOut,
+ int width, int nComps, GBool srcAlpha) {
+ Guchar *p, *q;
+
+ p = colorIn;
+ q = colorOut + (width - 1) * nComps;
+ for (int i = 0; i < width; ++i) {
+ for (int j = 0; j < nComps; ++j) {
+ q[j] = p[j];
+ }
+ p += nComps;
+ q -= nComps;
+ }
+
+ if (srcAlpha) {
+ p = alphaIn;
+ q = alphaOut + (width - 1);
+ for (int i = 0; i < width; ++i) {
+ *q = *p;
+ ++p;
+ --q;
+ }
+ }
+}
+
+void Splash::drawImageRowNoClipNoAlpha(SplashDrawImageRowData *data,
+ Guchar *colorData, Guchar *alphaData,
+ int x, int y, int width) {
+ (this->*data->pipe.run)(&data->pipe, x, x + width - 1, y, NULL, colorData);
+}
+
+void Splash::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::drawImageRowClipNoAlphaNoAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
+ }
+ 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->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::drawImageRowClipNoAlphaAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
+ }
+ 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);
+}
+
+void Splash::drawImageRowClipAlphaNoAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
+ }
+ if (x < 0) {
+ colorData -= x * data->nComps;
+ alphaData -= x;
+ width += x;
+ x = 0;
+ }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
+ }
+ 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::drawImageRowClipAlphaAA(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width) {
+ if (y < 0 || y >= bitmap->height) {
+ return;
+ }
+ if (x < 0) {
+ colorData -= x * data->nComps;
+ alphaData -= x;
+ width += x;
+ x = 0;
+ }
+ if (x + width > bitmap->width) {
+ width = bitmap->width - x;
+ }
+ if (width <= 0) {
+ return;
+ }
+ 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,
+ int xDest, int yDest, int w, int h,
+ GBool noClip, GBool nonIsolated) {
+ SplashPipe pipe;
+ Guchar *mono1Ptr, *lineBuf, *linePtr;
+ Guchar mono1Mask, b;
+ int x0, x1, x, 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);
+ if (src->mode == splashModeMono1) {
+ // in mono1 mode, pipeRun expects the source to be in mono8
+ // format, so we need to extract the source color values into
+ // scanBuf, expanding them from mono1 to mono8
+ if (noClip) {
+ if (src->alpha) {
+ for (y = 0; y < h; ++y) {
+ mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3);
+ mono1Mask = (Guchar)(0x80 >> (xSrc & 7));
+ for (x = 0; x < w; ++x) {
+ scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
+ mono1Ptr += mono1Mask & 1;
+ mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
+ }
+ // 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,
+ scanBuf);
+ }
+ } else {
+ for (y = 0; y < h; ++y) {
+ mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3);
+ mono1Mask = (Guchar)(0x80 >> (xSrc & 7));
+ for (x = 0; x < w; ++x) {
+ scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
+ mono1Ptr += mono1Mask & 1;
+ mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
+ }
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ NULL,
+ scanBuf);
+ }
+ }
+ } 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) {
+ mono1Ptr = src->data
+ + (ySrc + y - yDest) * src->rowSize
+ + ((xSrc + x0 - xDest) >> 3);
+ mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7));
+ for (x = x0; x < x1; ++x) {
+ scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
+ mono1Ptr += mono1Mask & 1;
+ mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
+ }
+ memcpy(scanBuf2 + x0,
+ src->alpha + (ySrc + y - yDest) * src->alphaRowSize +
+ (xSrc + x0 - xDest),
+ x1 - x0);
+ if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1,
+ state->strokeAdjust)) {
+ continue;
+ }
+ // this uses shape instead of alpha, which isn't technically
+ // correct, but works out the same
+ (this->*pipe.run)(&pipe, x0, x1 - 1, y,
+ scanBuf2 + x0,
+ scanBuf + x0);
+ }
+ } else {
+ for (y = y0; y < y1; ++y) {
+ mono1Ptr = src->data
+ + (ySrc + y - yDest) * src->rowSize
+ + ((xSrc + x0 - xDest) >> 3);
+ mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7));
+ for (x = x0; x < x1; ++x) {
+ scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00;
+ mono1Ptr += mono1Mask & 1;
+ mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1));
+ }
+ memset(scanBuf2 + x0, 0xff, x1 - x0);
+ if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1,
+ state->strokeAdjust)) {
+ continue;
+ }
+ (this->*pipe.run)(&pipe, x0, x1 - 1, y,
+ scanBuf2 + x0,
+ scanBuf + x0);
+ }
+ }
+ }
+ }
+
+ } else if (src->mode == splashModeBGR8) {
+ // in BGR8 mode, pipeRun expects the source to be in RGB8 format,
+ // so we need to swap bytes
+ lineBuf = (Guchar *)gmallocn(w, 3);
+ if (noClip) {
+ if (src->alpha) {
+ for (y = 0; y < h; ++y) {
+ memcpy(lineBuf,
+ src->data + (ySrc + y) * src->rowSize + xSrc * 3,
+ w * 3);
+ for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) {
+ b = linePtr[0];
+ linePtr[0] = linePtr[2];
+ linePtr[2] = b;
+ }
+ // 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,
+ lineBuf);
+ }
+ } else {
+ for (y = 0; y < h; ++y) {
+ memcpy(lineBuf,
+ src->data + (ySrc + y) * src->rowSize + xSrc * 3,
+ w * 3);
+ for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) {
+ b = linePtr[0];
+ linePtr[0] = linePtr[2];
+ linePtr[2] = b;
+ }
+ (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y,
+ NULL, lineBuf);
+ }
+ }
+ } 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);
+ memcpy(lineBuf,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * 3,
+ (x1 - x0) * 3);
+ for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) {
+ b = linePtr[0];
+ linePtr[0] = linePtr[2];
+ linePtr[2] = b;
+ }
+ // 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, lineBuf);
+ }
+ } else {
+ for (y = y0; y < y1; ++y) {
+ memset(scanBuf + x0, 0xff, x1 - x0);
+ state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust);
+ memcpy(lineBuf,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * 3,
+ (x1 - x0) * 3);
+ for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) {
+ b = linePtr[0];
+ linePtr[0] = linePtr[2];
+ linePtr[2] = b;
+ }
+ (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y,
+ scanBuf + x0,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * bitmapComps);
+ }
+ }
+ }
+ }
+ gfree(lineBuf);
+
+ } else { // src->mode not mono1 or BGR8
+ if (noClip) {
+ if (src->alpha) {
+ for (y = 0; y < h; ++y) {
+ // 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) {
+ (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);
+ // 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);
+ (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y,
+ scanBuf + x0,
+ src->data +
+ (ySrc + y - yDest) * src->rowSize +
+ (xSrc + x0 - xDest) * bitmapComps);
+ }
+ }
+ }
+ }
+ }
+
+ 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;
+ Guchar alpha, alpha1, c, color0, color1, color2, mask;
+#if SPLASH_CMYK
+ Guchar color3;
+#endif
+ int x, y;
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ color0 = color[0];
+ for (y = 0; y < bitmap->height; ++y) {
+ p = &bitmap->data[y * bitmap->rowSize];
+ q = &bitmap->alpha[y * bitmap->alphaRowSize];
+ mask = 0x80;
+ for (x = 0; x < bitmap->width; ++x) {
+ alpha = *q++;
+ if (alpha == 0) {
+ if (color0 & 0x80) {
+ *p |= mask;
+ } else {
+ *p &= (Guchar)~mask;
+ }
+ } else if (alpha != 255) {
+ alpha1 = (Guchar)(255 - alpha);
+ c = (*p & mask) ? 0xff : 0x00;
+ c = div255(alpha1 * color0 + alpha * c);
+ if (c & 0x80) {
+ *p |= mask;
+ } else {
+ *p &= (Guchar)~mask;
+ }
+ }
+ if (!(mask = (Guchar)(mask >> 1))) {
+ mask = 0x80;
+ ++p;
+ }
+ }
+ }
+ break;
+ case splashModeMono8:
+ color0 = color[0];
+ for (y = 0; y < bitmap->height; ++y) {
+ p = &bitmap->data[y * bitmap->rowSize];
+ q = &bitmap->alpha[y * bitmap->alphaRowSize];
+ for (x = 0; x < bitmap->width; ++x) {
+ alpha = *q++;
+ if (alpha == 0) {
+ p[0] = color0;
+ } else if (alpha != 255) {
+ alpha1 = (Guchar)(255 - alpha);
+ p[0] = div255(alpha1 * color0 + alpha * p[0]);
+ }
+ ++p;
+ }
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ color0 = color[0];
+ color1 = color[1];
+ color2 = color[2];
+ for (y = 0; y < bitmap->height; ++y) {
+ p = &bitmap->data[y * bitmap->rowSize];
+ q = &bitmap->alpha[y * bitmap->alphaRowSize];
+ for (x = 0; x < bitmap->width; ++x) {
+ alpha = *q++;
+ if (alpha == 0) {
+ p[0] = color0;
+ p[1] = color1;
+ p[2] = color2;
+ } else if (alpha != 255) {
+ alpha1 = (Guchar)(255 - alpha);
+ p[0] = div255(alpha1 * color0 + alpha * p[0]);
+ p[1] = div255(alpha1 * color1 + alpha * p[1]);
+ p[2] = div255(alpha1 * color2 + alpha * p[2]);
+ }
+ p += 3;
+ }
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ color0 = color[0];
+ color1 = color[1];
+ color2 = color[2];
+ color3 = color[3];
+ for (y = 0; y < bitmap->height; ++y) {
+ p = &bitmap->data[y * bitmap->rowSize];
+ q = &bitmap->alpha[y * bitmap->alphaRowSize];
+ for (x = 0; x < bitmap->width; ++x) {
+ alpha = *q++;
+ if (alpha == 0) {
+ p[0] = color0;
+ p[1] = color1;
+ p[2] = color2;
+ p[3] = color3;
+ } else if (alpha != 255) {
+ alpha1 = (Guchar)(255 - alpha);
+ p[0] = div255(alpha1 * color0 + alpha * p[0]);
+ p[1] = div255(alpha1 * color1 + alpha * p[1]);
+ p[2] = div255(alpha1 * color2 + alpha * p[2]);
+ p[3] = div255(alpha1 * color3 + alpha * p[3]);
+ }
+ p += 4;
+ }
+ }
+ break;
+#endif
+ }
+ memset(bitmap->alpha, 255, bitmap->alphaRowSize * bitmap->height);
+}
+
+SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
+ int xDest, int yDest, int w, int h) {
+ SplashColorPtr p, q;
+ Guchar mask, srcMask;
+ int x, y;
+
+ if (src->mode != bitmap->mode) {
+ return splashErrModeMismatch;
+ }
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ for (y = 0; y < h; ++y) {
+ p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
+ mask = (Guchar)(0x80 >> (xDest & 7));
+ q = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)];
+ srcMask = (Guchar)(0x80 >> (xSrc & 7));
+ for (x = 0; x < w; ++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;
+ }
+ }
+ }
+ break;
+ case splashModeMono8:
+ for (y = 0; y < h; ++y) {
+ p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
+ q = &src->data[(ySrc + y) * src->rowSize + xSrc];
+ memcpy(p, q, w);
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ for (y = 0; y < h; ++y) {
+ p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
+ q = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc];
+ memcpy(p, q, 3 * w);
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ for (y = 0; y < h; ++y) {
+ p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
+ q = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
+ memcpy(p, q, 4 * w);
+ }
+ break;
+#endif
+ }
+
+ if (bitmap->alpha) {
+ for (y = 0; y < h; ++y) {
+ q = &bitmap->alpha[(yDest + y) * bitmap->alphaRowSize + xDest];
+ memset(q, 0, w);
+ }
+ }
+
+ return splashOk;
+}
+
+SplashError Splash::blitCorrectedAlpha(SplashBitmap *dest, int xSrc, int ySrc,
+ int xDest, int yDest, int w, int h) {
+ SplashColorPtr p, q;
+ Guchar *alpha0Ptr;
+ Guchar alpha0, aSrc, mask, srcMask;
+ int x, y;
+
+ if (bitmap->mode != dest->mode ||
+ !bitmap->alpha ||
+ !dest->alpha ||
+ !groupBackBitmap) {
+ return splashErrModeMismatch;
+ }
+
+ switch (bitmap->mode) {
+ case splashModeMono1:
+ for (y = 0; y < h; ++y) {
+ p = &dest->data[(yDest + y) * dest->rowSize + (xDest >> 3)];
+ mask = (Guchar)(0x80 >> (xDest & 7));
+ q = &bitmap->data[(ySrc + y) * bitmap->rowSize + (xSrc >> 3)];
+ srcMask = (Guchar)(0x80 >> (xSrc & 7));
+ for (x = 0; x < w; ++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;
+ }
+ }
+ }
+ break;
+ case splashModeMono8:
+ for (y = 0; y < h; ++y) {
+ p = &dest->data[(yDest + y) * dest->rowSize + xDest];
+ q = &bitmap->data[(ySrc + y) * bitmap->rowSize + xSrc];
+ memcpy(p, q, w);
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ for (y = 0; y < h; ++y) {
+ p = &dest->data[(yDest + y) * dest->rowSize + 3 * xDest];
+ q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 3 * xSrc];
+ memcpy(p, q, 3 * w);
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ for (y = 0; y < h; ++y) {
+ p = &dest->data[(yDest + y) * dest->rowSize + 4 * xDest];
+ q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 4 * xSrc];
+ memcpy(p, q, 4 * w);
+ }
+ break;
+#endif
+ }
+
+ for (y = 0; y < h; ++y) {
+ p = &dest->alpha[(yDest + y) * dest->alphaRowSize + xDest];
+ q = &bitmap->alpha[(ySrc + y) * bitmap->alphaRowSize + xSrc];
+ alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + ySrc + y)
+ * groupBackBitmap->alphaRowSize +
+ (groupBackX + xSrc)];
+ for (x = 0; x < w; ++x) {
+ alpha0 = *alpha0Ptr++;
+ aSrc = *q++;
+ *p++ = (Guchar)(alpha0 + aSrc - div255(alpha0 * aSrc));
+ }
+ }
+
+ return splashOk;
+}
+
+SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w,
+ int lineCap, int lineJoin,
+ GBool flatten) {
+ SplashPath *pathIn, *dashPath, *pathOut;
+ SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
+ SplashCoord crossprod, dotprod, miter, m;
+ SplashCoord angle, angleNext, dAngle, xc, yc;
+ SplashCoord dxJoin, dyJoin, dJoin, kappa;
+ SplashCoord cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
+ GBool first, last, closed;
+ int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1;
+ int left0, left1, left2, right0, right1, right2, join0, join1, join2;
+ int leftFirst, rightFirst, firstPt;
+
+ pathOut = new SplashPath();
+
+ if (path->length == 0) {
+ return pathOut;
+ }
+
+ if (flatten) {
+ pathIn = flattenPath(path, state->matrix, state->flatness);
+ if (state->lineDashLength > 0) {
+ dashPath = makeDashedPath(pathIn);
+ delete pathIn;
+ pathIn = dashPath;
+ if (pathIn->length == 0) {
+ delete pathIn;
+ return pathOut;
+ }
+ }
+ } else {
+ pathIn = path;
+ }
+
+ subpathStart0 = subpathStart1 = 0; // make gcc happy
+ seg = 0; // make gcc happy
+ closed = gFalse; // make gcc happy
+ left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
+ leftFirst = rightFirst = firstPt = 0; // make gcc happy
+
+ i0 = 0;
+ for (i1 = i0;
+ !(pathIn->flags[i1] & splashPathLast) &&
+ i1 + 1 < pathIn->length &&
+ pathIn->pts[i1+1].x == pathIn->pts[i1].x &&
+ pathIn->pts[i1+1].y == pathIn->pts[i1].y;
+ ++i1) ;
+
+ while (i1 < pathIn->length) {
+ if ((first = pathIn->flags[i0] & splashPathFirst)) {
+ subpathStart0 = i0;
+ subpathStart1 = i1;
+ seg = 0;
+ closed = pathIn->flags[i0] & splashPathClosed;
+ }
+ j0 = i1 + 1;
+ if (j0 < pathIn->length) {
+ for (j1 = j0;
+ !(pathIn->flags[j1] & splashPathLast) &&
+ j1 + 1 < pathIn->length &&
+ pathIn->pts[j1+1].x == pathIn->pts[j1].x &&
+ pathIn->pts[j1+1].y == pathIn->pts[j1].y;
+ ++j1) ;
+ } else {
+ j1 = j0;
+ }
+ if (pathIn->flags[i1] & splashPathLast) {
+ if (first && lineCap == splashLineCapRound) {
+ // special case: zero-length subpath with round line caps -->
+ // draw a circle
+ pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y);
+ pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y + bezierCircle2 * w,
+ pathIn->pts[i0].x + bezierCircle2 * w,
+ pathIn->pts[i0].y + (SplashCoord)0.5 * w,
+ pathIn->pts[i0].x,
+ pathIn->pts[i0].y + (SplashCoord)0.5 * w);
+ pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w,
+ pathIn->pts[i0].y + (SplashCoord)0.5 * w,
+ pathIn->pts[i0].x - (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y + bezierCircle2 * w,
+ pathIn->pts[i0].x - (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y);
+ pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y - bezierCircle2 * w,
+ pathIn->pts[i0].x - bezierCircle2 * w,
+ pathIn->pts[i0].y - (SplashCoord)0.5 * w,
+ pathIn->pts[i0].x,
+ pathIn->pts[i0].y - (SplashCoord)0.5 * w);
+ pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w,
+ pathIn->pts[i0].y - (SplashCoord)0.5 * w,
+ pathIn->pts[i0].x + (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y - bezierCircle2 * w,
+ pathIn->pts[i0].x + (SplashCoord)0.5 * w,
+ pathIn->pts[i0].y);
+ pathOut->close();
+ }
+ i0 = j0;
+ i1 = j1;
+ continue;
+ }
+ last = pathIn->flags[j1] & splashPathLast;
+ if (last) {
+ k0 = subpathStart1 + 1;
+ } else {
+ k0 = j1 + 1;
+ }
+ for (k1 = k0;
+ !(pathIn->flags[k1] & splashPathLast) &&
+ k1 + 1 < pathIn->length &&
+ pathIn->pts[k1+1].x == pathIn->pts[k1].x &&
+ pathIn->pts[k1+1].y == pathIn->pts[k1].y;
+ ++k1) ;
+
+ // compute the deltas for segment (i1, j0)
+#if USE_FIXEDPOINT
+ // the 1/d value can be small, which introduces significant
+ // inaccuracies in fixed point mode
+ d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
+ pathIn->pts[j0].x, pathIn->pts[j0].y);
+ dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d;
+ dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d;
+#else
+ d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
+ pathIn->pts[j0].x, pathIn->pts[j0].y);
+ dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x);
+ dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y);
+#endif
+ wdx = (SplashCoord)0.5 * w * dx;
+ wdy = (SplashCoord)0.5 * w * dy;
+
+ // draw the start cap
+ if (i0 == subpathStart0) {
+ firstPt = pathOut->length;
+ }
+ if (first && !closed) {
+ switch (lineCap) {
+ case splashLineCapButt:
+ pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
+ pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
+ break;
+ case splashLineCapRound:
+ pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
+ pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx,
+ pathIn->pts[i0].y + wdx - bezierCircle * wdy,
+ pathIn->pts[i0].x - wdx - bezierCircle * wdy,
+ pathIn->pts[i0].y - wdy + bezierCircle * wdx,
+ pathIn->pts[i0].x - wdx,
+ pathIn->pts[i0].y - wdy);
+ pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy,
+ pathIn->pts[i0].y - wdy - bezierCircle * wdx,
+ pathIn->pts[i0].x + wdy - bezierCircle * wdx,
+ pathIn->pts[i0].y - wdx - bezierCircle * wdy,
+ pathIn->pts[i0].x + wdy,
+ pathIn->pts[i0].y - wdx);
+ break;
+ case splashLineCapProjecting:
+ pathOut->moveTo(pathIn->pts[i0].x - wdx - wdy,
+ pathIn->pts[i0].y + wdx - wdy);
+ pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy,
+ pathIn->pts[i0].y - wdx - wdy);
+ break;
+ }
+ } else {
+ pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
+ pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
+ }
+
+ // draw the left side of the segment rectangle and the end cap
+ left2 = pathOut->length - 1;
+ if (last && !closed) {
+ switch (lineCap) {
+ case splashLineCapButt:
+ pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
+ pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
+ break;
+ case splashLineCapRound:
+ pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
+ pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx,
+ pathIn->pts[j0].y - wdx + bezierCircle * wdy,
+ pathIn->pts[j0].x + wdx + bezierCircle * wdy,
+ pathIn->pts[j0].y + wdy - bezierCircle * wdx,
+ pathIn->pts[j0].x + wdx,
+ pathIn->pts[j0].y + wdy);
+ pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy,
+ pathIn->pts[j0].y + wdy + bezierCircle * wdx,
+ pathIn->pts[j0].x - wdy + bezierCircle * wdx,
+ pathIn->pts[j0].y + wdx + bezierCircle * wdy,
+ pathIn->pts[j0].x - wdy,
+ pathIn->pts[j0].y + wdx);
+ break;
+ case splashLineCapProjecting:
+ pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx,
+ pathIn->pts[j0].y - wdx + wdy);
+ pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx,
+ pathIn->pts[j0].y + wdx + wdy);
+ break;
+ }
+ } else {
+ pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
+ pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
+ }
+
+ // draw the right side of the segment rectangle
+ // (NB: if stroke adjustment is enabled, the closepath operation MUST
+ // add a segment because this segment is used for a hint)
+ right2 = pathOut->length - 1;
+ pathOut->close(state->strokeAdjust != splashStrokeAdjustOff);
+
+ // draw the join
+ join2 = pathOut->length;
+ if (!last || closed) {
+
+ // compute the deltas for segment (j1, k0)
+#if USE_FIXEDPOINT
+ // the 1/d value can be small, which introduces significant
+ // inaccuracies in fixed point mode
+ d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
+ pathIn->pts[k0].x, pathIn->pts[k0].y);
+ dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d;
+ dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d;
+#else
+ d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
+ pathIn->pts[k0].x, pathIn->pts[k0].y);
+ dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x);
+ dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y);
+#endif
+ wdxNext = (SplashCoord)0.5 * w * dxNext;
+ wdyNext = (SplashCoord)0.5 * w * dyNext;
+
+ // compute the join parameters
+ crossprod = dx * dyNext - dy * dxNext;
+ dotprod = -(dx * dxNext + dy * dyNext);
+ if (dotprod > 0.9999) {
+ // avoid a divide-by-zero -- set miter to something arbitrary
+ // such that sqrt(miter) will exceed miterLimit (and m is never
+ // used in that situation)
+ // (note: the comparison value (0.9999) has to be less than
+ // 1-epsilon, where epsilon is the smallest value
+ // representable in the fixed point format)
+ miter = (state->miterLimit + 1) * (state->miterLimit + 1);
+ m = 0;
+ } else {
+ miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
+ if (miter < 1) {
+ // this can happen because of floating point inaccuracies
+ miter = 1;
+ }
+ m = splashSqrt(miter - 1);
+ }
+
+ // round join
+ if (lineJoin == splashLineJoinRound) {
+ // join angle < 180
+ if (crossprod < 0) {
+ angle = atan2((double)dx, (double)-dy);
+ angleNext = atan2((double)dxNext, (double)-dyNext);
+ if (angle < angleNext) {
+ angle += 2 * M_PI;
+ }
+ dAngle = (angle - angleNext) / M_PI;
+ if (dAngle < 0.501) {
+ // span angle is <= 90 degrees -> draw a single arc
+ kappa = dAngle * bezierCircle * w;
+ cx1 = pathIn->pts[j0].x - wdy + kappa * dx;
+ cy1 = pathIn->pts[j0].y + wdx + kappa * dy;
+ cx2 = pathIn->pts[j0].x - wdyNext - kappa * dxNext;
+ cy2 = pathIn->pts[j0].y + wdxNext - kappa * dyNext;
+ pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
+ pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
+ pathIn->pts[j0].y + wdxNext);
+ pathOut->curveTo(cx2, cy2, cx1, cy1,
+ pathIn->pts[j0].x - wdy,
+ pathIn->pts[j0].y + wdx);
+ } else {
+ // span angle is > 90 degrees -> split into two arcs
+ dJoin = splashDist(-wdy, wdx, -wdyNext, wdxNext);
+ if (dJoin > 0) {
+ dxJoin = (-wdyNext + wdy) / dJoin;
+ dyJoin = (wdxNext - wdx) / dJoin;
+ xc = pathIn->pts[j0].x
+ + (SplashCoord)0.5 * w
+ * cos((double)((SplashCoord)0.5 * (angle + angleNext)));
+ yc = pathIn->pts[j0].y
+ + (SplashCoord)0.5 * w
+ * sin((double)((SplashCoord)0.5 * (angle + angleNext)));
+ kappa = dAngle * bezierCircle2 * w;
+ cx1 = pathIn->pts[j0].x - wdy + kappa * dx;
+ cy1 = pathIn->pts[j0].y + wdx + kappa * dy;
+ cx2 = xc - kappa * dxJoin;
+ cy2 = yc - kappa * dyJoin;
+ cx3 = xc + kappa * dxJoin;
+ cy3 = yc + kappa * dyJoin;
+ cx4 = pathIn->pts[j0].x - wdyNext - kappa * dxNext;
+ cy4 = pathIn->pts[j0].y + wdxNext - kappa * dyNext;
+ pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
+ pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
+ pathIn->pts[j0].y + wdxNext);
+ pathOut->curveTo(cx4, cy4, cx3, cy3, xc, yc);
+ pathOut->curveTo(cx2, cy2, cx1, cy1,
+ pathIn->pts[j0].x - wdy,
+ pathIn->pts[j0].y + wdx);
+ }
+ }
+
+ // join angle >= 180
+ } else {
+ angle = atan2((double)-dx, (double)dy);
+ angleNext = atan2((double)-dxNext, (double)dyNext);
+ if (angleNext < angle) {
+ angleNext += 2 * M_PI;
+ }
+ dAngle = (angleNext - angle) / M_PI;
+ if (dAngle < 0.501) {
+ // span angle is <= 90 degrees -> draw a single arc
+ kappa = dAngle * bezierCircle * w;
+ cx1 = pathIn->pts[j0].x + wdy + kappa * dx;
+ cy1 = pathIn->pts[j0].y - wdx + kappa * dy;
+ cx2 = pathIn->pts[j0].x + wdyNext - kappa * dxNext;
+ cy2 = pathIn->pts[j0].y - wdxNext - kappa * dyNext;
+ pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
+ pathOut->lineTo(pathIn->pts[j0].x + wdy,
+ pathIn->pts[j0].y - wdx);
+ pathOut->curveTo(cx1, cy1, cx2, cy2,
+ pathIn->pts[j0].x + wdyNext,
+ pathIn->pts[j0].y - wdxNext);
+ } else {
+ // span angle is > 90 degrees -> split into two arcs
+ dJoin = splashDist(wdy, -wdx, wdyNext, -wdxNext);
+ if (dJoin > 0) {
+ dxJoin = (wdyNext - wdy) / dJoin;
+ dyJoin = (-wdxNext + wdx) / dJoin;
+ xc = pathIn->pts[j0].x
+ + (SplashCoord)0.5 * w
+ * cos((double)((SplashCoord)0.5 * (angle + angleNext)));
+ yc = pathIn->pts[j0].y
+ + (SplashCoord)0.5 * w
+ * sin((double)((SplashCoord)0.5 * (angle + angleNext)));
+ kappa = dAngle * bezierCircle2 * w;
+ cx1 = pathIn->pts[j0].x + wdy + kappa * dx;
+ cy1 = pathIn->pts[j0].y - wdx + kappa * dy;
+ cx2 = xc - kappa * dxJoin;
+ cy2 = yc - kappa * dyJoin;
+ cx3 = xc + kappa * dxJoin;
+ cy3 = yc + kappa * dyJoin;
+ cx4 = pathIn->pts[j0].x + wdyNext - kappa * dxNext;
+ cy4 = pathIn->pts[j0].y - wdxNext - kappa * dyNext;
+ pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
+ pathOut->lineTo(pathIn->pts[j0].x + wdy,
+ pathIn->pts[j0].y - wdx);
+ pathOut->curveTo(cx1, cy1, cx2, cy2, xc, yc);
+ pathOut->curveTo(cx3, cy3, cx4, cy4,
+ pathIn->pts[j0].x + wdyNext,
+ pathIn->pts[j0].y - wdxNext);
+ }
+ }
+ }
+
+ } else {
+ pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
+
+ // join angle < 180
+ if (crossprod < 0) {
+ pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
+ pathIn->pts[j0].y + wdxNext);
+ // miter join inside limit
+ if (lineJoin == splashLineJoinMiter &&
+ splashSqrt(miter) <= state->miterLimit) {
+ pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m,
+ pathIn->pts[j0].y + wdx + wdy * m);
+ pathOut->lineTo(pathIn->pts[j0].x - wdy,
+ pathIn->pts[j0].y + wdx);
+ // bevel join or miter join outside limit
+ } else {
+ pathOut->lineTo(pathIn->pts[j0].x - wdy,
+ pathIn->pts[j0].y + wdx);
+ }
+
+ // join angle >= 180
+ } else {
+ pathOut->lineTo(pathIn->pts[j0].x + wdy,
+ pathIn->pts[j0].y - wdx);
+ // miter join inside limit
+ if (lineJoin == splashLineJoinMiter &&
+ splashSqrt(miter) <= state->miterLimit) {
+ pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m,
+ pathIn->pts[j0].y - wdx + wdy * m);
+ pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
+ pathIn->pts[j0].y - wdxNext);
+ // bevel join or miter join outside limit
+ } else {
+ pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
+ pathIn->pts[j0].y - wdxNext);
+ }
+ }
+ }
+
+ pathOut->close();
+ }
+
+ // add stroke adjustment hints
+ if (state->strokeAdjust != splashStrokeAdjustOff) {
+
+ // subpath with one segment
+ if (seg == 0 && last) {
+ switch (lineCap) {
+ case splashLineCapButt:
+ pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
+ firstPt, pathOut->length - 1);
+ break;
+ case splashLineCapProjecting:
+ pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
+ firstPt, pathOut->length - 1, gTrue);
+ break;
+ case splashLineCapRound:
+ break;
+ }
+ pathOut->addStrokeAdjustHint(left2, right2,
+ firstPt, pathOut->length - 1);
+ } else {
+
+ // start of subpath
+ if (seg == 1) {
+
+ // start cap
+ if (!closed) {
+ switch (lineCap) {
+ case splashLineCapButt:
+ pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
+ firstPt, firstPt + 1);
+ pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
+ right1 + 1, right1 + 1);
+ break;
+ case splashLineCapProjecting:
+ pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
+ firstPt, firstPt + 1, gTrue);
+ pathOut->addStrokeAdjustHint(firstPt, left1 + 1,
+ right1 + 1, right1 + 1, gTrue);
+ break;
+ case splashLineCapRound:
+ break;
+ }
+ }
+
+ // first segment
+ pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
+ pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
+ }
+
+ // middle of subpath
+ if (seg > 1) {
+ pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
+ pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
+ pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
+ }
+
+ // end of subpath
+ if (last) {
+
+ if (closed) {
+ // first segment
+ pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
+ left2 + 1, right2);
+ pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
+ join2, pathOut->length - 1);
+
+ // last segment
+ pathOut->addStrokeAdjustHint(left2, right2,
+ left1 + 1, right1);
+ pathOut->addStrokeAdjustHint(left2, right2,
+ join1, pathOut->length - 1);
+ pathOut->addStrokeAdjustHint(left2, right2,
+ leftFirst - 1, leftFirst);
+ pathOut->addStrokeAdjustHint(left2, right2,
+ rightFirst + 1, rightFirst + 1);
+
+ } else {
+
+ // last segment
+ pathOut->addStrokeAdjustHint(left2, right2,
+ left1 + 1, right1);
+ pathOut->addStrokeAdjustHint(left2, right2,
+ join1, pathOut->length - 1);
+
+ // end cap
+ switch (lineCap) {
+ case splashLineCapButt:
+ pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1,
+ left2 + 1, left2 + 2);
+ break;
+ case splashLineCapProjecting:
+ pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1,
+ left2 + 1, left2 + 2, gTrue);
+ break;
+ case splashLineCapRound:
+ break;
+ }
+ }
+ }
+ }
+
+ left0 = left1;
+ left1 = left2;
+ right0 = right1;
+ right1 = right2;
+ join0 = join1;
+ join1 = join2;
+ if (seg == 0) {
+ leftFirst = left2;
+ rightFirst = right2;
+ }
+ }
+
+ i0 = j0;
+ i1 = j1;
+ ++seg;
+ }
+
+ if (pathIn != path) {
+ delete pathIn;
+ }
+
+ return pathOut;
+}
+
+SplashClipResult Splash::limitRectToClipRect(int *xMin, int *yMin,
+ int *xMax, int *yMax) {
+ int t;
+
+ if ((t = state->clip->getXMinI(state->strokeAdjust)) > *xMin) {
+ *xMin = t;
+ }
+ if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < *xMax) {
+ *xMax = t;
+ }
+ if ((t = state->clip->getYMinI(state->strokeAdjust)) > *yMin) {
+ *yMin = t;
+ }
+ if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < *yMax) {
+ *yMax = t;
+ }
+ if (*xMin >= *xMax || *yMin >= *yMax) {
+ return splashClipAllOutside;
+ }
+ return state->clip->testRect(*xMin, *yMin, *xMax - 1, *yMax - 1,
+ state->strokeAdjust);
+}
+
+void Splash::dumpPath(SplashPath *path) {
+ int i;
+
+ for (i = 0; i < path->length; ++i) {
+ printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n",
+ i, (double)path->pts[i].x, (double)path->pts[i].y,
+ (path->flags[i] & splashPathFirst) ? " first" : "",
+ (path->flags[i] & splashPathLast) ? " last" : "",
+ (path->flags[i] & splashPathClosed) ? " closed" : "",
+ (path->flags[i] & splashPathCurve) ? " curve" : "");
+ }
+ if (path->hintsLength == 0) {
+ printf(" no hints\n");
+ } else {
+ for (i = 0; i < path->hintsLength; ++i) {
+ printf(" hint %3d: ctrl0=%d ctrl1=%d pts=%d..%d\n",
+ i, path->hints[i].ctrl0, path->hints[i].ctrl1,
+ path->hints[i].firstPt, path->hints[i].lastPt);
+ }
+ }
+}
+
+void Splash::dumpXPath(SplashXPath *path) {
+ int i;
+
+ for (i = 0; i < path->length; ++i) {
+ printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f count=%d\n",
+ i, (double)path->segs[i].x0, (double)path->segs[i].y0,
+ (double)path->segs[i].x1, (double)path->segs[i].y1,
+ path->segs[i].count);
+ }
+}
+
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/Splash.h b/Build/source/libs/xpdf/xpdf-src/splash/Splash.h
new file mode 100644
index 00000000000..d6cc33a5fad
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/Splash.h
@@ -0,0 +1,532 @@
+//========================================================================
+//
+// Splash.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASH_H
+#define SPLASH_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+#include "SplashClip.h"
+
+class GString;
+class Splash;
+class SplashBitmap;
+struct SplashGlyphBitmap;
+class SplashState;
+class SplashPattern;
+class SplashScreen;
+class SplashPath;
+class SplashXPath;
+class SplashFont;
+struct SplashPipe;
+struct SplashDrawImageMaskRowData;
+class ImageScaler;
+typedef void (Splash::*SplashDrawImageMaskRowFunc)(
+ SplashDrawImageMaskRowData *data,
+ Guchar *maskData,
+ int x, int y, int width);
+struct SplashDrawImageRowData;
+typedef void (Splash::*SplashDrawImageRowFunc)(SplashDrawImageRowData *data,
+ Guchar *colorData,
+ Guchar *alphaData,
+ int x, int y, int width);
+
+//------------------------------------------------------------------------
+
+// Retrieves the next line of pixels in an image mask. Normally,
+// fills in *<line> and returns true. If the image stream is
+// exhausted, returns false.
+typedef GBool (*SplashImageMaskSource)(void *data, Guchar *pixel);
+
+// Retrieves the next line of pixels in an image. Normally, fills in
+// *<line> and returns true. If the image stream is exhausted,
+// returns false.
+typedef GBool (*SplashImageSource)(void *data, SplashColorPtr colorLine,
+ Guchar *alphaLine);
+
+
+//------------------------------------------------------------------------
+
+enum SplashPipeResultColorCtrl {
+ splashPipeResultColorNoAlphaBlendMono,
+ splashPipeResultColorNoAlphaBlendRGB,
+#if SPLASH_CMYK
+ splashPipeResultColorNoAlphaBlendCMYK,
+#endif
+ splashPipeResultColorAlphaNoBlendMono,
+ splashPipeResultColorAlphaNoBlendRGB,
+#if SPLASH_CMYK
+ splashPipeResultColorAlphaNoBlendCMYK,
+#endif
+ splashPipeResultColorAlphaBlendMono,
+ splashPipeResultColorAlphaBlendRGB
+#if SPLASH_CMYK
+ ,
+ splashPipeResultColorAlphaBlendCMYK
+#endif
+};
+
+//------------------------------------------------------------------------
+
+// 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
+//------------------------------------------------------------------------
+
+// This holds a cached image, and is shared by multiple Splash objects
+// in the same thread.
+class SplashImageCache {
+public:
+
+ SplashImageCache();
+ ~SplashImageCache();
+ GBool match(GString *aTag, int aWidth, int aHeight,
+ SplashColorMode aMode, GBool aAlpha,
+ GBool aInterpolate);
+ void reset(GString *aTag, int aWidth, int aHeight,
+ SplashColorMode aMode, GBool aAlpha,
+ GBool aInterpolate);
+ void incRefCount();
+ void decRefCount();
+
+ GString *tag;
+ int width;
+ int height;
+ SplashColorMode mode;
+ GBool alpha;
+ GBool interpolate;
+ Guchar *colorData;
+ Guchar *alphaData;
+
+ int refCount;
+};
+
+//------------------------------------------------------------------------
+// Splash
+//------------------------------------------------------------------------
+
+class Splash {
+public:
+
+ // Create a new rasterizer object.
+ Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
+ SplashImageCache *imageCacheA,
+ SplashScreenParams *screenParams = NULL);
+ Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
+ SplashImageCache *imageCacheA, SplashScreen *screenA);
+
+ ~Splash();
+
+ //----- state read
+
+ SplashCoord *getMatrix();
+ SplashPattern *getStrokePattern();
+ SplashPattern *getFillPattern();
+ SplashScreen *getScreen();
+ SplashBlendFunc getBlendFunc();
+ SplashCoord getStrokeAlpha();
+ SplashCoord getFillAlpha();
+ SplashCoord getLineWidth();
+ int getLineCap();
+ int getLineJoin();
+ SplashCoord getMiterLimit();
+ SplashCoord getFlatness();
+ SplashCoord *getLineDash();
+ int getLineDashLength();
+ SplashCoord getLineDashPhase();
+ SplashStrokeAdjustMode getStrokeAdjust();
+ SplashClip *getClip();
+ SplashBitmap *getSoftMask();
+ GBool getInNonIsolatedGroup();
+ GBool getInKnockoutGroup();
+
+ //----- state write
+
+ void setMatrix(SplashCoord *matrix);
+ void setStrokePattern(SplashPattern *strokeColor);
+ void setFillPattern(SplashPattern *fillColor);
+ void setScreen(SplashScreen *screen);
+ void setBlendFunc(SplashBlendFunc func);
+ void setStrokeAlpha(SplashCoord alpha);
+ void setFillAlpha(SplashCoord alpha);
+ void setLineWidth(SplashCoord lineWidth);
+ void setLineCap(int lineCap);
+ void setLineJoin(int lineJoin);
+ void setMiterLimit(SplashCoord miterLimit);
+ void setFlatness(SplashCoord flatness);
+ // the <lineDash> array will be copied
+ void setLineDash(SplashCoord *lineDash, int lineDashLength,
+ SplashCoord lineDashPhase);
+ void setStrokeAdjust(SplashStrokeAdjustMode strokeAdjust);
+ // NB: uses transformed coordinates.
+ void clipResetToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1);
+ // NB: uses transformed coordinates.
+ SplashError clipToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1);
+ // NB: uses untransformed coordinates.
+ SplashError clipToPath(SplashPath *path, GBool eo);
+ 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);
+
+ //----- state save/restore
+
+ void saveState();
+ SplashError restoreState();
+
+ //----- drawing operations
+
+ // Fill the bitmap with <color>. This is not subject to clipping.
+ void clear(SplashColorPtr color, Guchar alpha = 0x00);
+
+ // Stroke a path using the current stroke pattern.
+ SplashError stroke(SplashPath *path);
+
+ // Fill a path using the current fill pattern.
+ SplashError fill(SplashPath *path, GBool eo);
+
+ // Draw a character, using the current fill pattern.
+ SplashError fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font);
+
+ // Draw a glyph, using the current fill pattern. This function does
+ // not free any data, i.e., it ignores glyph->freeData.
+ SplashError fillGlyph(SplashCoord x, SplashCoord y,
+ SplashGlyphBitmap *glyph);
+
+ // Draws an image mask using the fill color. This will read <h>
+ // lines of <w> pixels from <src>, starting with the top line. "1"
+ // pixels will be drawn with the current fill color; "0" pixels are
+ // transparent. The matrix:
+ // [ mat[0] mat[1] 0 ]
+ // [ mat[2] mat[3] 0 ]
+ // [ mat[4] mat[5] 1 ]
+ // maps a unit square to the desired destination for the image, in
+ // PostScript style:
+ // [x' y' 1] = [x y 1] * mat
+ // Note that the Splash y axis points downward, and the image source
+ // is assumed to produce pixels in raster order, starting from the
+ // top line.
+ SplashError fillImageMask(GString *imageTag,
+ SplashImageMaskSource src, void *srcData,
+ int w, int h, SplashCoord *mat,
+ GBool glyphMode, GBool interpolate);
+
+ // Draw an image. This will read <h> lines of <w> pixels from
+ // <src>, starting with the top line. These pixels are assumed to
+ // be in the source mode, <srcMode>. If <srcAlpha> is true, the
+ // alpha values returned by <src> are used; otherwise they are
+ // ignored. The following combinations of source and target modes
+ // are supported:
+ // source target
+ // ------ ------
+ // Mono8 Mono1 -- with dithering
+ // Mono8 Mono8
+ // RGB8 RGB8
+ // BGR8 RGB8
+ // CMYK8 CMYK8
+ // The matrix behaves as for fillImageMask.
+ SplashError drawImage(GString *imageTag,
+ SplashImageSource src, void *srcData,
+ SplashColorMode srcMode, GBool srcAlpha,
+ int w, int h, SplashCoord *mat,
+ GBool interpolate);
+
+ // Composite a rectangular region from <src> onto this Splash
+ // object.
+ SplashError composite(SplashBitmap *src, int xSrc, int ySrc,
+ 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);
+
+ // Copy a rectangular region from <src> onto the bitmap belonging to
+ // this Splash object. The destination alpha values are all set to
+ // zero.
+ SplashError blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
+ int xDest, int yDest, int w, int h);
+
+ // Copy a rectangular region from the bitmap belonging to this
+ // Splash object to <dest>. The alpha values are corrected for a
+ // non-isolated group.
+ SplashError blitCorrectedAlpha(SplashBitmap *dest, int xSrc, int ySrc,
+ int xDest, int yDest, int w, int h);
+
+ //----- misc
+
+ // Construct a path for a stroke, given the path to be stroked and
+ // the line width <w>. All other stroke parameters are taken from
+ // the current state. If <flatten> is true, this function will
+ // first flatten the path and handle the linedash.
+ SplashPath *makeStrokePath(SplashPath *path, SplashCoord w,
+ int lineCap, int lineJoin,
+ GBool flatten = gTrue);
+
+ // Reduce the size of a rectangle as much as possible by moving any
+ // edges that are completely outside the clip region. Returns the
+ // clipping status of the resulting rectangle.
+ SplashClipResult limitRectToClipRect(int *xMin, int *yMin,
+ int *xMax, int *yMax);
+
+ // 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; }
+
+ // Get a bounding box which includes all modifications since the
+ // last call to clearModRegion.
+ void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax)
+ { *xMin = modXMin; *yMin = modYMin; *xMax = modXMax; *yMax = modYMax; }
+
+ // Clear the modified region bounding box.
+ void clearModRegion();
+
+ // Get clipping status for the last drawing operation subject to
+ // clipping.
+ SplashClipResult getClipRes() { return opClipRes; }
+
+ // Toggle debug mode on or off.
+ void setDebugMode(GBool debugModeA) { debugMode = debugModeA; }
+
+ SplashImageCache *getImageCache() { return imageCache; }
+
+#if 1 //~tmp: turn off anti-aliasing temporarily
+ void setInShading(GBool sh) { inShading = sh; }
+#endif
+
+
+private:
+
+ void pipeInit(SplashPipe *pipe, SplashPattern *pattern,
+ Guchar aInput, GBool usesShape,
+ 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,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+#if SPLASH_CMYK
+ void pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+#endif
+ void pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+#if SPLASH_CMYK
+ 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,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+#if SPLASH_CMYK
+ 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,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+ void pipeRunNonIsoBGR8(SplashPipe *pipe, int x0, int x1, int y,
+ Guchar *shapePtr, SplashColorPtr cSrcPtr);
+#if SPLASH_CMYK
+ 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);
+ void updateModY(int y);
+ void strokeNarrow(SplashPath *path);
+ void drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip);
+ void strokeWide(SplashPath *path, SplashCoord w,
+ int lineCap, int lineJoin);
+ SplashPath *flattenPath(SplashPath *path, SplashCoord *matrix,
+ SplashCoord flatness);
+ void flattenCurve(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1,
+ SplashCoord x2, SplashCoord y2,
+ SplashCoord x3, SplashCoord y3,
+ SplashCoord *matrix, SplashCoord flatness2,
+ SplashPath *fPath);
+ SplashPath *makeDashedPath(SplashPath *xPath);
+ SplashError fillWithPattern(SplashPath *path, GBool eo,
+ SplashPattern *pattern, SplashCoord alpha);
+ SplashPath *tweakFillPath(SplashPath *path);
+ GBool pathAllOutside(SplashPath *path);
+ SplashError fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph);
+ void getImageBounds(SplashCoord xyMin, SplashCoord xyMax,
+ int *xyMinI, int *xyMaxI);
+ 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,
+ 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);
+
+
+ static SplashPipeResultColorCtrl pipeResultColorNoAlphaBlend[];
+ static SplashPipeResultColorCtrl pipeResultColorAlphaNoBlend[];
+ static SplashPipeResultColorCtrl pipeResultColorAlphaBlend[];
+ static int pipeNonIsoGroupCorrection[];
+
+ SplashBitmap *bitmap;
+ int bitmapComps;
+ SplashState *state;
+ Guchar *scanBuf;
+ Guchar *scanBuf2;
+ 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;
+ GBool vectorAntialias;
+ GBool inShading;
+ GBool debugMode;
+
+ SplashImageCache *imageCache;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc
new file mode 100644
index 00000000000..db341d7c733
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.cc
@@ -0,0 +1,288 @@
+//========================================================================
+//
+// SplashBitmap.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <limits.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "gfile.h"
+#include "Trace.h"
+#include "SplashErrorCodes.h"
+#include "SplashBitmap.h"
+
+//------------------------------------------------------------------------
+// SplashBitmap
+//------------------------------------------------------------------------
+
+SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPad,
+ SplashColorMode modeA, GBool alphaA,
+ GBool topDown, SplashBitmap *parentA) {
+ // NB: this code checks that rowSize fits in a signed 32-bit
+ // integer, because some code (outside this class) makes that
+ // assumption
+ width = widthA;
+ height = heightA;
+ mode = modeA;
+ switch (mode) {
+ case splashModeMono1:
+ if (width <= 0) {
+ gMemError("invalid bitmap width");
+ }
+ rowSize = (width + 7) >> 3;
+ break;
+ case splashModeMono8:
+ if (width <= 0) {
+ gMemError("invalid bitmap width");
+ }
+ rowSize = width;
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ if (width <= 0 || width > INT_MAX / 3) {
+ gMemError("invalid bitmap width");
+ }
+ rowSize = (SplashBitmapRowSize)width * 3;
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ if (width <= 0 || width > INT_MAX / 4) {
+ gMemError("invalid bitmap width");
+ }
+ rowSize = (SplashBitmapRowSize)width * 4;
+ break;
+#endif
+ }
+ 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;
+ oldRowSize = 0;
+ oldAlphaRowSize = 0;
+ oldHeight = 0;
+ if (parent && parent->oldData &&
+ parent->oldRowSize == rowSize &&
+ 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;
+ rowSize = -rowSize;
+ }
+ if (alphaA) {
+ alphaRowSize = width;
+ if (parent && parent->oldAlpha &&
+ parent->oldAlphaRowSize == alphaRowSize &&
+ parent->oldHeight == height) {
+ alpha = parent->oldAlpha;
+ parent->oldAlpha = NULL;
+ } else {
+ alpha = (Guchar *)gmallocn64(height, alphaRowSize);
+ }
+ } else {
+ alphaRowSize = 0;
+ alpha = NULL;
+ }
+}
+
+SplashBitmap::~SplashBitmap() {
+ traceFree(this, "free bitmap");
+ if (data && rowSize < 0) {
+ rowSize = -rowSize;
+ data -= (height - 1) * rowSize;
+ }
+ if (parent && rowSize > 4000000 / height) {
+ gfree(parent->oldData);
+ gfree(parent->oldAlpha);
+ parent->oldData = data;
+ parent->oldAlpha = alpha;
+ parent->oldRowSize = rowSize;
+ parent->oldAlphaRowSize = alphaRowSize;
+ parent->oldHeight = height;
+ } else {
+ gfree(data);
+ gfree(alpha);
+ }
+ gfree(oldData);
+ gfree(oldAlpha);
+}
+
+SplashError SplashBitmap::writePNMFile(char *fileName) {
+ FILE *f;
+ SplashError err;
+
+ if (!(f = openFile(fileName, "wb"))) {
+ return splashErrOpenFile;
+ }
+ err = writePNMFile(f);
+ fclose(f);
+ return err;
+}
+
+SplashError SplashBitmap::writePNMFile(FILE *f) {
+ SplashColorPtr row, p;
+ int x, y;
+
+ switch (mode) {
+
+ case splashModeMono1:
+ fprintf(f, "P4\n%d %d\n", width, height);
+ row = data;
+ for (y = 0; y < height; ++y) {
+ p = row;
+ for (x = 0; x < width; x += 8) {
+ fputc(*p ^ 0xff, f);
+ ++p;
+ }
+ row += rowSize;
+ }
+ break;
+
+ case splashModeMono8:
+ fprintf(f, "P5\n%d %d\n255\n", width, height);
+ row = data;
+ for (y = 0; y < height; ++y) {
+ fwrite(row, 1, width, f);
+ row += rowSize;
+ }
+ break;
+
+ case splashModeRGB8:
+ fprintf(f, "P6\n%d %d\n255\n", width, height);
+ row = data;
+ for (y = 0; y < height; ++y) {
+ fwrite(row, 1, 3 * width, f);
+ row += rowSize;
+ }
+ break;
+
+ case splashModeBGR8:
+ fprintf(f, "P6\n%d %d\n255\n", width, height);
+ row = data;
+ for (y = 0; y < height; ++y) {
+ p = row;
+ for (x = 0; x < width; ++x) {
+ fputc(splashBGR8R(p), f);
+ fputc(splashBGR8G(p), f);
+ fputc(splashBGR8B(p), f);
+ p += 3;
+ }
+ row += rowSize;
+ }
+ break;
+
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ fprintf(f, "P7\n");
+ fprintf(f, "WIDTH %d\n", width);
+ fprintf(f, "HEIGHT %d\n", height);
+ fprintf(f, "DEPTH 4\n");
+ fprintf(f, "MAXVAL 255\n");
+ fprintf(f, "TUPLTYPE CMYK\n");
+ fprintf(f, "ENDHDR\n");
+ row = data;
+ for (y = 0; y < height; ++y) {
+ fwrite(row, 1, 4 * width, f);
+ row += rowSize;
+ }
+ break;
+#endif
+
+ }
+
+ return splashOk;
+}
+
+SplashError SplashBitmap::writeAlphaPGMFile(char *fileName) {
+ FILE *f;
+
+ if (!alpha) {
+ return splashErrModeMismatch;
+ }
+ if (!(f = openFile(fileName, "wb"))) {
+ return splashErrOpenFile;
+ }
+ fprintf(f, "P5\n%d %d\n255\n", width, height);
+ fwrite(alpha, 1, width * height, f);
+ fclose(f);
+ return splashOk;
+}
+
+
+void SplashBitmap::getPixel(int x, int y, SplashColorPtr pixel) {
+ SplashColorPtr p;
+
+ if (y < 0 || y >= height || x < 0 || x >= width) {
+ return;
+ }
+ switch (mode) {
+ case splashModeMono1:
+ p = &data[y * rowSize + (x >> 3)];
+ pixel[0] = (p[0] & (0x80 >> (x & 7))) ? 0xff : 0x00;
+ break;
+ case splashModeMono8:
+ p = &data[y * rowSize + x];
+ pixel[0] = p[0];
+ break;
+ case splashModeRGB8:
+ p = &data[y * rowSize + 3 * x];
+ pixel[0] = p[0];
+ pixel[1] = p[1];
+ pixel[2] = p[2];
+ break;
+ case splashModeBGR8:
+ p = &data[y * rowSize + 3 * x];
+ pixel[0] = p[2];
+ pixel[1] = p[1];
+ pixel[2] = p[0];
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ p = &data[y * rowSize + 4 * x];
+ pixel[0] = p[0];
+ pixel[1] = p[1];
+ pixel[2] = p[2];
+ pixel[3] = p[3];
+ break;
+#endif
+ }
+}
+
+Guchar SplashBitmap::getAlpha(int x, int y) {
+ return alpha[y * (size_t)width + x];
+}
+
+SplashColorPtr SplashBitmap::takeData() {
+ SplashColorPtr data2;
+
+ data2 = data;
+ data = NULL;
+ return data2;
+}
+
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h
new file mode 100644
index 00000000000..54975a377a2
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashBitmap.h
@@ -0,0 +1,98 @@
+//========================================================================
+//
+// SplashBitmap.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHBITMAP_H
+#define SPLASHBITMAP_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#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"
+
+//------------------------------------------------------------------------
+
+// ssize_t isn't well-defined, so define our own
+#if SIZE_MAX == UINT_MAX
+ typedef int SplashBitmapRowSize;
+# define SplashBitmapRowSizeMax INT_MAX
+#else
+ typedef long long SplashBitmapRowSize;
+# define SplashBitmapRowSizeMax LLONG_MAX
+#endif
+
+//------------------------------------------------------------------------
+// SplashBitmap
+//------------------------------------------------------------------------
+
+class SplashBitmap {
+public:
+
+ // Create a new bitmap. It will have <widthA> x <heightA> pixels in
+ // color mode <modeA>. Rows will be padded out to a multiple of
+ // <rowPad> bytes. If <topDown> is false, the bitmap will be stored
+ // upside-down, i.e., with the last row first in memory.
+ SplashBitmap(int widthA, int heightA, int rowPad,
+ SplashColorMode modeA, GBool alphaA,
+ GBool topDown, SplashBitmap *parentA);
+
+ ~SplashBitmap();
+
+ int getWidth() { return width; }
+ int getHeight() { return height; }
+ SplashBitmapRowSize getRowSize() { return rowSize; }
+ size_t getAlphaRowSize() { return alphaRowSize; }
+ SplashColorMode getMode() { return mode; }
+ SplashColorPtr getDataPtr() { return data; }
+ Guchar *getAlphaPtr() { return alpha; }
+
+ SplashError writePNMFile(char *fileName);
+ SplashError writePNMFile(FILE *f);
+ SplashError writeAlphaPGMFile(char *fileName);
+
+ void getPixel(int x, int y, SplashColorPtr pixel);
+ Guchar getAlpha(int x, int y);
+
+ // Caller takes ownership of the bitmap data. The SplashBitmap
+ // object is no longer valid -- the next call should be to the
+ // destructor.
+ SplashColorPtr takeData();
+
+private:
+
+ int width, height; // size of bitmap
+ SplashBitmapRowSize rowSize; // size of one row of data, in bytes
+ // - negative for bottom-up bitmaps
+ size_t alphaRowSize; // size of one row of alpha, in bytes
+ SplashColorMode mode; // color mode
+ SplashColorPtr data; // pointer to row zero of the color data
+ Guchar *alpha; // pointer to row zero of the alpha data
+ // (always top-down)
+
+ // save the last-allocated (large) bitmap data and reuse if possible
+ SplashBitmap *parent;
+ SplashColorPtr oldData;
+ Guchar *oldAlpha;
+ SplashBitmapRowSize oldRowSize;
+ size_t oldAlphaRowSize;
+ int oldHeight;
+
+ friend class Splash;
+};
+
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashClip.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashClip.cc
new file mode 100644
index 00000000000..bf7b802cf0d
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashClip.cc
@@ -0,0 +1,521 @@
+//========================================================================
+//
+// SplashClip.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "SplashErrorCodes.h"
+#include "SplashPath.h"
+#include "SplashXPath.h"
+#include "SplashXPathScanner.h"
+#include "SplashClip.h"
+
+//------------------------------------------------------------------------
+
+// Compute x * y / 255, where x and y are in [0, 255].
+static inline Guchar mul255(Guchar x, Guchar y) {
+ int z;
+
+ z = (int)x * (int)y;
+ return (Guchar)((z + (z >> 8) + 0x80) >> 8);
+}
+
+//------------------------------------------------------------------------
+// SplashClip
+//------------------------------------------------------------------------
+
+SplashClip::SplashClip(int hardXMinA, int hardYMinA,
+ int hardXMaxA, int hardYMaxA) {
+ int w;
+
+ hardXMin = hardXMinA;
+ hardYMin = hardYMinA;
+ hardXMax = hardXMaxA;
+ hardYMax = hardYMaxA;
+ xMin = hardXMin;
+ yMin = hardYMin;
+ xMax = hardXMax;
+ yMax = hardYMax;
+ intBoundsValid = gFalse;
+ paths = NULL;
+ eo = NULL;
+ scanners = NULL;
+ length = size = 0;
+ isSimple = gTrue;
+ prev = NULL;
+ if ((w = hardXMax + 1) <= 0) {
+ w = 1;
+ }
+ buf = (Guchar *)gmalloc(w);
+}
+
+SplashClip::SplashClip(SplashClip *clip) {
+ int w;
+
+ hardXMin = clip->hardXMin;
+ hardYMin = clip->hardYMin;
+ hardXMax = clip->hardXMax;
+ hardYMax = clip->hardYMax;
+ xMin = clip->xMin;
+ yMin = clip->yMin;
+ xMax = clip->xMax;
+ yMax = clip->yMax;
+ xMinI = clip->xMinI;
+ yMinI = clip->yMinI;
+ xMaxI = clip->xMaxI;
+ yMaxI = clip->yMaxI;
+ intBoundsValid = clip->intBoundsValid;
+ intBoundsStrokeAdjust = clip->intBoundsStrokeAdjust;
+ paths = NULL;
+ eo = NULL;
+ scanners = NULL;
+ length = size = 0;
+ isSimple = clip->isSimple;
+ prev = clip;
+ if ((w = splashCeil(xMax)) <= 0) {
+ w = 1;
+ }
+ buf = (Guchar *)gmalloc(w);
+}
+
+SplashClip::~SplashClip() {
+ int i;
+
+ for (i = 0; i < length; ++i) {
+ delete scanners[i];
+ delete paths[i];
+ }
+ gfree(paths);
+ gfree(eo);
+ gfree(scanners);
+ gfree(buf);
+}
+
+void SplashClip::grow(int nPaths) {
+ if (length + nPaths > size) {
+ if (size == 0) {
+ size = 32;
+ }
+ while (size < length + nPaths) {
+ size *= 2;
+ }
+ paths = (SplashXPath **)greallocn(paths, size, sizeof(SplashXPath *));
+ eo = (Guchar *)greallocn(eo, size, sizeof(Guchar));
+ scanners = (SplashXPathScanner **)
+ greallocn(scanners, size, sizeof(SplashXPathScanner *));
+ }
+}
+
+void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ int w, i;
+
+ for (i = 0; i < length; ++i) {
+ delete paths[i];
+ delete scanners[i];
+ }
+ gfree(paths);
+ gfree(eo);
+ gfree(scanners);
+ gfree(buf);
+ paths = NULL;
+ eo = NULL;
+ scanners = NULL;
+ length = size = 0;
+ isSimple = gTrue;
+ prev = NULL;
+
+ if (x0 < x1) {
+ xMin = x0;
+ xMax = x1;
+ } else {
+ xMin = x1;
+ xMax = x0;
+ }
+ if (y0 < y1) {
+ yMin = y0;
+ yMax = y1;
+ } else {
+ yMin = y1;
+ yMax = y0;
+ }
+ intBoundsValid = gFalse;
+ if ((w = splashCeil(xMax)) <= 0) {
+ w = 1;
+ }
+ buf = (Guchar *)gmalloc(w);
+}
+
+SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ if (x0 < x1) {
+ if (x0 > xMin) {
+ xMin = x0;
+ intBoundsValid = gFalse;
+ }
+ if (x1 < xMax) {
+ xMax = x1;
+ intBoundsValid = gFalse;
+ }
+ } else {
+ if (x1 > xMin) {
+ xMin = x1;
+ intBoundsValid = gFalse;
+ }
+ if (x0 < xMax) {
+ xMax = x0;
+ intBoundsValid = gFalse;
+ }
+ }
+ if (y0 < y1) {
+ if (y0 > yMin) {
+ yMin = y0;
+ intBoundsValid = gFalse;
+ }
+ if (y1 < yMax) {
+ yMax = y1;
+ intBoundsValid = gFalse;
+ }
+ } else {
+ if (y1 > yMin) {
+ yMin = y1;
+ intBoundsValid = gFalse;
+ }
+ if (y0 < yMax) {
+ yMax = y0;
+ intBoundsValid = gFalse;
+ }
+ }
+ return splashOk;
+}
+
+SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix,
+ SplashCoord flatness, GBool eoA,
+ GBool enablePathSimplification,
+ SplashStrokeAdjustMode strokeAdjust) {
+ SplashXPath *xPath;
+ SplashCoord t;
+
+ xPath = new SplashXPath(path, matrix, flatness, gTrue,
+ enablePathSimplification,
+ strokeAdjust);
+
+ // check for an empty path
+ if (xPath->length == 0) {
+ xMin = yMin = 1;
+ xMax = yMax = 0;
+ intBoundsValid = gFalse;
+ delete xPath;
+ return splashOk;
+ }
+
+ // check for a rectangle
+ if (xPath->isRect) {
+ clipToRect(xPath->rectX0, xPath->rectY0, xPath->rectX1, xPath->rectY1);
+ delete xPath;
+ return splashOk;
+ }
+
+ grow(1);
+ paths[length] = xPath;
+ eo[length] = (Guchar)eoA;
+ if ((t = xPath->getXMin()) > xMin) {
+ xMin = t;
+ }
+ if ((t = xPath->getYMin()) > yMin) {
+ yMin = t;
+ }
+ if ((t = xPath->getXMax() + 1) < xMax) {
+ xMax = t;
+ }
+ if ((t = xPath->getYMax() + 1) < yMax) {
+ yMax = t;
+ }
+ intBoundsValid = gFalse;
+ scanners[length] = new SplashXPathScanner(xPath, eoA, splashFloor(yMin),
+ splashCeil(yMax) - 1);
+ ++length;
+ isSimple = gFalse;
+
+ return splashOk;
+}
+
+SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin,
+ int rectXMax, int rectYMax,
+ SplashStrokeAdjustMode strokeAdjust) {
+ // In general, this function tests the rectangle:
+ // x = [rectXMin, rectXMax + 1) (note: coords are ints)
+ // y = [rectYMin, rectYMax + 1)
+ // against the clipping region:
+ // x = [xMin, xMax) (note: coords are fp)
+ // y = [yMin, yMax)
+
+ if (strokeAdjust != splashStrokeAdjustOff && isSimple) {
+ // special case for stroke adjustment with a simple clipping
+ // rectangle -- the clipping region is:
+ // x = [xMinI, xMaxI + 1)
+ // y = [yMinI, yMaxI + 1)
+ updateIntBounds(strokeAdjust);
+ if (xMinI > xMaxI || yMinI > yMaxI) {
+ return splashClipAllOutside;
+ }
+ if (rectXMax + 1 <= xMinI ||
+ rectXMin >= xMaxI + 1 ||
+ rectYMax + 1 <= yMinI ||
+ rectYMin >= yMaxI + 1) {
+ return splashClipAllOutside;
+ }
+ if (rectXMin >= xMinI &&
+ rectXMax <= xMaxI &&
+ rectYMin >= yMinI &&
+ rectYMax <= yMaxI) {
+ return splashClipAllInside;
+ }
+ } else {
+ if (xMin >= xMax || yMin >= yMax) {
+ return splashClipAllOutside;
+ }
+ if ((SplashCoord)(rectXMax + 1) <= xMin ||
+ (SplashCoord)rectXMin >= xMax ||
+ (SplashCoord)(rectYMax + 1) <= yMin ||
+ (SplashCoord)rectYMin >= yMax) {
+ return splashClipAllOutside;
+ }
+ if (isSimple &&
+ (SplashCoord)rectXMin >= xMin &&
+ (SplashCoord)(rectXMax + 1) <= xMax &&
+ (SplashCoord)rectYMin >= yMin &&
+ (SplashCoord)(rectYMax + 1) <= yMax) {
+ return splashClipAllInside;
+ }
+ }
+ return splashClipPartial;
+}
+
+void SplashClip::clipSpan(Guchar *line, int y, int x0, int x1,
+ SplashStrokeAdjustMode strokeAdjust) {
+ SplashClip *clip;
+ SplashCoord d;
+ int x0a, x1a, x0b, x1b, x, i;
+
+ updateIntBounds(strokeAdjust);
+
+ //--- clip to the integer rectangle
+
+ if (y < yMinI || y > yMaxI ||
+ x1 < xMinI || x0 > xMaxI) {
+ memset(line + x0, 0, x1 - x0 + 1);
+ return;
+ }
+
+ if (x0 > xMinI) {
+ x0a = x0;
+ } else {
+ x0a = xMinI;
+ memset(line + x0, 0, x0a - x0);
+ }
+
+ if (x1 < xMaxI) {
+ x1a = x1;
+ } else {
+ x1a = xMaxI;
+ memset(line + x1a + 1, 0, x1 - x1a);
+ }
+
+ if (x0a > x1a) {
+ return;
+ }
+
+ //--- clip to the floating point rectangle
+ // (if stroke adjustment is disabled)
+
+ if (strokeAdjust == splashStrokeAdjustOff) {
+
+ // clip left edge (xMin)
+ if (x0a == xMinI) {
+ d = (SplashCoord)(xMinI + 1) - xMin;
+ line[x0a] = (Guchar)(int)((SplashCoord)line[x0a] * d);
+ }
+
+ // clip right edge (xMax)
+ if (x1a == xMaxI) {
+ d = xMax - (SplashCoord)xMaxI;
+ line[x1a] = (Guchar)(int)((SplashCoord)line[x1a] * d);
+ }
+
+ // clip top edge (yMin)
+ if (y == yMinI) {
+ d = (SplashCoord)(yMinI + 1) - yMin;
+ for (x = x0a; x <= x1a; ++x) {
+ line[x] = (Guchar)(int)((SplashCoord)line[x] * d);
+ }
+ }
+
+ // clip bottom edge (yMax)
+ if (y == yMaxI) {
+ d = yMax - (SplashCoord)yMaxI;
+ for (x = x0a; x <= x1a; ++x) {
+ line[x] = (Guchar)(int)((SplashCoord)line[x] * d);
+ }
+ }
+ }
+
+ if (isSimple) {
+ return;
+ }
+
+ //--- clip to the paths
+
+ for (clip = this; clip; clip = clip->prev) {
+ for (i = 0; i < clip->length; ++i) {
+ clip->scanners[i]->getSpan(buf, y, x0a, x1a, &x0b, &x1b);
+ if (x0a < x0b) {
+ memset(line + x0a, 0, x0b - x0a);
+ }
+ for (x = x0b; x <= x1b; ++x) {
+ line[x] = mul255(line[x], buf[x]);
+ }
+ if (x1b < x1a) {
+ memset(line + x1b + 1, 0, x1a - x1b);
+ }
+ }
+ }
+}
+
+GBool SplashClip::clipSpanBinary(Guchar *line, int y, int x0, int x1,
+ SplashStrokeAdjustMode strokeAdjust) {
+ SplashClip *clip;
+ int x0a, x1a, x0b, x1b, x, i;
+ Guchar any;
+
+ updateIntBounds(strokeAdjust);
+
+ if (y < yMinI || y > yMaxI ||
+ x1 < xMinI || x0 > xMaxI) {
+ if (x0 <= x1) {
+ memset(line + x0, 0, x1 - x0 + 1);
+ }
+ return gFalse;
+ }
+
+ if (x0 > xMinI) {
+ x0a = x0;
+ } else {
+ x0a = xMinI;
+ memset(line + x0, 0, x0a - x0);
+ }
+
+ if (x1 < xMaxI) {
+ x1a = x1;
+ } else {
+ x1a = xMaxI;
+ memset(line + x1a + 1, 0, x1 - x1a);
+ }
+
+ if (x0a > x1a) {
+ return gFalse;
+ }
+
+ if (isSimple) {
+ for (x = x0a; x <= x1a; ++x) {
+ if (line[x]) {
+ return gTrue;
+ }
+ }
+ return gFalse;
+ }
+
+ any = 0;
+ for (clip = this; clip; clip = clip->prev) {
+ for (i = 0; i < clip->length; ++i) {
+ clip->scanners[i]->getSpanBinary(buf, y, x0a, x1a, &x0b, &x1b);
+ if (x0a < x0b) {
+ memset(line + x0a, 0, x0b - x0a);
+ }
+ for (x = x0b; x <= x1b; ++x) {
+ line[x] &= buf[x];
+ any |= line[x];
+ }
+ if (x1b < x1a) {
+ memset(line + x1b + 1, 0, x1a - x1b);
+ }
+ }
+ }
+
+ return any != 0;
+}
+
+int SplashClip::getXMinI(SplashStrokeAdjustMode strokeAdjust) {
+ updateIntBounds(strokeAdjust);
+ return xMinI;
+}
+
+int SplashClip::getXMaxI(SplashStrokeAdjustMode strokeAdjust) {
+ updateIntBounds(strokeAdjust);
+ return xMaxI;
+}
+
+int SplashClip::getYMinI(SplashStrokeAdjustMode strokeAdjust) {
+ updateIntBounds(strokeAdjust);
+ return yMinI;
+}
+
+int SplashClip::getYMaxI(SplashStrokeAdjustMode strokeAdjust) {
+ updateIntBounds(strokeAdjust);
+ return yMaxI;
+}
+
+int SplashClip::getNumPaths() {
+ SplashClip *clip;
+ int n;
+
+ n = 0;
+ for (clip = this; clip; clip = clip->prev) {
+ n += clip->length;
+ }
+ return n;
+}
+
+void SplashClip::updateIntBounds(SplashStrokeAdjustMode strokeAdjust) {
+ if (intBoundsValid && strokeAdjust == intBoundsStrokeAdjust) {
+ return;
+ }
+ if (strokeAdjust != splashStrokeAdjustOff && isSimple) {
+ splashStrokeAdjust(xMin, xMax, &xMinI, &xMaxI, strokeAdjust);
+ splashStrokeAdjust(yMin, yMax, &yMinI, &yMaxI, strokeAdjust);
+ } else {
+ xMinI = splashFloor(xMin);
+ yMinI = splashFloor(yMin);
+ xMaxI = splashCeil(xMax);
+ yMaxI = splashCeil(yMax);
+ }
+ if (xMinI < hardXMin) {
+ xMinI = hardXMin;
+ }
+ if (yMinI < hardYMin) {
+ yMinI = hardYMin;
+ }
+ if (xMaxI > hardXMax) {
+ xMaxI = hardXMax;
+ }
+ if (yMaxI > hardYMax) {
+ yMaxI = hardYMax;
+ }
+ // the clipping code uses [xMinI, xMaxI] instead of [xMinI, xMaxI)
+ --xMaxI;
+ --yMaxI;
+ intBoundsValid = gTrue;
+ intBoundsStrokeAdjust = strokeAdjust;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashClip.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashClip.h
new file mode 100644
index 00000000000..fb86ed8fd72
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashClip.h
@@ -0,0 +1,130 @@
+//========================================================================
+//
+// SplashClip.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHCLIP_H
+#define SPLASHCLIP_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+#include "SplashMath.h"
+
+class SplashPath;
+class SplashXPath;
+class SplashXPathScanner;
+class SplashBitmap;
+
+//------------------------------------------------------------------------
+
+enum SplashClipResult {
+ splashClipAllInside,
+ splashClipAllOutside,
+ splashClipPartial
+};
+
+//------------------------------------------------------------------------
+// SplashClip
+//------------------------------------------------------------------------
+
+class SplashClip {
+public:
+
+ // Create a clip, for the given rectangle.
+ SplashClip(int hardXMinA, int hardYMinA,
+ int hardXMaxA, int hardYMaxA);
+
+ // Copy a clip.
+ SplashClip *copy() { return new SplashClip(this); }
+
+ ~SplashClip();
+
+ // Reset the clip to a rectangle.
+ void resetToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1);
+
+ // Intersect the clip with a rectangle.
+ SplashError clipToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1);
+
+ // Interesect the clip with <path>.
+ SplashError clipToPath(SplashPath *path, SplashCoord *matrix,
+ SplashCoord flatness, GBool eoA,
+ GBool enablePathSimplification,
+ SplashStrokeAdjustMode strokeAdjust);
+
+ // Tests a rectangle against the clipping region. Returns one of:
+ // - splashClipAllInside if the entire rectangle is inside the
+ // clipping region, i.e., all pixels in the rectangle are
+ // visible
+ // - splashClipAllOutside if the entire rectangle is outside the
+ // clipping region, i.e., all the pixels in the rectangle are
+ // clipped
+ // - splashClipPartial if the rectangle is part inside and part
+ // outside the clipping region
+ SplashClipResult testRect(int rectXMin, int rectYMin,
+ int rectXMax, int rectYMax,
+ SplashStrokeAdjustMode strokeAdjust);
+
+ // Clip a scan line. Modifies line[] by multiplying with clipping
+ // shape values for one scan line: ([x0, x1], y).
+ void clipSpan(Guchar *line, int y, int x0, int x1,
+ SplashStrokeAdjustMode strokeAdjust);
+
+ // Like clipSpan(), but uses the values 0 and 255 only.
+ // Returns true if there are any non-zero values in the result
+ // (i.e., returns false if the entire line is clipped out).
+ GBool clipSpanBinary(Guchar *line, int y, int x0, int x1,
+ SplashStrokeAdjustMode strokeAdjust);
+
+ // Get the rectangle part of the clip region.
+ SplashCoord getXMin() { return xMin; }
+ SplashCoord getXMax() { return xMax; }
+ SplashCoord getYMin() { return yMin; }
+ SplashCoord getYMax() { return yMax; }
+
+ // Get the rectangle part of the clip region, in integer coordinates.
+ int getXMinI(SplashStrokeAdjustMode strokeAdjust);
+ int getXMaxI(SplashStrokeAdjustMode strokeAdjust);
+ int getYMinI(SplashStrokeAdjustMode strokeAdjust);
+ int getYMaxI(SplashStrokeAdjustMode strokeAdjust);
+
+ // Get the number of arbitrary paths used by the clip region.
+ int getNumPaths();
+
+private:
+
+ SplashClip(SplashClip *clip);
+ void grow(int nPaths);
+ void updateIntBounds(SplashStrokeAdjustMode strokeAdjust);
+
+ int hardXMin, hardYMin, // coordinates cannot fall outside of
+ hardXMax, hardYMax; // [hardXMin, hardXMax), [hardYMin, hardYMax)
+
+ SplashCoord xMin, yMin, // current clip bounding rectangle
+ xMax, yMax; // (these coordinates may be adjusted if
+ // stroke adjustment is enabled)
+
+ int xMinI, yMinI, xMaxI, yMaxI;
+ GBool intBoundsValid; // true if xMinI, etc. are valid
+ GBool intBoundsStrokeAdjust; // value of strokeAdjust used to compute
+ // xMinI, etc.
+
+ SplashXPath **paths;
+ Guchar *eo;
+ SplashXPathScanner **scanners;
+ int length, size;
+ GBool isSimple;
+ SplashClip *prev;
+ Guchar *buf;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashErrorCodes.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashErrorCodes.h
new file mode 100644
index 00000000000..ebe6091faa2
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashErrorCodes.h
@@ -0,0 +1,34 @@
+//========================================================================
+//
+// SplashErrorCodes.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHERRORCODES_H
+#define SPLASHERRORCODES_H
+
+#include <aconf.h>
+
+//------------------------------------------------------------------------
+
+#define splashOk 0 // no error
+
+#define splashErrNoCurPt 1 // no current point
+
+#define splashErrEmptyPath 2 // zero points in path
+
+#define splashErrBogusPath 3 // only one point in subpath
+
+#define splashErrNoSave 4 // state stack is empty
+
+#define splashErrOpenFile 5 // couldn't open file
+
+#define splashErrNoGlyph 6 // couldn't get the requested glyph
+
+#define splashErrModeMismatch 7 // invalid combination of color modes
+
+#define splashErrSingularMatrix 8 // matrix is singular
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFont.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFont.cc
new file mode 100644
index 00000000000..91481444bca
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFont.cc
@@ -0,0 +1,460 @@
+//========================================================================
+//
+// SplashFTFont.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#if HAVE_FREETYPE_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <ft2build.h>
+#include FT_OUTLINE_H
+#include FT_SIZES_H
+#include FT_GLYPH_H
+#include "gmem.h"
+#include "gmempp.h"
+#include "SplashMath.h"
+#include "SplashGlyphBitmap.h"
+#include "SplashPath.h"
+#include "SplashFontEngine.h"
+#include "SplashFTFontEngine.h"
+#include "SplashFTFontFile.h"
+#include "SplashFTFont.h"
+
+//------------------------------------------------------------------------
+
+static int glyphPathMoveTo(const FT_Vector *pt, void *path);
+static int glyphPathLineTo(const FT_Vector *pt, void *path);
+static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt,
+ void *path);
+static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2,
+ const FT_Vector *pt, void *path);
+
+//------------------------------------------------------------------------
+// SplashFTFont
+//------------------------------------------------------------------------
+
+SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA,
+ SplashCoord *textMatA):
+ SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa)
+{
+ FT_Face face;
+ int size, div;
+ int x, y;
+#if USE_FIXEDPOINT
+ SplashCoord scale;
+#endif
+
+ face = fontFileA->face;
+ if (FT_New_Size(face, &sizeObj)) {
+ return;
+ }
+ face->size = sizeObj;
+ size = splashRound(splashDist(0, 0, mat[2], mat[3]));
+ if (size < 1) {
+ size = 1;
+ }
+ if (FT_Set_Pixel_Sizes(face, 0, size)) {
+ return;
+ }
+ // if the textMat values are too small, FreeType's fixed point
+ // arithmetic doesn't work so well
+ textScale = splashDist(0, 0, textMat[2], textMat[3]) / size;
+ // avoid problems with singular (or close-to-singular) matrices
+ if (textScale < 0.00001) {
+ textScale = 0.00001;
+ }
+
+ div = face->bbox.xMax > 20000 ? 65536 : 1;
+
+#if USE_FIXEDPOINT
+ scale = (SplashCoord)1 / (SplashCoord)face->units_per_EM;
+
+ // transform the four corners of the font bounding box -- the min
+ // and max values form the bounding box of the transformed font
+ x = (int)(mat[0] * (scale * (face->bbox.xMin / div)) +
+ mat[2] * (scale * (face->bbox.yMin / div)));
+ xMin = xMax = x;
+ y = (int)(mat[1] * (scale * (face->bbox.xMin / div)) +
+ mat[3] * (scale * (face->bbox.yMin / div)));
+ yMin = yMax = y;
+ x = (int)(mat[0] * (scale * (face->bbox.xMin / div)) +
+ mat[2] * (scale * (face->bbox.yMax / div)));
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ y = (int)(mat[1] * (scale * (face->bbox.xMin / div)) +
+ mat[3] * (scale * (face->bbox.yMax / div)));
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ x = (int)(mat[0] * (scale * (face->bbox.xMax / div)) +
+ mat[2] * (scale * (face->bbox.yMin / div)));
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ y = (int)(mat[1] * (scale * (face->bbox.xMax / div)) +
+ mat[3] * (scale * (face->bbox.yMin / div)));
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ x = (int)(mat[0] * (scale * (face->bbox.xMax / div)) +
+ mat[2] * (scale * (face->bbox.yMax / div)));
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ y = (int)(mat[1] * (scale * (face->bbox.xMax / div)) +
+ mat[3] * (scale * (face->bbox.yMax / div)));
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+#else // USE_FIXEDPOINT
+ // transform the four corners of the font bounding box -- the min
+ // and max values form the bounding box of the transformed font
+ x = (int)((mat[0] * (SplashCoord)face->bbox.xMin
+ + mat[2] * (SplashCoord)face->bbox.yMin) /
+ (div * face->units_per_EM));
+ xMin = xMax = x;
+ y = (int)((mat[1] * (SplashCoord)face->bbox.xMin
+ + mat[3] * (SplashCoord)face->bbox.yMin) /
+ (div * face->units_per_EM));
+ yMin = yMax = y;
+ x = (int)((mat[0] * (SplashCoord)face->bbox.xMin
+ + mat[2] * (SplashCoord)face->bbox.yMax) /
+ (div * face->units_per_EM));
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ y = (int)((mat[1] * (SplashCoord)face->bbox.xMin
+ + mat[3] * (SplashCoord)face->bbox.yMax) /
+ (div * face->units_per_EM));
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ x = (int)((mat[0] * (SplashCoord)face->bbox.xMax
+ + mat[2] * (SplashCoord)face->bbox.yMin) /
+ (div * face->units_per_EM));
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ y = (int)((mat[1] * (SplashCoord)face->bbox.xMax
+ + mat[3] * (SplashCoord)face->bbox.yMin) /
+ (div * face->units_per_EM));
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ x = (int)((mat[0] * (SplashCoord)face->bbox.xMax
+ + mat[2] * (SplashCoord)face->bbox.yMax) /
+ (div * face->units_per_EM));
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ y = (int)((mat[1] * (SplashCoord)face->bbox.xMax
+ + mat[3] * (SplashCoord)face->bbox.yMax) /
+ (div * face->units_per_EM));
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+#endif // USE_FIXEDPOINT
+ // This is a kludge: some buggy PDF generators embed fonts with
+ // zero bounding boxes.
+ if (xMax == xMin) {
+ xMin = 0;
+ xMax = size;
+ }
+ if (yMax == yMin) {
+ yMin = 0;
+ yMax = (int)((SplashCoord)1.2 * size);
+ }
+
+ // compute the transform matrix
+#if USE_FIXEDPOINT
+ matrix.xx = (FT_Fixed)((mat[0] / size).get16Dot16());
+ matrix.yx = (FT_Fixed)((mat[1] / size).get16Dot16());
+ matrix.xy = (FT_Fixed)((mat[2] / size).get16Dot16());
+ matrix.yy = (FT_Fixed)((mat[3] / size).get16Dot16());
+ textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)).get16Dot16());
+ textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)).get16Dot16());
+ textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)).get16Dot16());
+ textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)).get16Dot16());
+#else
+ matrix.xx = (FT_Fixed)((mat[0] / size) * 65536);
+ matrix.yx = (FT_Fixed)((mat[1] / size) * 65536);
+ matrix.xy = (FT_Fixed)((mat[2] / size) * 65536);
+ matrix.yy = (FT_Fixed)((mat[3] / size) * 65536);
+ textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)) * 65536);
+ textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)) * 65536);
+ textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)) * 65536);
+ textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)) * 65536);
+#endif
+}
+
+SplashFTFont::~SplashFTFont() {
+}
+
+GBool SplashFTFont::getGlyph(int c, int xFrac, int yFrac,
+ SplashGlyphBitmap *bitmap) {
+ return SplashFont::getGlyph(c, xFrac, 0, bitmap);
+}
+
+GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac,
+ SplashGlyphBitmap *bitmap) {
+ SplashFTFontFile *ff;
+ FT_Vector offset;
+ FT_GlyphSlot slot;
+ int gid;
+ FT_Int32 flags;
+ int rowSize;
+ Guchar *p, *q;
+ int i;
+
+ ff = (SplashFTFontFile *)fontFile;
+
+ ff->face->size = sizeObj;
+ offset.x = (FT_Pos)(int)((SplashCoord)xFrac * splashFontFractionMul * 64);
+ offset.y = 0;
+ FT_Set_Transform(ff->face, &matrix, &offset);
+ slot = ff->face->glyph;
+
+ if (ff->codeToGID && c < ff->codeToGIDLen) {
+ gid = ff->codeToGID[c];
+ } else {
+ gid = c;
+ }
+ if (ff->fontType == splashFontTrueType && gid < 0) {
+ // skip the TrueType notdef glyph
+ return gFalse;
+ }
+
+ // Set up the load flags:
+ // * disable bitmaps because they look ugly when scaled, rotated,
+ // etc.
+ // * disable autohinting because it can fail badly with font subsets
+ // that use invalid glyph names (the FreeType autohinter depends
+ // on the glyph name to figure out how to autohint the glyph)
+ // * but enable light autohinting for Type 1 fonts because regular
+ // hinting looks pretty bad, and the invalid glyph name issue
+ // seems to be very rare (Type 1 fonts are mostly used for
+ // substitution, in which case the full font is being used, which
+ // means we have the glyph names)
+ // This also sets the "pedantic" flag, running the FreeType hinter
+ // in paranoid mode. If that triggers any errors, we disable
+ // hinting below.
+ flags = FT_LOAD_NO_BITMAP | FT_LOAD_PEDANTIC;
+ if (ff->engine->flags & splashFTNoHinting) {
+ flags |= FT_LOAD_NO_HINTING;
+ } else if (ff->fontType == splashFontType1) {
+ flags |= FT_LOAD_TARGET_LIGHT;
+ } else {
+ flags |= FT_LOAD_NO_AUTOHINT;
+ }
+ if (FT_Load_Glyph(ff->face, (FT_UInt)gid, flags)) {
+ // fonts with broken hinting instructions can cause errors here;
+ // try again with no hinting (this is probably only relevant for
+ // TrueType fonts)
+ flags = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING;
+ if (FT_Load_Glyph(ff->face, (FT_UInt)gid, flags)) {
+ return gFalse;
+ }
+ }
+ if (FT_Render_Glyph(slot, aa ? FT_RENDER_MODE_NORMAL
+ : FT_RENDER_MODE_MONO)) {
+ return gFalse;
+ }
+ if (slot->bitmap.width == 0 || slot->bitmap.rows == 0) {
+ // this can happen if (a) the glyph is really tiny or (b) the
+ // metrics in the TrueType file are broken
+ return gFalse;
+ }
+
+ bitmap->x = -slot->bitmap_left;
+ bitmap->y = slot->bitmap_top;
+ bitmap->w = slot->bitmap.width;
+ bitmap->h = slot->bitmap.rows;
+ bitmap->aa = aa;
+ if (aa) {
+ rowSize = bitmap->w;
+ } else {
+ rowSize = (bitmap->w + 7) >> 3;
+ }
+ bitmap->data = (Guchar *)gmallocn(bitmap->h, rowSize);
+ bitmap->freeData = gTrue;
+ for (i = 0, p = bitmap->data, q = slot->bitmap.buffer;
+ i < bitmap->h;
+ ++i, p += rowSize, q += slot->bitmap.pitch) {
+ memcpy(p, q, rowSize);
+ }
+
+ return gTrue;
+}
+
+struct SplashFTFontPath {
+ SplashPath *path;
+ SplashCoord textScale;
+ GBool needClose;
+};
+
+SplashPath *SplashFTFont::getGlyphPath(int c) {
+ static FT_Outline_Funcs outlineFuncs = {
+#if FREETYPE_MINOR <= 1
+ (int (*)(FT_Vector *, void *))&glyphPathMoveTo,
+ (int (*)(FT_Vector *, void *))&glyphPathLineTo,
+ (int (*)(FT_Vector *, FT_Vector *, void *))&glyphPathConicTo,
+ (int (*)(FT_Vector *, FT_Vector *, FT_Vector *, void *))&glyphPathCubicTo,
+#else
+ &glyphPathMoveTo,
+ &glyphPathLineTo,
+ &glyphPathConicTo,
+ &glyphPathCubicTo,
+#endif
+ 0, 0
+ };
+ SplashFTFontFile *ff;
+ SplashFTFontPath path;
+ FT_GlyphSlot slot;
+ int gid;
+ FT_Glyph glyph;
+
+ ff = (SplashFTFontFile *)fontFile;
+ ff->face->size = sizeObj;
+ FT_Set_Transform(ff->face, &textMatrix, NULL);
+ slot = ff->face->glyph;
+ if (ff->codeToGID && c < ff->codeToGIDLen) {
+ gid = ff->codeToGID[c];
+ } else {
+ gid = c;
+ }
+ if (ff->fontType == splashFontTrueType && gid < 0) {
+ // skip the TrueType notdef glyph
+ return NULL;
+ }
+ if (FT_Load_Glyph(ff->face, (FT_UInt)gid, FT_LOAD_NO_BITMAP)) {
+ // fonts with broken hinting instructions can cause errors here;
+ // try again with no hinting (this is probably only relevant for
+ // TrueType fonts)
+ if (FT_Load_Glyph(ff->face, (FT_UInt)gid,
+ FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) {
+ return NULL;
+ }
+ }
+ if (FT_Get_Glyph(slot, &glyph)) {
+ return NULL;
+ }
+ path.path = new SplashPath();
+ path.textScale = textScale;
+ path.needClose = gFalse;
+ FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline,
+ &outlineFuncs, &path);
+ if (path.needClose) {
+ path.path->close();
+ }
+ FT_Done_Glyph(glyph);
+ return path.path;
+}
+
+static int glyphPathMoveTo(const FT_Vector *pt, void *path) {
+ SplashFTFontPath *p = (SplashFTFontPath *)path;
+
+ if (p->needClose) {
+ p->path->close();
+ p->needClose = gFalse;
+ }
+ p->path->moveTo((SplashCoord)pt->x * p->textScale / 64.0,
+ (SplashCoord)pt->y * p->textScale / 64.0);
+ return 0;
+}
+
+static int glyphPathLineTo(const FT_Vector *pt, void *path) {
+ SplashFTFontPath *p = (SplashFTFontPath *)path;
+
+ p->path->lineTo((SplashCoord)pt->x * p->textScale / 64.0,
+ (SplashCoord)pt->y * p->textScale / 64.0);
+ p->needClose = gTrue;
+ return 0;
+}
+
+static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt,
+ void *path) {
+ SplashFTFontPath *p = (SplashFTFontPath *)path;
+ SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc;
+
+ if (!p->path->getCurPt(&x0, &y0)) {
+ return 0;
+ }
+ xc = (SplashCoord)ctrl->x * p->textScale / 64.0;
+ yc = (SplashCoord)ctrl->y * p->textScale / 64.0;
+ x3 = (SplashCoord)pt->x * p->textScale / 64.0;
+ y3 = (SplashCoord)pt->y * p->textScale / 64.0;
+
+ // A second-order Bezier curve is defined by two endpoints, p0 and
+ // p3, and one control point, pc:
+ //
+ // p(t) = (1-t)^2*p0 + t*(1-t)*pc + t^2*p3
+ //
+ // A third-order Bezier curve is defined by the same two endpoints,
+ // p0 and p3, and two control points, p1 and p2:
+ //
+ // p(t) = (1-t)^3*p0 + 3t*(1-t)^2*p1 + 3t^2*(1-t)*p2 + t^3*p3
+ //
+ // Applying some algebra, we can convert a second-order curve to a
+ // third-order curve:
+ //
+ // p1 = (1/3) * (p0 + 2pc)
+ // p2 = (1/3) * (2pc + p3)
+
+ x1 = (SplashCoord)(1.0 / 3.0) * (x0 + (SplashCoord)2 * xc);
+ y1 = (SplashCoord)(1.0 / 3.0) * (y0 + (SplashCoord)2 * yc);
+ x2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * xc + x3);
+ y2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * yc + y3);
+
+ p->path->curveTo(x1, y1, x2, y2, x3, y3);
+ p->needClose = gTrue;
+ return 0;
+}
+
+static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2,
+ const FT_Vector *pt, void *path) {
+ SplashFTFontPath *p = (SplashFTFontPath *)path;
+
+ p->path->curveTo((SplashCoord)ctrl1->x * p->textScale / 64.0,
+ (SplashCoord)ctrl1->y * p->textScale / 64.0,
+ (SplashCoord)ctrl2->x * p->textScale / 64.0,
+ (SplashCoord)ctrl2->y * p->textScale / 64.0,
+ (SplashCoord)pt->x * p->textScale / 64.0,
+ (SplashCoord)pt->y * p->textScale / 64.0);
+ p->needClose = gTrue;
+ return 0;
+}
+
+#endif // HAVE_FREETYPE_H
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFont.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFont.h
new file mode 100644
index 00000000000..ac08b007747
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFont.h
@@ -0,0 +1,60 @@
+//========================================================================
+//
+// SplashFTFont.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHFTFONT_H
+#define SPLASHFTFONT_H
+
+#include <aconf.h>
+
+#if HAVE_FREETYPE_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include "SplashFont.h"
+
+class SplashFTFontFile;
+
+//------------------------------------------------------------------------
+// SplashFTFont
+//------------------------------------------------------------------------
+
+class SplashFTFont: public SplashFont {
+public:
+
+ SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA,
+ SplashCoord *textMatA);
+
+ virtual ~SplashFTFont();
+
+ // Munge xFrac and yFrac before calling SplashFont::getGlyph.
+ virtual GBool getGlyph(int c, int xFrac, int yFrac,
+ SplashGlyphBitmap *bitmap);
+
+ // Rasterize a glyph. The <xFrac> and <yFrac> values are the same
+ // as described for getGlyph.
+ virtual GBool makeGlyph(int c, int xFrac, int yFrac,
+ SplashGlyphBitmap *bitmap);
+
+ // Return the path for a glyph.
+ virtual SplashPath *getGlyphPath(int c);
+
+private:
+
+ FT_Size sizeObj;
+ FT_Matrix matrix;
+ FT_Matrix textMatrix;
+ SplashCoord textScale;
+};
+
+#endif // HAVE_FREETYPE_H
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontEngine.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontEngine.cc
new file mode 100644
index 00000000000..33939541051
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontEngine.cc
@@ -0,0 +1,392 @@
+//========================================================================
+//
+// SplashFTFontEngine.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#if HAVE_FREETYPE_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#ifndef _WIN32
+# include <unistd.h>
+#endif
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "gfile.h"
+#include "FoFiTrueType.h"
+#include "FoFiType1C.h"
+#include "SplashFTFontFile.h"
+#include "SplashFTFontEngine.h"
+#include FT_MODULE_H
+#ifdef FT_CFF_DRIVER_H
+# include FT_CFF_DRIVER_H
+#endif
+
+#ifdef VMS
+#if (__VMS_VER < 70000000)
+extern "C" int unlink(char *filename);
+#endif
+#endif
+
+//------------------------------------------------------------------------
+
+static void fileWrite(void *stream, const char *data, int len) {
+ fwrite(data, 1, len, (FILE *)stream);
+}
+
+#if LOAD_FONTS_FROM_MEM
+static void gstringWrite(void *stream, const char *data, int len) {
+ ((GString *)stream)->append(data, len);
+}
+#endif
+
+//------------------------------------------------------------------------
+// SplashFTFontEngine
+//------------------------------------------------------------------------
+
+SplashFTFontEngine::SplashFTFontEngine(GBool aaA, Guint flagsA,
+ FT_Library libA) {
+ FT_Int major, minor, patch;
+
+ aa = aaA;
+ flags = flagsA;
+ lib = libA;
+
+ // as of FT 2.1.8, CID fonts are indexed by CID instead of GID
+ FT_Library_Version(lib, &major, &minor, &patch);
+ useCIDs = major > 2 ||
+ (major == 2 && (minor > 1 || (minor == 1 && patch > 7)));
+}
+
+SplashFTFontEngine *SplashFTFontEngine::init(GBool aaA, Guint flagsA) {
+ FT_Library libA;
+
+ if (FT_Init_FreeType(&libA)) {
+ return NULL;
+ }
+ return new SplashFTFontEngine(aaA, flagsA, libA);
+}
+
+SplashFTFontEngine::~SplashFTFontEngine() {
+ FT_Done_FreeType(lib);
+}
+
+SplashFontFile *SplashFTFontEngine::loadType1Font(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName,
+ GBool deleteFile,
+#endif
+ const char **enc) {
+ return SplashFTFontFile::loadType1Font(this, idA, splashFontType1,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ enc);
+}
+
+SplashFontFile *SplashFTFontEngine::loadType1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName,
+ GBool deleteFile,
+#endif
+ const char **enc) {
+ return SplashFTFontFile::loadType1Font(this, idA, splashFontType1C,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ enc);
+}
+
+SplashFontFile *SplashFTFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName,
+ GBool deleteFile,
+#endif
+ const char **enc) {
+ FoFiTrueType *ff;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf2;
+#else
+ GString *tmpFileName;
+ FILE *tmpFile;
+#endif
+ SplashFontFile *ret;
+
+#if LOAD_FONTS_FROM_MEM
+ if (!(ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
+ 0, gTrue))) {
+#else
+ if (!(ff = FoFiTrueType::load(fileName, 0, gTrue))) {
+#endif
+ return NULL;
+ }
+ if (ff->isHeadlessCFF()) {
+#if LOAD_FONTS_FROM_MEM
+ fontBuf2 = new GString();
+ ff->convertToType1(NULL, enc, gFalse, &gstringWrite, fontBuf2);
+ delete ff;
+ ret = SplashFTFontFile::loadType1Font(this, idA, splashFontType1,
+ fontBuf2, enc);
+ if (ret) {
+ delete fontBuf;
+ } else {
+ delete fontBuf2;
+ }
+#else
+ tmpFileName = NULL;
+ if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
+ delete ff;
+ return NULL;
+ }
+ ff->convertToType1(NULL, enc, gFalse, &fileWrite, tmpFile);
+ delete ff;
+ fclose(tmpFile);
+ ret = SplashFTFontFile::loadType1Font(this, idA, splashFontType1,
+ tmpFileName->getCString(),
+ gTrue, enc);
+ if (ret) {
+ if (deleteFile) {
+ unlink(fileName);
+ }
+ } else {
+ unlink(tmpFileName->getCString());
+ }
+ delete tmpFileName;
+#endif
+ } else {
+ delete ff;
+ ret = SplashFTFontFile::loadType1Font(this, idA, splashFontOpenTypeT1C,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ enc);
+ }
+ return ret;
+}
+
+SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName,
+ GBool deleteFile,
+#endif
+ int *codeToGID,
+ int codeToGIDLen) {
+ FoFiType1C *ff;
+ int *cidToGIDMap;
+ int nCIDs;
+ SplashFontFile *ret;
+
+ // check for a CFF font
+ if (codeToGID) {
+ cidToGIDMap = NULL;
+ nCIDs = 0;
+ } else if (useCIDs) {
+ cidToGIDMap = NULL;
+ nCIDs = 0;
+#if LOAD_FONTS_FROM_MEM
+ } else if ((ff = FoFiType1C::make(fontBuf->getCString(),
+ fontBuf->getLength()))) {
+#else
+ } else if ((ff = FoFiType1C::load(fileName))) {
+#endif
+ cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);
+ delete ff;
+ } else {
+ cidToGIDMap = NULL;
+ nCIDs = 0;
+ }
+ ret = SplashFTFontFile::loadCIDFont(this, idA, splashFontCID,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ codeToGID ? codeToGID : cidToGIDMap,
+ codeToGID ? codeToGIDLen : nCIDs);
+ if (!ret) {
+ gfree(cidToGIDMap);
+ }
+ return ret;
+}
+
+SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName,
+ GBool deleteFile,
+#endif
+ int *codeToGID,
+ int codeToGIDLen) {
+ FoFiTrueType *ff;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf2;
+#else
+ GString *tmpFileName;
+ FILE *tmpFile;
+#endif
+ char *cffStart;
+ int cffLength;
+ int *cidToGIDMap;
+ int nCIDs;
+ SplashFontFile *ret;
+
+#if LOAD_FONTS_FROM_MEM
+ if (!(ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
+ 0, gTrue))) {
+#else
+ if (!(ff = FoFiTrueType::load(fileName, 0, gTrue))) {
+#endif
+ return NULL;
+ }
+ cidToGIDMap = NULL;
+ nCIDs = 0;
+ if (ff->isHeadlessCFF()) {
+ if (!ff->getCFFBlock(&cffStart, &cffLength)) {
+ return NULL;
+ }
+#if LOAD_FONTS_FROM_MEM
+ fontBuf2 = new GString(cffStart, cffLength);
+ if (!useCIDs) {
+ cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);
+ }
+ ret = SplashFTFontFile::loadCIDFont(this, idA, splashFontOpenTypeCFF,
+ fontBuf2, cidToGIDMap, nCIDs);
+ if (ret) {
+ delete fontBuf;
+ } else {
+ delete fontBuf2;
+ }
+#else
+ tmpFileName = NULL;
+ if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
+ delete ff;
+ return NULL;
+ }
+ fwrite(cffStart, 1, cffLength, tmpFile);
+ fclose(tmpFile);
+ if (!useCIDs) {
+ cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);
+ }
+ ret = SplashFTFontFile::loadCIDFont(this, idA, splashFontOpenTypeCFF,
+ tmpFileName->getCString(), gTrue,
+ cidToGIDMap, nCIDs);
+ if (ret) {
+ if (deleteFile) {
+ unlink(fileName);
+ }
+ } else {
+ unlink(tmpFileName->getCString());
+ }
+ delete tmpFileName;
+#endif
+ } else {
+ if (!codeToGID && !useCIDs && ff->isOpenTypeCFF()) {
+ cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);
+ }
+ ret = SplashFTFontFile::loadCIDFont(this, idA, splashFontOpenTypeCFF,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ codeToGID ? codeToGID : cidToGIDMap,
+ codeToGID ? codeToGIDLen : nCIDs);
+ }
+ delete ff;
+ if (!ret) {
+ gfree(cidToGIDMap);
+ }
+ return ret;
+}
+
+SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName,
+ GBool deleteFile,
+#endif
+ int fontNum,
+ int *codeToGID,
+ int codeToGIDLen) {
+ FoFiTrueType *ff;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf2;
+#else
+ GString *tmpFileName;
+ FILE *tmpFile;
+#endif
+ SplashFontFile *ret;
+
+#if LOAD_FONTS_FROM_MEM
+ if (!(ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
+ fontNum))) {
+#else
+ if (!(ff = FoFiTrueType::load(fileName, fontNum))) {
+#endif
+ return NULL;
+ }
+#if LOAD_FONTS_FROM_MEM
+ fontBuf2 = new GString;
+ ff->writeTTF(&gstringWrite, fontBuf2);
+#else
+ tmpFileName = NULL;
+ if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
+ delete ff;
+ return NULL;
+ }
+ ff->writeTTF(&fileWrite, tmpFile);
+ fclose(tmpFile);
+#endif
+ delete ff;
+ ret = SplashFTFontFile::loadTrueTypeFont(this, idA, splashFontTrueType,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf2,
+#else
+ tmpFileName->getCString(), gTrue,
+#endif
+ 0, codeToGID, codeToGIDLen);
+#if LOAD_FONTS_FROM_MEM
+ if (ret) {
+ delete fontBuf;
+ } else {
+ delete fontBuf2;
+ }
+#else
+ if (ret) {
+ if (deleteFile) {
+ unlink(fileName);
+ }
+ } else {
+ unlink(tmpFileName->getCString());
+ }
+ delete tmpFileName;
+#endif
+ return ret;
+}
+
+#endif // HAVE_FREETYPE_H
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontEngine.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontEngine.h
new file mode 100644
index 00000000000..24db3c6cdba
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontEngine.h
@@ -0,0 +1,99 @@
+//========================================================================
+//
+// SplashFTFontEngine.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHFTFONTENGINE_H
+#define SPLASHFTFONTENGINE_H
+
+#include <aconf.h>
+
+#if HAVE_FREETYPE_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include "gtypes.h"
+class GString;
+
+class SplashFontFile;
+class SplashFontFileID;
+
+//------------------------------------------------------------------------
+// SplashFTFontEngine
+//------------------------------------------------------------------------
+
+class SplashFTFontEngine {
+public:
+
+ static SplashFTFontEngine *init(GBool aaA, Guint flagsA);
+
+ ~SplashFTFontEngine();
+
+ // Load fonts.
+ SplashFontFile *loadType1Font(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ const char **enc);
+ SplashFontFile *loadType1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ const char **enc);
+ SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ const char **enc);
+ SplashFontFile *loadCIDFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ int *codeToGID, int codeToGIDLen);
+ SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ int *codeToGID, int codeToGIDLen);
+ SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ int fontNum,
+ int *codeToGID, int codeToGIDLen);
+
+private:
+
+ SplashFTFontEngine(GBool aaA, Guint flagsA, FT_Library libA);
+
+ GBool aa;
+ Guint flags;
+ FT_Library lib;
+ GBool useCIDs;
+
+ friend class SplashFTFontFile;
+ friend class SplashFTFont;
+};
+
+#endif // HAVE_FREETYPE_H
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontFile.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontFile.cc
new file mode 100644
index 00000000000..b5fd02af4a0
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontFile.cc
@@ -0,0 +1,171 @@
+//========================================================================
+//
+// SplashFTFontFile.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#if HAVE_FREETYPE_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "SplashFTFontEngine.h"
+#include "SplashFTFont.h"
+#include "SplashFTFontFile.h"
+
+//------------------------------------------------------------------------
+// SplashFTFontFile
+//------------------------------------------------------------------------
+
+SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA,
+ SplashFontFileID *idA,
+ SplashFontType fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
+ char *fileNameA,
+ GBool deleteFileA,
+#endif
+ const char **encA) {
+ FT_Face faceA;
+ int *codeToGIDA;
+ const char *name;
+ int i;
+
+#if LOAD_FONTS_FROM_MEM
+ if (FT_New_Memory_Face(engineA->lib, (FT_Byte *)fontBufA->getCString(),
+ fontBufA->getLength(), 0, &faceA)) {
+#else
+ if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {
+#endif
+ return NULL;
+ }
+ codeToGIDA = (int *)gmallocn(256, sizeof(int));
+ for (i = 0; i < 256; ++i) {
+ codeToGIDA[i] = 0;
+ if ((name = encA[i])) {
+ codeToGIDA[i] = (int)FT_Get_Name_Index(faceA, (char *)name);
+ }
+ }
+
+ return new SplashFTFontFile(engineA, idA, fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ fontBufA,
+#else
+ fileNameA, deleteFileA,
+#endif
+ faceA, codeToGIDA, 256);
+}
+
+SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA,
+ SplashFontFileID *idA,
+ SplashFontType fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
+ char *fileNameA,
+ GBool deleteFileA,
+#endif
+ int *codeToGIDA,
+ int codeToGIDLenA) {
+ FT_Face faceA;
+
+#if LOAD_FONTS_FROM_MEM
+ if (FT_New_Memory_Face(engineA->lib, (FT_Byte *)fontBufA->getCString(),
+ fontBufA->getLength(), 0, &faceA)) {
+#else
+ if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {
+#endif
+ return NULL;
+ }
+
+ return new SplashFTFontFile(engineA, idA, fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ fontBufA,
+#else
+ fileNameA, deleteFileA,
+#endif
+ faceA, codeToGIDA, codeToGIDLenA);
+}
+
+SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA,
+ SplashFontFileID *idA,
+ SplashFontType fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
+ char *fileNameA,
+ GBool deleteFileA,
+#endif
+ int fontNum,
+ int *codeToGIDA,
+ int codeToGIDLenA) {
+ FT_Face faceA;
+
+#if LOAD_FONTS_FROM_MEM
+ if (FT_New_Memory_Face(engineA->lib, (FT_Byte *)fontBufA->getCString(),
+ fontBufA->getLength(), fontNum, &faceA)) {
+#else
+ if (FT_New_Face(engineA->lib, fileNameA, fontNum, &faceA)) {
+#endif
+ return NULL;
+ }
+
+ return new SplashFTFontFile(engineA, idA, fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ fontBufA,
+#else
+ fileNameA, deleteFileA,
+#endif
+ faceA, codeToGIDA, codeToGIDLenA);
+}
+
+SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA,
+ SplashFontFileID *idA,
+ SplashFontType fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
+ char *fileNameA, GBool deleteFileA,
+#endif
+ FT_Face faceA,
+ int *codeToGIDA, int codeToGIDLenA):
+#if LOAD_FONTS_FROM_MEM
+ SplashFontFile(idA, fontTypeA, fontBufA)
+#else
+ SplashFontFile(idA, fontTypeA, fileNameA, deleteFileA)
+#endif
+{
+ engine = engineA;
+ face = faceA;
+ codeToGID = codeToGIDA;
+ codeToGIDLen = codeToGIDLenA;
+}
+
+SplashFTFontFile::~SplashFTFontFile() {
+ if (face) {
+ FT_Done_Face(face);
+ }
+ if (codeToGID) {
+ gfree(codeToGID);
+ }
+}
+
+SplashFont *SplashFTFontFile::makeFont(SplashCoord *mat,
+ SplashCoord *textMat) {
+ SplashFont *font;
+
+ font = new SplashFTFont(this, mat, textMat);
+ font->initCache();
+ return font;
+}
+
+#endif // HAVE_FREETYPE_H
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontFile.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontFile.h
new file mode 100644
index 00000000000..3a6b9b7aa43
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFTFontFile.h
@@ -0,0 +1,95 @@
+//========================================================================
+//
+// SplashFTFontFile.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHFTFONTFILE_H
+#define SPLASHFTFONTFILE_H
+
+#include <aconf.h>
+
+#if HAVE_FREETYPE_H
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include "SplashFontFile.h"
+
+class SplashFontFileID;
+class SplashFTFontEngine;
+
+//------------------------------------------------------------------------
+// SplashFTFontFile
+//------------------------------------------------------------------------
+
+class SplashFTFontFile: public SplashFontFile {
+public:
+
+ static SplashFontFile *loadType1Font(SplashFTFontEngine *engineA,
+ SplashFontFileID *idA,
+ SplashFontType fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
+ char *fileNameA, GBool deleteFileA,
+#endif
+ const char **encA);
+ static SplashFontFile *loadCIDFont(SplashFTFontEngine *engineA,
+ SplashFontFileID *idA,
+ SplashFontType fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
+ char *fileNameA, GBool deleteFileA,
+#endif
+ int *codeToGIDA, int codeToGIDLenA);
+ static SplashFontFile *loadTrueTypeFont(SplashFTFontEngine *engineA,
+ SplashFontFileID *idA,
+ SplashFontType fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
+ char *fileNameA,
+ GBool deleteFileA,
+#endif
+ int fontNum,
+ int *codeToGIDA,
+ int codeToGIDLenA);
+
+ virtual ~SplashFTFontFile();
+
+ // Create a new SplashFTFont, i.e., a scaled instance of this font
+ // file.
+ virtual SplashFont *makeFont(SplashCoord *mat,
+ SplashCoord *textMat);
+
+private:
+
+ SplashFTFontFile(SplashFTFontEngine *engineA,
+ SplashFontFileID *idA,
+ SplashFontType fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA,
+#else
+ char *fileNameA, GBool deleteFileA,
+#endif
+ FT_Face faceA,
+ int *codeToGIDA, int codeToGIDLenA);
+
+ SplashFTFontEngine *engine;
+ FT_Face face;
+ int *codeToGID;
+ int codeToGIDLen;
+
+ friend class SplashFTFont;
+};
+
+#endif // HAVE_FREETYPE_H
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc
new file mode 100644
index 00000000000..2fcd83d1266
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.cc
@@ -0,0 +1,195 @@
+//========================================================================
+//
+// SplashFont.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "SplashMath.h"
+#include "SplashGlyphBitmap.h"
+#include "SplashFontFile.h"
+#include "SplashFont.h"
+
+//------------------------------------------------------------------------
+
+// font cache size parameters
+#define splashFontCacheAssoc 8
+#define splashFontCacheMaxSets 8
+#define splashFontCacheSize (128*1024)
+
+//------------------------------------------------------------------------
+
+struct SplashFontCacheTag {
+ int c;
+ short xFrac, yFrac; // x and y fractions
+ int mru; // valid bit (0x80000000) and MRU index
+ int x, y, w, h; // offset and size of glyph
+};
+
+//------------------------------------------------------------------------
+// SplashFont
+//------------------------------------------------------------------------
+
+SplashFont::SplashFont(SplashFontFile *fontFileA, SplashCoord *matA,
+ SplashCoord *textMatA, GBool aaA) {
+ fontFile = fontFileA;
+ fontFile->incRefCnt();
+ mat[0] = matA[0];
+ mat[1] = matA[1];
+ mat[2] = matA[2];
+ mat[3] = matA[3];
+ textMat[0] = textMatA[0];
+ textMat[1] = textMatA[1];
+ textMat[2] = textMatA[2];
+ textMat[3] = textMatA[3];
+ aa = aaA;
+
+ cache = NULL;
+ cacheTags = NULL;
+
+ xMin = yMin = xMax = yMax = 0;
+}
+
+void SplashFont::initCache() {
+ int i;
+
+ // this should be (max - min + 1), but we add some padding to
+ // deal with rounding errors
+ glyphW = xMax - xMin + 3;
+ glyphH = yMax - yMin + 3;
+ if (glyphW > 1000 || glyphH > 1000) {
+ // 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 = 0;
+ glyphSize = 0;
+ cacheSets = 0;
+ cacheAssoc = 0;
+ return;
+ }
+ if (aa) {
+ glyphSize = glyphW * glyphH;
+ } else {
+ glyphSize = ((glyphW + 7) >> 3) * glyphH;
+ }
+
+ // set up the glyph pixmap cache
+ cacheAssoc = splashFontCacheAssoc;
+ for (cacheSets = splashFontCacheMaxSets;
+ cacheSets > 1 &&
+ glyphSize > splashFontCacheSize / (cacheSets * cacheAssoc);
+ cacheSets >>= 1) ;
+ cache = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize);
+ cacheTags = (SplashFontCacheTag *)gmallocn(cacheSets * cacheAssoc,
+ sizeof(SplashFontCacheTag));
+ for (i = 0; i < cacheSets * cacheAssoc; ++i) {
+ cacheTags[i].mru = i & (cacheAssoc - 1);
+ }
+}
+
+SplashFont::~SplashFont() {
+ fontFile->decRefCnt();
+ if (cache) {
+ gfree(cache);
+ }
+ if (cacheTags) {
+ gfree(cacheTags);
+ }
+}
+
+GBool SplashFont::getGlyph(int c, int xFrac, int yFrac,
+ SplashGlyphBitmap *bitmap) {
+ SplashGlyphBitmap bitmap2;
+ int size;
+ Guchar *p;
+ int i, j, k;
+
+ // no fractional coordinates for large glyphs or non-anti-aliased
+ // glyphs
+ if (!aa || glyphH > 50) {
+ xFrac = yFrac = 0;
+ }
+
+ // check the cache
+ 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+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
+ if (!makeGlyph(c, xFrac, yFrac, &bitmap2)) {
+ return gFalse;
+ }
+
+ // if the glyph doesn't fit in the bounding box, return a temporary
+ // uncached bitmap
+ if (!cache || bitmap2.w > glyphW || bitmap2.h > glyphH) {
+ *bitmap = bitmap2;
+ return gTrue;
+ }
+
+ // insert glyph pixmap in cache
+ if (aa) {
+ size = bitmap2.w * bitmap2.h;
+ } else {
+ size = ((bitmap2.w + 7) >> 3) * bitmap2.h;
+ }
+ p = NULL; // make gcc happy
+ for (j = 0; j < cacheAssoc; ++j) {
+ if ((cacheTags[i+j].mru & 0x7fffffff) == cacheAssoc - 1) {
+ cacheTags[i+j].mru = 0x80000000;
+ cacheTags[i+j].c = c;
+ cacheTags[i+j].xFrac = (short)xFrac;
+ cacheTags[i+j].yFrac = (short)yFrac;
+ cacheTags[i+j].x = bitmap2.x;
+ cacheTags[i+j].y = bitmap2.y;
+ cacheTags[i+j].w = bitmap2.w;
+ cacheTags[i+j].h = bitmap2.h;
+ p = cache + (i+j) * glyphSize;
+ memcpy(p, bitmap2.data, size);
+ } else {
+ ++cacheTags[i+j].mru;
+ }
+ }
+ *bitmap = bitmap2;
+ bitmap->data = p;
+ bitmap->freeData = gFalse;
+ if (bitmap2.freeData) {
+ gfree(bitmap2.data);
+ }
+ return gTrue;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.h
new file mode 100644
index 00000000000..2bb96545867
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFont.h
@@ -0,0 +1,111 @@
+//========================================================================
+//
+// SplashFont.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHFONT_H
+#define SPLASHFONT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "SplashTypes.h"
+#include "SplashMath.h"
+
+struct SplashGlyphBitmap;
+struct SplashFontCacheTag;
+class SplashFontFile;
+class SplashPath;
+
+//------------------------------------------------------------------------
+
+// Fractional positioning uses this many bits to the right of the
+// decimal points.
+#define splashFontFractionBits 2
+#define splashFontFraction (1 << splashFontFractionBits)
+#define splashFontFractionMul \
+ ((SplashCoord)1 / (SplashCoord)splashFontFraction)
+
+//------------------------------------------------------------------------
+// SplashFont
+//------------------------------------------------------------------------
+
+class SplashFont {
+public:
+
+ SplashFont(SplashFontFile *fontFileA, SplashCoord *matA,
+ SplashCoord *textMatA, GBool aaA);
+
+ // This must be called after the constructor, so that the subclass
+ // constructor has a chance to compute the bbox.
+ void initCache();
+
+ virtual ~SplashFont();
+
+ SplashFontFile *getFontFile() { return fontFile; }
+
+ // Return true if <this> matches the specified font file and matrix.
+ GBool matches(SplashFontFile *fontFileA, SplashCoord *matA,
+ SplashCoord *textMatA) {
+ return fontFileA == fontFile &&
+ splashAbs(matA[0] - mat[0]) < 0.0001 &&
+ splashAbs(matA[1] - mat[1]) < 0.0001 &&
+ splashAbs(matA[2] - mat[2]) < 0.0001 &&
+ splashAbs(matA[3] - mat[3]) < 0.0001 &&
+ splashAbs(textMatA[0] - textMat[0]) < 0.0001 &&
+ splashAbs(textMatA[1] - textMat[1]) < 0.0001 &&
+ splashAbs(textMatA[2] - textMat[2]) < 0.0001 &&
+ splashAbs(textMatA[3] - textMat[3]) < 0.0001;
+ }
+
+ // Get a glyph - this does a cache lookup first, and if not found,
+ // creates a new bitmap and adds it to the cache. The <xFrac> and
+ // <yFrac> values are splashFontFractionBits bits each, representing
+ // the numerators of fractions in [0, 1), where the denominator is
+ // splashFontFraction = 1 << splashFontFractionBits. Subclasses
+ // should override this to zero out xFrac and/or yFrac if they don't
+ // support fractional coordinates.
+ virtual GBool getGlyph(int c, int xFrac, int yFrac,
+ SplashGlyphBitmap *bitmap);
+
+ // Rasterize a glyph. The <xFrac> and <yFrac> values are the same
+ // as described for getGlyph.
+ virtual GBool makeGlyph(int c, int xFrac, int yFrac,
+ SplashGlyphBitmap *bitmap) = 0;
+
+ // Return the path for a glyph.
+ virtual SplashPath *getGlyphPath(int c) = 0;
+
+ // Return the font transform matrix.
+ SplashCoord *getMatrix() { return mat; }
+
+ // Return the glyph bounding box.
+ void getBBox(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA)
+ { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
+
+protected:
+
+ SplashFontFile *fontFile;
+ SplashCoord mat[4]; // font transform matrix
+ // (text space -> device space)
+ SplashCoord textMat[4]; // text transform matrix
+ // (text space -> user space)
+ GBool aa; // anti-aliasing
+ int xMin, yMin, xMax, yMax; // glyph bounding box
+ Guchar *cache; // glyph bitmap cache
+ SplashFontCacheTag * // cache tags
+ cacheTags;
+ int glyphW, glyphH; // size of glyph bitmaps
+ int glyphSize; // size of glyph bitmaps, in bytes
+ int cacheSets; // number of sets in cache
+ int cacheAssoc; // cache associativity (glyphs per set)
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc
new file mode 100644
index 00000000000..81afe0cd914
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.cc
@@ -0,0 +1,409 @@
+//========================================================================
+//
+// SplashFontEngine.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#ifndef _WIN32
+# include <unistd.h>
+#endif
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "SplashMath.h"
+#include "SplashFTFontEngine.h"
+#include "SplashFontFile.h"
+#include "SplashFontFileID.h"
+#include "SplashFont.h"
+#include "SplashFontEngine.h"
+
+#ifdef VMS
+#if (__VMS_VER < 70000000)
+extern "C" int unlink(char *filename);
+#endif
+#endif
+
+//------------------------------------------------------------------------
+// SplashFontEngine
+//------------------------------------------------------------------------
+
+SplashFontEngine::SplashFontEngine(
+#if HAVE_FREETYPE_H
+ GBool enableFreeType,
+ Guint freeTypeFlags,
+#endif
+ GBool aa) {
+ int i;
+
+ for (i = 0; i < splashFontCacheSize; ++i) {
+ fontCache[i] = NULL;
+ }
+ badFontFiles = new GList();
+
+#if HAVE_FREETYPE_H
+ if (enableFreeType) {
+ ftEngine = SplashFTFontEngine::init(aa, freeTypeFlags);
+ } else {
+ ftEngine = NULL;
+ }
+#endif
+}
+
+SplashFontEngine::~SplashFontEngine() {
+ int i;
+
+ for (i = 0; i < splashFontCacheSize; ++i) {
+ if (fontCache[i]) {
+ delete fontCache[i];
+ }
+ }
+ deleteGList(badFontFiles, SplashFontFileID);
+
+#if HAVE_FREETYPE_H
+ if (ftEngine) {
+ delete ftEngine;
+ }
+#endif
+}
+
+SplashFontFile *SplashFontEngine::getFontFile(SplashFontFileID *id) {
+ SplashFontFile *fontFile;
+ int i;
+
+ for (i = 0; i < splashFontCacheSize; ++i) {
+ if (fontCache[i]) {
+ fontFile = fontCache[i]->getFontFile();
+ if (fontFile && fontFile->getID()->matches(id)) {
+ return fontFile;
+ }
+ }
+ }
+ 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,
+#else
+ char *fileName,
+ GBool deleteFile,
+#endif
+ const char **enc) {
+ SplashFontFile *fontFile;
+
+ fontFile = NULL;
+#if HAVE_FREETYPE_H
+ if (!fontFile && ftEngine) {
+ fontFile = ftEngine->loadType1Font(idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ enc);
+ }
+#endif
+
+#if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) && !defined(__ANDROID__)
+ // delete the (temporary) font file -- with Unix hard link
+ // semantics, this will remove the last link; otherwise it will
+ // return an error, leaving the file to be deleted later (if
+ // loadXYZFont failed, the file will always be deleted)
+ if (deleteFile) {
+ unlink(fontFile ? fontFile->fileName->getCString() : fileName);
+ }
+#endif
+
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
+ return fontFile;
+}
+
+SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName,
+ GBool deleteFile,
+#endif
+ int *codeToGID,
+ const char **enc) {
+ SplashFontFile *fontFile;
+
+ fontFile = NULL;
+ if (!fontFile) {
+ gfree(codeToGID);
+ }
+#if HAVE_FREETYPE_H
+ if (!fontFile && ftEngine) {
+ fontFile = ftEngine->loadType1CFont(idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ enc);
+ }
+#endif
+
+#if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) && !defined(__ANDROID__)
+ // delete the (temporary) font file -- with Unix hard link
+ // semantics, this will remove the last link; otherwise it will
+ // return an error, leaving the file to be deleted later (if
+ // loadXYZFont failed, the file will always be deleted)
+ if (deleteFile) {
+ unlink(fontFile ? fontFile->fileName->getCString() : fileName);
+ }
+#endif
+
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
+ return fontFile;
+}
+
+SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName,
+ GBool deleteFile,
+#endif
+ int *codeToGID,
+ const char **enc) {
+ SplashFontFile *fontFile;
+
+ fontFile = NULL;
+ if (!fontFile) {
+ gfree(codeToGID);
+ }
+#if HAVE_FREETYPE_H
+ if (!fontFile && ftEngine) {
+ fontFile = ftEngine->loadOpenTypeT1CFont(idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ enc);
+ }
+#endif
+
+#if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) && !defined(__ANDROID__)
+ // delete the (temporary) font file -- with Unix hard link
+ // semantics, this will remove the last link; otherwise it will
+ // return an error, leaving the file to be deleted later (if
+ // loadXYZFont failed, the file will always be deleted)
+ if (deleteFile) {
+ unlink(fontFile ? fontFile->fileName->getCString() : fileName);
+ }
+#endif
+
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
+ return fontFile;
+}
+
+SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName,
+ GBool deleteFile,
+#endif
+ int *codeToGID,
+ int codeToGIDLen) {
+ SplashFontFile *fontFile;
+
+ fontFile = NULL;
+#if HAVE_FREETYPE_H
+ if (!fontFile && ftEngine) {
+ fontFile = ftEngine->loadCIDFont(idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ codeToGID, codeToGIDLen);
+ }
+#endif
+
+ if (!fontFile) {
+ gfree(codeToGID);
+ }
+
+#if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) && !defined(__ANDROID__)
+ // delete the (temporary) font file -- with Unix hard link
+ // semantics, this will remove the last link; otherwise it will
+ // return an error, leaving the file to be deleted later (if
+ // loadXYZFont failed, the file will always be deleted)
+ if (deleteFile) {
+ unlink(fontFile ? fontFile->fileName->getCString() : fileName);
+ }
+#endif
+
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
+ return fontFile;
+}
+
+SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName,
+ GBool deleteFile,
+#endif
+ int *codeToGID,
+ int codeToGIDLen) {
+ SplashFontFile *fontFile;
+
+ fontFile = NULL;
+#if HAVE_FREETYPE_H
+ if (!fontFile && ftEngine) {
+ fontFile = ftEngine->loadOpenTypeCFFFont(idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ codeToGID, codeToGIDLen);
+ }
+#endif
+
+ if (!fontFile) {
+ gfree(codeToGID);
+ }
+
+#if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) && !defined(__ANDROID__)
+ // delete the (temporary) font file -- with Unix hard link
+ // semantics, this will remove the last link; otherwise it will
+ // return an error, leaving the file to be deleted later (if
+ // loadXYZFont failed, the file will always be deleted)
+ if (deleteFile) {
+ unlink(fontFile ? fontFile->fileName->getCString() : fileName);
+ }
+#endif
+
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
+ return fontFile;
+}
+
+SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName,
+ GBool deleteFile,
+#endif
+ int fontNum,
+ int *codeToGID,
+ int codeToGIDLen,
+ char *fontName) {
+ SplashFontFile *fontFile;
+
+ fontFile = NULL;
+#if HAVE_FREETYPE_H
+ if (!fontFile && ftEngine) {
+ fontFile = ftEngine->loadTrueTypeFont(idA,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName, deleteFile,
+#endif
+ fontNum, codeToGID, codeToGIDLen);
+ }
+#endif
+
+ if (!fontFile) {
+ gfree(codeToGID);
+ }
+
+#if !LOAD_FONTS_FROM_MEM && !defined(_WIN32) && !defined(__ANDROID__)
+ // delete the (temporary) font file -- with Unix hard link
+ // semantics, this will remove the last link; otherwise it will
+ // return an error, leaving the file to be deleted later (if
+ // loadXYZFont failed, the file will always be deleted)
+ if (deleteFile) {
+ unlink(fontFile ? fontFile->fileName->getCString() : fileName);
+ }
+#endif
+
+ if (!fontFile) {
+ badFontFiles->append(idA);
+ }
+
+ return fontFile;
+}
+
+SplashFont *SplashFontEngine::getFont(SplashFontFile *fontFile,
+ SplashCoord *textMat,
+ SplashCoord *ctm) {
+ SplashCoord mat[4];
+ SplashFont *font;
+ int i, j;
+
+ mat[0] = textMat[0] * ctm[0] + textMat[1] * ctm[2];
+ mat[1] = -(textMat[0] * ctm[1] + textMat[1] * ctm[3]);
+ mat[2] = textMat[2] * ctm[0] + textMat[3] * ctm[2];
+ mat[3] = -(textMat[2] * ctm[1] + textMat[3] * ctm[3]);
+ if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.01)) {
+ // avoid a singular (or close-to-singular) matrix
+ mat[0] = 0.01; mat[1] = 0;
+ mat[2] = 0; mat[3] = 0.01;
+ }
+
+ font = fontCache[0];
+ if (font && font->matches(fontFile, mat, textMat)) {
+ return font;
+ }
+ for (i = 1; i < splashFontCacheSize; ++i) {
+ font = fontCache[i];
+ if (font && font->matches(fontFile, mat, textMat)) {
+ for (j = i; j > 0; --j) {
+ fontCache[j] = fontCache[j-1];
+ }
+ fontCache[0] = font;
+ return font;
+ }
+ }
+ font = fontFile->makeFont(mat, textMat);
+ if (fontCache[splashFontCacheSize - 1]) {
+ delete fontCache[splashFontCacheSize - 1];
+ }
+ for (j = splashFontCacheSize - 1; j > 0; --j) {
+ fontCache[j] = fontCache[j-1];
+ }
+ fontCache[0] = font;
+ return font;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h
new file mode 100644
index 00000000000..59adb95858a
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontEngine.h
@@ -0,0 +1,129 @@
+//========================================================================
+//
+// SplashFontEngine.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHFONTENGINE_H
+#define SPLASHFONTENGINE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+class GString;
+class GList;
+
+class SplashFTFontEngine;
+class SplashDTFontEngine;
+class SplashDT4FontEngine;
+class SplashFontFile;
+class SplashFontFileID;
+class SplashFont;
+
+//------------------------------------------------------------------------
+
+#define splashFontCacheSize 16
+
+#if HAVE_FREETYPE_H
+#define splashFTNoHinting (1 << 0)
+#endif
+
+//------------------------------------------------------------------------
+// SplashFontEngine
+//------------------------------------------------------------------------
+
+class SplashFontEngine {
+public:
+
+ // Create a font engine.
+ SplashFontEngine(
+#if HAVE_FREETYPE_H
+ GBool enableFreeType,
+ Guint freeTypeFlags,
+#endif
+ GBool aa);
+
+ ~SplashFontEngine();
+
+ // Get a font file from the cache. Returns NULL if there is no
+ // 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
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ const char **enc);
+ SplashFontFile *loadType1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ int *codeToGID, const char **enc);
+ SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ int *codeToGID, const char **enc);
+ SplashFontFile *loadCIDFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ int *codeToGID, int codeToGIDLen);
+ SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ int *codeToGID, int codeToGIDLen);
+ SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf,
+#else
+ char *fileName, GBool deleteFile,
+#endif
+ int fontNum,
+ int *codeToGID, int codeToGIDLen,
+ char *fontName);
+
+ // Get a font - this does a cache lookup first, and if not found,
+ // creates a new SplashFont object and adds it to the cache. The
+ // matrix, mat = textMat * ctm:
+ // [ mat[0] mat[1] ]
+ // [ mat[2] mat[3] ]
+ // specifies the font transform in PostScript style:
+ // [x' y'] = [x y] * mat
+ // Note that the Splash y axis points downward.
+ SplashFont *getFont(SplashFontFile *fontFile,
+ SplashCoord *textMat, SplashCoord *ctm);
+
+private:
+
+ SplashFont *fontCache[splashFontCacheSize];
+ GList *badFontFiles; // [SplashFontFileID]
+
+#if HAVE_FREETYPE_H
+ SplashFTFontEngine *ftEngine;
+#endif
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFontFile.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontFile.cc
new file mode 100644
index 00000000000..2d260e38239
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontFile.cc
@@ -0,0 +1,84 @@
+//========================================================================
+//
+// SplashFontFile.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#ifndef _WIN32
+# include <unistd.h>
+#endif
+#include "gmempp.h"
+#include "GString.h"
+#include "SplashFontFile.h"
+#include "SplashFontFileID.h"
+
+#ifdef VMS
+#if (__VMS_VER < 70000000)
+extern "C" int unlink(char *filename);
+#endif
+#endif
+
+//------------------------------------------------------------------------
+// SplashFontFile
+//------------------------------------------------------------------------
+
+SplashFontFile::SplashFontFile(SplashFontFileID *idA,
+ SplashFontType fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA
+#else
+ char *fileNameA, GBool deleteFileA
+#endif
+ ) {
+ id = idA;
+ fontType = fontTypeA;
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = fontBufA;
+#else
+ fileName = new GString(fileNameA);
+ deleteFile = deleteFileA;
+#endif
+ refCnt = 0;
+}
+
+SplashFontFile::~SplashFontFile() {
+#if LOAD_FONTS_FROM_MEM
+ delete fontBuf;
+#else
+ if (deleteFile) {
+ unlink(fileName->getCString());
+ }
+ delete fileName;
+#endif
+ delete id;
+}
+
+void SplashFontFile::incRefCnt() {
+#if MULTITHREADED
+ gAtomicIncrement(&refCnt);
+#else
+ ++refCnt;
+#endif
+}
+
+void SplashFontFile::decRefCnt() {
+ GBool done;
+
+#if MULTITHREADED
+ done = gAtomicDecrement(&refCnt) == 0;
+#else
+ done = --refCnt == 0;
+#endif
+ if (done) {
+ delete this;
+ }
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFontFile.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontFile.h
new file mode 100644
index 00000000000..133e6ab87dc
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontFile.h
@@ -0,0 +1,95 @@
+//========================================================================
+//
+// SplashFontFile.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHFONTFILE_H
+#define SPLASHFONTFILE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "SplashTypes.h"
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+class GString;
+class SplashFontEngine;
+class SplashFont;
+class SplashFontFileID;
+
+//------------------------------------------------------------------------
+// SplashFontType
+//------------------------------------------------------------------------
+
+enum SplashFontType {
+ splashFontType1, // GfxFontType.fontType1
+ splashFontType1C, // GfxFontType.fontType1C
+ splashFontOpenTypeT1C, // GfxFontType.fontType1COT
+ splashFontCID, // GfxFontType.fontCIDType0/fontCIDType0C
+ splashFontOpenTypeCFF, // GfxFontType.fontCIDType0COT
+ splashFontTrueType // GfxFontType.fontTrueType/fontTrueTypeOT/
+ // fontCIDType2/fontCIDType2OT
+};
+
+//------------------------------------------------------------------------
+// SplashFontFile
+//------------------------------------------------------------------------
+
+class SplashFontFile {
+public:
+
+ virtual ~SplashFontFile();
+
+ // Create a new SplashFont, i.e., a scaled instance of this font
+ // file.
+ virtual SplashFont *makeFont(SplashCoord *mat, SplashCoord *textMat) = 0;
+
+ // Get the font file ID.
+ SplashFontFileID *getID() { return id; }
+
+ // Increment the reference count.
+ void incRefCnt();
+
+ // Decrement the reference count. If the new value is zero, delete
+ // the SplashFontFile object.
+ void decRefCnt();
+
+protected:
+
+ SplashFontFile(SplashFontFileID *idA,
+ SplashFontType fontTypeA,
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBufA
+#else
+ char *fileNameA, GBool deleteFileA
+#endif
+ );
+
+ SplashFontFileID *id;
+ SplashFontType fontType;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf;
+#else
+ GString *fileName;
+ GBool deleteFile;
+#endif
+#if MULTITHREADED
+ GAtomicCounter refCnt;
+#else
+ int refCnt;
+#endif
+
+ friend class SplashFontEngine;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFontFileID.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontFileID.cc
new file mode 100644
index 00000000000..27f52031d50
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontFileID.cc
@@ -0,0 +1,26 @@
+//========================================================================
+//
+// SplashFontFileID.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmempp.h"
+#include "SplashFontFileID.h"
+
+//------------------------------------------------------------------------
+// SplashFontFileID
+//------------------------------------------------------------------------
+
+SplashFontFileID::SplashFontFileID() {
+}
+
+SplashFontFileID::~SplashFontFileID() {
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashFontFileID.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontFileID.h
new file mode 100644
index 00000000000..384018a2f3a
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashFontFileID.h
@@ -0,0 +1,32 @@
+//========================================================================
+//
+// SplashFontFileID.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHFONTFILEID_H
+#define SPLASHFONTFILEID_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+// SplashFontFileID
+//------------------------------------------------------------------------
+
+class SplashFontFileID {
+public:
+
+ SplashFontFileID();
+ virtual ~SplashFontFileID();
+ virtual GBool matches(SplashFontFileID *id) = 0;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashGlyphBitmap.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashGlyphBitmap.h
new file mode 100644
index 00000000000..d375b3ccb46
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashGlyphBitmap.h
@@ -0,0 +1,28 @@
+//========================================================================
+//
+// SplashGlyphBitmap.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHGLYPHBITMAP_H
+#define SPLASHGLYPHBITMAP_H
+
+#include <aconf.h>
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+// SplashGlyphBitmap
+//------------------------------------------------------------------------
+
+struct SplashGlyphBitmap {
+ int x, y, w, h; // offset and size of glyph
+ GBool aa; // anti-aliased: true means 8-bit alpha
+ // bitmap; false means 1-bit
+ Guchar *data; // bitmap data
+ GBool freeData; // true if data memory should be freed
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashMath.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashMath.h
new file mode 100644
index 00000000000..45f4e53e6bb
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashMath.h
@@ -0,0 +1,378 @@
+//========================================================================
+//
+// SplashMath.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHMATH_H
+#define SPLASHMATH_H
+
+#include <aconf.h>
+
+#if USE_FIXEDPONT
+# include "FixedPoint.h"
+#else
+# include <math.h>
+# if (defined(__GNUC__) && defined(__SSE2__)) || \
+ (defined(_WIN32) && (_M_IX86_FP == 2 || defined(_M_X64)))
+# include <emmintrin.h>
+# endif
+#endif
+#include "SplashTypes.h"
+
+static inline SplashCoord splashAbs(SplashCoord x) {
+#if USE_FIXEDPOINT
+ return FixedPoint::abs(x);
+#else
+ return fabs(x);
+#endif
+}
+
+// floor() and (int)() are implemented separately, which results
+// in changing the FPCW multiple times - so we optimize it with
+// some inline assembly or SSE intrinsics.
+static inline int splashFloor(SplashCoord x) {
+#if USE_FIXEDPOINT
+
+ //--- fixed point
+
+ return FixedPoint::floor(x);
+
+#elif (defined(__GNUC__) && defined(__SSE2__)) || \
+ (defined(_WIN32) && (_M_IX86_FP == 2 || defined(_M_X64)))
+
+ //--- SSE2 intrinsics
+ // NB: 64-bit x86 guarantees availability of SSE2.
+
+ __m128d m1, m2;
+ int i, s;
+
+ m1 = _mm_set_sd(x);
+ i = _mm_cvttsd_si32(m1);
+ m2 = _mm_cvtsi32_sd(m1, i);
+ s = _mm_ucomigt_sd(m2, m1);
+ return i - s;
+
+#elif defined(__GNUC__) && defined(__i386__) && !defined(__APPLE__)
+
+ //--- x87 inline assembly (gcc/clang)
+ // (this code fails on OSX for reasons I don't understand)
+
+ Gushort oldCW, newCW, t;
+ int result;
+
+ __asm__ volatile("fnstcw %0\n"
+ "movw %0, %3\n"
+ "andw $0xf3ff, %3\n"
+ "orw $0x0400, %3\n"
+ "movw %3, %1\n" // round down
+ "fldcw %1\n"
+ "fistl %2\n"
+ "fldcw %0\n"
+ : "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t)
+ : "t" (x));
+ return result;
+
+#elif defined(_WIN32) && defined(_M_IX86)
+
+ //--- x87 inline assembly (VC)
+
+ Gushort oldCW, newCW;
+ int result;
+
+ __asm fld QWORD PTR x
+ __asm fnstcw WORD PTR oldCW
+ __asm mov ax, WORD PTR oldCW
+ __asm and ax, 0xf3ff
+ __asm or ax, 0x0400
+ __asm mov WORD PTR newCW, ax // round down
+ __asm fldcw WORD PTR newCW
+ __asm fistp DWORD PTR result
+ __asm fldcw WORD PTR oldCW
+ return result;
+
+#else
+
+ //--- all others
+
+ return (int)floor(x);
+
+#endif
+}
+
+// ceil() and (int)() are implemented separately, which results
+// in changing the FPCW multiple times - so we optimize it with
+// some inline assembly or SSE intrinsics.
+static inline int splashCeil(SplashCoord x) {
+#if USE_FIXEDPOINT
+
+ //--- fixed point
+
+ return FixedPoint::ceil(x);
+
+#elif (defined(__GNUC__) && defined(__SSE2__)) || \
+ (defined(_WIN32) && (_M_IX86_FP == 2 || defined(_M_X64)))
+
+ //--- SSE2 intrinsics
+ // NB: 64-bit x86 guarantees availability of SSE2.
+
+ __m128d m1, m2;
+ int i, s;
+
+ m1 = _mm_set_sd(x);
+ i = _mm_cvttsd_si32(m1);
+ m2 = _mm_cvtsi32_sd(m1, i);
+ s = _mm_ucomilt_sd(m2, m1);
+ return i + s;
+
+#elif defined(__GNUC__) && defined(__i386__) && !defined(__APPLE__)
+
+ //--- x87 inline assembly (gcc/clang)
+ // (this code fails on OSX for reasons I don't understand)
+
+ Gushort oldCW, newCW, t;
+ int result;
+
+ __asm__ volatile("fnstcw %0\n"
+ "movw %0, %3\n"
+ "andw $0xf3ff, %3\n"
+ "orw $0x0800, %3\n"
+ "movw %3, %1\n" // round up
+ "fldcw %1\n"
+ "fistl %2\n"
+ "fldcw %0\n"
+ : "=m" (oldCW), "=m" (newCW), "=m" (result), "=r" (t)
+ : "t" (x));
+ return result;
+
+#elif defined(_WIN32) && defined(_M_IX86)
+
+ //--- x87 inline assembly (VC)
+
+ // ceil() and (int)() are implemented separately, which results
+ // in changing the FPCW multiple times - so we optimize it with
+ // some inline assembly
+ Gushort oldCW, newCW;
+ int result;
+
+ __asm fld QWORD PTR x
+ __asm fnstcw WORD PTR oldCW
+ __asm mov ax, WORD PTR oldCW
+ __asm and ax, 0xf3ff
+ __asm or ax, 0x0800
+ __asm mov WORD PTR newCW, ax // round up
+ __asm fldcw WORD PTR newCW
+ __asm fistp DWORD PTR result
+ __asm fldcw WORD PTR oldCW
+ return result;
+
+#else
+
+ //--- all others
+
+ return (int)ceil(x);
+
+#endif
+}
+
+static inline int splashRound(SplashCoord x) {
+#if USE_FIXEDPOINT
+
+ //--- fixed point
+
+ return FixedPoint::round(x);
+
+#else
+
+ //--- all others
+
+ return splashFloor(x + 0.5);
+
+#endif
+}
+
+static inline SplashCoord splashAvg(SplashCoord x, SplashCoord y) {
+#if USE_FIXEDPOINT
+ return FixedPoint::avg(x, y);
+#else
+ return 0.5 * (x + y);
+#endif
+}
+
+static inline SplashCoord splashSqrt(SplashCoord x) {
+#if USE_FIXEDPOINT
+ return FixedPoint::sqrt(x);
+#else
+ return sqrt(x);
+#endif
+}
+
+static inline SplashCoord splashPow(SplashCoord x, SplashCoord y) {
+#if USE_FIXEDPOINT
+ return FixedPoint::pow(x, y);
+#else
+ return pow(x, y);
+#endif
+}
+
+static inline SplashCoord splashDist(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ SplashCoord dx, dy;
+ dx = x1 - x0;
+ dy = y1 - y0;
+#if USE_FIXEDPOINT
+ // this handles the situation where dx*dx or dy*dy is too large to
+ // fit in the 16.16 fixed point format
+ SplashCoord dxa, dya, d;
+ dxa = splashAbs(dx);
+ dya = splashAbs(dy);
+ if (dxa == 0 && dya == 0) {
+ return 0;
+ } else if (dxa > dya) {
+ d = dya / dxa;
+ return dxa * FixedPoint::sqrt(d*d + 1);
+ } else {
+ d = dxa / dya;
+ return dya * FixedPoint::sqrt(d*d + 1);
+ }
+#else
+ return sqrt(dx * dx + dy * dy);
+#endif
+}
+
+static inline GBool splashCheckDet(SplashCoord m11, SplashCoord m12,
+ SplashCoord m21, SplashCoord m22,
+ SplashCoord epsilon) {
+#if USE_FIXEDPOINT
+ return FixedPoint::checkDet(m11, m12, m21, m22, epsilon);
+#else
+ return fabs(m11 * m22 - m12 * m21) >= epsilon;
+#endif
+}
+
+// Perform stroke adjustment on a SplashCoord range [xMin, xMax),
+// resulting in an int range [*xMinI, *xMaxI).
+//
+// There are several options:
+//
+// 1. Round both edge coordinates.
+// Pro: adjacent strokes/fills line up without any gaps or
+// overlaps
+// Con: lines with the same original floating point width can
+// end up with different integer widths, e.g.:
+// xMin = 10.1 xMax = 11.3 (width = 1.2)
+// --> xMinI = 10 xMaxI = 11 (width = 1)
+// but
+// xMin = 10.4 xMax = 11.6 (width = 1.2)
+// --> xMinI = 10 xMaxI = 12 (width = 2)
+//
+// 2. Round the min coordinate; add the ceiling of the width.
+// Pro: lines with the same original floating point width will
+// always end up with the same integer width
+// Con: adjacent strokes/fills can have overlaps (which is
+// problematic with transparency)
+// (This could use floor on the min coordinate, instead of
+// rounding, with similar results.)
+// (If the width is rounded instead of using ceiling, the results
+// Are similar, except that adjacent strokes/fills can have gaps
+// as well as overlaps.)
+//
+// 3. Use floor on the min coordinate and ceiling on the max
+// coordinate.
+// Pro: lines always end up at least as wide as the original
+// floating point width
+// Con: adjacent strokes/fills can have overlaps, and lines with
+// the same original floating point width can end up with
+// different integer widths; the integer width can be more
+// than one pixel wider than the original width, e.g.:
+// xMin = 10.9 xMax = 12.1 (width = 1.2)
+// --> xMinI = 10 xMaxI = 13 (width = 3)
+// but
+// xMin = 10.1 xMax = 11.3 (width = 1.2)
+// --> xMinI = 10 xMaxI = 12 (width = 2)
+//
+// 4. Use a hybrid approach, choosing between two of the above
+// options, based on width. E.g., use #2 if width <= 4, and use #1
+// if width > 4.
+//
+// If w >= 0 and strokeAdjMode is splashStrokeAdjustCAD then a special
+// mode for projecting line caps is enabled, with w being the
+// transformed line width.
+
+static inline void splashStrokeAdjust(SplashCoord xMin, SplashCoord xMax,
+ int *xMinI, int *xMaxI,
+ SplashStrokeAdjustMode strokeAdjMode,
+ SplashCoord w = -1) {
+ int x0, x1;
+
+ // make sure the coords fit in 32-bit ints
+#if USE_FIXEDPOINT
+ if (xMin < -32767) {
+ xMin = -32767;
+ } else if (xMin > 32767) {
+ xMin = 32767;
+ }
+ if (xMax < -32767) {
+ xMax = -32767;
+ } else if (xMax > 32767) {
+ xMax = 32767;
+ }
+#else
+ if (xMin < -1e9) {
+ xMin = -1e9;
+ } else if (xMin > 1e9) {
+ xMin = 1e9;
+ }
+ if (xMax < -1e9) {
+ xMax = -1e9;
+ } else if (xMax > 1e9) {
+ xMax = 1e9;
+ }
+#endif
+
+ // this will never be called with strokeAdjMode == splashStrokeAdjustOff
+ if (strokeAdjMode == splashStrokeAdjustCAD) {
+ x0 = splashRound(xMin);
+ if (w >= 0) {
+ x1 = splashRound(xMax - w) + splashRound(w);
+ } else {
+ x1 = x0 + splashRound(xMax - xMin);
+ }
+ } else {
+ // NB: enable exactly one of these.
+#if 1 // 1. Round both edge coordinates.
+ x0 = splashRound(xMin);
+ x1 = splashRound(xMax);
+#endif
+#if 0 // 2. Round the min coordinate; add the ceiling of the width.
+ x0 = splashRound(xMin);
+ x1 = x0 + splashCeil(xMax - xMin);
+#endif
+#if 0 // 3. Use floor on the min coord and ceiling on the max coord.
+ x0 = splashFloor(xMin);
+ x1 = splashCeil(xMax);
+#endif
+#if 0 // 4. Hybrid.
+ SplashCoord w = xMax - xMin;
+ x0 = splashRound(xMin);
+ if (w > 4) {
+ x1 = splashRound(xMax);
+ } else {
+ x1 = x0 + splashRound(w);
+ }
+#endif
+ }
+ if (x0 == x1) {
+ if (xMin + xMax < 2 * x0) {
+ --x0;
+ } else {
+ ++x1;
+ }
+ }
+ *xMinI = x0;
+ *xMaxI = x1;
+}
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashPath.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashPath.cc
new file mode 100644
index 00000000000..792166715e5
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashPath.cc
@@ -0,0 +1,213 @@
+//========================================================================
+//
+// SplashPath.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "SplashErrorCodes.h"
+#include "SplashPath.h"
+
+//------------------------------------------------------------------------
+// SplashPath
+//------------------------------------------------------------------------
+
+// A path can be in three possible states:
+//
+// 1. no current point -- zero or more finished subpaths
+// [curSubpath == length]
+//
+// 2. one point in subpath
+// [curSubpath == length - 1]
+//
+// 3. open subpath with two or more points
+// [curSubpath < length - 1]
+
+SplashPath::SplashPath() {
+ pts = NULL;
+ flags = NULL;
+ length = size = 0;
+ curSubpath = 0;
+ hints = NULL;
+ hintsLength = hintsSize = 0;
+}
+
+SplashPath::SplashPath(SplashPath *path) {
+ length = path->length;
+ size = path->size;
+ pts = (SplashPathPoint *)gmallocn(size, sizeof(SplashPathPoint));
+ flags = (Guchar *)gmallocn(size, sizeof(Guchar));
+ memcpy(pts, path->pts, length * sizeof(SplashPathPoint));
+ memcpy(flags, path->flags, length * sizeof(Guchar));
+ curSubpath = path->curSubpath;
+ if (path->hints) {
+ hintsLength = hintsSize = path->hintsLength;
+ hints = (SplashPathHint *)gmallocn(hintsSize, sizeof(SplashPathHint));
+ memcpy(hints, path->hints, hintsLength * sizeof(SplashPathHint));
+ } else {
+ hints = NULL;
+ hintsLength = hintsSize = 0;
+ }
+}
+
+SplashPath::~SplashPath() {
+ gfree(pts);
+ gfree(flags);
+ gfree(hints);
+}
+
+// Add space for <nPts> more points.
+void SplashPath::grow(int nPts) {
+ if (length + nPts > size) {
+ if (size == 0) {
+ size = 32;
+ }
+ while (size < length + nPts) {
+ size *= 2;
+ }
+ pts = (SplashPathPoint *)greallocn(pts, size, sizeof(SplashPathPoint));
+ flags = (Guchar *)greallocn(flags, size, sizeof(Guchar));
+ }
+}
+
+void SplashPath::append(SplashPath *path) {
+ int i;
+
+ curSubpath = length + path->curSubpath;
+ grow(path->length);
+ for (i = 0; i < path->length; ++i) {
+ pts[length] = path->pts[i];
+ flags[length] = path->flags[i];
+ ++length;
+ }
+}
+
+SplashError SplashPath::moveTo(SplashCoord x, SplashCoord y) {
+ if (onePointSubpath()) {
+ return splashErrBogusPath;
+ }
+ grow(1);
+ pts[length].x = x;
+ pts[length].y = y;
+ flags[length] = splashPathFirst | splashPathLast;
+ curSubpath = length++;
+ return splashOk;
+}
+
+SplashError SplashPath::lineTo(SplashCoord x, SplashCoord y) {
+ if (noCurrentPoint()) {
+ return splashErrNoCurPt;
+ }
+ flags[length-1] &= (Guchar)~splashPathLast;
+ grow(1);
+ pts[length].x = x;
+ pts[length].y = y;
+ flags[length] = splashPathLast;
+ ++length;
+ return splashOk;
+}
+
+SplashError SplashPath::curveTo(SplashCoord x1, SplashCoord y1,
+ SplashCoord x2, SplashCoord y2,
+ SplashCoord x3, SplashCoord y3) {
+ if (noCurrentPoint()) {
+ return splashErrNoCurPt;
+ }
+ flags[length-1] &= (Guchar)~splashPathLast;
+ grow(3);
+ pts[length].x = x1;
+ pts[length].y = y1;
+ flags[length] = splashPathCurve;
+ ++length;
+ pts[length].x = x2;
+ pts[length].y = y2;
+ flags[length] = splashPathCurve;
+ ++length;
+ pts[length].x = x3;
+ pts[length].y = y3;
+ flags[length] = splashPathLast;
+ ++length;
+ return splashOk;
+}
+
+SplashError SplashPath::close(GBool force) {
+ if (noCurrentPoint()) {
+ return splashErrNoCurPt;
+ }
+ if (force ||
+ curSubpath == length - 1 ||
+ pts[length - 1].x != pts[curSubpath].x ||
+ pts[length - 1].y != pts[curSubpath].y) {
+ lineTo(pts[curSubpath].x, pts[curSubpath].y);
+ }
+ flags[curSubpath] |= splashPathClosed;
+ flags[length - 1] |= splashPathClosed;
+ curSubpath = length;
+ return splashOk;
+}
+
+void SplashPath::addStrokeAdjustHint(int ctrl0, int ctrl1,
+ int firstPt, int lastPt,
+ GBool projectingCap) {
+ if (hintsLength == hintsSize) {
+ hintsSize = hintsLength ? 2 * hintsLength : 8;
+ hints = (SplashPathHint *)greallocn(hints, hintsSize,
+ sizeof(SplashPathHint));
+ }
+ hints[hintsLength].ctrl0 = ctrl0;
+ hints[hintsLength].ctrl1 = ctrl1;
+ hints[hintsLength].firstPt = firstPt;
+ hints[hintsLength].lastPt = lastPt;
+ hints[hintsLength].projectingCap = projectingCap;
+ ++hintsLength;
+}
+
+void SplashPath::offset(SplashCoord dx, SplashCoord dy) {
+ int i;
+
+ for (i = 0; i < length; ++i) {
+ pts[i].x += dx;
+ pts[i].y += dy;
+ }
+}
+
+GBool SplashPath::getCurPt(SplashCoord *x, SplashCoord *y) {
+ if (noCurrentPoint()) {
+ return gFalse;
+ }
+ *x = pts[length - 1].x;
+ *y = pts[length - 1].y;
+ return gTrue;
+}
+
+GBool SplashPath::containsZeroLengthSubpaths() {
+ GBool zeroLength;
+ int i;
+
+ zeroLength = gTrue; // make gcc happy
+ for (i = 0; i < length; ++i) {
+ if (flags[i] & splashPathFirst) {
+ zeroLength = gTrue;
+ } else {
+ if (pts[i].x != pts[i-1].x || pts[i].y != pts[i-1].y) {
+ zeroLength = gFalse;
+ }
+ if (flags[i] & splashPathLast) {
+ if (zeroLength) {
+ return gTrue;
+ }
+ }
+ }
+ }
+ return gFalse;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashPath.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashPath.h
new file mode 100644
index 00000000000..b5732b8b408
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashPath.h
@@ -0,0 +1,133 @@
+//========================================================================
+//
+// SplashPath.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHPATH_H
+#define SPLASHPATH_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+//------------------------------------------------------------------------
+// SplashPathPoint
+//------------------------------------------------------------------------
+
+struct SplashPathPoint {
+ SplashCoord x, y;
+};
+
+//------------------------------------------------------------------------
+// SplashPath.flags
+//------------------------------------------------------------------------
+
+// first point on each subpath sets this flag
+#define splashPathFirst 0x01
+
+// last point on each subpath sets this flag
+#define splashPathLast 0x02
+
+// if the subpath is closed, its first and last points must be
+// identical, and must set this flag
+#define splashPathClosed 0x04
+
+// curve control points set this flag
+#define splashPathCurve 0x08
+
+//------------------------------------------------------------------------
+// SplashPathHint
+//------------------------------------------------------------------------
+
+struct SplashPathHint {
+ int ctrl0, ctrl1;
+ int firstPt, lastPt;
+ GBool projectingCap;
+};
+
+//------------------------------------------------------------------------
+// SplashPath
+//------------------------------------------------------------------------
+
+class SplashPath {
+public:
+
+ // Create an empty path.
+ SplashPath();
+
+ // Copy a path.
+ SplashPath *copy() { return new SplashPath(this); }
+
+ ~SplashPath();
+
+ // Append <path> to <this>.
+ void append(SplashPath *path);
+
+ // Start a new subpath.
+ SplashError moveTo(SplashCoord x, SplashCoord y);
+
+ // Add a line segment to the last subpath.
+ SplashError lineTo(SplashCoord x, SplashCoord y);
+
+ // Add a third-order (cubic) Bezier curve segment to the last
+ // subpath.
+ SplashError curveTo(SplashCoord x1, SplashCoord y1,
+ SplashCoord x2, SplashCoord y2,
+ SplashCoord x3, SplashCoord y3);
+
+ // Close the last subpath, adding a line segment if necessary. If
+ // <force> is true, this adds a line segment even if the current
+ // point is equal to the first point in the subpath.
+ SplashError close(GBool force = gFalse);
+
+ // Add a stroke adjustment hint. The controlling segments are
+ // <ctrl0> and <ctrl1> (where segments are identified by their first
+ // point), and the points to be adjusted are <firstPt> .. <lastPt>.
+ // <projectingCap> is true if the points are part of a projecting
+ // line cap.
+ void addStrokeAdjustHint(int ctrl0, int ctrl1, int firstPt, int lastPt,
+ GBool projectingCap = gFalse);
+
+ // Add (<dx>, <dy>) to every point on this path.
+ void offset(SplashCoord dx, SplashCoord dy);
+
+ // Get the points on the path.
+ int getLength() { return length; }
+ void getPoint(int i, SplashCoord *x, SplashCoord *y, Guchar *f)
+ { *x = pts[i].x; *y = pts[i].y; *f = flags[i]; }
+
+ // Get the current point.
+ GBool getCurPt(SplashCoord *x, SplashCoord *y);
+
+ // Returns true if the path contains one or more zero length
+ // subpaths.
+ GBool containsZeroLengthSubpaths();
+
+private:
+
+ SplashPath(SplashPath *path);
+ void grow(int nPts);
+ GBool noCurrentPoint() { return curSubpath == length; }
+ GBool onePointSubpath() { return curSubpath == length - 1; }
+ GBool openSubpath() { return curSubpath < length - 1; }
+
+ SplashPathPoint *pts; // array of points
+ Guchar *flags; // array of flags
+ int length, size; // length/size of the pts and flags arrays
+ int curSubpath; // index of first point in last subpath
+
+ SplashPathHint *hints; // list of hints
+ int hintsLength, hintsSize;
+
+ friend class SplashXPath;
+ friend class Splash;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashPattern.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashPattern.cc
new file mode 100644
index 00000000000..0c5b5093fd5
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashPattern.cc
@@ -0,0 +1,44 @@
+//========================================================================
+//
+// SplashPattern.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmempp.h"
+#include "SplashMath.h"
+#include "SplashScreen.h"
+#include "SplashPattern.h"
+
+//------------------------------------------------------------------------
+// SplashPattern
+//------------------------------------------------------------------------
+
+SplashPattern::SplashPattern() {
+}
+
+SplashPattern::~SplashPattern() {
+}
+
+//------------------------------------------------------------------------
+// SplashSolidColor
+//------------------------------------------------------------------------
+
+SplashSolidColor::SplashSolidColor(SplashColorPtr colorA) {
+ splashColorCopy(color, colorA);
+}
+
+SplashSolidColor::~SplashSolidColor() {
+}
+
+void SplashSolidColor::getColor(int x, int y, SplashColorPtr c) {
+ splashColorCopy(c, color);
+}
+
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashPattern.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashPattern.h
new file mode 100644
index 00000000000..fcbe21d31cb
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashPattern.h
@@ -0,0 +1,69 @@
+//========================================================================
+//
+// SplashPattern.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHPATTERN_H
+#define SPLASHPATTERN_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+class SplashScreen;
+
+//------------------------------------------------------------------------
+// SplashPattern
+//------------------------------------------------------------------------
+
+class SplashPattern {
+public:
+
+ SplashPattern();
+
+ virtual SplashPattern *copy() = 0;
+
+ virtual ~SplashPattern();
+
+ // Return the color value for a specific pixel.
+ virtual void getColor(int x, int y, SplashColorPtr c) = 0;
+
+
+ // Returns true if this pattern object will return the same color
+ // value for all pixels.
+ virtual GBool isStatic() = 0;
+
+private:
+};
+
+//------------------------------------------------------------------------
+// SplashSolidColor
+//------------------------------------------------------------------------
+
+class SplashSolidColor: public SplashPattern {
+public:
+
+ SplashSolidColor(SplashColorPtr colorA);
+
+ virtual SplashPattern *copy() { return new SplashSolidColor(color); }
+
+ virtual ~SplashSolidColor();
+
+ virtual void getColor(int x, int y, SplashColorPtr c);
+
+ virtual GBool isStatic() { return gTrue; }
+
+private:
+
+ SplashColor color;
+};
+
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashScreen.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashScreen.cc
new file mode 100644
index 00000000000..9993edc2a38
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashScreen.cc
@@ -0,0 +1,383 @@
+//========================================================================
+//
+// SplashScreen.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#if HAVE_STD_SORT
+#include <algorithm>
+#endif
+#include "gmem.h"
+#include "gmempp.h"
+#include "SplashMath.h"
+#include "SplashScreen.h"
+
+//------------------------------------------------------------------------
+
+static SplashScreenParams defaultParams = {
+ splashScreenDispersed, // type
+ 2, // size
+ 2, // dotRadius
+ 1.0, // gamma
+ 0.0, // blackThreshold
+ 1.0 // whiteThreshold
+};
+
+//------------------------------------------------------------------------
+
+struct SplashScreenPoint {
+ int x, y;
+ int dist;
+};
+
+#if HAVE_STD_SORT
+
+struct cmpDistancesFunctor {
+ bool operator()(const SplashScreenPoint &p0, const SplashScreenPoint &p1) {
+ return p0.dist < p1.dist;
+ }
+};
+
+#else // HAVE_STD_SORT
+
+static int cmpDistances(const void *p0, const void *p1) {
+ return ((SplashScreenPoint *)p0)->dist - ((SplashScreenPoint *)p1)->dist;
+}
+
+#endif
+
+//------------------------------------------------------------------------
+// SplashScreen
+//------------------------------------------------------------------------
+
+// If <clustered> is true, this generates a 45 degree screen using a
+// circular dot spot function. DPI = resolution / ((size / 2) *
+// sqrt(2)). If <clustered> is false, this generates an optimal
+// threshold matrix using recursive tesselation. Gamma correction
+// (gamma = 1 / 1.33) is also computed here.
+SplashScreen::SplashScreen(SplashScreenParams *params) {
+ Guchar u;
+ int black, white, i;
+
+ if (!params) {
+ params = &defaultParams;
+ }
+
+ // size must be a power of 2, and at least 2
+ for (size = 2, log2Size = 1; size < params->size; size <<= 1, ++log2Size) ;
+
+ switch (params->type) {
+
+ case splashScreenDispersed:
+ mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
+ buildDispersedMatrix(size/2, size/2, 1, size/2, 1);
+ break;
+
+ case splashScreenClustered:
+ mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
+ buildClusteredMatrix();
+ break;
+
+ case splashScreenStochasticClustered:
+ // size must be at least 2*r
+ while (size < (params->dotRadius << 1)) {
+ size <<= 1;
+ ++log2Size;
+ }
+ mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
+ buildSCDMatrix(params->dotRadius);
+ break;
+ }
+
+ sizeM1 = size - 1;
+
+ // do gamma correction and compute minVal/maxVal
+ minVal = 255;
+ maxVal = 0;
+ black = splashRound((SplashCoord)255.0 * params->blackThreshold);
+ if (black < 1) {
+ black = 1;
+ }
+ white = splashRound((SplashCoord)255.0 * params->whiteThreshold);
+ if (white > 255) {
+ white = 255;
+ }
+ for (i = 0; i < size * size; ++i) {
+ u = (Guchar)splashRound((SplashCoord)255.0 *
+ splashPow((SplashCoord)mat[i] / 255.0,
+ params->gamma));
+ if (u < black) {
+ u = (Guchar)black;
+ } else if (u >= white) {
+ u = (Guchar)white;
+ }
+ mat[i] = u;
+ if (u < minVal) {
+ minVal = u;
+ } else if (u > maxVal) {
+ maxVal = u;
+ }
+ }
+}
+
+void SplashScreen::buildDispersedMatrix(int i, int j, int val,
+ int delta, int offset) {
+ if (delta == 0) {
+ // map values in [1, size^2] --> [1, 255]
+ mat[(i << log2Size) + j] =
+ (Guchar)(1 + (254 * (val - 1)) / (size * size - 1));
+ } else {
+ buildDispersedMatrix(i, j,
+ val, delta / 2, 4*offset);
+ buildDispersedMatrix((i + delta) % size, (j + delta) % size,
+ val + offset, delta / 2, 4*offset);
+ buildDispersedMatrix((i + delta) % size, j,
+ val + 2*offset, delta / 2, 4*offset);
+ buildDispersedMatrix((i + 2*delta) % size, (j + delta) % size,
+ val + 3*offset, delta / 2, 4*offset);
+ }
+}
+
+void SplashScreen::buildClusteredMatrix() {
+ SplashCoord *dist;
+ SplashCoord u, v, d;
+ Guchar val;
+ int size2, x, y, x1, y1, i;
+
+ size2 = size >> 1;
+
+ // initialize the threshold matrix
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) {
+ mat[(y << log2Size) + x] = 0;
+ }
+ }
+
+ // build the distance matrix
+ dist = (SplashCoord *)gmallocn(size * size2, sizeof(SplashCoord));
+ for (y = 0; y < size2; ++y) {
+ for (x = 0; x < size2; ++x) {
+ if (x + y < size2 - 1) {
+ u = (SplashCoord)x + 0.5 - 0;
+ v = (SplashCoord)y + 0.5 - 0;
+ } else {
+ u = (SplashCoord)x + 0.5 - (SplashCoord)size2;
+ v = (SplashCoord)y + 0.5 - (SplashCoord)size2;
+ }
+ dist[y * size2 + x] = u*u + v*v;
+ }
+ }
+ for (y = 0; y < size2; ++y) {
+ for (x = 0; x < size2; ++x) {
+ if (x < y) {
+ u = (SplashCoord)x + 0.5 - 0;
+ v = (SplashCoord)y + 0.5 - (SplashCoord)size2;
+ } else {
+ u = (SplashCoord)x + 0.5 - (SplashCoord)size2;
+ v = (SplashCoord)y + 0.5 - 0;
+ }
+ dist[(size2 + y) * size2 + x] = u*u + v*v;
+ }
+ }
+
+ // build the threshold matrix
+ x1 = y1 = 0; // make gcc happy
+ for (i = 0; i < size * size2; ++i) {
+ d = -1;
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size2; ++x) {
+ if (mat[(y << log2Size) + x] == 0 &&
+ dist[y * size2 + x] > d) {
+ x1 = x;
+ y1 = y;
+ d = dist[y1 * size2 + x1];
+ }
+ }
+ }
+ // map values in [0, 2*size*size2-1] --> [1, 255]
+ val = (Guchar)(1 + (254 * (2*i)) / (2*size*size2 - 1));
+ mat[(y1 << log2Size) + x1] = val;
+ val = (Guchar)(1 + (254 * (2*i+1)) / (2*size*size2 - 1));
+ if (y1 < size2) {
+ mat[((y1 + size2) << log2Size) + x1 + size2] = val;
+ } else {
+ mat[((y1 - size2) << log2Size) + x1 + size2] = val;
+ }
+ }
+
+ gfree(dist);
+}
+
+// Compute the distance between two points on a toroid.
+int SplashScreen::distance(int x0, int y0, int x1, int y1) {
+ int dx0, dx1, dx, dy0, dy1, dy;
+
+ dx0 = abs(x0 - x1);
+ dx1 = size - dx0;
+ dx = dx0 < dx1 ? dx0 : dx1;
+ dy0 = abs(y0 - y1);
+ dy1 = size - dy0;
+ dy = dy0 < dy1 ? dy0 : dy1;
+ return dx * dx + dy * dy;
+}
+
+// Algorithm taken from:
+// Victor Ostromoukhov and Roger D. Hersch, "Stochastic Clustered-Dot
+// Dithering" in Color Imaging: Device-Independent Color, Color
+// Hardcopy, and Graphic Arts IV, SPIE Vol. 3648, pp. 496-505, 1999.
+void SplashScreen::buildSCDMatrix(int r) {
+ SplashScreenPoint *dots, *pts;
+ int dotsLen, dotsSize;
+ char *tmpl;
+ char *grid;
+ int *region, *dist;
+ int x, y, xx, yy, x0, x1, y0, y1, i, j, d, iMin, dMin, n;
+
+ //~ this should probably happen somewhere else
+ srand(123);
+
+ // generate the random space-filling curve
+ pts = (SplashScreenPoint *)gmallocn(size * size, sizeof(SplashScreenPoint));
+ i = 0;
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) {
+ pts[i].x = x;
+ pts[i].y = y;
+ ++i;
+ }
+ }
+ for (i = 0; i < size * size; ++i) {
+ j = i + (int)((double)(size * size - i) *
+ (double)rand() / ((double)RAND_MAX + 1.0));
+ x = pts[i].x;
+ y = pts[i].y;
+ pts[i].x = pts[j].x;
+ pts[i].y = pts[j].y;
+ pts[j].x = x;
+ pts[j].y = y;
+ }
+
+ // construct the circle template
+ tmpl = (char *)gmallocn((r+1)*(r+1), sizeof(char));
+ for (y = 0; y <= r; ++y) {
+ for (x = 0; x <= r; ++x) {
+ tmpl[y*(r+1) + x] = (x * y <= r * r) ? 1 : 0;
+ }
+ }
+
+ // mark all grid cells as free
+ grid = (char *)gmallocn(size * size, sizeof(char));
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) {
+ grid[(y << log2Size) + x] = 0;
+ }
+ }
+
+ // walk the space-filling curve, adding dots
+ dotsLen = 0;
+ dotsSize = 32;
+ dots = (SplashScreenPoint *)gmallocn(dotsSize, sizeof(SplashScreenPoint));
+ for (i = 0; i < size * size; ++i) {
+ x = pts[i].x;
+ y = pts[i].y;
+ if (!grid[(y << log2Size) + x]) {
+ if (dotsLen == dotsSize) {
+ dotsSize *= 2;
+ dots = (SplashScreenPoint *)greallocn(dots, dotsSize,
+ sizeof(SplashScreenPoint));
+ }
+ dots[dotsLen++] = pts[i];
+ for (yy = 0; yy <= r; ++yy) {
+ y0 = (y + yy) % size;
+ y1 = (y - yy + size) % size;
+ for (xx = 0; xx <= r; ++xx) {
+ if (tmpl[yy*(r+1) + xx]) {
+ x0 = (x + xx) % size;
+ x1 = (x - xx + size) % size;
+ grid[(y0 << log2Size) + x0] = 1;
+ grid[(y0 << log2Size) + x1] = 1;
+ grid[(y1 << log2Size) + x0] = 1;
+ grid[(y1 << log2Size) + x1] = 1;
+ }
+ }
+ }
+ }
+ }
+
+ gfree(tmpl);
+ gfree(grid);
+
+ // assign each cell to a dot, compute distance to center of dot
+ region = (int *)gmallocn(size * size, sizeof(int));
+ dist = (int *)gmallocn(size * size, sizeof(int));
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) {
+ iMin = 0;
+ dMin = distance(dots[0].x, dots[0].y, x, y);
+ for (i = 1; i < dotsLen; ++i) {
+ d = distance(dots[i].x, dots[i].y, x, y);
+ if (d < dMin) {
+ iMin = i;
+ dMin = d;
+ }
+ }
+ region[(y << log2Size) + x] = iMin;
+ dist[(y << log2Size) + x] = dMin;
+ }
+ }
+
+ // compute threshold values
+ for (i = 0; i < dotsLen; ++i) {
+ n = 0;
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) {
+ if (region[(y << log2Size) + x] == i) {
+ pts[n].x = x;
+ pts[n].y = y;
+ pts[n].dist = distance(dots[i].x, dots[i].y, x, y);
+ ++n;
+ }
+ }
+ }
+#if HAVE_STD_SORT
+ std::sort(pts, pts + n, cmpDistancesFunctor());
+#else
+ qsort(pts, n, sizeof(SplashScreenPoint), &cmpDistances);
+#endif
+ for (j = 0; j < n; ++j) {
+ // map values in [0 .. n-1] --> [255 .. 1]
+ mat[(pts[j].y << log2Size) + pts[j].x] =
+ (Guchar)(255 - (254 * j) / (n - 1));
+ }
+ }
+
+ gfree(pts);
+ gfree(region);
+ gfree(dist);
+
+ gfree(dots);
+}
+
+SplashScreen::SplashScreen(SplashScreen *screen) {
+ size = screen->size;
+ sizeM1 = screen->sizeM1;
+ log2Size = screen->log2Size;
+ mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
+ memcpy(mat, screen->mat, size * size * sizeof(Guchar));
+ minVal = screen->minVal;
+ maxVal = screen->maxVal;
+}
+
+SplashScreen::~SplashScreen() {
+ gfree(mat);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashScreen.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashScreen.h
new file mode 100644
index 00000000000..560e2759d82
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashScreen.h
@@ -0,0 +1,81 @@
+//========================================================================
+//
+// SplashScreen.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHSCREEN_H
+#define SPLASHSCREEN_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+//------------------------------------------------------------------------
+// SplashScreen
+//------------------------------------------------------------------------
+
+typedef Guchar *SplashScreenCursor;
+
+class SplashScreen {
+public:
+
+ SplashScreen(SplashScreenParams *params);
+ SplashScreen(SplashScreen *screen);
+ ~SplashScreen();
+
+ SplashScreen *copy() { return new SplashScreen(this); }
+
+ // Return the computed pixel value (0=black, 1=white) for the gray
+ // level <value> at (<x>, <y>).
+ int test(int x, int y, Guchar value) {
+ int xx, yy;
+ xx = x & sizeM1;
+ yy = y & sizeM1;
+ return value < mat[(yy << log2Size) + xx] ? 0 : 1;
+ }
+
+ // To do a series of tests with the same y value, call
+ // getTestCursor(y), and then call testWithCursor(cursor, x, value)
+ // for each x.
+ SplashScreenCursor getTestCursor(int y) {
+ int yy;
+ yy = y & sizeM1;
+ return &mat[yy << log2Size];
+ }
+
+ int testWithCursor(SplashScreenCursor cursor, int x, Guchar value) {
+ int xx = x & sizeM1;
+ return value >= cursor[xx];
+ }
+
+ // Returns true if value is above the white threshold or below the
+ // black threshold, i.e., if the corresponding halftone will be
+ // solid white or black.
+ GBool isStatic(Guchar value) { return value < minVal || value >= maxVal; }
+
+private:
+
+ void buildDispersedMatrix(int i, int j, int val,
+ int delta, int offset);
+ void buildClusteredMatrix();
+ int distance(int x0, int y0, int x1, int y1);
+ void buildSCDMatrix(int r);
+
+ Guchar *mat; // threshold matrix
+ int size; // size of the threshold matrix
+ int sizeM1; // size - 1
+ int log2Size; // log2(size)
+ Guchar minVal; // any pixel value below minVal generates
+ // solid black
+ Guchar maxVal; // any pixel value above maxVal generates
+ // solid white
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashState.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashState.cc
new file mode 100644
index 00000000000..74421c48a4a
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashState.cc
@@ -0,0 +1,295 @@
+//========================================================================
+//
+// SplashState.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "SplashPattern.h"
+#include "SplashScreen.h"
+#include "SplashClip.h"
+#include "SplashBitmap.h"
+#include "SplashState.h"
+
+//------------------------------------------------------------------------
+// SplashState
+//------------------------------------------------------------------------
+
+// number of components in each color mode
+int splashColorModeNComps[] = {
+ 1, 1, 3, 3
+#if SPLASH_CMYK
+ , 4
+#endif
+};
+
+SplashState::SplashState(int width, int height, GBool vectorAntialias,
+ SplashScreenParams *screenParams) {
+ SplashColor color;
+ int i;
+
+ matrix[0] = 1; matrix[1] = 0;
+ matrix[2] = 0; matrix[3] = 1;
+ matrix[4] = 0; matrix[5] = 0;
+ memset(&color, 0, sizeof(SplashColor));
+ strokePattern = new SplashSolidColor(color);
+ fillPattern = new SplashSolidColor(color);
+ screen = new SplashScreen(screenParams);
+ blendFunc = NULL;
+ strokeAlpha = 1;
+ fillAlpha = 1;
+ lineWidth = 1;
+ lineCap = splashLineCapButt;
+ lineJoin = splashLineJoinMiter;
+ miterLimit = 10;
+ flatness = 1;
+ lineDash = NULL;
+ lineDashLength = 0;
+ lineDashPhase = 0;
+ strokeAdjust = splashStrokeAdjustOff;
+ clip = new SplashClip(0, 0, width, height);
+ clipIsShared = gFalse;
+ softMask = NULL;
+ deleteSoftMask = gFalse;
+ inNonIsolatedGroup = gFalse;
+ inKnockoutGroup = gFalse;
+ for (i = 0; i < 256; ++i) {
+ rgbTransferR[i] = (Guchar)i;
+ rgbTransferG[i] = (Guchar)i;
+ rgbTransferB[i] = (Guchar)i;
+ grayTransfer[i] = (Guchar)i;
+#if SPLASH_CMYK
+ cmykTransferC[i] = (Guchar)i;
+ cmykTransferM[i] = (Guchar)i;
+ cmykTransferY[i] = (Guchar)i;
+ cmykTransferK[i] = (Guchar)i;
+#endif
+ }
+ overprintMask = 0xffffffff;
+ enablePathSimplification = gFalse;
+ next = NULL;
+}
+
+SplashState::SplashState(int width, int height, GBool vectorAntialias,
+ SplashScreen *screenA) {
+ SplashColor color;
+ int i;
+
+ matrix[0] = 1; matrix[1] = 0;
+ matrix[2] = 0; matrix[3] = 1;
+ matrix[4] = 0; matrix[5] = 0;
+ memset(&color, 0, sizeof(SplashColor));
+ strokePattern = new SplashSolidColor(color);
+ fillPattern = new SplashSolidColor(color);
+ screen = screenA->copy();
+ blendFunc = NULL;
+ strokeAlpha = 1;
+ fillAlpha = 1;
+ lineWidth = 1;
+ lineCap = splashLineCapButt;
+ lineJoin = splashLineJoinMiter;
+ miterLimit = 10;
+ flatness = 1;
+ lineDash = NULL;
+ lineDashLength = 0;
+ lineDashPhase = 0;
+ strokeAdjust = splashStrokeAdjustOff;
+ clip = new SplashClip(0, 0, width, height);
+ clipIsShared = gFalse;
+ softMask = NULL;
+ deleteSoftMask = gFalse;
+ inNonIsolatedGroup = gFalse;
+ inKnockoutGroup = gFalse;
+ for (i = 0; i < 256; ++i) {
+ rgbTransferR[i] = (Guchar)i;
+ rgbTransferG[i] = (Guchar)i;
+ rgbTransferB[i] = (Guchar)i;
+ grayTransfer[i] = (Guchar)i;
+#if SPLASH_CMYK
+ cmykTransferC[i] = (Guchar)i;
+ cmykTransferM[i] = (Guchar)i;
+ cmykTransferY[i] = (Guchar)i;
+ cmykTransferK[i] = (Guchar)i;
+#endif
+ }
+ overprintMask = 0xffffffff;
+ enablePathSimplification = gFalse;
+ next = NULL;
+}
+
+SplashState::SplashState(SplashState *state) {
+ memcpy(matrix, state->matrix, 6 * sizeof(SplashCoord));
+ strokePattern = state->strokePattern->copy();
+ fillPattern = state->fillPattern->copy();
+ screen = state->screen->copy();
+ blendFunc = state->blendFunc;
+ strokeAlpha = state->strokeAlpha;
+ fillAlpha = state->fillAlpha;
+ lineWidth = state->lineWidth;
+ lineCap = state->lineCap;
+ lineJoin = state->lineJoin;
+ miterLimit = state->miterLimit;
+ flatness = state->flatness;
+ if (state->lineDash) {
+ lineDashLength = state->lineDashLength;
+ lineDash = (SplashCoord *)gmallocn(lineDashLength, sizeof(SplashCoord));
+ memcpy(lineDash, state->lineDash, lineDashLength * sizeof(SplashCoord));
+ } else {
+ lineDash = NULL;
+ lineDashLength = 0;
+ }
+ lineDashPhase = state->lineDashPhase;
+ strokeAdjust = state->strokeAdjust;
+ clip = state->clip;
+ clipIsShared = gTrue;
+ softMask = state->softMask;
+ deleteSoftMask = gFalse;
+ inNonIsolatedGroup = state->inNonIsolatedGroup;
+ inKnockoutGroup = state->inKnockoutGroup;
+ memcpy(rgbTransferR, state->rgbTransferR, 256);
+ memcpy(rgbTransferG, state->rgbTransferG, 256);
+ memcpy(rgbTransferB, state->rgbTransferB, 256);
+ memcpy(grayTransfer, state->grayTransfer, 256);
+#if SPLASH_CMYK
+ memcpy(cmykTransferC, state->cmykTransferC, 256);
+ memcpy(cmykTransferM, state->cmykTransferM, 256);
+ memcpy(cmykTransferY, state->cmykTransferY, 256);
+ memcpy(cmykTransferK, state->cmykTransferK, 256);
+#endif
+ overprintMask = state->overprintMask;
+ enablePathSimplification = state->enablePathSimplification;
+ next = NULL;
+}
+
+SplashState::~SplashState() {
+ delete strokePattern;
+ delete fillPattern;
+ delete screen;
+ gfree(lineDash);
+ if (!clipIsShared) {
+ delete clip;
+ }
+ if (deleteSoftMask && softMask) {
+ delete softMask;
+ }
+}
+
+void SplashState::setStrokePattern(SplashPattern *strokePatternA) {
+ delete strokePattern;
+ strokePattern = strokePatternA;
+}
+
+void SplashState::setFillPattern(SplashPattern *fillPatternA) {
+ delete fillPattern;
+ fillPattern = fillPatternA;
+}
+
+void SplashState::setScreen(SplashScreen *screenA) {
+ delete screen;
+ screen = screenA;
+}
+
+void SplashState::setLineDash(SplashCoord *lineDashA, int lineDashLengthA,
+ SplashCoord lineDashPhaseA) {
+ gfree(lineDash);
+ lineDashLength = lineDashLengthA;
+ if (lineDashLength > 0) {
+ lineDash = (SplashCoord *)gmallocn(lineDashLength, sizeof(SplashCoord));
+ memcpy(lineDash, lineDashA, lineDashLength * sizeof(SplashCoord));
+ } else {
+ lineDash = NULL;
+ }
+ lineDashPhase = lineDashPhaseA;
+}
+
+GBool SplashState::lineDashContainsZeroLengthDashes() {
+ int i;
+
+ if (lineDashLength == 0) {
+ return gFalse;
+ }
+
+ // if the line dash array has an odd number of elements, we need to
+ // check all of the elements; if the length is even, we only need to
+ // check even-number elements
+ if (lineDashLength & 1) {
+ for (i = 0; i < lineDashLength; ++i) {
+ if (lineDash[i] == 0) {
+ return gTrue;
+ }
+ }
+ } else {
+ for (i = 0; i < lineDashLength; i += 2) {
+ if (lineDash[i] == 0) {
+ return gTrue;
+ }
+ }
+ }
+ return gFalse;
+}
+
+void SplashState::clipResetToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ if (clipIsShared) {
+ clip = clip->copy();
+ clipIsShared = gFalse;
+ }
+ clip->resetToRect(x0, y0, x1, y1);
+}
+
+SplashError SplashState::clipToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ if (clipIsShared) {
+ clip = clip->copy();
+ clipIsShared = gFalse;
+ }
+ return clip->clipToRect(x0, y0, x1, y1);
+}
+
+SplashError SplashState::clipToPath(SplashPath *path, GBool eo) {
+ if (clipIsShared) {
+ clip = clip->copy();
+ clipIsShared = gFalse;
+ }
+ return clip->clipToPath(path, matrix, flatness, eo,
+ enablePathSimplification, strokeAdjust);
+}
+
+void SplashState::setSoftMask(SplashBitmap *softMaskA) {
+ if (deleteSoftMask) {
+ delete softMask;
+ }
+ softMask = softMaskA;
+ deleteSoftMask = gTrue;
+}
+
+void SplashState::setTransfer(Guchar *red, Guchar *green, Guchar *blue,
+ Guchar *gray) {
+#if SPLASH_CMYK
+ int i;
+#endif
+
+ memcpy(rgbTransferR, red, 256);
+ memcpy(rgbTransferG, green, 256);
+ memcpy(rgbTransferB, blue, 256);
+ memcpy(grayTransfer, gray, 256);
+#if SPLASH_CMYK
+ for (i = 0; i < 256; ++i) {
+ cmykTransferC[i] = (Guchar)(255 - rgbTransferR[255 - i]);
+ cmykTransferM[i] = (Guchar)(255 - rgbTransferG[255 - i]);
+ cmykTransferY[i] = (Guchar)(255 - rgbTransferB[255 - i]);
+ cmykTransferK[i] = (Guchar)(255 - grayTransfer[255 - i]);
+ }
+#endif
+}
+
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashState.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashState.h
new file mode 100644
index 00000000000..38cb1188e94
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashState.h
@@ -0,0 +1,135 @@
+//========================================================================
+//
+// SplashState.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHSTATE_H
+#define SPLASHSTATE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+class SplashPattern;
+class SplashScreen;
+class SplashClip;
+class SplashBitmap;
+class SplashPath;
+
+//------------------------------------------------------------------------
+// line cap values
+//------------------------------------------------------------------------
+
+#define splashLineCapButt 0
+#define splashLineCapRound 1
+#define splashLineCapProjecting 2
+
+//------------------------------------------------------------------------
+// line join values
+//------------------------------------------------------------------------
+
+#define splashLineJoinMiter 0
+#define splashLineJoinRound 1
+#define splashLineJoinBevel 2
+
+//------------------------------------------------------------------------
+// SplashState
+//------------------------------------------------------------------------
+
+class SplashState {
+public:
+
+ // Create a new state object, initialized with default settings.
+ SplashState(int width, int height, GBool vectorAntialias,
+ SplashScreenParams *screenParams);
+ SplashState(int width, int height, GBool vectorAntialias,
+ SplashScreen *screenA);
+
+ // Copy a state object.
+ SplashState *copy() { return new SplashState(this); }
+
+ ~SplashState();
+
+ // Set the stroke pattern. This does not copy <strokePatternA>.
+ void setStrokePattern(SplashPattern *strokePatternA);
+
+ // Set the fill pattern. This does not copy <fillPatternA>.
+ void setFillPattern(SplashPattern *fillPatternA);
+
+ // Set the screen. This does not copy <screenA>.
+ void setScreen(SplashScreen *screenA);
+
+ // Set the line dash pattern. This copies the <lineDashA> array.
+ void setLineDash(SplashCoord *lineDashA, int lineDashLengthA,
+ SplashCoord lineDashPhaseA);
+
+ // Returns true if the current line dash pattern contains one or
+ // more zero-length "on" sections (dashes).
+ GBool lineDashContainsZeroLengthDashes();
+
+ void clipResetToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1);
+ SplashError clipToRect(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1);
+ SplashError clipToPath(SplashPath *path, GBool eo);
+
+ // Set the soft mask bitmap.
+ void setSoftMask(SplashBitmap *softMaskA);
+
+ // Set the transfer function.
+ void setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray);
+
+
+private:
+
+ SplashState(SplashState *state);
+
+ SplashCoord matrix[6];
+ SplashPattern *strokePattern;
+ SplashPattern *fillPattern;
+ SplashScreen *screen;
+ SplashBlendFunc blendFunc;
+ SplashCoord strokeAlpha;
+ SplashCoord fillAlpha;
+ SplashCoord lineWidth;
+ int lineCap;
+ int lineJoin;
+ SplashCoord miterLimit;
+ SplashCoord flatness;
+ SplashCoord *lineDash;
+ int lineDashLength;
+ SplashCoord lineDashPhase;
+ SplashStrokeAdjustMode strokeAdjust;
+ SplashClip *clip;
+ GBool clipIsShared;
+ SplashBitmap *softMask;
+ GBool deleteSoftMask;
+ GBool inNonIsolatedGroup;
+ GBool inKnockoutGroup;
+ Guchar rgbTransferR[256],
+ rgbTransferG[256],
+ rgbTransferB[256];
+ Guchar grayTransfer[256];
+#if SPLASH_CMYK
+ Guchar cmykTransferC[256],
+ cmykTransferM[256],
+ cmykTransferY[256],
+ cmykTransferK[256];
+#endif
+ Guint overprintMask;
+ GBool enablePathSimplification;
+
+
+ SplashState *next; // used by Splash class
+
+ friend class Splash;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashTypes.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashTypes.h
new file mode 100644
index 00000000000..5860884a30b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashTypes.h
@@ -0,0 +1,145 @@
+//========================================================================
+//
+// SplashTypes.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHTYPES_H
+#define SPLASHTYPES_H
+
+#include <aconf.h>
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+// coordinates
+//------------------------------------------------------------------------
+
+#if USE_FIXEDPOINT
+#include "FixedPoint.h"
+typedef FixedPoint SplashCoord;
+#else
+typedef double SplashCoord;
+#endif
+
+//------------------------------------------------------------------------
+// antialiasing
+//------------------------------------------------------------------------
+
+#define splashAASize 4
+
+//------------------------------------------------------------------------
+// colors
+//------------------------------------------------------------------------
+
+enum SplashColorMode {
+ splashModeMono1, // 1 bit per component, 8 pixels per byte,
+ // MSbit is on the left
+ splashModeMono8, // 1 byte per component, 1 byte per pixel
+ splashModeRGB8, // 1 byte per component, 3 bytes per pixel:
+ // RGBRGB...
+ splashModeBGR8 // 1 byte per component, 3 bytes per pixel:
+ // BGRBGR...
+
+#if SPLASH_CMYK
+ ,
+ splashModeCMYK8 // 1 byte per component, 4 bytes per pixel:
+ // CMYKCMYK...
+#endif
+};
+
+// number of components in each color mode
+// (defined in SplashState.cc)
+extern int splashColorModeNComps[];
+
+// max number of components in any SplashColor
+#define splashMaxColorComps 3
+#if SPLASH_CMYK
+# undef splashMaxColorComps
+# define splashMaxColorComps 4
+#endif
+
+typedef Guchar SplashColor[splashMaxColorComps];
+typedef Guchar *SplashColorPtr;
+
+// RGB8
+static inline Guchar splashRGB8R(SplashColorPtr rgb8) { return rgb8[0]; }
+static inline Guchar splashRGB8G(SplashColorPtr rgb8) { return rgb8[1]; }
+static inline Guchar splashRGB8B(SplashColorPtr rgb8) { return rgb8[2]; }
+
+// BGR8
+static inline Guchar splashBGR8R(SplashColorPtr bgr8) { return bgr8[2]; }
+static inline Guchar splashBGR8G(SplashColorPtr bgr8) { return bgr8[1]; }
+static inline Guchar splashBGR8B(SplashColorPtr bgr8) { return bgr8[0]; }
+
+#if SPLASH_CMYK
+// CMYK8
+static inline Guchar splashCMYK8C(SplashColorPtr cmyk8) { return cmyk8[0]; }
+static inline Guchar splashCMYK8M(SplashColorPtr cmyk8) { return cmyk8[1]; }
+static inline Guchar splashCMYK8Y(SplashColorPtr cmyk8) { return cmyk8[2]; }
+static inline Guchar splashCMYK8K(SplashColorPtr cmyk8) { return cmyk8[3]; }
+#endif
+
+static inline void splashColorCopy(SplashColorPtr dest, SplashColorPtr src) {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+#if SPLASH_CMYK
+ dest[3] = src[3];
+#endif
+}
+
+static inline void splashColorXor(SplashColorPtr dest, SplashColorPtr src) {
+ dest[0] ^= src[0];
+ dest[1] ^= src[1];
+ dest[2] ^= src[2];
+#if SPLASH_CMYK
+ dest[3] ^= src[3];
+#endif
+}
+
+//------------------------------------------------------------------------
+// blend functions
+//------------------------------------------------------------------------
+
+typedef void (*SplashBlendFunc)(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend, SplashColorMode cm);
+
+//------------------------------------------------------------------------
+// stroke adjustment mode
+//------------------------------------------------------------------------
+
+enum SplashStrokeAdjustMode {
+ splashStrokeAdjustOff,
+ splashStrokeAdjustNormal,
+ splashStrokeAdjustCAD
+};
+
+//------------------------------------------------------------------------
+// screen parameters
+//------------------------------------------------------------------------
+
+enum SplashScreenType {
+ splashScreenDispersed,
+ splashScreenClustered,
+ splashScreenStochasticClustered
+};
+
+struct SplashScreenParams {
+ SplashScreenType type;
+ int size;
+ int dotRadius;
+ SplashCoord gamma;
+ SplashCoord blackThreshold;
+ SplashCoord whiteThreshold;
+};
+
+//------------------------------------------------------------------------
+// error results
+//------------------------------------------------------------------------
+
+typedef int SplashError;
+
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc
new file mode 100644
index 00000000000..4c77765723e
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.cc
@@ -0,0 +1,645 @@
+//========================================================================
+//
+// SplashXPath.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#if HAVE_STD_SORT
+#include <algorithm>
+#endif
+#include "gmem.h"
+#include "gmempp.h"
+#include "SplashMath.h"
+#include "SplashPath.h"
+#include "SplashXPath.h"
+
+//------------------------------------------------------------------------
+
+#define minCosSquaredJoinAngle 0.75
+#define maxPointToLineDistanceSquared 0.04
+
+//------------------------------------------------------------------------
+
+struct SplashXPathPoint {
+ SplashCoord x, y;
+};
+
+struct SplashXPathAdjust {
+ int firstPt, lastPt; // range of points
+ GBool vert; // vertical or horizontal hint
+ SplashCoord x0a, x0b, // hint boundaries
+ xma, xmb,
+ x1a, x1b;
+ SplashCoord x0, x1, xm; // adjusted coordinates
+};
+
+//------------------------------------------------------------------------
+
+// Transform a point from user space to device space.
+inline void SplashXPath::transform(SplashCoord *matrix,
+ SplashCoord xi, SplashCoord yi,
+ SplashCoord *xo, SplashCoord *yo) {
+ // [ m[0] m[1] 0 ]
+ // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ]
+ // [ m[4] m[5] 1 ]
+ *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
+ *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
+}
+
+//------------------------------------------------------------------------
+// SplashXPath
+//------------------------------------------------------------------------
+
+// SplashXPath segment coords are clipped to +/-maxCoord to avoid
+// problems. The xMin/yMin/xMax/yMax fields are 32-bit integers, so
+// coords need to be < 2^31 / aa{Horiz,Vert}.
+#define maxCoord 100000000.0
+
+void SplashXPath::clampCoords(SplashCoord *x, SplashCoord *y) {
+#if !USE_FIXEDPOINT
+ if (*x > maxCoord) {
+ *x = maxCoord;
+ } else if (*x < -maxCoord) {
+ *x = -maxCoord;
+ }
+ if (*y > maxCoord) {
+ *y = maxCoord;
+ } else if (*y < -maxCoord) {
+ *y = -maxCoord;
+ }
+#endif
+}
+
+SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,
+ SplashCoord flatness, GBool closeSubpaths,
+ GBool simplify,
+ SplashStrokeAdjustMode strokeAdjMode) {
+ SplashXPathPoint *pts;
+ SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp, t;
+ int curSubpath, firstSegInSubpath, i;
+ GBool adjusted;
+
+ //--- transform the points
+ pts = (SplashXPathPoint *)gmallocn(path->length, sizeof(SplashXPathPoint));
+ for (i = 0; i < path->length; ++i) {
+ transform(matrix, path->pts[i].x, path->pts[i].y, &pts[i].x, &pts[i].y);
+ clampCoords(&pts[i].x, &pts[i].y);
+ }
+
+ //--- do stroke adjustment
+ if (path->hints) {
+ adjusted = strokeAdjust(pts, path->hints, path->hintsLength,
+ strokeAdjMode);
+ } else {
+ adjusted = gFalse;
+ }
+
+ //--- construct the segments
+
+ segs = NULL;
+ length = size = 0;
+
+ x0 = y0 = xsp = ysp = 0; // make gcc happy
+ curSubpath = 0;
+ firstSegInSubpath = 0;
+ i = 0;
+ while (i < path->length) {
+
+ // first point in subpath - skip it
+ if (path->flags[i] & splashPathFirst) {
+ x0 = pts[i].x;
+ y0 = pts[i].y;
+ xsp = x0;
+ ysp = y0;
+ curSubpath = i;
+ ++i;
+
+ } else {
+
+ // curve segment
+ if (path->flags[i] & splashPathCurve) {
+ x1 = pts[i].x;
+ y1 = pts[i].y;
+ x2 = pts[i+1].x;
+ y2 = pts[i+1].y;
+ x3 = pts[i+2].x;
+ y3 = pts[i+2].y;
+ addCurve(x0, y0, x1, y1, x2, y2, x3, y3,
+ flatness,
+ (path->flags[i-1] & splashPathFirst),
+ (path->flags[i+2] & splashPathLast),
+ !closeSubpaths &&
+ (path->flags[i-1] & splashPathFirst) &&
+ !(path->flags[i-1] & splashPathClosed),
+ !closeSubpaths &&
+ (path->flags[i+2] & splashPathLast) &&
+ !(path->flags[i+2] & splashPathClosed));
+ x0 = x3;
+ y0 = y3;
+ i += 3;
+
+ // line segment
+ } else {
+ x1 = pts[i].x;
+ y1 = pts[i].y;
+ addSegment(x0, y0, x1, y1);
+ x0 = x1;
+ y0 = y1;
+ ++i;
+ }
+
+ // end a subpath
+ if (path->flags[i-1] & splashPathLast) {
+ if (closeSubpaths &&
+ (pts[i-1].x != pts[curSubpath].x ||
+ pts[i-1].y != pts[curSubpath].y)) {
+ addSegment(x0, y0, xsp, ysp);
+ }
+ if (simplify && !adjusted) {
+ mergeSegments(firstSegInSubpath);
+ }
+ firstSegInSubpath = length;
+ }
+ }
+ }
+
+ gfree(pts);
+
+ finishSegments();
+
+ //--- check for a rectangle
+ isRect = gFalse;
+ rectX0 = rectY0 = rectX1 = rectY1 = 0;
+ if (length == 4) {
+#if HAVE_STD_SORT
+ std::sort(segs, segs + length, SplashXPathSeg::cmpY);
+#else
+ qsort(segs, length, sizeof(SplashXPathSeg), &SplashXPathSeg::cmpY);
+#endif
+ if (segs[0].y0 == segs[0].y1 &&
+ segs[1].x0 == segs[1].x1 &&
+ segs[2].x0 == segs[2].x1 &&
+ segs[3].y0 == segs[3].y1) {
+ isRect = gTrue;
+ rectX0 = segs[1].x0;
+ rectX1 = segs[2].x0;
+ rectY0 = segs[0].y0;
+ rectY1 = segs[3].y0;
+ } else if (segs[0].x0 == segs[0].x1 &&
+ segs[1].y0 == segs[1].y1 &&
+ segs[2].x0 == segs[2].x1 &&
+ segs[3].y0 == segs[3].y1) {
+ isRect = gTrue;
+ rectX0 = segs[0].x0;
+ rectX1 = segs[2].x0;
+ rectY0 = segs[1].y0;
+ rectY1 = segs[3].y0;
+ } else if (segs[0].x0 == segs[0].x1 &&
+ segs[1].x0 == segs[1].x1 &&
+ segs[2].y0 == segs[2].y1 &&
+ segs[3].y0 == segs[3].y1) {
+ isRect = gTrue;
+ rectX0 = segs[0].x0;
+ rectX1 = segs[1].x0;
+ rectY0 = segs[2].y0;
+ rectY1 = segs[3].y0;
+ }
+ if (isRect) {
+ if (rectX0 > rectX1) {
+ t = rectX0; rectX0 = rectX1; rectX1 = t;
+ }
+ if (rectY0 > rectY1) {
+ t = rectY0; rectY0 = rectY1; rectY1 = t;
+ }
+ }
+ }
+}
+
+GBool SplashXPath::strokeAdjust(SplashXPathPoint *pts,
+ SplashPathHint *hints, int nHints,
+ SplashStrokeAdjustMode strokeAdjMode) {
+ SplashXPathAdjust *adjusts, *adjust;
+ SplashPathHint *hint;
+ SplashCoord x0, y0, x1, y1, x2, y2, x3, y3;
+ SplashCoord adj0, adj1, w, d;
+ int xi0, xi1;
+ int i, j;
+ GBool adjusted;
+
+ adjusted = gFalse;
+
+ // set up the stroke adjustment hints
+ adjusts = (SplashXPathAdjust *)gmallocn(nHints, sizeof(SplashXPathAdjust));
+ for (i = 0; i < nHints; ++i) {
+ hint = &hints[i];
+ x0 = pts[hint->ctrl0 ].x; y0 = pts[hint->ctrl0 ].y;
+ x1 = pts[hint->ctrl0 + 1].x; y1 = pts[hint->ctrl0 + 1].y;
+ x2 = pts[hint->ctrl1 ].x; y2 = pts[hint->ctrl1 ].y;
+ x3 = pts[hint->ctrl1 + 1].x; y3 = pts[hint->ctrl1 + 1].y;
+ w = -1;
+ if (splashAbs(x0 - x1) < 0.01 && splashAbs(x2 - x3) < 0.01) {
+ adjusts[i].vert = gTrue;
+ adj0 = x0;
+ adj1 = x2;
+ if (hint->projectingCap) {
+ w = splashAbs(y1 - y0);
+ }
+ } else if (splashAbs(y0 - y1) < 0.01 && splashAbs(y2 - y3) < 0.01) {
+ adjusts[i].vert = gFalse;
+ adj0 = y0;
+ adj1 = y2;
+ if (hint->projectingCap) {
+ w = splashAbs(x1 - x0);
+ }
+ } else {
+ goto done;
+ }
+ if (adj0 > adj1) {
+ x0 = adj0;
+ adj0 = adj1;
+ adj1 = x0;
+ }
+ d = adj1 - adj0;
+ if (d > 0.04) {
+ d = 0.01;
+ } else {
+ d *= 0.25;
+ }
+ adjusts[i].x0a = adj0 - d;
+ adjusts[i].x0b = adj0 + d;
+ adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - d;
+ adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + d;
+ adjusts[i].x1a = adj1 - d;
+ adjusts[i].x1b = adj1 + d;
+ splashStrokeAdjust(adj0, adj1, &xi0, &xi1, strokeAdjMode, w);
+ adjusts[i].x0 = (SplashCoord)xi0;
+ // the "minus epsilon" thing here is needed when vector
+ // antialiasing is turned off -- otherwise stroke adjusted lines
+ // will touch an extra pixel on one edge
+ adjusts[i].x1 = (SplashCoord)xi1 - 0.001;
+ adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1);
+ adjusts[i].firstPt = hint->firstPt;
+ adjusts[i].lastPt = hint->lastPt;
+ }
+
+ // perform stroke adjustment
+ for (i = 0, adjust = adjusts; i < nHints; ++i, ++adjust) {
+ for (j = adjust->firstPt; j <= adjust->lastPt; ++j) {
+ if (adjust->vert) {
+ x0 = pts[j].x;
+ if (x0 > adjust->x0a && x0 < adjust->x0b) {
+ pts[j].x = adjust->x0;
+ } else if (x0 > adjust->xma && x0 < adjust->xmb) {
+ pts[j].x = adjust->xm;
+ } else if (x0 > adjust->x1a && x0 < adjust->x1b) {
+ pts[j].x = adjust->x1;
+ }
+ } else {
+ y0 = pts[j].y;
+ if (y0 > adjust->x0a && y0 < adjust->x0b) {
+ pts[j].y = adjust->x0;
+ } else if (y0 > adjust->xma && y0 < adjust->xmb) {
+ pts[j].y = adjust->xm;
+ } else if (y0 > adjust->x1a && y0 < adjust->x1b) {
+ pts[j].y = adjust->x1;
+ }
+ }
+ }
+ }
+ adjusted = gTrue;
+
+ done:
+ gfree(adjusts);
+ return adjusted;
+}
+
+SplashXPath::SplashXPath(SplashXPath *xPath) {
+ length = xPath->length;
+ size = xPath->size;
+ segs = (SplashXPathSeg *)gmallocn(size, sizeof(SplashXPathSeg));
+ memcpy(segs, xPath->segs, length * sizeof(SplashXPathSeg));
+ xMin = xPath->xMin;
+ yMin = xPath->yMin;
+ xMax = xPath->xMax;
+ yMax = xPath->yMax;
+}
+
+SplashXPath::~SplashXPath() {
+ gfree(segs);
+}
+
+// Add space for <nSegs> more segments
+void SplashXPath::grow(int nSegs) {
+ if (length + nSegs > size) {
+ if (size == 0) {
+ size = 32;
+ }
+ while (size < length + nSegs) {
+ size *= 2;
+ }
+ segs = (SplashXPathSeg *)greallocn(segs, size, sizeof(SplashXPathSeg));
+ }
+}
+
+void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1,
+ SplashCoord x2, SplashCoord y2,
+ SplashCoord x3, SplashCoord y3,
+ SplashCoord flatness,
+ GBool first, GBool last, GBool end0, GBool end1) {
+ SplashCoord cx[splashMaxCurveSplits + 1][3];
+ SplashCoord cy[splashMaxCurveSplits + 1][3];
+ int cNext[splashMaxCurveSplits + 1];
+ SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
+ SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
+ SplashCoord dx, dy, mx, my, d1, d2, flatness2;
+ int p1, p2, p3;
+
+#if USE_FIXEDPOINT
+ flatness2 = flatness;
+#else
+ flatness2 = flatness * flatness;
+#endif
+
+ // initial segment
+ p1 = 0;
+ p2 = splashMaxCurveSplits;
+ cx[p1][0] = x0; cy[p1][0] = y0;
+ cx[p1][1] = x1; cy[p1][1] = y1;
+ cx[p1][2] = x2; cy[p1][2] = y2;
+ cx[p2][0] = x3; cy[p2][0] = y3;
+ cNext[p1] = p2;
+
+ while (p1 < splashMaxCurveSplits) {
+
+ // get the next segment
+ xl0 = cx[p1][0]; yl0 = cy[p1][0];
+ xx1 = cx[p1][1]; yy1 = cy[p1][1];
+ xx2 = cx[p1][2]; yy2 = cy[p1][2];
+ p2 = cNext[p1];
+ xr3 = cx[p2][0]; yr3 = cy[p2][0];
+
+ // compute the distances from the control points to the
+ // midpoint of the straight line (this is a bit of a hack, but
+ // it's much faster than computing the actual distances to the
+ // line)
+ mx = (xl0 + xr3) * 0.5;
+ my = (yl0 + yr3) * 0.5;
+#if USE_FIXEDPOINT
+ d1 = splashDist(xx1, yy1, mx, my);
+ d2 = splashDist(xx2, yy2, mx, my);
+#else
+ dx = xx1 - mx;
+ dy = yy1 - my;
+ d1 = dx*dx + dy*dy;
+ dx = xx2 - mx;
+ dy = yy2 - my;
+ d2 = dx*dx + dy*dy;
+#endif
+
+ // if the curve is flat enough, or no more subdivisions are
+ // allowed, add the straight line segment
+ if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
+ addSegment(xl0, yl0, xr3, yr3);
+ p1 = p2;
+
+ // otherwise, subdivide the curve
+ } else {
+ xl1 = (xl0 + xx1) * 0.5;
+ yl1 = (yl0 + yy1) * 0.5;
+ xh = (xx1 + xx2) * 0.5;
+ yh = (yy1 + yy2) * 0.5;
+ xl2 = (xl1 + xh) * 0.5;
+ yl2 = (yl1 + yh) * 0.5;
+ xr2 = (xx2 + xr3) * 0.5;
+ yr2 = (yy2 + yr3) * 0.5;
+ xr1 = (xh + xr2) * 0.5;
+ yr1 = (yh + yr2) * 0.5;
+ xr0 = (xl2 + xr1) * 0.5;
+ yr0 = (yl2 + yr1) * 0.5;
+ // add the new subdivision points
+ p3 = (p1 + p2) / 2;
+ cx[p1][1] = xl1; cy[p1][1] = yl1;
+ cx[p1][2] = xl2; cy[p1][2] = yl2;
+ cNext[p1] = p3;
+ cx[p3][0] = xr0; cy[p3][0] = yr0;
+ cx[p3][1] = xr1; cy[p3][1] = yr1;
+ cx[p3][2] = xr2; cy[p3][2] = yr2;
+ cNext[p3] = p2;
+ }
+ }
+}
+
+void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1) {
+ grow(1);
+ segs[length].x0 = x0;
+ segs[length].y0 = y0;
+ segs[length].x1 = x1;
+ segs[length].y1 = y1;
+ ++length;
+}
+
+// Returns true if the angle between (x0,y0)-(x1,y1) and
+// (x1,y1)-(x2,y2) is close to 180 degrees.
+static GBool joinAngleIsFlat(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1,
+ SplashCoord x2, SplashCoord y2) {
+ SplashCoord dx1, dy1, dx2, dy2, d, len1, len2;
+
+ dx1 = x1 - x0;
+ dy1 = y1 - y0;
+ dx2 = x2 - x1;
+ dy2 = y2 - y1;
+ d = dx1 * dx2 + dy1 * dy2;
+ len1 = dx1 * dx1 + dy1 * dy1;
+ len2 = dx2 * dx2 + dy2 * dy2;
+ return d > 0 && d * d > len1 * len2 * minCosSquaredJoinAngle;
+}
+
+// Returns true if (x1,y1) is sufficiently close to the segment
+// (x0,y0)-(x2,y2), looking at the perpendicular point-to-line
+// distance.
+static GBool pointCloseToSegment(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1,
+ SplashCoord x2, SplashCoord y2) {
+ SplashCoord t1, t2, dx, dy;
+
+ // compute the perpendicular distance from the point to the segment,
+ // i.e., the projection of (x0,y0)-(x1,y1) onto a unit normal to the
+ // segment (this actually computes the square of the distance)
+ dx = x2 - x0;
+ dy = y2 - y0;
+ t1 = dx*dx + dy*dy;
+ if (t1 < 0.0001) {
+ // degenerate case: (x0,y0) and (x2,y2) are (nearly) identical --
+ // just compute the distance to (x1,y1)
+ dx = x0 - x1;
+ dy = y0 - y1;
+ t2 = dx*dx + dy*dy;
+ return t2 < maxPointToLineDistanceSquared;
+ }
+ t2 = x1 * dy - dx * y1 - x0 * y2 + x2 * y0;
+ // actual distance = t2 / sqrt(t1)
+ return t2 * t2 < t1 * maxPointToLineDistanceSquared;
+}
+
+// Attempt to simplify the path by merging sequences of consecutive
+// segments in [first] .. [length]-1.
+void SplashXPath::mergeSegments(int first) {
+ GBool horiz, vert;
+ int in, out, prev, i, j;
+
+ in = out = first;
+ while (in < length) {
+
+ // skip zero-length segments
+ if (segs[in].x0 == segs[in].x1 && segs[in].y0 == segs[in].y1) {
+ ++in;
+ continue;
+ }
+
+ horiz = segs[in].y0 == segs[in].y1;
+ vert = segs[in].x0 == segs[in].x1;
+
+ // check for a sequence of mergeable segments: in .. i
+ prev = in;
+ for (i = in + 1; i < length; ++i) {
+
+ // skip zero-length segments
+ if (segs[i].x0 == segs[i].x1 && segs[i].y0 == segs[i].y1) {
+ continue;
+ }
+
+ // check for a horizontal or vertical segment
+ if ((horiz && segs[in].y0 != segs[in].y1) ||
+ (vert && segs[in].x0 != segs[in].x1)) {
+ break;
+ }
+
+ // check the angle between segs i-1 and i
+ // (actually, we compare seg i to the previous non-zero-length
+ // segment, which may not be i-1)
+ if (!joinAngleIsFlat(segs[prev].x0, segs[prev].y0,
+ segs[i].x0, segs[i].y0,
+ segs[i].x1, segs[i].y1)) {
+ break;
+ }
+
+ // check the distances from the ends of segs in .. i-1 to the
+ // proposed new segment
+ for (j = in; j < i; ++j) {
+ if (!pointCloseToSegment(segs[in].x0, segs[in].y0,
+ segs[j].x1, segs[j].y1,
+ segs[i].x1, segs[i].y1)) {
+ break;
+ }
+ }
+ if (j < i) {
+ break;
+ }
+
+ prev = i;
+ }
+
+ // we can merge segs: in .. i-1
+ // (this may be the single segment: in)
+ segs[out].x0 = segs[in].x0;
+ segs[out].y0 = segs[in].y0;
+ segs[out].x1 = segs[i-1].x1;
+ segs[out].y1 = segs[i-1].y1;
+ in = i;
+ ++out;
+ }
+
+ length = out;
+}
+
+void SplashXPath::finishSegments() {
+ SplashXPathSeg *seg;
+ SplashCoord xMinFP, xMaxFP, yMinFP, yMaxFP, t;
+ int i;
+
+ xMinFP = yMinFP = xMaxFP = yMaxFP = 0;
+
+ for (i = 0; i < length; ++i) {
+ seg = &segs[i];
+
+ //--- compute the slopes
+ if (seg->y0 <= seg->y1) {
+ seg->count = 1;
+ } else {
+ t = seg->x0; seg->x0 = seg->x1; seg->x1 = t;
+ t = seg->y0; seg->y0 = seg->y1; seg->y1 = t;
+ seg->count = -1;
+ }
+#if USE_FIXEDPOINT
+ if (seg->y0 == seg->y1 || seg->x0 == seg->x1 ||
+ !FixedPoint::divCheck(seg->x1 - seg->x0, seg->y1 - seg->y0,
+ &seg->dxdy) ||
+ !FixedPoint::divCheck(seg->y1 - seg->y0, seg->x1 - seg->x0,
+ &seg->dydx)) {
+ seg->dxdy = 0;
+ seg->dydx = 0;
+ }
+#else
+ if (splashAbs(seg->y1 - seg->y0) < 1e-200 ||
+ splashAbs(seg->x1 - seg->x0) < 1e-200) {
+ seg->dxdy = 0;
+ seg->dydx = 0;
+ } else {
+ seg->dxdy = (seg->x1 - seg->x0) / (seg->y1 - seg->y0);
+ if (seg->dxdy == 0) {
+ seg->dydx = 0;
+ } else {
+ seg->dydx = 1 / seg->dxdy;
+ }
+ }
+#endif
+
+ //--- update bbox
+ if (i == 0) {
+ if (seg->x0 <= seg->x1) {
+ xMinFP = seg->x0;
+ xMaxFP = seg->x1;
+ } else {
+ xMinFP = seg->x1;
+ xMaxFP = seg->x0;
+ }
+ yMinFP = seg->y0;
+ yMaxFP = seg->y1;
+ } else {
+ if (seg->x0 < xMinFP) {
+ xMinFP = seg->x0;
+ } else if (seg->x0 > xMaxFP) {
+ xMaxFP = seg->x0;
+ }
+ if (seg->x1 < xMinFP) {
+ xMinFP = seg->x1;
+ } else if (seg->x1 > xMaxFP) {
+ xMaxFP = seg->x1;
+ }
+ if (seg->y0 < yMinFP) {
+ yMinFP = seg->y0;
+ }
+ if (seg->y1 > yMaxFP) {
+ yMaxFP = seg->y1;
+ }
+ }
+ }
+
+ xMin = splashFloor(xMinFP);
+ yMin = splashFloor(yMinFP);
+ xMax = splashFloor(xMaxFP);
+ yMax = splashFloor(yMaxFP);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.h
new file mode 100644
index 00000000000..82d7889c447
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashXPath.h
@@ -0,0 +1,143 @@
+//========================================================================
+//
+// SplashXPath.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHXPATH_H
+#define SPLASHXPATH_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+class SplashPath;
+struct SplashXPathPoint;
+struct SplashPathHint;
+
+//------------------------------------------------------------------------
+
+#define splashMaxCurveSplits (1 << 10)
+
+//------------------------------------------------------------------------
+// SplashXPathSeg
+//------------------------------------------------------------------------
+
+struct SplashXPathSeg {
+ SplashCoord x0, y0; // first endpoint (y0 <= y1)
+ SplashCoord x1, y1; // second endpoint
+ SplashCoord dxdy; // slope: delta-x / delta-y
+ SplashCoord dydx; // slope: delta-y / delta-x
+ int count; // EO/NZWN counter increment
+
+ //----- used by SplashXPathScanner
+ int iy;
+ SplashCoord sx0, sx1, mx;
+ SplashXPathSeg *prev, *next;
+
+#if HAVE_STD_SORT
+
+ static bool cmpMX(const SplashXPathSeg &s0,
+ const SplashXPathSeg &s1) {
+ if (s0.iy != s1.iy) {
+ return s0.iy < s1.iy;
+ }
+ return s0.mx < s1.mx;
+ }
+
+ static bool cmpY(const SplashXPathSeg &seg0,
+ const SplashXPathSeg &seg1) {
+ return seg0.y0 < seg1.y0;
+ }
+
+#else
+
+ static int cmpMX(const void *p0, const void *p1) {
+ SplashXPathSeg *s0 = (SplashXPathSeg *)p0;
+ SplashXPathSeg *s1 = (SplashXPathSeg *)p1;
+ SplashCoord cmp;
+
+ if (s0->iy != s1->iy) {
+ return s0->iy - s1->iy;
+ }
+ cmp = s0->mx - s1->mx;
+ return (cmp < 0) ? -1 : (cmp > 0) ? 1 : 0;
+ }
+
+ static int cmpY(const void *seg0, const void *seg1) {
+ SplashCoord cmp;
+
+ cmp = ((SplashXPathSeg *)seg0)->y0
+ - ((SplashXPathSeg *)seg1)->y0;
+ return (cmp > 0) ? 1 : (cmp < 0) ? -1 : 0;
+ }
+
+#endif
+};
+
+//------------------------------------------------------------------------
+// SplashXPath
+//------------------------------------------------------------------------
+
+class SplashXPath {
+public:
+
+ // Expands (converts to segments) and flattens (converts curves to
+ // lines) <path>. Transforms all points from user space to device
+ // space, via <matrix>. If <closeSubpaths> is true, closes all open
+ // subpaths.
+ SplashXPath(SplashPath *path, SplashCoord *matrix,
+ SplashCoord flatness, GBool closeSubpaths,
+ GBool simplify, SplashStrokeAdjustMode strokeAdjMode);
+
+ // Copy an expanded path.
+ SplashXPath *copy() { return new SplashXPath(this); }
+
+ ~SplashXPath();
+
+ int getXMin() { return xMin; }
+ int getXMax() { return xMax; }
+ int getYMin() { return yMin; }
+ int getYMax() { return yMax; }
+
+private:
+
+ static void clampCoords(SplashCoord *x, SplashCoord *y);
+ SplashXPath(SplashXPath *xPath);
+ void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi,
+ SplashCoord *xo, SplashCoord *yo);
+ GBool strokeAdjust(SplashXPathPoint *pts,
+ SplashPathHint *hints, int nHints,
+ SplashStrokeAdjustMode strokeAdjMode);
+ void grow(int nSegs);
+ void addCurve(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1,
+ SplashCoord x2, SplashCoord y2,
+ SplashCoord x3, SplashCoord y3,
+ SplashCoord flatness,
+ GBool first, GBool last, GBool end0, GBool end1);
+ void mergeSegments(int first);
+ void addSegment(SplashCoord x0, SplashCoord y0,
+ SplashCoord x1, SplashCoord y1);
+ void finishSegments();
+
+ SplashXPathSeg *segs;
+ int length, size; // length and size of segs array
+ int xMin, xMax;
+ int yMin, yMax;
+
+ GBool isRect;
+ SplashCoord rectX0, rectY0, rectX1, rectY1;
+
+ friend class SplashXPathScanner;
+ friend class SplashClip;
+ friend class Splash;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashXPathScanner.cc b/Build/source/libs/xpdf/xpdf-src/splash/SplashXPathScanner.cc
new file mode 100644
index 00000000000..3f167b8b311
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashXPathScanner.cc
@@ -0,0 +1,620 @@
+//========================================================================
+//
+// SplashXPathScanner.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#if HAVE_STD_SORT
+#include <algorithm>
+#endif
+#include "gmem.h"
+#include "gmempp.h"
+#include "GList.h"
+#include "SplashMath.h"
+#include "SplashXPath.h"
+#include "SplashXPathScanner.h"
+
+//------------------------------------------------------------------------
+
+#if ANTIALIAS_256
+
+#define aaVert 15
+#define aaHoriz 17
+
+#else
+
+#define aaVert 4
+#define aaHoriz 4
+
+static Guchar map16to255[17] = {
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 143,
+ 159,
+ 175,
+ 191,
+ 207,
+ 223,
+ 239,
+ 255
+};
+
+#endif
+
+//------------------------------------------------------------------------
+
+SplashXPathScanner::SplashXPathScanner(SplashXPath *xPathA, GBool eo,
+ int yMinA, int yMaxA) {
+ xPath = xPathA;
+ eoMask = eo ? 1 : 0xffffffff;
+ yMin = yMinA;
+ yMax = yMaxA;
+ if (xPath->isRect) {
+ rectX0I = splashFloor(xPath->rectX0);
+ rectY0I = splashFloor(xPath->rectY0);
+ rectX1I = splashFloor(xPath->rectX1);
+ rectY1I = splashFloor(xPath->rectY1);
+ }
+
+ pre = &preSeg;
+ post = &postSeg;
+ pre->mx = xPath->xMin - 1;
+ post->mx = xPath->xMax + 1;
+
+ resetDone = gFalse;
+ resetAA = gFalse;
+}
+
+SplashXPathScanner::~SplashXPathScanner() {
+}
+
+void SplashXPathScanner::insertSegmentBefore(SplashXPathSeg *s,
+ SplashXPathSeg *sNext) {
+ SplashXPathSeg *sPrev;
+
+ sPrev = sNext->prev;
+ sPrev->next = s;
+ s->prev = sPrev;
+ s->next = sNext;
+ sNext->prev = s;
+}
+
+void SplashXPathScanner::removeSegment(SplashXPathSeg *s) {
+ SplashXPathSeg *sPrev, *sNext;
+
+ sPrev = s->prev;
+ sNext = s->next;
+ sPrev->next = sNext;
+ sNext->prev = sPrev;
+ s->prev = s->next = NULL;
+}
+
+void SplashXPathScanner::moveSegmentAfter(SplashXPathSeg *s,
+ SplashXPathSeg *sPrev) {
+ SplashXPathSeg *sNext;
+
+ s->prev->next = s->next;
+ s->next->prev = s->prev;
+
+ sNext = sPrev->next;
+ sPrev->next = s;
+ s->prev = sPrev;
+ s->next = sNext;
+ sNext->prev = s;
+}
+
+void SplashXPathScanner::reset(GBool aa, GBool aaChanged) {
+ SplashXPathSeg *seg;
+ SplashCoord y;
+ int i;
+
+ //--- initialize segment parameters
+ for (i = 0; i < xPath->length; ++i) {
+ seg = &xPath->segs[i];
+ if (aa) {
+ if (aaChanged) {
+ seg->iy = splashFloor(seg->y0 * aaVert);
+ }
+ y = (SplashCoord)(seg->iy + 1) / (SplashCoord)aaVert;
+ } else {
+ if (aaChanged) {
+ seg->iy = splashFloor(seg->y0);
+ }
+ y = (SplashCoord)(seg->iy + 1);
+ }
+ seg->sx0 = seg->x0;
+ if (seg->y1 <= y) {
+ seg->sx1 = seg->x1;
+ } else {
+ seg->sx1 = seg->x0 + (y - seg->y0) * seg->dxdy;
+ }
+ seg->mx = (seg->sx0 <= seg->sx1) ? seg->sx0 : seg->sx1;
+ seg->prev = seg->next = NULL;
+ }
+
+ //--- sort the inactive segments by iy, mx
+ if (aaChanged) {
+#if HAVE_STD_SORT
+ std::sort(xPath->segs, xPath->segs + xPath->length, &SplashXPathSeg::cmpMX);
+#else
+ qsort(xPath->segs, xPath->length, sizeof(SplashXPathSeg),
+ &SplashXPathSeg::cmpMX);
+#endif
+ }
+
+ //--- initialize the active list
+ pre->prev = NULL;
+ pre->next = post;
+ post->prev = pre;
+ post->next = NULL;
+
+ //--- initialize the scan state
+ nextSeg = 0;
+ if (xPath->length) {
+ yBottomI = xPath->segs[0].iy;
+ if (aa) {
+ yBottomI -= yBottomI % aaVert;
+ }
+ } else {
+ yBottomI = 0;
+ }
+ yTopI = yBottomI - 1;
+ if (aa) {
+ yTop = (SplashCoord)yTopI / (SplashCoord)aaVert;
+ yBottom = (SplashCoord)yBottomI / (SplashCoord)aaVert;
+ } else {
+ yTop = (SplashCoord)yTopI;
+ yBottom = (SplashCoord)yBottomI;
+ }
+
+ resetDone = gTrue;
+ resetAA = aa;
+}
+
+void SplashXPathScanner::skip(int newYBottomI, GBool aa) {
+ SplashXPathSeg *s0, *s1,*s2;
+ int iy;
+
+ yTopI = newYBottomI - 1;
+ yBottomI = newYBottomI;
+ if (aa) {
+ yTop = (SplashCoord)yTopI / (SplashCoord)aaVert;
+ yBottom = (SplashCoord)yBottomI / (SplashCoord)aaVert;
+ } else {
+ yTop = (SplashCoord)yTopI;
+ yBottom = (SplashCoord)yBottomI;
+ }
+
+ //--- remove finished segments; update sx0, sx1, mx for active segments
+ s0 = pre->next;
+ while (s0 != post) {
+ s1 = s0->next;
+
+ // check for a finished segment
+ if (s0->y1 < yTop) {
+ removeSegment(s0);
+
+ // compute sx0, sx1, mx
+ } else {
+ if (s0->y0 >= yTop) {
+ s0->sx0 = s0->x0;
+ } else {
+ s0->sx0 = s0->x0 + (yTop - s0->y0) * s0->dxdy;
+ }
+ if (s0->y1 <= yBottom) {
+ s0->sx1 = s0->x1;
+ } else {
+ s0->sx1 = s0->x0 + (yBottom - s0->y0) * s0->dxdy;
+ }
+ s0->mx = (s0->sx0 <= s0->sx1) ? s0->sx0 : s0->sx1;
+ }
+
+ s0 = s1;
+ }
+
+ //--- check for intersections
+ s0 = pre->next;
+ if (s0 != post) {
+ s1 = s0->next;
+ while (s1 != post) {
+ if (s1->mx < s0->mx) {
+ for (s2 = s0->prev; s1->mx < s2->mx; s2 = s2->prev) ;
+ moveSegmentAfter(s1, s2);
+ } else {
+ s0 = s1;
+ }
+ s1 = s0->next;
+ }
+ }
+
+ //--- insert new segments with a merge sort
+ // - this has to be done one (sub)scanline at a time, because the
+ // inactive list is bin-sorted by (sub)scanline
+ while (nextSeg < xPath->length && xPath->segs[nextSeg].iy <= yTopI) {
+ // the first inactive segment determines the next scanline to process
+ iy = xPath->segs[nextSeg].iy;
+ s0 = pre->next;
+ do {
+ s1 = &xPath->segs[nextSeg];
+ ++nextSeg;
+ if (s1->y1 < yTop) {
+ continue;
+ }
+ if (s1->y0 >= yTop) {
+ s1->sx0 = s1->x0;
+ } else {
+ s1->sx0 = s1->x0 + (yTop - s1->y0) * s1->dxdy;
+ }
+ if (s1->y1 <= yBottom) {
+ s1->sx1 = s1->x1;
+ } else {
+ s1->sx1 = s1->x0 + (yBottom - s1->y0) * s1->dxdy;
+ }
+ s1->mx = (s1->sx0 <= s1->sx1) ? s1->sx0 : s1->sx1;
+ insertSegmentBefore(s1, s0);
+ } while (nextSeg < xPath->length && xPath->segs[nextSeg].iy <= iy);
+ }
+}
+
+void SplashXPathScanner::advance(GBool aa) {
+ SplashXPathSeg *s, *sNext, *s1;
+
+ yTopI = yBottomI;
+ yTop = yBottom;
+ yBottomI = yTopI + 1;
+ if (aa) {
+ yBottom = (SplashCoord)yBottomI / (SplashCoord)aaVert;
+ } else {
+ yBottom = (SplashCoord)yBottomI;
+ }
+
+ s = pre->next;
+ while (s != post) {
+ sNext = s->next;
+
+ // check for a finished segment
+ if (s->y1 < yTop) {
+ removeSegment(s);
+
+ } else {
+
+ // compute sx0, sx1, mx
+ s->sx0 = s->sx1;
+ if (s->y1 <= yBottom) {
+ s->sx1 = s->x1;
+ } else {
+ s->sx1 = s->x0 + (yBottom - s->y0) * s->dxdy;
+ }
+ s->mx = (s->sx0 <= s->sx1) ? s->sx0 : s->sx1;
+
+ // check for intersection
+ if (s->mx < s->prev->mx) {
+ for (s1 = s->prev->prev; s->mx < s1->mx; s1 = s1->prev) ;
+ moveSegmentAfter(s, s1);
+ }
+ }
+
+ s = sNext;
+ }
+
+ // insert new segments
+ s = pre->next;
+ while (nextSeg < xPath->length && xPath->segs[nextSeg].iy <= yTopI) {
+ s1 = &xPath->segs[nextSeg];
+ ++nextSeg;
+ while (s1->mx > s->mx) {
+ s = s->next;
+ }
+ insertSegmentBefore(s1, s);
+ }
+}
+
+void SplashXPathScanner::generatePixels(int x0, int x1, Guchar *line,
+ int *xMin, int *xMax) {
+ SplashXPathSeg *s;
+ int fillCount, x, xEnd, ix0, ix1, t;
+
+ fillCount = 0;
+ x = x0 * aaHoriz;
+ xEnd = (x1 + 1) * aaHoriz;
+ for (s = pre->next; s != post && x < xEnd; s = s->next) {
+ ix0 = splashFloor(s->sx0 * aaHoriz);
+ ix1 = splashFloor(s->sx1 * aaHoriz);
+ if (ix0 > ix1) {
+ t = ix0; ix0 = ix1; ix1 = t;
+ }
+ if (!(fillCount & eoMask)) {
+ if (ix0 > x) {
+ x = ix0;
+ }
+ }
+ if (ix1 >= xEnd) {
+ ix1 = xEnd - 1;
+ }
+ if (x / aaHoriz < *xMin) {
+ *xMin = x / aaHoriz;
+ }
+ if (ix1 / aaHoriz > *xMax) {
+ *xMax = ix1 / aaHoriz;
+ }
+ for (; x <= ix1; ++x) {
+ ++line[x / aaHoriz];
+ }
+ if (s->y0 <= yTop && s->y1 > yTop) {
+ fillCount += s->count;
+ }
+ }
+}
+
+void SplashXPathScanner::generatePixelsBinary(int x0, int x1, Guchar *line,
+ int *xMin, int *xMax) {
+ SplashXPathSeg *s;
+ int fillCount, x, xEnd, ix0, ix1, t;
+
+ fillCount = 0;
+ x = x0;
+ xEnd = x1 + 1;
+ for (s = pre->next; s != post && x < xEnd; s = s->next) {
+ ix0 = splashFloor(s->sx0);
+ ix1 = splashFloor(s->sx1);
+ if (ix0 > ix1) {
+ t = ix0; ix0 = ix1; ix1 = t;
+ }
+ if (!(fillCount & eoMask)) {
+ if (ix0 > x) {
+ x = ix0;
+ }
+ }
+ if (ix1 >= xEnd) {
+ ix1 = xEnd - 1;
+ }
+ if (x < *xMin) {
+ *xMin = x;
+ }
+ if (ix1 > *xMax) {
+ *xMax = ix1;
+ }
+ for (; x <= ix1; ++x) {
+ line[x] = 255;
+ }
+ if (s->y0 <= yTop && s->y1 > yTop) {
+ fillCount += s->count;
+ }
+ }
+}
+
+void SplashXPathScanner::drawRectangleSpan(Guchar *line, int y,
+ int x0, int x1,
+ int *xMin, int *xMax) {
+ SplashCoord edge;
+ Guchar pix;
+ int xe, x;
+
+ if (rectX0I > x1 || rectX1I < x0) {
+ return;
+ }
+
+ *xMin = x0 <= rectX0I ? rectX0I : x0;
+ *xMax = rectX1I <= x1 ? rectX1I : x1;
+
+ //--- upper edge
+ if (y == rectY0I) {
+
+ // handle a rectangle less than one pixel high
+ if (rectY0I == rectY1I) {
+ edge = xPath->rectY1 - xPath->rectY0;
+ } else {
+ edge = (SplashCoord)1 - (xPath->rectY0 - rectY0I);
+ }
+
+ // upper left corner
+ if (x0 <= rectX0I) {
+ pix = (Guchar)splashCeil(edge
+ * ((SplashCoord)1 - (xPath->rectX0 - rectX0I))
+ * 255);
+ if (pix < 16) {
+ pix = 16;
+ }
+ line[rectX0I] = pix;
+ x = rectX0I + 1;
+ } else {
+ x = x0;
+ }
+
+ // upper right corner
+ if (rectX1I <= x1) {
+ pix = (Guchar)splashCeil(edge * (xPath->rectX1 - rectX1I) * 255);
+ if (pix < 16) {
+ pix = 16;
+ }
+ line[rectX1I] = pix;
+ xe = rectX1I - 1;
+ } else {
+ xe = x1;
+ }
+
+ // upper edge
+ pix = (Guchar)splashCeil(edge * 255);
+ if (pix < 16) {
+ pix = 16;
+ }
+ for (; x <= xe; ++x) {
+ line[x] = pix;
+ }
+
+ //--- lower edge
+ } else if (y == rectY1I) {
+ edge = xPath->rectY1 - rectY1I;
+
+ // lower left corner
+ if (x0 <= rectX0I) {
+ pix = (Guchar)splashCeil(edge
+ * ((SplashCoord)1 - (xPath->rectX0 - rectX0I))
+ * 255);
+ if (pix < 16) {
+ pix = 16;
+ }
+ line[rectX0I] = pix;
+ x = rectX0I + 1;
+ } else {
+ x = x0;
+ }
+
+ // lower right corner
+ if (rectX1I <= x1) {
+ pix = (Guchar)splashCeil(edge * (xPath->rectX1 - rectX1I) * 255);
+ if (pix < 16) {
+ pix = 16;
+ }
+ line[rectX1I] = pix;
+ xe = rectX1I - 1;
+ } else {
+ xe = x1;
+ }
+
+ // lower edge
+ pix = (Guchar)splashCeil(edge * 255);
+ if (pix < 16) {
+ pix = 16;
+ }
+ for (; x <= xe; ++x) {
+ line[x] = pix;
+ }
+
+ //--- between the upper and lower edges
+ } else if (y > rectY0I && y < rectY1I) {
+
+ // left edge
+ if (x0 <= rectX0I) {
+ pix = (Guchar)splashCeil(((SplashCoord)1 - (xPath->rectX0 - rectX0I))
+ * 255);
+ if (pix < 16) {
+ pix = 16;
+ }
+ line[rectX0I] = pix;
+ x = rectX0I + 1;
+ } else {
+ x = x0;
+ }
+
+ // right edge
+ if (rectX1I <= x1) {
+ pix = (Guchar)splashCeil((xPath->rectX1 - rectX1I) * 255);
+ if (pix < 16) {
+ pix = 16;
+ }
+ line[rectX1I] = pix;
+ xe = rectX1I - 1;
+ } else {
+ xe = x1;
+ }
+
+ // middle
+ for (; x <= xe; ++x) {
+ line[x] = 255;
+ }
+ }
+}
+
+void SplashXPathScanner::drawRectangleSpanBinary(Guchar *line, int y,
+ int x0, int x1,
+ int *xMin, int *xMax) {
+ int xe, x;
+
+ if (y >= rectY0I && y <= rectY1I) {
+ if (x0 <= rectX0I) {
+ x = rectX0I;
+ } else {
+ x = x0;
+ }
+ *xMin = x;
+ if (rectX1I <= x1) {
+ xe = rectX1I;
+ } else {
+ xe = x1;
+ }
+ *xMax = xe;
+ for (; x <= xe; ++x) {
+ line[x] = 255;
+ }
+ }
+}
+
+void SplashXPathScanner::getSpan(Guchar *line, int y, int x0, int x1,
+ int *xMin, int *xMax) {
+ int iy, x, k;
+
+ iy = y * aaVert;
+ if (!resetDone || !resetAA) {
+ reset(gTrue, gTrue);
+ } else if (yBottomI > iy) {
+ reset(gTrue, gFalse);
+ }
+ memset(line + x0, 0, x1 - x0 + 1);
+
+ *xMin = x1 + 1;
+ *xMax = x0 - 1;
+
+ if (xPath->isRect) {
+ drawRectangleSpan(line, y, x0, x1, xMin, xMax);
+ return;
+ }
+
+ if (yBottomI < iy) {
+ skip(iy, gTrue);
+ }
+ for (k = 0; k < aaVert; ++k, ++iy) {
+ advance(gTrue);
+ generatePixels(x0, x1, line, xMin, xMax);
+ }
+
+#if !ANTIALIAS_256
+ for (x = *xMin; x <= *xMax; ++x) {
+ line[x] = map16to255[line[x]];
+ }
+#endif
+}
+
+void SplashXPathScanner::getSpanBinary(Guchar *line, int y, int x0, int x1,
+ int *xMin, int *xMax) {
+ int iy;
+
+ iy = y;
+ if (!resetDone || resetAA) {
+ reset(gFalse, gTrue);
+ } else if (yBottomI > iy) {
+ reset(gFalse, gFalse);
+ }
+ memset(line + x0, 0, x1 - x0 + 1);
+
+ *xMin = x1 + 1;
+ *xMax = x0 - 1;
+
+ if (xPath->isRect) {
+ drawRectangleSpanBinary(line, y, x0, x1, xMin, xMax);
+ return;
+ }
+
+ if (yBottomI < iy) {
+ skip(iy, gFalse);
+ }
+ advance(gFalse);
+ generatePixelsBinary(x0, x1, line, xMin, xMax);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/splash/SplashXPathScanner.h b/Build/source/libs/xpdf/xpdf-src/splash/SplashXPathScanner.h
new file mode 100644
index 00000000000..39cc0eb022a
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/splash/SplashXPathScanner.h
@@ -0,0 +1,84 @@
+//========================================================================
+//
+// SplashXPathScanner.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHXPATHSCANNER_H
+#define SPLASHXPATHSCANNER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+#include "SplashXPath.h"
+
+class GList;
+
+//------------------------------------------------------------------------
+
+// Set this to 0 for antialiasing with 16 levels of gray.
+// Set it to 1 for (much slower) antialiasing with 256 levels of gray.
+#define ANTIALIAS_256 0
+
+//------------------------------------------------------------------------
+// SplashXPathScanner
+//------------------------------------------------------------------------
+
+class SplashXPathScanner {
+public:
+
+ // Create a new SplashXPathScanner object. <xPathA> must be sorted.
+ SplashXPathScanner(SplashXPath *xPathA, GBool eo,
+ int yMinA, int yMaxA);
+
+ ~SplashXPathScanner();
+
+ // Compute shape values for a scan line. Fills in line[] with shape
+ // values for one scan line: ([x0, x1], y). The values are in [0,
+ // 255]. Also returns the min/max x positions with non-zero shape
+ // values.
+ void getSpan(Guchar *line, int y, int x0, int x1, int *xMin, int *xMax);
+
+ // Like getSpan(), but uses the values 0 and 255 only. Writes 255
+ // for all pixels which include non-zero area inside the path.
+ void getSpanBinary(Guchar *line, int y, int x0, int x1,
+ int *xMin, int *xMax);
+
+private:
+
+ void insertSegmentBefore(SplashXPathSeg *s, SplashXPathSeg *sNext);
+ void removeSegment(SplashXPathSeg *s);
+ void moveSegmentAfter(SplashXPathSeg *s, SplashXPathSeg *sPrev);
+ void reset(GBool aa, GBool aaChanged);
+ void skip(int newYBottomI, GBool aa);
+ void advance(GBool aa);
+ void generatePixels(int x0, int x1, Guchar *line, int *xMin, int *xMax);
+ void generatePixelsBinary(int x0, int x1, Guchar *line,
+ int *xMin, int *xMax);
+ void drawRectangleSpan(Guchar *line, int y, int x0, int x1,
+ int *xMin, int *xMax);
+ void drawRectangleSpanBinary(Guchar *line, int y, int x0, int x1,
+ int *xMin, int *xMax);
+
+ SplashXPath *xPath;
+ int eoMask;
+ int yMin, yMax;
+ int rectX0I, rectY0I, rectX1I, rectY1I;
+
+ SplashXPathSeg preSeg, postSeg;
+ SplashXPathSeg *pre, *post;
+
+ GBool resetDone;
+ GBool resetAA;
+ int nextSeg;
+ int yTopI, yBottomI;
+ SplashCoord yTop, yBottom;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc
new file mode 100644
index 00000000000..b98ca2f0189
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.cc
@@ -0,0 +1,3886 @@
+//========================================================================
+//
+// AcroForm.cc
+//
+// Copyright 2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "Object.h"
+#include "PDFDoc.h"
+#include "TextString.h"
+#include "Gfx.h"
+#include "GfxFont.h"
+#include "OptionalContent.h"
+#include "Annot.h"
+#include "Lexer.h"
+#include "XFAScanner.h"
+#include "UTF8.h"
+#include "PDF417Barcode.h"
+#include "AcroForm.h"
+
+//------------------------------------------------------------------------
+
+#define acroFormFlagReadOnly (1 << 0) // all
+#define acroFormFlagRequired (1 << 1) // all
+#define acroFormFlagNoExport (1 << 2) // all
+#define acroFormFlagMultiline (1 << 12) // text
+#define acroFormFlagPassword (1 << 13) // text
+#define acroFormFlagNoToggleToOff (1 << 14) // button
+#define acroFormFlagRadio (1 << 15) // button
+#define acroFormFlagPushbutton (1 << 16) // button
+#define acroFormFlagCombo (1 << 17) // choice
+#define acroFormFlagEdit (1 << 18) // choice
+#define acroFormFlagSort (1 << 19) // choice
+#define acroFormFlagFileSelect (1 << 20) // text
+#define acroFormFlagMultiSelect (1 << 21) // choice
+#define acroFormFlagDoNotSpellCheck (1 << 22) // text, choice
+#define acroFormFlagDoNotScroll (1 << 23) // text
+#define acroFormFlagComb (1 << 24) // text
+#define acroFormFlagRadiosInUnison (1 << 25) // button
+#define acroFormFlagRichText (1 << 25) // text
+#define acroFormFlagCommitOnSelChange (1 << 26) // choice
+
+#define acroFormQuadLeft 0
+#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
+
+// distance of Bezier control point from center for circle approximation
+// = (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:
+ AcroFormAnnotPage(int annotNumA, int annotGenA, int pageNumA)
+ { annotNum = annotNumA; annotGen = annotGenA; pageNum = pageNumA; }
+ int annotNum;
+ int annotGen;
+ int pageNum;
+};
+
+//------------------------------------------------------------------------
+// AcroForm
+//------------------------------------------------------------------------
+
+AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) {
+ Object acroFormObj2;
+ AcroForm *acroForm;
+ AcroFormField *field;
+ Object xfaObj, fieldsObj, annotsObj, annotRef, annotObj, obj1, obj2;
+ int pageNum, i, j;
+
+ // this is the normal case: acroFormObj is a dictionary, as expected
+ if (acroFormObjA->isDict()) {
+ acroForm = new AcroForm(docA, acroFormObjA);
+
+ if (!acroFormObjA->dictLookup("XFA", &xfaObj)->isNull()) {
+ acroForm->xfaScanner = XFAScanner::load(&xfaObj);
+ if (!catalog->getNeedsRendering()) {
+ acroForm->isStaticXFA = gTrue;
+ }
+ }
+ xfaObj.free();
+
+ if (acroFormObjA->dictLookup("NeedAppearances", &obj1)->isBool()) {
+ acroForm->needAppearances = obj1.getBool();
+ }
+ obj1.free();
+
+ acroForm->buildAnnotPageList(catalog);
+
+ if (!acroFormObjA->dictLookup("Fields", &obj1)->isArray()) {
+ if (!obj1.isNull()) {
+ error(errSyntaxError, -1, "AcroForm Fields entry is wrong type");
+ }
+ obj1.free();
+ delete acroForm;
+ return NULL;
+ }
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ obj1.arrayGetNF(i, &obj2);
+ acroForm->scanField(&obj2);
+ obj2.free();
+ }
+ obj1.free();
+
+ // scan the annotations, looking for Widget-type annots that are
+ // not attached to the AcroForm object
+ for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
+ if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
+ for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
+ if (annotsObj.arrayGetNF(i, &annotRef)->isRef()) {
+ for (j = 0; j < acroForm->fields->getLength(); ++j) {
+ field = (AcroFormField *)acroForm->fields->get(j);
+ if (field->fieldRef.isRef()) {
+ if (field->fieldRef.getRefNum() == annotRef.getRefNum() &&
+ field->fieldRef.getRefGen() == annotRef.getRefGen()) {
+ break;
+ }
+ }
+ }
+ if (j == acroForm->fields->getLength()) {
+ annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
+ if (annotObj.isDict()) {
+ if (annotObj.dictLookup("Subtype", &obj1)->isName("Widget")) {
+ acroForm->scanField(&annotRef);
+ }
+ obj1.free();
+ }
+ annotObj.free();
+ }
+ }
+ annotRef.free();
+ }
+ }
+ annotsObj.free();
+ }
+
+ // if acroFormObjA is a null object, but there are Widget-type
+ // annots, we still create an AcroForm
+ } else {
+ // create an empty dict for acroFormObj
+ acroFormObj2.initDict(docA->getXRef());
+ acroForm = new AcroForm(docA, &acroFormObj2);
+ acroFormObj2.free();
+
+ acroForm->buildAnnotPageList(catalog);
+
+ // scan the annotations, looking for any Widget-type annots
+ for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
+ if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
+ for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
+ if (annotsObj.arrayGetNF(i, &annotRef)->isRef()) {
+ annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
+ if (annotObj.isDict()) {
+ if (annotObj.dictLookup("Subtype", &obj1)->isName("Widget")) {
+ acroForm->scanField(&annotRef);
+ }
+ obj1.free();
+ }
+ annotObj.free();
+ }
+ annotRef.free();
+ }
+ }
+ annotsObj.free();
+ }
+
+ if (acroForm->fields->getLength() == 0) {
+ delete acroForm;
+ acroForm = NULL;
+ }
+ }
+
+ return acroForm;
+}
+
+AcroForm::AcroForm(PDFDoc *docA, Object *acroFormObjA) {
+ doc = docA;
+ acroFormObjA->copy(&acroFormObj);
+ needAppearances = gFalse;
+ annotPages = new GList();
+ fields = new GList();
+ xfaScanner = NULL;
+ isStaticXFA = gFalse;
+}
+
+AcroForm::~AcroForm() {
+ acroFormObj.free();
+ deleteGList(annotPages, AcroFormAnnotPage);
+ deleteGList(fields, AcroFormField);
+ delete xfaScanner;
+}
+
+const char *AcroForm::getType() {
+ return isStaticXFA ? "static XFA" : "AcroForm";
+}
+
+void AcroForm::buildAnnotPageList(Catalog *catalog) {
+ Object annotsObj, annotObj;
+ int pageNum, i;
+
+ for (pageNum = 1; pageNum <= catalog->getNumPages(); ++pageNum) {
+ if (catalog->getPage(pageNum)->getAnnots(&annotsObj)->isArray()) {
+ for (i = 0; i < annotsObj.arrayGetLength(); ++i) {
+ if (annotsObj.arrayGetNF(i, &annotObj)->isRef()) {
+ annotPages->append(new AcroFormAnnotPage(annotObj.getRefNum(),
+ annotObj.getRefGen(),
+ pageNum));
+ }
+ annotObj.free();
+ }
+ }
+ annotsObj.free();
+ }
+ //~ sort the list
+}
+
+int AcroForm::lookupAnnotPage(Object *annotRef) {
+ AcroFormAnnotPage *annotPage;
+ int num, gen, i;
+
+ if (!annotRef->isRef()) {
+ return 0;
+ }
+ num = annotRef->getRefNum();
+ gen = annotRef->getRefGen();
+ //~ use bin search
+ for (i = 0; i < annotPages->getLength(); ++i) {
+ annotPage = (AcroFormAnnotPage *)annotPages->get(i);
+ if (annotPage->annotNum == num && annotPage->annotGen == gen) {
+ return annotPage->pageNum;
+ }
+ }
+ return 0;
+}
+
+void AcroForm::scanField(Object *fieldRef) {
+ AcroFormField *field;
+ Object fieldObj, kidsObj, kidRef, kidObj, subtypeObj;
+ GBool isTerminal;
+ int i;
+
+ fieldRef->fetch(doc->getXRef(), &fieldObj);
+ if (!fieldObj.isDict()) {
+ error(errSyntaxError, -1, "AcroForm field object is wrong type");
+ fieldObj.free();
+ return;
+ }
+
+ // if this field has a Kids array, and all of the kids have a Parent
+ // reference (i.e., they're all form fields, not widget
+ // annotations), then this is a non-terminal field, and we need to
+ // scan the kids
+ isTerminal = gTrue;
+ if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
+ isTerminal = gFalse;
+ for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
+ kidsObj.arrayGet(i, &kidObj);
+ if (kidObj.isDict()) {
+ if (kidObj.dictLookup("Parent", &subtypeObj)->isNull()) {
+ isTerminal = gTrue;
+ }
+ subtypeObj.free();
+ }
+ kidObj.free();
+ }
+ if (!isTerminal) {
+ for (i = 0; !isTerminal && i < kidsObj.arrayGetLength(); ++i) {
+ kidsObj.arrayGetNF(i, &kidRef);
+ scanField(&kidRef);
+ kidRef.free();
+ }
+ }
+ }
+ kidsObj.free();
+
+ if (isTerminal) {
+ if ((field = AcroFormField::load(this, fieldRef))) {
+ fields->append(field);
+ }
+ }
+
+ fieldObj.free();
+}
+
+void AcroForm::draw(int pageNum, Gfx *gfx, GBool printing) {
+ int i;
+
+ for (i = 0; i < fields->getLength(); ++i) {
+ ((AcroFormField *)fields->get(i))->draw(pageNum, gfx, printing);
+ }
+}
+
+int AcroForm::getNumFields() {
+ return fields->getLength();
+}
+
+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
+//------------------------------------------------------------------------
+
+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);
+
+ //----- get field info
+
+ if (fieldObjA.dictLookup("T", &obj1)->isString()) {
+ nameA = new TextString(obj1.getString());
+ } else {
+ nameA = new TextString();
+ }
+ obj1.free();
+
+ if (fieldObjA.dictLookup("FT", &obj1)->isName()) {
+ typeStr = new GString(obj1.getName());
+ typeFromParentA = gFalse;
+ } else {
+ typeStr = NULL;
+ typeFromParentA = gTrue;
+ }
+ obj1.free();
+
+ if (fieldObjA.dictLookup("Ff", &obj1)->isInt()) {
+ flagsA = (Guint)obj1.getInt();
+ haveFlags = gTrue;
+ } else {
+ flagsA = 0;
+ haveFlags = gFalse;
+ }
+ obj1.free();
+
+ //----- get info from parent non-terminal fields
+
+ fieldObjA.dictLookup("Parent", &parentObj);
+ depth = 0;
+ while (parentObj.isDict() && depth < maxFieldObjectDepth) {
+
+ if (parentObj.dictLookup("T", &obj1)->isString()) {
+ if (nameA->getLength()) {
+ nameA->insert(0, (Unicode)'.');
+ }
+ nameA->insert(0, obj1.getString());
+ }
+ obj1.free();
+
+ if (!typeStr) {
+ if (parentObj.dictLookup("FT", &obj1)->isName()) {
+ typeStr = new GString(obj1.getName());
+ }
+ obj1.free();
+ }
+
+ if (!haveFlags) {
+ if (parentObj.dictLookup("Ff", &obj1)->isInt()) {
+ flagsA = (Guint)obj1.getInt();
+ haveFlags = gTrue;
+ }
+ obj1.free();
+ }
+
+ parentObj.dictLookup("Parent", &parentObj2);
+ parentObj.free();
+ parentObj = parentObj2;
+
+ ++depth;
+ }
+ parentObj.free();
+
+ if (!typeStr) {
+ error(errSyntaxError, -1, "Missing type in AcroForm field");
+ 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
+ // Ff entry is missing, assume the kids are radio buttons
+ if (typeFromParentA && !typeStr->cmp("Btn") && !haveFlags) {
+ flagsA = acroFormFlagRadio;
+ }
+
+ //----- determine field type
+
+ if (!typeStr->cmp("Btn")) {
+ if (flagsA & acroFormFlagPushbutton) {
+ typeA = acroFormFieldPushbutton;
+ } else if (flagsA & acroFormFlagRadio) {
+ typeA = acroFormFieldRadioButton;
+ } else {
+ typeA = acroFormFieldCheckbox;
+ }
+ } else if (!typeStr->cmp("Tx")) {
+ if (xfaFieldA && xfaFieldA->getBarcodeInfo()) {
+ typeA = acroFormFieldBarcode;
+ } else if (flagsA & acroFormFlagFileSelect) {
+ typeA = acroFormFieldFileSelect;
+ } else if (flagsA & acroFormFlagMultiline) {
+ typeA = acroFormFieldMultilineText;
+ } else {
+ typeA = acroFormFieldText;
+ }
+ } else if (!typeStr->cmp("Ch")) {
+ if (flagsA & acroFormFlagCombo) {
+ typeA = acroFormFieldComboBox;
+ } else {
+ typeA = acroFormFieldListBox;
+ }
+ } else if (!typeStr->cmp("Sig")) {
+ typeA = acroFormFieldSignature;
+ } else {
+ error(errSyntaxError, -1, "Invalid type in AcroForm field");
+ goto err1;
+ }
+ delete typeStr;
+
+ field = new AcroFormField(acroFormA, fieldRefA, &fieldObjA,
+ typeA, nameA, flagsA, typeFromParentA, xfaFieldA);
+ fieldObjA.free();
+ return field;
+
+ err1:
+ delete typeStr;
+ delete nameA;
+ fieldObjA.free();
+ return NULL;
+}
+
+AcroFormField::AcroFormField(AcroForm *acroFormA,
+ Object *fieldRefA, Object *fieldObjA,
+ AcroFormFieldType typeA, TextString *nameA,
+ Guint flagsA, GBool typeFromParentA,
+ XFAField *xfaFieldA) {
+ acroForm = acroFormA;
+ fieldRefA->copy(&fieldRef);
+ fieldObjA->copy(&fieldObj);
+ type = typeA;
+ name = nameA;
+ flags = flagsA;
+ typeFromParent = typeFromParentA;
+ xfaField = xfaFieldA;
+}
+
+AcroFormField::~AcroFormField() {
+ fieldRef.free();
+ fieldObj.free();
+ delete name;
+}
+
+int AcroFormField::getPageNum() {
+ Object kidsObj, annotRef;
+ int pageNum;
+
+ pageNum = 0;
+ if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
+ if (kidsObj.arrayGetLength() > 0) {
+ kidsObj.arrayGetNF(0, &annotRef);
+ pageNum = acroForm->lookupAnnotPage(&annotRef);
+ annotRef.free();
+ }
+ } else {
+ pageNum = acroForm->lookupAnnotPage(&fieldRef);
+ }
+ kidsObj.free();
+ return pageNum;
+}
+
+const char *AcroFormField::getType() {
+ switch (type) {
+ case acroFormFieldPushbutton: return "PushButton";
+ case acroFormFieldRadioButton: return "RadioButton";
+ case acroFormFieldCheckbox: return "Checkbox";
+ 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";
+ default: return NULL;
+ }
+}
+
+Unicode *AcroFormField::getName(int *length) {
+ Unicode *u, *ret;
+ int n;
+
+ u = name->getUnicode();
+ n = name->getLength();
+ ret = (Unicode *)gmallocn(n, sizeof(Unicode));
+ memcpy(ret, u, n * sizeof(Unicode));
+ *length = n;
+ return ret;
+}
+
+Unicode *AcroFormField::getValue(int *length) {
+ Object obj1, obj2;
+ Unicode *u;
+ char *s;
+ TextString *ts;
+ GString *gs;
+ int n, i;
+
+ u = NULL;
+ *length = 0;
+
+ // if this field has a counterpart in the XFA form, take the value
+ // from the XFA field (NB: an XFA field with no value overrides the
+ // AcroForm value)
+ if (xfaField) {
+ if (xfaField->getValue()) {
+ u = utf8ToUnicode(xfaField->getValue(), length);
+ }
+
+ // no XFA form - take the AcroForm value
+ } else {
+ fieldLookup("V", &obj1);
+ if (obj1.isName()) {
+ s = obj1.getName();
+ n = (int)strlen(s);
+ u = (Unicode *)gmallocn(n, sizeof(Unicode));
+ for (i = 0; i < n; ++i) {
+ u[i] = 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();
+ }
+ obj1.free();
+ }
+
+ return u;
+}
+
+void AcroFormField::getBBox(double *llx, double *lly,
+ double *urx, double *ury) {
+ Object annotObj, rectObj, numObj;
+ double t;
+
+ *llx = *lly = *urx = *ury = 0;
+
+ if (getAnnotObj(&annotObj)->isDict()) {
+ annotObj.dictLookup("Rect", &rectObj);
+ if (rectObj.isArray() && rectObj.arrayGetLength() == 4) {
+ rectObj.arrayGet(0, &numObj);
+ if (numObj.isNum()) {
+ *llx = numObj.getNum();
+ }
+ numObj.free();
+ rectObj.arrayGet(1, &numObj);
+ if (numObj.isNum()) {
+ *lly = numObj.getNum();
+ }
+ numObj.free();
+ rectObj.arrayGet(2, &numObj);
+ if (numObj.isNum()) {
+ *urx = numObj.getNum();
+ }
+ numObj.free();
+ rectObj.arrayGet(3, &numObj);
+ if (numObj.isNum()) {
+ *ury = numObj.getNum();
+ }
+ numObj.free();
+ }
+ rectObj.free();
+ }
+ annotObj.free();
+
+ if (*llx > *urx) {
+ t = *llx; *llx = *urx; *urx = t;
+ }
+ if (*lly > *ury) {
+ t = *lly; *lly = *ury; *ury = t;
+ }
+}
+
+void AcroFormField::getFont(Ref *fontID, double *fontSize) {
+ Object daObj;
+ GList *daToks;
+ char *fontTag;
+ double tfSize, m2, m3;
+ int tfPos, tmPos, i;
+
+ fontID->num = fontID->gen = -1;
+ *fontSize = 0;
+
+ if (fieldLookup("DA", &daObj)->isString()) {
+
+ // parse the default appearance string
+ daToks = tokenize(daObj.getString());
+ tfPos = tmPos = -1;
+ for (i = 2; i < daToks->getLength(); ++i) {
+ if (!((GString *)daToks->get(i))->cmp("Tf")) {
+ tfPos = i - 2;
+ } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
+ tmPos = i - 6;
+ }
+ }
+
+ // handle the Tf operator
+ if (tfPos >= 0) {
+ fontTag = ((GString *)daToks->get(tfPos))->getCString();
+ if (*fontTag == '/') {
+ ++fontTag;
+ }
+ *fontID = findFontName(fontTag);
+ tfSize = atof(((GString *)daToks->get(tfPos + 1))->getCString());
+ } else {
+ tfSize = 1;
+ }
+
+ // handle the Tm operator
+ if (tmPos >= 0) {
+ // transformed font size = sqrt(m[2]^2 + m[3]^2) * size
+ m2 = atof(((GString *)daToks->get(tfPos + 2))->getCString());
+ m3 = atof(((GString *)daToks->get(tfPos + 3))->getCString());
+ tfSize *= sqrt(m2*m2 + m3*m3);
+ }
+ *fontSize = tfSize;
+
+ deleteGList(daToks, GString);
+ }
+
+ daObj.free();
+}
+
+Ref AcroFormField::findFontName(char *fontTag) {
+ Object drObj, fontDictObj, fontObj, baseFontObj;
+ Ref fontID;
+ GBool found;
+
+ fontID.num = fontID.gen = -1;
+ found = gFalse;
+
+ if (fieldObj.dictLookup("DR", &drObj)->isDict()) {
+ if (drObj.dictLookup("Font", &fontDictObj)->isDict()) {
+ if (fontDictObj.dictLookupNF(fontTag, &fontObj)->isRef()) {
+ fontID = fontObj.getRef();
+ found = gTrue;
+ }
+ fontObj.free();
+ }
+ fontDictObj.free();
+ }
+ drObj.free();
+ if (found) {
+ return fontID;
+ }
+
+ if (acroForm->acroFormObj.dictLookup("DR", &drObj)->isDict()) {
+ if (drObj.dictLookup("Font", &fontDictObj)->isDict()) {
+ if (fontDictObj.dictLookupNF(fontTag, &fontObj)->isRef()) {
+ fontID = fontObj.getRef();
+ }
+ fontObj.free();
+ }
+ fontDictObj.free();
+ }
+ drObj.free();
+ return fontID;
+}
+
+void AcroFormField::getColor(double *red, double *green, double *blue) {
+ Object daObj;
+ GList *daToks;
+ int i;
+
+ *red = *green = *blue = 0;
+
+ if (fieldLookup("DA", &daObj)->isString()) {
+
+ // parse the default appearance string
+ daToks = tokenize(daObj.getString());
+ for (i = 1; i < daToks->getLength(); ++i) {
+
+ // handle the g operator
+ if (!((GString *)daToks->get(i))->cmp("g")) {
+ *red = *green = *blue =
+ atof(((GString *)daToks->get(i - 1))->getCString());
+ break;
+
+ // handle the rg operator
+ } else if (i >= 3 && !((GString *)daToks->get(i))->cmp("rg")) {
+ *red = atof(((GString *)daToks->get(i - 3))->getCString());
+ *green = atof(((GString *)daToks->get(i - 2))->getCString());
+ *blue = atof(((GString *)daToks->get(i - 1))->getCString());
+ break;
+ }
+ }
+
+ deleteGList(daToks, GString);
+ }
+
+ daObj.free();
+}
+
+int AcroFormField::getMaxLen() {
+ Object obj;
+ int len;
+
+ if (fieldLookup("MaxLen", &obj)->isInt()) {
+ len = obj.getInt();
+ } else {
+ len = -1;
+ }
+ obj.free();
+ return len;
+}
+
+void AcroFormField::draw(int pageNum, Gfx *gfx, GBool printing) {
+ Object kidsObj, annotRef, annotObj;
+ int i;
+
+ // find the annotation object(s)
+ if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
+ for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
+ kidsObj.arrayGetNF(i, &annotRef);
+ annotRef.fetch(acroForm->doc->getXRef(), &annotObj);
+ drawAnnot(pageNum, gfx, printing, &annotRef, &annotObj);
+ annotObj.free();
+ annotRef.free();
+ }
+ } else {
+ drawAnnot(pageNum, gfx, printing, &fieldRef, &fieldObj);
+ }
+ kidsObj.free();
+}
+
+void AcroFormField::drawAnnot(int pageNum, Gfx *gfx, GBool printing,
+ Object *annotRef, Object *annotObj) {
+ Object obj1, obj2;
+ double xMin, yMin, xMax, yMax, t;
+ int annotFlags;
+ GBool oc, render;
+
+ if (!annotObj->isDict()) {
+ return;
+ }
+
+ //----- get the page number
+
+ // the "P" (page) field in annotations is optional, so we can't
+ // depend on it here
+ if (acroForm->lookupAnnotPage(annotRef) != pageNum) {
+ return;
+ }
+
+ //----- check annotation flags
+
+ if (annotObj->dictLookup("F", &obj1)->isInt()) {
+ annotFlags = obj1.getInt();
+ } else {
+ annotFlags = 0;
+ }
+ obj1.free();
+ if ((annotFlags & annotFlagHidden) ||
+ (printing && !(annotFlags & annotFlagPrint)) ||
+ (!printing && (annotFlags & annotFlagNoView))) {
+ return;
+ }
+
+ //----- check the optional content entry
+
+ annotObj->dictLookupNF("OC", &obj1);
+ if (acroForm->doc->getOptionalContent()->evalOCObject(&obj1, &oc) && !oc) {
+ obj1.free();
+ return;
+ }
+ obj1.free();
+
+ //----- get the bounding box
+
+ if (annotObj->dictLookup("Rect", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ xMin = yMin = xMax = yMax = 0;
+ if (obj1.arrayGet(0, &obj2)->isNum()) {
+ xMin = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGet(1, &obj2)->isNum()) {
+ yMin = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGet(2, &obj2)->isNum()) {
+ xMax = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGet(3, &obj2)->isNum()) {
+ yMax = obj2.getNum();
+ }
+ obj2.free();
+ if (xMin > xMax) {
+ t = xMin; xMin = xMax; xMax = t;
+ }
+ if (yMin > yMax) {
+ t = yMin; yMin = yMax; yMax = t;
+ }
+ } else {
+ error(errSyntaxError, -1, "Bad bounding box for annotation");
+ obj1.free();
+ return;
+ }
+ obj1.free();
+
+ //----- draw it
+
+ render = gFalse;
+ if (acroForm->needAppearances) {
+ render = gTrue;
+ } else if (xfaField && xfaField->getValue()) {
+ render = gTrue;
+ } else {
+ if (!annotObj->dictLookup("AP", &obj1)->isDict()) {
+ render = gTrue;
+ }
+ obj1.free();
+ }
+ if (render) {
+ drawNewAppearance(gfx, annotObj->getDict(),
+ xMin, yMin, xMax, yMax);
+ } else {
+ drawExistingAppearance(gfx, annotObj->getDict(),
+ xMin, yMin, xMax, yMax);
+ }
+}
+
+// Draw the existing appearance stream for a single annotation
+// attached to this field.
+void AcroFormField::drawExistingAppearance(Gfx *gfx, Dict *annot,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ Object apObj, asObj, appearance, obj1;
+
+ //----- get the appearance stream
+
+ if (annot->lookup("AP", &apObj)->isDict()) {
+ apObj.dictLookup("N", &obj1);
+ if (obj1.isDict()) {
+ if (annot->lookup("AS", &asObj)->isName()) {
+ obj1.dictLookupNF(asObj.getName(), &appearance);
+ } else if (obj1.dictGetLength() == 1) {
+ obj1.dictGetValNF(0, &appearance);
+ } else {
+ obj1.dictLookupNF("Off", &appearance);
+ }
+ asObj.free();
+ } else {
+ apObj.dictLookupNF("N", &appearance);
+ }
+ obj1.free();
+ }
+ apObj.free();
+
+ //----- draw it
+
+ if (!appearance.isNone()) {
+ gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax);
+ appearance.free();
+ }
+}
+
+// Regenerate the appearance for this field, and draw it.
+void AcroFormField::drawNewAppearance(Gfx *gfx, Dict *annot,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ GString *appearBuf;
+ Object appearance, mkObj, ftObj, appearDict, drObj, apObj, asObj;
+ Object resources, fontResources, defaultFont, gfxStateDict;
+ Object obj1, obj2, obj3, obj4;
+ Dict *mkDict;
+ MemStream *appearStream;
+ GfxFontDict *fontDict;
+ GBool hasCaption;
+ double dx, dy, r;
+ GString *val, *caption, *da;
+ GString **text;
+ GBool done;
+ GBool *selection;
+ AnnotBorderType borderType;
+ double borderWidth;
+ double *borderDash;
+ GString *appearanceState;
+ 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()) {
+ mkDict = mkObj.getDict();
+ } else {
+ mkDict = NULL;
+ }
+
+ // draw the background
+ if (mkDict) {
+ if (mkDict->lookup("BG", &obj1)->isArray() &&
+ obj1.arrayGetLength() > 0) {
+ setColor(obj1.getArray(), gTrue, 0, appearBuf);
+ appearBuf->appendf("0 0 {0:.4f} {1:.4f} re f\n",
+ xMax - xMin, yMax - yMin);
+ }
+ obj1.free();
+ }
+
+ // get the field type
+ fieldLookup("FT", &ftObj);
+
+ // draw the border
+ borderType = annotBorderSolid;
+ borderWidth = 1;
+ borderDash = NULL;
+ borderDashLength = 0;
+ if (annot->lookup("BS", &obj1)->isDict()) {
+ if (obj1.dictLookup("S", &obj2)->isName()) {
+ if (obj2.isName("S")) {
+ borderType = annotBorderSolid;
+ } else if (obj2.isName("D")) {
+ borderType = annotBorderDashed;
+ } else if (obj2.isName("B")) {
+ borderType = annotBorderBeveled;
+ } else if (obj2.isName("I")) {
+ borderType = annotBorderInset;
+ } else if (obj2.isName("U")) {
+ borderType = annotBorderUnderlined;
+ }
+ }
+ obj2.free();
+ if (obj1.dictLookup("W", &obj2)->isNum()) {
+ borderWidth = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.dictLookup("D", &obj2)->isArray()) {
+ borderDashLength = obj2.arrayGetLength();
+ borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
+ for (i = 0; i < borderDashLength; ++i) {
+ if (obj2.arrayGet(i, &obj3)->isNum()) {
+ borderDash[i] = obj3.getNum();
+ } else {
+ borderDash[i] = 1;
+ }
+ obj3.free();
+ }
+ }
+ obj2.free();
+ } else {
+ obj1.free();
+ if (annot->lookup("Border", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() >= 3) {
+ if (obj1.arrayGet(2, &obj2)->isNum()) {
+ borderWidth = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGetLength() >= 4) {
+ if (obj1.arrayGet(3, &obj2)->isArray()) {
+ borderType = annotBorderDashed;
+ borderDashLength = obj2.arrayGetLength();
+ borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
+ for (i = 0; i < borderDashLength; ++i) {
+ if (obj2.arrayGet(i, &obj3)->isNum()) {
+ borderDash[i] = obj3.getNum();
+ } else {
+ borderDash[i] = 1;
+ }
+ obj3.free();
+ }
+ } else {
+ // Adobe draws no border at all if the last element is of
+ // the wrong type.
+ borderWidth = 0;
+ }
+ obj2.free();
+ }
+ }
+ }
+ }
+ obj1.free();
+ if (mkDict) {
+ if (borderWidth > 0) {
+ mkDict->lookup("BC", &obj1);
+ if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {
+ obj1.free();
+ mkDict->lookup("BG", &obj1);
+ }
+ if (obj1.isArray() && obj1.arrayGetLength() > 0) {
+ dx = xMax - xMin;
+ dy = yMax - yMin;
+
+ // radio buttons with no caption have a round border
+ hasCaption = mkDict->lookup("CA", &obj2)->isString();
+ obj2.free();
+ if (ftObj.isName("Btn") && (flags & acroFormFlagRadio) && !hasCaption) {
+ r = 0.5 * (dx < dy ? dx : dy);
+ switch (borderType) {
+ case annotBorderDashed:
+ appearBuf->append("[");
+ for (i = 0; i < borderDashLength; ++i) {
+ appearBuf->appendf(" {0:.4f}", borderDash[i]);
+ }
+ appearBuf->append("] 0 d\n");
+ // fall through to the solid case
+ case annotBorderSolid:
+ case annotBorderUnderlined:
+ appearBuf->appendf("{0:.4f} w\n", borderWidth);
+ setColor(obj1.getArray(), gFalse, 0, appearBuf);
+ drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * borderWidth, "s",
+ appearBuf);
+ break;
+ case annotBorderBeveled:
+ case annotBorderInset:
+ appearBuf->appendf("{0:.4f} w\n", 0.5 * borderWidth);
+ setColor(obj1.getArray(), gFalse, 0, appearBuf);
+ drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * borderWidth, "s",
+ appearBuf);
+ setColor(obj1.getArray(), gFalse,
+ borderType == annotBorderBeveled ? 1 : -1,
+ appearBuf);
+ drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * borderWidth,
+ appearBuf);
+ setColor(obj1.getArray(), gFalse,
+ borderType == annotBorderBeveled ? -1 : 1,
+ appearBuf);
+ drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * borderWidth,
+ appearBuf);
+ break;
+ }
+
+ } else {
+ switch (borderType) {
+ case annotBorderDashed:
+ appearBuf->append("[");
+ for (i = 0; i < borderDashLength; ++i) {
+ appearBuf->appendf(" {0:.4f}", borderDash[i]);
+ }
+ appearBuf->append("] 0 d\n");
+ // fall through to the solid case
+ case annotBorderSolid:
+ appearBuf->appendf("{0:.4f} w\n", borderWidth);
+ setColor(obj1.getArray(), gFalse, 0, appearBuf);
+ appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re s\n",
+ 0.5 * borderWidth,
+ dx - borderWidth, dy - borderWidth);
+ break;
+ case annotBorderBeveled:
+ case annotBorderInset:
+ setColor(obj1.getArray(), gTrue,
+ borderType == annotBorderBeveled ? 1 : -1,
+ appearBuf);
+ appearBuf->append("0 0 m\n");
+ appearBuf->appendf("0 {0:.4f} l\n", dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ dx - borderWidth, dy - borderWidth);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ borderWidth, dy - borderWidth);
+ appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth);
+ appearBuf->append("f\n");
+ setColor(obj1.getArray(), gTrue,
+ borderType == annotBorderBeveled ? -1 : 1,
+ appearBuf);
+ appearBuf->append("0 0 m\n");
+ appearBuf->appendf("{0:.4f} 0 l\n", dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", dx, dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ dx - borderWidth, dy - borderWidth);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ dx - borderWidth, borderWidth);
+ appearBuf->appendf("{0:.4f} {0:.4f} l\n", borderWidth);
+ appearBuf->append("f\n");
+ break;
+ case annotBorderUnderlined:
+ appearBuf->appendf("{0:.4f} w\n", borderWidth);
+ setColor(obj1.getArray(), gFalse, 0, appearBuf);
+ appearBuf->appendf("0 0 m {0:.4f} 0 l s\n", dx);
+ break;
+ }
+
+ // clip to the inside of the border
+ appearBuf->appendf("{0:.4f} {0:.4f} {1:.4f} {2:.4f} re W n\n",
+ borderWidth,
+ dx - 2 * borderWidth, dy - 2 * borderWidth);
+ }
+ }
+ obj1.free();
+ }
+ }
+ gfree(borderDash);
+
+ // get the resource dictionary
+ buildDefaultResourceDict(&drObj);
+
+ // build the font dictionary
+ if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {
+ fontDict = new GfxFontDict(acroForm->doc->getXRef(), NULL, obj1.getDict());
+ } else {
+ fontDict = NULL;
+ }
+ obj1.free();
+
+ // get the default appearance string
+ if (fieldLookup("DA", &obj1)->isString()) {
+ da = obj1.getString()->copy();
+ } else {
+ da = NULL;
+ }
+ obj1.free();
+
+ // get the rotation value
+ rot = 0;
+ if (mkDict) {
+ if (mkDict->lookup("R", &obj1)->isInt()) {
+ rot = obj1.getInt();
+ }
+ obj1.free();
+ }
+
+ // get the appearance state
+ annot->lookup("AP", &apObj);
+ annot->lookup("AS", &asObj);
+ appearanceState = NULL;
+ if (asObj.isName()) {
+ appearanceState = new GString(asObj.getName());
+ } else if (apObj.isDict()) {
+ apObj.dictLookup("N", &obj1);
+ if (obj1.isDict() && obj1.dictGetLength() == 1) {
+ appearanceState = new GString(obj1.dictGetKey(0));
+ }
+ obj1.free();
+ }
+ if (!appearanceState) {
+ appearanceState = new GString("Off");
+ }
+ asObj.free();
+ apObj.free();
+
+ int valueLength;
+ Unicode *value = getValue(&valueLength);
+
+ // draw the field contents
+ if (ftObj.isName("Btn")) {
+ caption = NULL;
+ if (mkDict) {
+ if (mkDict->lookup("CA", &obj1)->isString()) {
+ caption = obj1.getString()->copy();
+ }
+ obj1.free();
+ }
+ // radio button
+ if (flags & acroFormFlagRadio) {
+ //~ Acrobat doesn't draw a caption if there is no AP dict (?)
+ if (value && unicodeStringEqual(value, valueLength,
+ appearanceState->getCString())) {
+ if (caption) {
+ 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() &&
+ obj2.arrayGetLength() > 0) {
+ dx = xMax - xMin;
+ dy = yMax - yMin;
+ setColor(obj2.getArray(), gTrue, 0, appearBuf);
+ drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy), "f",
+ appearBuf);
+ }
+ obj2.free();
+ }
+ }
+ }
+ // pushbutton
+ } else if (flags & acroFormFlagPushbutton) {
+ if (caption) {
+ drawText(caption, da, fontDict, gFalse, 0,
+ acroFormQuadCenter, acroFormVAlignMiddle,
+ gFalse, gFalse, rot, 0, 0, xMax - xMin, yMax - yMin,
+ borderWidth, gFalse, appearBuf);
+ }
+ // checkbox
+ } else {
+ 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, acroFormVAlignMiddleNoDescender,
+ gFalse, gTrue, rot, 0, 0, xMax - xMin, yMax - yMin,
+ borderWidth, gFalse, appearBuf);
+ }
+ }
+ if (caption) {
+ delete caption;
+ }
+ } else if (ftObj.isName("Tx")) {
+ 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 {
+ 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;
+ }
+ }
+ delete valueLatin1;
+ }
+ } else if (ftObj.isName("Ch")) {
+ //~ value/option strings can be Unicode
+ if (fieldLookup("Q", &obj1)->isInt()) {
+ quadding = obj1.getInt();
+ } else {
+ 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 (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);
+ if (obj3.isArray() && obj3.arrayGetLength() == 2) {
+ if (obj3.arrayGet(0, &obj4)->isString() &&
+ obj4.getString()->cmp(val) == 0) {
+ obj4.free();
+ if (obj3.arrayGet(1, &obj4)->isString()) {
+ delete val;
+ val = obj4.getString()->copy();
+ }
+ done = gTrue;
+ }
+ obj4.free();
+ }
+ obj3.free();
+ }
+ }
+ obj2.free();
+ drawText(val, da, fontDict,
+ 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
+ }
+ // list box
+ } else {
+ if (fieldObj.dictLookup("Opt", &obj1)->isArray()) {
+ nOptions = obj1.arrayGetLength();
+ // get the option text
+ text = (GString **)gmallocn(nOptions, sizeof(GString *));
+ for (i = 0; i < nOptions; ++i) {
+ text[i] = NULL;
+ obj1.arrayGet(i, &obj2);
+ if (obj2.isString()) {
+ text[i] = obj2.getString()->copy();
+ } else if (obj2.isArray() && obj2.arrayGetLength() == 2) {
+ if (obj2.arrayGet(1, &obj3)->isString()) {
+ text[i] = obj3.getString()->copy();
+ }
+ obj3.free();
+ }
+ obj2.free();
+ if (!text[i]) {
+ text[i] = new GString();
+ }
+ }
+ // get the selected option(s)
+ selection = (GBool *)gmallocn(nOptions, sizeof(GBool));
+ //~ need to use the I field in addition to the V field
+ for (i = 0; i < nOptions; ++i) {
+ selection[i] = unicodeStringEqual(value, valueLength, text[i]);
+ }
+ // get the top index
+ if (fieldObj.dictLookup("TI", &obj2)->isInt()) {
+ topIdx = obj2.getInt();
+ if (topIdx < 0 || topIdx >= nOptions) {
+ topIdx = 0;
+ }
+ } else {
+ topIdx = 0;
+ }
+ obj2.free();
+ // draw the text
+ drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding,
+ xMin, yMin, xMax, yMax, borderWidth, appearBuf);
+ for (i = 0; i < nOptions; ++i) {
+ delete text[i];
+ }
+ gfree(text);
+ gfree(selection);
+ }
+ obj1.free();
+ }
+ } else if (ftObj.isName("Sig")) {
+ //~ check to see if background is already drawn
+ gfxStateDict.initDict(acroForm->doc->getXRef());
+ obj1.initReal(0.5);
+ gfxStateDict.dictAdd(copyString("ca"), &obj1);
+ appearBuf->append("/GS1 gs\n");
+ appearBuf->appendf("0.7 0.7 1 rg 0 0 {0:.2f} {1:.2f} re f\n",
+ xMax - xMin, yMax - yMin);
+ caption = new GString("SIGN HERE");
+ if (da) {
+ delete da;
+ }
+ da = new GString("/Helv 10 Tf 1 0 0 rg");
+ 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;
+ }
+
+ // build the appearance stream dictionary
+ appearDict.initDict(acroForm->doc->getXRef());
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(acroForm->doc->getXRef());
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(xMax - xMin));
+ obj1.arrayAdd(obj2.initReal(yMax - yMin));
+ appearDict.dictAdd(copyString("BBox"), &obj1);
+
+ // set the resource dictionary; add a default font
+ if (drObj.isDict()) {
+ drObj.copy(&resources);
+ } else {
+ resources.initDict(acroForm->doc->getXRef());
+ }
+ drObj.free();
+ fontResources.initDict(acroForm->doc->getXRef());
+ if (resources.dictLookup("Font", &obj1)->isDict()) {
+ for (i = 0; i < obj1.dictGetLength(); ++i) {
+ obj1.dictGetValNF(i, &obj2);
+ fontResources.dictAdd(copyString(obj1.dictGetKey(i)), &obj2);
+ }
+ }
+ obj1.free();
+ defaultFont.initDict(acroForm->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);
+ resources.dictAdd(copyString("Font"), &fontResources);
+ if (gfxStateDict.isDict()) {
+ obj1.initDict(acroForm->doc->getXRef());
+ obj1.dictAdd(copyString("GS1"), &gfxStateDict);
+ resources.dictAdd(copyString("ExtGState"), &obj1);
+ }
+ appearDict.dictAdd(copyString("Resources"), &resources);
+
+ // build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.initStream(appearStream);
+
+ // draw it
+ gfx->drawAnnot(&appearance, NULL, xMin, yMin, xMax, yMax);
+
+ appearance.free();
+ delete appearBuf;
+ appearBuf = NULL;
+ if (fontDict) {
+ delete fontDict;
+ }
+ ftObj.free();
+ mkObj.free();
+}
+
+// Set the current fill or stroke color, based on <a> (which should
+// have 1, 3, or 4 elements). If <adjust> is +1, color is brightened;
+// if <adjust> is -1, color is darkened; otherwise color is not
+// modified.
+void AcroFormField::setColor(Array *a, GBool fill, int adjust,
+ GString *appearBuf) {
+ Object obj1;
+ double color[4];
+ int nComps, i;
+
+ nComps = a->getLength();
+ if (nComps > 4) {
+ nComps = 4;
+ }
+ for (i = 0; i < nComps && i < 4; ++i) {
+ if (a->get(i, &obj1)->isNum()) {
+ color[i] = obj1.getNum();
+ } else {
+ color[i] = 0;
+ }
+ obj1.free();
+ }
+ if (nComps == 4) {
+ adjust = -adjust;
+ }
+ if (adjust > 0) {
+ for (i = 0; i < nComps; ++i) {
+ color[i] = 0.5 * color[i] + 0.5;
+ }
+ } else if (adjust < 0) {
+ for (i = 0; i < nComps; ++i) {
+ color[i] = 0.5 * color[i];
+ }
+ }
+ if (nComps == 4) {
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n",
+ color[0], color[1], color[2], color[3],
+ fill ? 'k' : 'K');
+ } else if (nComps == 3) {
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:s}\n",
+ color[0], color[1], color[2],
+ fill ? "rg" : "RG");
+ } else {
+ appearBuf->appendf("{0:.2f} {1:c}\n",
+ color[0],
+ fill ? 'g' : 'G');
+ }
+}
+
+// Draw the variable text or caption for a field.
+void AcroFormField::drawText(GString *text, GString *da, GfxFontDict *fontDict,
+ GBool multiline, int comb,
+ int quadding, int vAlign,
+ GBool txField, GBool forceZapfDingbats, int rot,
+ 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, 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
+ //~ (this is only relevant for Tx fields)
+
+ // check for a Unicode string
+ //~ this currently drops all non-Latin1 characters
+ if (text->getLength() >= 2 &&
+ text->getChar(0) == '\xfe' && text->getChar(1) == '\xff') {
+ text2 = new GString();
+ for (i = 2; i+1 < text->getLength(); i += 2) {
+ c = ((text->getChar(i) & 0xff) << 8) + (text->getChar(i+1) & 0xff);
+ if (c <= 0xff) {
+ text2->append((char)c);
+ } else {
+ text2->append('?');
+ }
+ }
+ } else {
+ text2 = text;
+ }
+ if (text2->getLength() == 0) {
+ if (text2 != text) {
+ delete text2;
+ }
+ return;
+ }
+
+ // parse the default appearance string
+ tfPos = tmPos = -1;
+ if (da) {
+ daToks = tokenize(da);
+ for (i = 2; i < daToks->getLength(); ++i) {
+ if (!((GString *)daToks->get(i))->cmp("Tf")) {
+ tfPos = i - 2;
+ } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
+ tmPos = i - 6;
+ }
+ }
+ } else {
+ daToks = NULL;
+ }
+
+ // force ZapfDingbats
+ //~ this should create the font if needed (?)
+ if (forceZapfDingbats) {
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos);
+ if (tok->cmp("/ZaDb")) {
+ tok->clear();
+ tok->append("/ZaDb");
+ }
+ }
+ }
+
+ // get the font and font size
+ font = NULL;
+ fontSize = 0;
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos);
+ if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
+ if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
+ error(errSyntaxError, -1, "Unknown font in field's DA string");
+ tok->clear();
+ tok->append("/xpdf_default_font");
+ }
+ } else {
+ error(errSyntaxError, -1,
+ "Invalid font name in 'Tf' operator in field's DA string");
+ }
+ tok = (GString *)daToks->get(tfPos + 1);
+ fontSize = atof(tok->getCString());
+ } else {
+ error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
+ fontSize = 0;
+ if (!daToks) {
+ daToks = new GList();
+ }
+ tfPos = daToks->getLength();
+ daToks->append(new GString("/xpdf_default_font"));
+ daToks->append(new GString("10"));
+ daToks->append(new GString("Tf"));
+ }
+
+ // setup
+ if (txField) {
+ appearBuf->append("/Tx BMC\n");
+ }
+ appearBuf->append("q\n");
+ if (rot == 90) {
+ 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", width, height);
+ dx = width;
+ dy = height;
+ } else if (rot == 270) {
+ appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", height);
+ dx = height;
+ dy = width;
+ } else { // assume rot == 0
+ dx = width;
+ dy = height;
+ }
+
+ // multi-line text
+ if (multiline) {
+ // note: the comb flag is ignored in multiline mode
+
+ 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) {
+ yy = dy - topBorder;
+ i = 0;
+ while (i < text2->getLength()) {
+ getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
+ i = k;
+ yy -= fontSize;
+ }
+ // approximate the descender for the last line
+ if (yy >= 0.25 * fontSize && w <= wMax) {
+ break;
+ }
+ }
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos + 1);
+ tok->clear();
+ tok->appendf("{0:.2f}", fontSize);
+ }
+ }
+
+ // starting y coordinate
+ 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 {
+ 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->appendf("{0:.4f}", x);
+ tok = (GString *)daToks->get(tmPos + 5);
+ tok->clear();
+ tok->appendf("{0:.4f}", y + yy);
+ }
+
+ // write the DA string
+ if (daToks) {
+ for (i = 0; i < daToks->getLength(); ++i) {
+ appearBuf->append((GString *)daToks->get(i))->append(' ');
+ }
+ }
+
+ // 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 + yy);
+ }
+
+ // write a series of lines of text
+ i = 0;
+ xPrev = 0;
+ while (i < text2->getLength()) {
+
+ getNextLine(text2, i, font, fontSize, wMax, &j, &w, &k);
+
+ // compute text start position
+ switch (quadding) {
+ case acroFormQuadLeft:
+ default:
+ xx = border + 2;
+ break;
+ case acroFormQuadCenter:
+ xx = (dx - w) / 2;
+ break;
+ case acroFormQuadRight:
+ xx = dx - border - 2 - w;
+ break;
+ }
+
+ // draw the line
+ appearBuf->appendf("{0:.4f} {1:.4f} Td\n", xx - xPrev, -fontSize);
+ 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;
+ xPrev = xx;
+ }
+
+ appearBuf->append("ET\n");
+
+ // single-line text
+ } else {
+ //~ replace newlines with spaces? - what does Acrobat do?
+
+ // comb formatting
+ if (comb > 0) {
+
+ // compute comb spacing
+ w = dx / comb;
+
+ // compute font autosize
+ if (fontSize == 0) {
+ fontSize = dy - 2 * border;
+ if (w < fontSize) {
+ fontSize = w;
+ }
+ fontSize = floor(fontSize);
+ if (fontSize > 10) {
+ fontSize = 10;
+ }
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos + 1);
+ tok->clear();
+ tok->appendf("{0:.4f}", fontSize);
+ }
+ }
+
+ // compute text start position
+ switch (quadding) {
+ case acroFormQuadLeft:
+ default:
+ xx = 0;
+ break;
+ case acroFormQuadCenter:
+ xx = ((comb - text2->getLength()) / 2) * w;
+ break;
+ case acroFormQuadRight:
+ xx = (comb - text2->getLength()) * w;
+ break;
+ }
+ if (font) {
+ ascent = font->getDeclaredAscent() * fontSize;
+ descent = font->getDescent() * fontSize;
+ } else {
+ ascent = 0.75 * fontSize;
+ descent = -0.25 * fontSize;
+ }
+ switch (vAlign) {
+ case acroFormVAlignTop:
+ default:
+ yy = dy - ascent;
+ break;
+ case acroFormVAlignMiddle:
+ yy = 0.5 * (dy - ascent - descent);
+ break;
+ case acroFormVAlignMiddleNoDescender:
+ yy = 0.5 * (dy - ascent);
+ break;
+ case acroFormVAlignBottom:
+ yy = -descent;
+ break;
+ }
+
+ appearBuf->append("BT\n");
+
+ // set the font matrix
+ if (tmPos >= 0) {
+ tok = (GString *)daToks->get(tmPos + 4);
+ tok->clear();
+ tok->appendf("{0:.4f}", x + xx);
+ tok = (GString *)daToks->get(tmPos + 5);
+ tok->clear();
+ tok->appendf("{0:.4f}", y + yy);
+ }
+
+ // write the DA string
+ if (daToks) {
+ for (i = 0; i < daToks->getLength(); ++i) {
+ appearBuf->append((GString *)daToks->get(i))->append(' ');
+ }
+ }
+
+ // 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 + xx, y + yy);
+ }
+
+ // write the text string
+ offset = 0;
+ for (i = 0; i < text2->getLength(); ++i) {
+ c = text2->getChar(i) & 0xff;
+ 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 {
+ offset += w;
+ }
+ }
+
+ appearBuf->append("ET\n");
+
+ // regular (non-comb) formatting
+ } else {
+
+ // compute string width
+ if (font && !font->isCIDFont()) {
+ w = 0;
+ for (i = 0; i < text2->getLength(); ++i) {
+ w += ((Gfx8BitFont *)font)->getWidth(text2->getChar(i));
+ }
+ } else {
+ // otherwise, make a crude estimate
+ w = text2->getLength() * 0.5;
+ }
+
+ // compute font autosize
+ if (fontSize == 0) {
+ fontSize = dy - 2 * border;
+ fontSize2 = (dx - 4 - 2 * border) / w;
+ if (fontSize2 < fontSize) {
+ fontSize = fontSize2;
+ }
+ fontSize = floor(fontSize);
+ if (fontSize > 10) {
+ fontSize = 10;
+ }
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos + 1);
+ tok->clear();
+ tok->appendf("{0:.4f}", fontSize);
+ }
+ }
+
+ // compute text start position
+ w *= fontSize;
+ switch (quadding) {
+ case acroFormQuadLeft:
+ default:
+ xx = border + 2;
+ break;
+ case acroFormQuadCenter:
+ xx = (dx - w) / 2;
+ break;
+ case acroFormQuadRight:
+ xx = dx - border - 2 - w;
+ break;
+ }
+ if (font) {
+ ascent = font->getDeclaredAscent() * fontSize;
+ descent = font->getDescent() * fontSize;
+ } else {
+ ascent = 0.75 * fontSize;
+ descent = -0.25 * fontSize;
+ }
+ switch (vAlign) {
+ case acroFormVAlignTop:
+ default:
+ yy = dy - ascent;
+ break;
+ case acroFormVAlignMiddle:
+ yy = 0.5 * (dy - ascent - descent);
+ break;
+ case acroFormVAlignMiddleNoDescender:
+ yy = 0.5 * (dy - ascent);
+ break;
+ case acroFormVAlignBottom:
+ yy = -descent;
+ break;
+ }
+
+ 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 + xx);
+ tok = (GString *)daToks->get(tmPos + 5);
+ tok->clear();
+ tok->appendf("{0:.4f}", y + yy);
+ }
+
+ // write the DA string
+ if (daToks) {
+ for (i = 0; i < daToks->getLength(); ++i) {
+ appearBuf->append((GString *)daToks->get(i))->append(' ');
+ }
+ }
+
+ // 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 + xx, y + 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");
+ }
+
+ appearBuf->append("ET\n");
+ }
+
+ // cleanup
+ appearBuf->append("Q\n");
+ if (txField) {
+ appearBuf->append("EMC\n");
+ }
+
+ if (daToks) {
+ deleteGList(daToks, GString);
+ }
+ if (text2 != text) {
+ delete text2;
+ }
+}
+
+// Draw the variable text or caption for a field.
+void AcroFormField::drawListBox(GString **text, GBool *selection,
+ int nOptions, int topIdx,
+ GString *da, GfxFontDict *fontDict,
+ GBool quadding, double xMin, double yMin,
+ double xMax, double yMax, double border,
+ GString *appearBuf) {
+ GList *daToks;
+ GString *tok;
+ GfxFont *font;
+ double fontSize, fontSize2, x, y, w, wMax;
+ int tfPos, tmPos, i, j, c;
+
+ //~ if there is no MK entry, this should use the existing content stream,
+ //~ and only replace the marked content portion of it
+ //~ (this is only relevant for Tx fields)
+
+ // parse the default appearance string
+ tfPos = tmPos = -1;
+ if (da) {
+ daToks = tokenize(da);
+ for (i = 2; i < daToks->getLength(); ++i) {
+ if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
+ tfPos = i - 2;
+ } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
+ tmPos = i - 6;
+ }
+ }
+ } else {
+ daToks = NULL;
+ }
+
+ // get the font and font size
+ font = NULL;
+ fontSize = 0;
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos);
+ if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
+ if (!fontDict || !(font = fontDict->lookup(tok->getCString() + 1))) {
+ error(errSyntaxError, -1, "Unknown font in field's DA string");
+ tok->clear();
+ tok->append("/xpdf_default_font");
+ }
+ } else {
+ error(errSyntaxError, -1,
+ "Invalid font name in 'Tf' operator in field's DA string");
+ }
+ tok = (GString *)daToks->get(tfPos + 1);
+ fontSize = atof(tok->getCString());
+ } else {
+ error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
+ }
+
+ // compute font autosize
+ if (fontSize == 0) {
+ wMax = 0;
+ for (i = 0; i < nOptions; ++i) {
+ if (font && !font->isCIDFont()) {
+ w = 0;
+ for (j = 0; j < text[i]->getLength(); ++j) {
+ w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
+ }
+ } else {
+ // otherwise, make a crude estimate
+ w = text[i]->getLength() * 0.5;
+ }
+ if (w > wMax) {
+ wMax = w;
+ }
+ }
+ fontSize = yMax - yMin - 2 * border;
+ fontSize2 = (xMax - xMin - 4 - 2 * border) / wMax;
+ if (fontSize2 < fontSize) {
+ fontSize = fontSize2;
+ }
+ fontSize = floor(fontSize);
+ if (fontSize > 10) {
+ fontSize = 10;
+ }
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos + 1);
+ tok->clear();
+ tok->appendf("{0:.4f}", fontSize);
+ }
+ }
+
+ // draw the text
+ y = yMax - yMin - 1.1 * fontSize;
+ for (i = topIdx; i < nOptions; ++i) {
+
+ // setup
+ appearBuf->append("q\n");
+
+ // draw the background if selected
+ if (selection[i]) {
+ appearBuf->append("0 g f\n");
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re f\n",
+ border,
+ y - 0.2 * fontSize,
+ xMax - xMin - 2 * border,
+ 1.1 * fontSize);
+ }
+
+ // setup
+ appearBuf->append("BT\n");
+
+ // compute string width
+ if (font && !font->isCIDFont()) {
+ w = 0;
+ for (j = 0; j < text[i]->getLength(); ++j) {
+ w += ((Gfx8BitFont *)font)->getWidth(text[i]->getChar(j));
+ }
+ } else {
+ // otherwise, make a crude estimate
+ w = text[i]->getLength() * 0.5;
+ }
+
+ // compute text start position
+ w *= fontSize;
+ switch (quadding) {
+ case acroFormQuadLeft:
+ default:
+ x = border + 2;
+ break;
+ case acroFormQuadCenter:
+ x = (xMax - xMin - w) / 2;
+ break;
+ case acroFormQuadRight:
+ x = xMax - xMin - border - 2 - w;
+ break;
+ }
+
+ // set the font matrix
+ if (tmPos >= 0) {
+ tok = (GString *)daToks->get(tmPos + 4);
+ tok->clear();
+ tok->appendf("{0:.4f}", x);
+ tok = (GString *)daToks->get(tmPos + 5);
+ tok->clear();
+ tok->appendf("{0:.4f}", y);
+ }
+
+ // write the DA string
+ if (daToks) {
+ for (j = 0; j < daToks->getLength(); ++j) {
+ appearBuf->append((GString *)daToks->get(j))->append(' ');
+ }
+ }
+
+ // 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);
+ }
+
+ // change the text color if selected
+ if (selection[i]) {
+ appearBuf->append("1 g\n");
+ }
+
+ // write the text string
+ appearBuf->append('(');
+ for (j = 0; j < text[i]->getLength(); ++j) {
+ c = text[i]->getChar(j) & 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");
+ appearBuf->append("Q\n");
+
+ // next line
+ y -= 1.1 * fontSize;
+ }
+
+ if (daToks) {
+ deleteGList(daToks, GString);
+ }
+}
+
+// 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 AcroFormField::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;
+}
+
+// Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
+// <cmd> is used to draw the circle ("f", "s", or "b").
+void AcroFormField::drawCircle(double cx, double cy, double r,
+ const char *cmd, GString *appearBuf) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ cx + r, cy);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + r, cy + bezierCircle * r,
+ cx + bezierCircle * r, cy + r,
+ cx, cy + r);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - bezierCircle * r, cy + r,
+ cx - r, cy + bezierCircle * r,
+ cx - r, cy);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - r, cy - bezierCircle * r,
+ cx - bezierCircle * r, cy - r,
+ cx, cy - r);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + bezierCircle * r, cy - r,
+ cx + r, cy - bezierCircle * r,
+ cx + r, cy);
+ appearBuf->appendf("{0:s}\n", cmd);
+}
+
+// Draw the top-left half of an (approximate) circle of radius <r>
+// centered at (<cx>, <cy>).
+void AcroFormField::drawCircleTopLeft(double cx, double cy, double r,
+ GString *appearBuf) {
+ double r2;
+
+ r2 = r / sqrt(2.0);
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ cx + r2, cy + r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + (1 - bezierCircle) * r2,
+ cy + (1 + bezierCircle) * r2,
+ cx - (1 - bezierCircle) * r2,
+ cy + (1 + bezierCircle) * r2,
+ cx - r2,
+ cy + r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - (1 + bezierCircle) * r2,
+ cy + (1 - bezierCircle) * r2,
+ cx - (1 + bezierCircle) * r2,
+ cy - (1 - bezierCircle) * r2,
+ cx - r2,
+ cy - r2);
+ appearBuf->append("S\n");
+}
+
+// Draw the bottom-right half of an (approximate) circle of radius <r>
+// centered at (<cx>, <cy>).
+void AcroFormField::drawCircleBottomRight(double cx, double cy, double r,
+ GString *appearBuf) {
+ double r2;
+
+ r2 = r / sqrt(2.0);
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ cx - r2, cy - r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - (1 - bezierCircle) * r2,
+ cy - (1 + bezierCircle) * r2,
+ cx + (1 - bezierCircle) * r2,
+ cy - (1 + bezierCircle) * r2,
+ cx + r2,
+ cy - r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + (1 + bezierCircle) * r2,
+ cy - (1 - bezierCircle) * r2,
+ cx + (1 + bezierCircle) * r2,
+ cy + (1 - bezierCircle) * r2,
+ cx + r2,
+ cy + r2);
+ 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;
+
+ toks = new GList();
+ i = 0;
+ while (i < s->getLength()) {
+ while (i < s->getLength() && Lexer::isSpace(s->getChar(i))) {
+ ++i;
+ }
+ if (i < s->getLength()) {
+ for (j = i + 1;
+ j < s->getLength() && !Lexer::isSpace(s->getChar(j));
+ ++j) ;
+ toks->append(new GString(s, i, j - i));
+ i = j;
+ }
+ }
+ return toks;
+}
+
+Object *AcroFormField::getResources(Object *res) {
+ Object kidsObj, annotObj, obj1;
+ int i;
+
+ if (acroForm->needAppearances) {
+ fieldLookup("DR", res);
+ } else {
+ res->initArray(acroForm->doc->getXRef());
+ // find the annotation object(s)
+ if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
+ for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
+ kidsObj.arrayGet(i, &annotObj);
+ if (annotObj.isDict()) {
+ if (getAnnotResources(annotObj.getDict(), &obj1)->isDict()) {
+ res->arrayAdd(&obj1);
+ } else {
+ obj1.free();
+ }
+ }
+ annotObj.free();
+ }
+ } else {
+ if (getAnnotResources(fieldObj.getDict(), &obj1)->isDict()) {
+ res->arrayAdd(&obj1);
+ } else {
+ obj1.free();
+ }
+ }
+ kidsObj.free();
+ }
+
+ return res;
+}
+
+Object *AcroFormField::getFieldRef(Object *ref) {
+ return fieldRef.copy(ref);
+}
+
+Object *AcroFormField::getValueObj(Object *val) {
+ return fieldLookup("V", val);
+}
+
+Object *AcroFormField::getParentRef(Object *parent) {
+ return fieldObj.dictLookupNF("Parent", parent);
+}
+
+// Get the first annotation object associated with this field.
+Object *AcroFormField::getAnnotObj(Object *annotObj) {
+ Object kidsObj;
+
+ if (fieldObj.dictLookup("Kids", &kidsObj)->isArray()) {
+ if (kidsObj.arrayGetLength() > 0) {
+ kidsObj.arrayGet(0, annotObj);
+ } else {
+ annotObj->initNull();
+ }
+ } else {
+ fieldObj.copy(annotObj);
+ }
+ kidsObj.free();
+ return annotObj;
+}
+
+Object *AcroFormField::getAnnotResources(Dict *annot, Object *res) {
+ Object apObj, asObj, appearance, obj1;
+
+ // get the appearance stream
+ if (annot->lookup("AP", &apObj)->isDict()) {
+ apObj.dictLookup("N", &obj1);
+ if (obj1.isDict()) {
+ if (annot->lookup("AS", &asObj)->isName()) {
+ obj1.dictLookup(asObj.getName(), &appearance);
+ } else if (obj1.dictGetLength() == 1) {
+ obj1.dictGetVal(0, &appearance);
+ } else {
+ obj1.dictLookup("Off", &appearance);
+ }
+ asObj.free();
+ } else {
+ obj1.copy(&appearance);
+ }
+ obj1.free();
+ }
+ apObj.free();
+
+ if (appearance.isStream()) {
+ appearance.streamGetDict()->lookup("Resources", res);
+ } else {
+ res->initNull();
+ }
+ appearance.free();
+
+ return res;
+}
+
+// Merge the field and AcroForm DR objects.
+void AcroFormField::buildDefaultResourceDict(Object *dr) {
+ Object formDR, fieldDR, resDict, newResDict, resObj;
+ char *resType, *resName;
+ int i, j;
+
+ // NB: we need to deep-copy the dictionaries here, because multiple
+ // threads can be sharing these objects.
+
+ dr->initDict(acroForm->doc->getXRef());
+
+ acroForm->acroFormObj.dictLookup("DR", &formDR);
+ if (formDR.isDict()) {
+ for (i = 0; i < formDR.dictGetLength(); ++i) {
+ resType = formDR.dictGetKey(i);
+ formDR.dictGetVal(i, &resDict);
+ if (resDict.isDict()) {
+ newResDict.initDict(acroForm->doc->getXRef());
+ dr->dictAdd(copyString(resType), &newResDict);
+ for (j = 0; j < resDict.dictGetLength(); ++j) {
+ resName = resDict.dictGetKey(j);
+ resDict.dictGetValNF(j, &resObj);
+ newResDict.dictAdd(copyString(resName), &resObj);
+ }
+ }
+ resDict.free();
+ }
+ }
+ formDR.free();
+
+ fieldObj.dictLookup("DR", &fieldDR);
+ if (fieldDR.isDict()) {
+ for (i = 0; i < fieldDR.dictGetLength(); ++i) {
+ resType = fieldDR.dictGetKey(i);
+ fieldDR.dictGetVal(i, &resDict);
+ if (resDict.isDict()) {
+ dr->dictLookup(resType, &newResDict);
+ if (!newResDict.isDict()) {
+ newResDict.free();
+ newResDict.initDict(acroForm->doc->getXRef());
+ }
+ dr->dictAdd(copyString(resType), &newResDict);
+ for (j = 0; j < resDict.dictGetLength(); ++j) {
+ resName = resDict.dictGetKey(j);
+ resDict.dictGetValNF(j, &resObj);
+ newResDict.dictAdd(copyString(resName), &resObj);
+ }
+ }
+ resDict.free();
+ }
+ }
+ fieldDR.free();
+}
+
+// Look up an inheritable field dictionary entry.
+Object *AcroFormField::fieldLookup(const char *key, Object *obj) {
+ return fieldLookup(fieldObj.getDict(), key, obj);
+}
+
+Object *AcroFormField::fieldLookup(Dict *dict, const char *key, Object *obj) {
+ Object parent, parent2;
+ int depth;
+
+ if (!dict->lookup(key, obj)->isNull()) {
+ return obj;
+ }
+ obj->free();
+
+ dict->lookup("Parent", &parent)->isDict();
+ depth = 0;
+ while (parent.isDict() && depth < maxFieldObjectDepth) {
+ if (!parent.dictLookup(key, obj)->isNull()) {
+ parent.free();
+ return obj;
+ }
+ obj->free();
+ parent.dictLookup("Parent", &parent2);
+ parent.free();
+ parent = parent2;
+ ++depth;
+ }
+ parent.free();
+
+ // some fields don't specify a parent, so we check the AcroForm
+ // dictionary just in case
+ acroForm->acroFormObj.dictLookup(key, obj);
+ return obj;
+}
+
+Unicode *AcroFormField::utf8ToUnicode(GString *s, int *unicodeLength) {
+ int n = 0;
+ int i = 0;
+ Unicode u;
+ while (getUTF8(s, &i, &u)) {
+ ++n;
+ }
+ Unicode *uVec = (Unicode *)gmallocn(n, sizeof(Unicode));
+ n = 0;
+ i = 0;
+ while (getUTF8(s, &i, &uVec[n])) {
+ ++n;
+ }
+ *unicodeLength = n;
+ return uVec;
+}
+
+GString *AcroFormField::unicodeToLatin1(Unicode *u, int unicodeLength) {
+ GString *s = new GString();
+ for (int i = 0; i < unicodeLength; ++i) {
+ if (u[i] <= 0xff) {
+ s->append((char)u[i]);
+ }
+ }
+ return s;
+}
+
+GBool AcroFormField::unicodeStringEqual(Unicode *u, int unicodeLength,
+ GString *s) {
+ if (s->getLength() != unicodeLength) {
+ return gFalse;
+ }
+ for (int i = 0; i < unicodeLength; ++i) {
+ if ((s->getChar(i) & 0xff) != u[i]) {
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
+GBool AcroFormField::unicodeStringEqual(Unicode *u, int unicodeLength,
+ const char *s) {
+ for (int i = 0; i < unicodeLength; ++i) {
+ if (!s[i] || (s[i] & 0xff) != u[i]) {
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// 'picture' formatting
+//------------------------------------------------------------------------
+
+class PictureNode {
+public:
+ virtual ~PictureNode() {}
+ virtual GBool isLiteral() { return gFalse; }
+ virtual GBool isSign() { return gFalse; }
+ virtual GBool isDigit() { return gFalse; }
+ virtual GBool isDecPt() { return gFalse; }
+ virtual GBool isSeparator() { return gFalse; }
+ virtual GBool isYear() { return gFalse; }
+ virtual GBool isMonth() { return gFalse; }
+ virtual GBool isDay() { return gFalse; }
+ virtual GBool isHour() { return gFalse; }
+ virtual GBool isMinute() { return gFalse; }
+ virtual GBool isSecond() { return gFalse; }
+ virtual GBool isChar() { return gFalse; }
+};
+
+class PictureLiteral: public PictureNode {
+public:
+ PictureLiteral(GString *sA) { s = sA; }
+ virtual ~PictureLiteral() { delete s; }
+ virtual GBool isLiteral() { return gTrue; }
+ GString *s;
+};
+
+class PictureSign: public PictureNode {
+public:
+ PictureSign(char cA) { c = cA; }
+ virtual GBool isSign() { return gTrue; }
+ char c;
+};
+
+class PictureDigit: public PictureNode {
+public:
+ PictureDigit(char cA) { c = cA; pos = 0; }
+ virtual GBool isDigit() { return gTrue; }
+ char c;
+ int pos;
+};
+
+class PictureDecPt: public PictureNode {
+public:
+ PictureDecPt() { }
+ virtual GBool isDecPt() { return gTrue; }
+};
+
+class PictureSeparator: public PictureNode {
+public:
+ PictureSeparator() { }
+ virtual GBool isSeparator() { return gTrue; }
+};
+
+class PictureYear: public PictureNode {
+public:
+ PictureYear(int nDigitsA) { nDigits = nDigitsA; }
+ virtual GBool isYear() { return gTrue; }
+ int nDigits;
+};
+
+class PictureMonth: public PictureNode {
+public:
+ PictureMonth(int nDigitsA) { nDigits = nDigitsA; }
+ virtual GBool isMonth() { return gTrue; }
+ int nDigits;
+};
+
+class PictureDay: public PictureNode {
+public:
+ PictureDay(int nDigitsA) { nDigits = nDigitsA; }
+ virtual GBool isDay() { return gTrue; }
+ int nDigits;
+};
+
+class PictureHour: public PictureNode {
+public:
+ PictureHour(GBool is24HourA, int nDigitsA)
+ { is24Hour = is24HourA; nDigits = nDigitsA; }
+ virtual GBool isHour() { return gTrue; }
+ GBool is24Hour;
+ int nDigits;
+};
+
+class PictureMinute: public PictureNode {
+public:
+ PictureMinute(int nDigitsA) { nDigits = nDigitsA; }
+ virtual GBool isMinute() { return gTrue; }
+ int nDigits;
+};
+
+class PictureSecond: public PictureNode {
+public:
+ PictureSecond(int nDigitsA) { nDigits = nDigitsA; }
+ virtual GBool isSecond() { return gTrue; }
+ int nDigits;
+};
+
+class PictureChar: public PictureNode {
+public:
+ PictureChar() {}
+ virtual GBool isChar() { return gTrue; }
+};
+
+GString *AcroFormField::pictureFormatDateTime(GString *value,
+ GString *picture) {
+ GList *pic;
+ PictureNode *node;
+ GString *ret, *s;
+ char c;
+ int year, month, day, hour, min, sec;
+ int len, picStart, picEnd, u, n, i, j;
+
+ len = value->getLength();
+ if (len == 0) {
+ return value->copy();
+ }
+
+ //--- parse the value
+
+ // expected format is yyyy(-mm(-dd)?)?Thh(:mm(:ss)?)?
+ // where:
+ // - the '-'s and ':'s are optional
+ // - the 'T' is literal
+ // - we're ignoring optional time zone info at the end
+ // (if the value is not in this canonical format, we just punt and
+ // return the value string)
+ //~ another option would be to parse the value following the
+ //~ <ui><picture> element
+ year = month = day = hour = min = sec = 0;
+ i = 0;
+ if (!(i + 4 <= len && isValidInt(value, i, 4))) {
+ return value->copy();
+ }
+ year = convertInt(value, i, 4);
+ i += 4;
+ if (i < len && value->getChar(i) == '-') {
+ ++i;
+ }
+ if (i + 2 <= len && isValidInt(value, i, 2)) {
+ month = convertInt(value, i, 2);
+ i += 2;
+ if (i < len && value->getChar(i) == '-') {
+ ++i;
+ }
+ if (i + 2 <= len && isValidInt(value, i, 2)) {
+ day = convertInt(value, i, 2);
+ i += 2;
+ }
+ }
+ if (i < len) {
+ if (value->getChar(i) != 'T') {
+ return value->copy();
+ }
+ ++i;
+ if (!(i + 2 <= len && isValidInt(value, i, 2))) {
+ return value->copy();
+ }
+ hour = convertInt(value, i, 2);
+ i += 2;
+ if (i < len && value->getChar(i) == ':') {
+ ++i;
+ }
+ if (i + 2 <= len && isValidInt(value, i, 2)) {
+ min = convertInt(value, i, 2);
+ i += 2;
+ if (i < len && value->getChar(i) == ':') {
+ ++i;
+ }
+ if (i + 2 <= len && isValidInt(value, i, 2)) {
+ sec = convertInt(value, i, 2);
+ i += 2;
+ }
+ }
+ }
+ if (i < len) {
+ return value->copy();
+ }
+
+ //--- skip the category and locale in the picture
+
+ picStart = 0;
+ picEnd = picture->getLength();
+ for (i = 0; i < picture->getLength(); ++i) {
+ c = picture->getChar(i);
+ if (c == '{') {
+ picStart = i + 1;
+ for (picEnd = picStart;
+ picEnd < picture->getLength() && picture->getChar(picEnd) != '}';
+ ++picEnd) ;
+ break;
+ } else if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ c == '(' ||
+ c == ')')) {
+ break;
+ }
+ }
+
+ //--- parse the picture
+
+ pic = new GList();
+ i = picStart;
+ while (i < picEnd) {
+ c = picture->getChar(i);
+ ++i;
+ if (c == '\'') {
+ s = new GString();
+ while (i < picEnd) {
+ c = picture->getChar(i);
+ if (c == '\'') {
+ ++i;
+ if (i < picEnd && picture->getChar(i) == '\'') {
+ s->append('\'');
+ ++i;
+ } else {
+ break;
+ }
+ } else if (c == '\\') {
+ ++i;
+ if (i == picEnd) {
+ break;
+ }
+ c = picture->getChar(i);
+ ++i;
+ if (c == 'u' && i+4 <= picEnd) {
+ u = 0;
+ for (j = 0; j < 4; ++j, ++i) {
+ c = picture->getChar(i);
+ u <<= 4;
+ if (c >= '0' && c <= '9') {
+ u += c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ u += c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ u += c - 'A' + 10;
+ }
+ }
+ //~ this should convert to UTF-8 (?)
+ if (u <= 0xff) {
+ s->append((char)u);
+ }
+ } else {
+ s->append(c);
+ }
+ } else {
+ s->append(c);
+ }
+ }
+ pic->append(new PictureLiteral(s));
+ } else if (c == ',' || c == '-' || c == ':' ||
+ c == '/' || c == '.' || c == ' ') {
+ s = new GString();
+ s->append(c);
+ pic->append(new PictureLiteral(s));
+ } else if (c == 'Y') {
+ for (n = 1; n < 4 && i < picEnd && picture->getChar(i) == 'Y'; ++n, ++i) ;
+ pic->append(new PictureYear(n));
+ } else if (c == 'M') {
+ for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'M'; ++n, ++i) ;
+ pic->append(new PictureMonth(n));
+ } else if (c == 'D') {
+ for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'D'; ++n, ++i) ;
+ pic->append(new PictureDay(n));
+ } else if (c == 'h') {
+ for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'h'; ++n, ++i) ;
+ pic->append(new PictureHour(gFalse, n));
+ } else if (c == 'H') {
+ for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'H'; ++n, ++i) ;
+ pic->append(new PictureHour(gTrue, n));
+ } else if (c == 'M') {
+ for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'M'; ++n, ++i) ;
+ pic->append(new PictureMinute(n));
+ } else if (c == 'S') {
+ for (n = 1; n < 2 && i < picEnd && picture->getChar(i) == 'S'; ++n, ++i) ;
+ pic->append(new PictureSecond(n));
+ }
+ }
+
+ //--- generate formatted text
+
+ ret = new GString();
+ for (i = 0; i < pic->getLength(); ++i) {
+ node = (PictureNode *)pic->get(i);
+ if (node->isLiteral()) {
+ ret->append(((PictureLiteral *)node)->s);
+ } else if (node->isYear()) {
+ if (((PictureYear *)node)->nDigits == 2) {
+ if (year >= 1930 && year < 2030) {
+ ret->appendf("{0:02d}", year % 100);
+ } else {
+ ret->append("??");
+ }
+ } else {
+ ret->appendf("{0:04d}", year);
+ }
+ } else if (node->isMonth()) {
+ if (((PictureMonth *)node)->nDigits == 1) {
+ ret->appendf("{0:d}", month);
+ } else {
+ ret->appendf("{0:02d}", month);
+ }
+ } else if (node->isDay()) {
+ if (((PictureDay *)node)->nDigits == 1) {
+ ret->appendf("{0:d}", day);
+ } else {
+ ret->appendf("{0:02d}", day);
+ }
+ } else if (node->isHour()) {
+ if (((PictureHour *)node)->is24Hour) {
+ n = hour;
+ } else {
+ n = hour % 12;
+ if (n == 0) {
+ n = 12;
+ }
+ }
+ if (((PictureHour *)node)->nDigits == 1) {
+ ret->appendf("{0:d}", n);
+ } else {
+ ret->appendf("{0:02d}", n);
+ }
+ } else if (node->isMinute()) {
+ if (((PictureMinute *)node)->nDigits == 1) {
+ ret->appendf("{0:d}", min);
+ } else {
+ ret->appendf("{0:02d}", min);
+ }
+ } else if (node->isSecond()) {
+ if (((PictureSecond *)node)->nDigits == 1) {
+ ret->appendf("{0:d}", sec);
+ } else {
+ ret->appendf("{0:02d}", sec);
+ }
+ }
+ }
+ deleteGList(pic, PictureNode);
+
+ return ret;
+}
+
+GString *AcroFormField::pictureFormatNumber(GString *value, GString *picture) {
+ GList *pic;
+ PictureNode *node;
+ GString *ret, *s;
+ GBool neg, haveDigits;
+ char c;
+ int start, decPt, trailingZero, len;
+ int picStart, picEnd, u, pos, i, j;
+
+ len = value->getLength();
+ if (len == 0) {
+ return value->copy();
+ }
+
+ //--- parse the value
+
+ // -nnnn.nnnn0000
+ // ^ ^ ^ ^
+ // | | | +-- len
+ // | | +------ trailingZero
+ // | +----------- decPt
+ // +--------------- start
+ start = 0;
+ neg = gFalse;
+ if (value->getChar(start) == '-') {
+ neg = gTrue;
+ ++start;
+ } else if (value->getChar(start) == '+') {
+ ++start;
+ }
+ for (decPt = start; decPt < len && value->getChar(decPt) != '.'; ++decPt) ;
+ for (trailingZero = len;
+ trailingZero > decPt && value->getChar(trailingZero - 1) == '0';
+ --trailingZero) ;
+
+ //--- skip the category and locale in the picture
+
+ picStart = 0;
+ picEnd = picture->getLength();
+ for (i = 0; i < picture->getLength(); ++i) {
+ c = picture->getChar(i);
+ if (c == '{') {
+ picStart = i + 1;
+ for (picEnd = picStart;
+ picEnd < picture->getLength() && picture->getChar(picEnd) != '}';
+ ++picEnd) ;
+ break;
+ } else if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ c == '(' ||
+ c == ')')) {
+ break;
+ }
+ }
+
+ //--- parse the picture
+
+ pic = new GList();
+ i = picStart;
+ while (i < picEnd) {
+ c = picture->getChar(i);
+ ++i;
+ if (c == '\'') {
+ s = new GString();
+ while (i < picEnd) {
+ c = picture->getChar(i);
+ if (c == '\'') {
+ ++i;
+ if (i < picEnd && picture->getChar(i) == '\'') {
+ s->append('\'');
+ ++i;
+ } else {
+ break;
+ }
+ } else if (c == '\\') {
+ ++i;
+ if (i == picEnd) {
+ break;
+ }
+ c = picture->getChar(i);
+ ++i;
+ if (c == 'u' && i+4 <= picEnd) {
+ u = 0;
+ for (j = 0; j < 4; ++j, ++i) {
+ c = picture->getChar(i);
+ u <<= 4;
+ if (c >= '0' && c <= '9') {
+ u += c - '0';
+ } else if (c >= 'a' && c <= 'F') {
+ u += c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ u += c - 'A' + 10;
+ }
+ }
+ //~ this should convert to UTF-8 (?)
+ if (u <= 0xff) {
+ s->append((char)u);
+ }
+ } else {
+ s->append(c);
+ }
+ } else {
+ s->append(c);
+ ++i;
+ }
+ }
+ pic->append(new PictureLiteral(s));
+ } else if (c == '-' || c == ':' || c == '/' || c == ' ') {
+ s = new GString();
+ s->append(c);
+ pic->append(new PictureLiteral(s));
+ } else if (c == 's' || c == 'S') {
+ pic->append(new PictureSign(c));
+ } else if (c == 'Z' || c == 'z' || c == '8' || c == '9') {
+ pic->append(new PictureDigit(c));
+ } else if (c == '.') {
+ pic->append(new PictureDecPt());
+ } else if (c == ',') {
+ pic->append(new PictureSeparator());
+ }
+ }
+ for (i = 0; i < pic->getLength(); ++i) {
+ node = (PictureNode *)pic->get(i);
+ if (node->isDecPt()) {
+ break;
+ }
+ }
+ pos = 0;
+ for (j = i - 1; j >= 0; --j) {
+ node = (PictureNode *)pic->get(j);
+ if (node->isDigit()) {
+ ((PictureDigit *)node)->pos = pos;
+ ++pos;
+ }
+ }
+ pos = -1;
+ for (j = i + 1; j < pic->getLength(); ++j) {
+ node = (PictureNode *)pic->get(j);
+ if (node->isDigit()) {
+ ((PictureDigit *)node)->pos = pos;
+ --pos;
+ }
+ }
+
+ //--- generate formatted text
+
+ ret = new GString();
+ haveDigits = gFalse;
+ for (i = 0; i < pic->getLength(); ++i) {
+ node = (PictureNode *)pic->get(i);
+ if (node->isLiteral()) {
+ ret->append(((PictureLiteral *)node)->s);
+ } else if (node->isSign()) {
+ if (((PictureSign *)node)->c == 'S') {
+ ret->append(neg ? '-' : ' ');
+ } else {
+ if (neg) {
+ ret->append('-');
+ }
+ }
+ } else if (node->isDigit()) {
+ pos = ((PictureDigit *)node)->pos;
+ c = ((PictureDigit *)node)->c;
+ if (pos >= 0 && pos < decPt - start) {
+ ret->append(value->getChar(decPt - 1 - pos));
+ haveDigits = gTrue;
+ } else if (pos < 0 && -pos <= trailingZero - decPt - 1) {
+ ret->append(value->getChar(decPt - pos));
+ haveDigits = gTrue;
+ } else if (c == '8' &&
+ pos < 0 &&
+ -pos <= len - decPt - 1) {
+ ret->append('0');
+ haveDigits = gTrue;
+ } else if (c == '9') {
+ ret->append('0');
+ haveDigits = gTrue;
+ } else if (c == 'Z' && pos >= 0) {
+ ret->append(' ');
+ }
+ } else if (node->isDecPt()) {
+ if (!(i+1 < pic->getLength() &&
+ ((PictureNode *)pic->get(i+1))->isDigit() &&
+ ((PictureDigit *)pic->get(i+1))->c == 'z') ||
+ trailingZero > decPt + 1) {
+ ret->append('.');
+ }
+ } else if (node->isSeparator()) {
+ if (haveDigits) {
+ ret->append(',');
+ }
+ }
+ }
+ deleteGList(pic, PictureNode);
+
+ return ret;
+}
+
+GString *AcroFormField::pictureFormatText(GString *value, GString *picture) {
+ GList *pic;
+ PictureNode *node;
+ GString *ret, *s;
+ char c;
+ int len, picStart, picEnd, u, i, j;
+
+ len = value->getLength();
+ if (len == 0) {
+ return value->copy();
+ }
+
+ //--- skip the category and locale in the picture
+
+ picStart = 0;
+ picEnd = picture->getLength();
+ for (i = 0; i < picture->getLength(); ++i) {
+ c = picture->getChar(i);
+ if (c == '{') {
+ picStart = i + 1;
+ for (picEnd = picStart;
+ picEnd < picture->getLength() && picture->getChar(picEnd) != '}';
+ ++picEnd) ;
+ break;
+ } else if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ c == '(' ||
+ c == ')')) {
+ break;
+ }
+ }
+
+ //--- parse the picture
+
+ pic = new GList();
+ i = picStart;
+ while (i < picEnd) {
+ c = picture->getChar(i);
+ ++i;
+ if (c == '\'') {
+ s = new GString();
+ while (i < picEnd) {
+ c = picture->getChar(i);
+ if (c == '\'') {
+ ++i;
+ if (i < picEnd && picture->getChar(i) == '\'') {
+ s->append('\'');
+ ++i;
+ } else {
+ break;
+ }
+ } else if (c == '\\') {
+ ++i;
+ if (i == picEnd) {
+ break;
+ }
+ c = picture->getChar(i);
+ ++i;
+ if (c == 'u' && i+4 <= picEnd) {
+ u = 0;
+ for (j = 0; j < 4; ++j, ++i) {
+ c = picture->getChar(i);
+ u <<= 4;
+ if (c >= '0' && c <= '9') {
+ u += c - '0';
+ } else if (c >= 'a' && c <= 'F') {
+ u += c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ u += c - 'A' + 10;
+ }
+ }
+ //~ this should convert to UTF-8 (?)
+ if (u <= 0xff) {
+ s->append((char)u);
+ }
+ } else {
+ s->append(c);
+ }
+ } else {
+ s->append(c);
+ ++i;
+ }
+ }
+ pic->append(new PictureLiteral(s));
+ } else if (c == ',' || c == '-' || c == ':' ||
+ c == '/' || c == '.' || c == ' ') {
+ s = new GString();
+ s->append(c);
+ pic->append(new PictureLiteral(s));
+ } else if (c == 'A' || c == 'X' || c == 'O' || c == '0' || c == '9') {
+ pic->append(new PictureChar());
+ }
+ }
+
+ //--- generate formatted text
+
+ ret = new GString();
+ j = 0;
+ for (i = 0; i < pic->getLength(); ++i) {
+ node = (PictureNode *)pic->get(i);
+ if (node->isLiteral()) {
+ ret->append(((PictureLiteral *)node)->s);
+ } else if (node->isChar()) {
+ // if there are more chars in the picture than in the value,
+ // Adobe renders the value as-is, without picture formatting
+ if (j >= value->getLength()) {
+ delete ret;
+ ret = value->copy();
+ break;
+ }
+ ret->append(value->getChar(j));
+ ++j;
+ }
+ }
+ deleteGList(pic, PictureNode);
+
+ return ret;
+}
+
+GBool AcroFormField::isValidInt(GString *s, int start, int len) {
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ if (!(start + i < s->getLength() &&
+ s->getChar(start + i) >= '0' &&
+ s->getChar(start + i) <= '9')) {
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
+int AcroFormField::convertInt(GString *s, int start, int len) {
+ char c;
+ int x, i;
+
+ x = 0;
+ for (i = 0; i < len && start + i < s->getLength(); ++i) {
+ c = s->getChar(start + i);
+ if (c < '0' || c > '9') {
+ break;
+ }
+ x = x * 10 + (c - '0');
+ }
+ return x;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h
new file mode 100644
index 00000000000..7678d46dbc8
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/AcroForm.h
@@ -0,0 +1,169 @@
+//========================================================================
+//
+// AcroForm.h
+//
+// Copyright 2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ACROFORM_H
+#define ACROFORM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class TextString;
+class Gfx;
+class GfxFont;
+class GfxFontDict;
+class AcroFormField;
+class XFAScanner;
+class XFAField;
+class XFAFieldBarcodeInfo;
+
+//------------------------------------------------------------------------
+
+class AcroForm {
+public:
+
+ static AcroForm *load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA);
+
+ ~AcroForm();
+
+ const char *getType();
+
+ void draw(int pageNum, Gfx *gfx, GBool printing);
+
+ int getNumFields();
+ AcroFormField *getField(int idx);
+ AcroFormField *findField(int pg, double x, double y);
+ int findFieldIdx(int pg, double x, double y);
+
+private:
+
+ AcroForm(PDFDoc *docA, Object *acroFormObjA);
+ void buildAnnotPageList(Catalog *catalog);
+ 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;
+};
+
+//------------------------------------------------------------------------
+
+enum AcroFormFieldType {
+ acroFormFieldPushbutton,
+ acroFormFieldRadioButton,
+ acroFormFieldCheckbox,
+ acroFormFieldFileSelect,
+ acroFormFieldMultilineText,
+ acroFormFieldText,
+ acroFormFieldBarcode,
+ acroFormFieldComboBox,
+ acroFormFieldListBox,
+ acroFormFieldSignature
+};
+
+class AcroFormField {
+public:
+
+ static AcroFormField *load(AcroForm *acroFormA, Object *fieldRefA);
+
+ ~AcroFormField();
+
+ 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();
+
+ Object *getResources(Object *res);
+
+ AcroFormFieldType getAcroFormFieldType() { return type; }
+ Object *getFieldRef(Object *ref);
+ Object *getValueObj(Object *val);
+ Object *getParentRef(Object *parent);
+ GBool getTypeFromParent() { return typeFromParent; }
+
+private:
+
+ AcroFormField(AcroForm *acroFormA, Object *fieldRefA, Object *fieldObjA,
+ AcroFormFieldType typeA, TextString *nameA,
+ 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,
+ Object *annotRef, Object *annotObj);
+ void drawExistingAppearance(Gfx *gfx, Dict *annot,
+ double xMin, double yMin,
+ double xMax, double yMax);
+ void drawNewAppearance(Gfx *gfx, Dict *annot,
+ double xMin, double yMin,
+ 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, int vAlign,
+ GBool txField, GBool forceZapfDingbats, int rot,
+ 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,
+ GBool quadding, double xMin, double yMin,
+ double xMax, double yMax, double border,
+ GString *appearBuf);
+ void getNextLine(GString *text, int start,
+ GfxFont *font, double fontSize, double wMax,
+ int *end, double *width, int *next);
+ void drawCircle(double cx, double cy, double r, const char *cmd,
+ GString *appearBuf);
+ void drawCircleTopLeft(double cx, double cy, double r,
+ GString *appearBuf);
+ void drawCircleBottomRight(double cx, double cy, double r,
+ GString *appearBuf);
+ void drawBarcode(GString *value, GString *da, GfxFontDict *fontDict, int rot,
+ double xMin, double yMin, double xMax, double yMax,
+ XFAFieldBarcodeInfo *barcodeInfo, GString *appearBuf);
+ GList *tokenize(GString *s);
+ Object *getAnnotObj(Object *annotObj);
+ Object *getAnnotResources(Dict *annot, Object *res);
+ void buildDefaultResourceDict(Object *dr);
+ Object *fieldLookup(const char *key, Object *obj);
+ Object *fieldLookup(Dict *dict, const char *key, Object *obj);
+ Unicode *utf8ToUnicode(GString *s, int *unicodeLength);
+ GString *unicodeToLatin1(Unicode *u, int unicodeLength);
+ GBool unicodeStringEqual(Unicode *u, int unicodeLength, GString *s);
+ GBool unicodeStringEqual(Unicode *u, int unicodeLength, const char *s);
+ GString *pictureFormatDateTime(GString *value, GString *picture);
+ GString *pictureFormatNumber(GString *value, GString *picture);
+ GString *pictureFormatText(GString *value, GString *picture);
+ GBool isValidInt(GString *s, int start, int len);
+ int convertInt(GString *s, int start, int len);
+
+ AcroForm *acroForm;
+ Object fieldRef;
+ Object fieldObj;
+ AcroFormFieldType type;
+ TextString *name;
+ Guint flags;
+ GBool typeFromParent;
+ XFAField *xfaField;
+
+ friend class AcroForm;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc
new file mode 100644
index 00000000000..d7229a2fe86
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.cc
@@ -0,0 +1,1443 @@
+//========================================================================
+//
+// Annot.cc
+//
+// Copyright 2000-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GList.h"
+#include "Error.h"
+#include "Object.h"
+#include "Catalog.h"
+#include "Gfx.h"
+#include "GfxFont.h"
+#include "Lexer.h"
+#include "PDFDoc.h"
+#include "OptionalContent.h"
+#include "AcroForm.h"
+#include "BuiltinFontTables.h"
+#include "FontEncodingTables.h"
+#include "Annot.h"
+
+// the MSVC math.h doesn't define this
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+//------------------------------------------------------------------------
+
+#define annotFlagHidden 0x0002
+#define annotFlagPrint 0x0004
+#define annotFlagNoView 0x0020
+
+// distance of Bezier control point from center for circle approximation
+// = (4 * (sqrt(2) - 1) / 3) * r
+#define bezierCircle 0.55228475
+
+#define lineEndSize1 6
+#define lineEndSize2 10
+#define lineArrowAngle (M_PI / 6)
+
+//------------------------------------------------------------------------
+// AnnotBorderStyle
+//------------------------------------------------------------------------
+
+AnnotBorderStyle::AnnotBorderStyle(AnnotBorderType typeA, double widthA,
+ double *dashA, int dashLengthA,
+ double *colorA, int nColorCompsA) {
+ type = typeA;
+ width = widthA;
+ dash = dashA;
+ dashLength = dashLengthA;
+ color[0] = colorA[0];
+ color[1] = colorA[1];
+ color[2] = colorA[2];
+ color[3] = colorA[3];
+ nColorComps = nColorCompsA;
+}
+
+AnnotBorderStyle::~AnnotBorderStyle() {
+ if (dash) {
+ gfree(dash);
+ }
+}
+
+//------------------------------------------------------------------------
+// Annot
+//------------------------------------------------------------------------
+
+Annot::Annot(PDFDoc *docA, Dict *dict, Ref *refA) {
+ Object apObj, asObj, obj1, obj2, obj3;
+ AnnotBorderType borderType;
+ double borderWidth;
+ double *borderDash;
+ int borderDashLength;
+ double borderColor[4];
+ int nBorderColorComps;
+ double t;
+ int i;
+
+ ok = gTrue;
+ doc = docA;
+ xref = doc->getXRef();
+ ref = *refA;
+ type = NULL;
+ appearanceState = NULL;
+ appearBuf = NULL;
+ borderStyle = NULL;
+
+ //----- parse the type
+
+ if (dict->lookup("Subtype", &obj1)->isName()) {
+ type = new GString(obj1.getName());
+ }
+ obj1.free();
+
+ //----- parse the rectangle
+
+ if (dict->lookup("Rect", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ xMin = yMin = xMax = yMax = 0;
+ if (obj1.arrayGet(0, &obj2)->isNum()) {
+ xMin = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGet(1, &obj2)->isNum()) {
+ yMin = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGet(2, &obj2)->isNum()) {
+ xMax = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGet(3, &obj2)->isNum()) {
+ yMax = obj2.getNum();
+ }
+ obj2.free();
+ if (xMin > xMax) {
+ t = xMin; xMin = xMax; xMax = t;
+ }
+ if (yMin > yMax) {
+ t = yMin; yMin = yMax; yMax = t;
+ }
+ } else {
+ error(errSyntaxError, -1, "Bad bounding box for annotation");
+ ok = gFalse;
+ }
+ obj1.free();
+
+ //----- parse the flags
+
+ if (dict->lookup("F", &obj1)->isInt()) {
+ flags = obj1.getInt();
+ } else {
+ flags = 0;
+ }
+ obj1.free();
+
+ //----- parse the border style
+
+ borderType = annotBorderSolid;
+ borderWidth = 1;
+ borderDash = NULL;
+ borderDashLength = 0;
+ nBorderColorComps = 3;
+ borderColor[0] = 0;
+ borderColor[1] = 0;
+ borderColor[2] = 1;
+ borderColor[3] = 0;
+ if (dict->lookup("BS", &obj1)->isDict()) {
+ if (obj1.dictLookup("S", &obj2)->isName()) {
+ if (obj2.isName("S")) {
+ borderType = annotBorderSolid;
+ } else if (obj2.isName("D")) {
+ borderType = annotBorderDashed;
+ } else if (obj2.isName("B")) {
+ borderType = annotBorderBeveled;
+ } else if (obj2.isName("I")) {
+ borderType = annotBorderInset;
+ } else if (obj2.isName("U")) {
+ borderType = annotBorderUnderlined;
+ }
+ }
+ obj2.free();
+ if (obj1.dictLookup("W", &obj2)->isNum()) {
+ borderWidth = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.dictLookup("D", &obj2)->isArray()) {
+ borderDashLength = obj2.arrayGetLength();
+ borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
+ for (i = 0; i < borderDashLength; ++i) {
+ if (obj2.arrayGet(i, &obj3)->isNum()) {
+ borderDash[i] = obj3.getNum();
+ } else {
+ borderDash[i] = 1;
+ }
+ obj3.free();
+ }
+ }
+ obj2.free();
+ } else {
+ obj1.free();
+ if (dict->lookup("Border", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() >= 3) {
+ if (obj1.arrayGet(2, &obj2)->isNum()) {
+ borderWidth = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGetLength() >= 4) {
+ if (obj1.arrayGet(3, &obj2)->isArray()) {
+ borderType = annotBorderDashed;
+ borderDashLength = obj2.arrayGetLength();
+ borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
+ for (i = 0; i < borderDashLength; ++i) {
+ if (obj2.arrayGet(i, &obj3)->isNum()) {
+ borderDash[i] = obj3.getNum();
+ } else {
+ borderDash[i] = 1;
+ }
+ obj3.free();
+ }
+ } else {
+ // Adobe draws no border at all if the last element is of
+ // the wrong type.
+ borderWidth = 0;
+ }
+ obj2.free();
+ }
+ } else {
+ // an empty Border array also means "no border"
+ borderWidth = 0;
+ }
+ }
+ }
+ obj1.free();
+ if (dict->lookup("C", &obj1)->isArray() &&
+ (obj1.arrayGetLength() == 1 ||
+ obj1.arrayGetLength() == 3 ||
+ obj1.arrayGetLength() == 4)) {
+ nBorderColorComps = obj1.arrayGetLength();
+ for (i = 0; i < nBorderColorComps; ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ borderColor[i] = obj2.getNum();
+ } else {
+ borderColor[i] = 0;
+ }
+ obj2.free();
+ }
+ }
+ obj1.free();
+ borderStyle = new AnnotBorderStyle(borderType, borderWidth,
+ borderDash, borderDashLength,
+ borderColor, nBorderColorComps);
+
+ //----- get the appearance state
+
+ dict->lookup("AP", &apObj);
+ dict->lookup("AS", &asObj);
+ if (asObj.isName()) {
+ appearanceState = new GString(asObj.getName());
+ } else if (apObj.isDict()) {
+ apObj.dictLookup("N", &obj1);
+ if (obj1.isDict() && obj1.dictGetLength() == 1) {
+ appearanceState = new GString(obj1.dictGetKey(0));
+ }
+ obj1.free();
+ }
+ if (!appearanceState) {
+ appearanceState = new GString("Off");
+ }
+ asObj.free();
+
+ //----- get the annotation appearance
+
+ if (apObj.isDict()) {
+ apObj.dictLookup("N", &obj1);
+ apObj.dictLookupNF("N", &obj2);
+ if (obj1.isDict()) {
+ if (obj1.dictLookupNF(appearanceState->getCString(), &obj3)->isRef()) {
+ obj3.copy(&appearance);
+ }
+ obj3.free();
+ } else if (obj2.isRef()) {
+ obj2.copy(&appearance);
+ }
+ obj1.free();
+ obj2.free();
+ }
+ apObj.free();
+
+ //----- get the optional content entry
+
+ dict->lookupNF("OC", &ocObj);
+}
+
+Annot::~Annot() {
+ if (type) {
+ delete type;
+ }
+ if (appearanceState) {
+ delete appearanceState;
+ }
+ appearance.free();
+ if (appearBuf) {
+ delete appearBuf;
+ }
+ if (borderStyle) {
+ delete borderStyle;
+ }
+ ocObj.free();
+}
+
+void Annot::generateAnnotAppearance() {
+ Object obj;
+
+ appearance.fetch(doc->getXRef(), &obj);
+ if (!obj.isStream()) {
+ if (type) {
+ if (!type->cmp("Line")) {
+ generateLineAppearance();
+ } else if (!type->cmp("PolyLine")) {
+ generatePolyLineAppearance();
+ } else if (!type->cmp("Polygon")) {
+ generatePolygonAppearance();
+ } else if (!type->cmp("FreeText")) {
+ generateFreeTextAppearance();
+ }
+ }
+ }
+ obj.free();
+}
+
+//~ this doesn't draw the caption
+void Annot::generateLineAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ MemStream *appearStream;
+ double x1, y1, x2, y2, dx, dy, len, w;
+ double lx1, ly1, lx2, ly2;
+ double tx1, ty1, tx2, ty2;
+ double ax1, ay1, ax2, ay2;
+ double bx1, by1, bx2, by2;
+ double leaderLen, leaderExtLen, leaderOffLen;
+ AnnotLineEndType lineEnd1, lineEnd2;
+ GBool fill;
+
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
+ return;
+ }
+
+ appearBuf = new GString();
+
+ //----- check for transparency
+ if (annotObj.dictLookup("CA", &obj1)->isNum()) {
+ gfxStateDict.initDict(doc->getXRef());
+ gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
+ appearBuf->append("/GS1 gs\n");
+ }
+ obj1.free();
+
+ //----- set line style, colors
+ setLineStyle(borderStyle, &w);
+ setStrokeColor(borderStyle->getColor(), borderStyle->getNumColorComps());
+ fill = gFalse;
+ if (annotObj.dictLookup("IC", &obj1)->isArray()) {
+ if (setFillColor(&obj1)) {
+ fill = gTrue;
+ }
+ }
+ obj1.free();
+
+ //----- get line properties
+ if (annotObj.dictLookup("L", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ if (obj1.arrayGet(0, &obj2)->isNum()) {
+ x1 = obj2.getNum();
+ } else {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ obj2.free();
+ if (obj1.arrayGet(1, &obj2)->isNum()) {
+ y1 = obj2.getNum();
+ } else {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ obj2.free();
+ if (obj1.arrayGet(2, &obj2)->isNum()) {
+ x2 = obj2.getNum();
+ } else {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ obj2.free();
+ if (obj1.arrayGet(3, &obj2)->isNum()) {
+ y2 = obj2.getNum();
+ } else {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ obj2.free();
+ } else {
+ obj1.free();
+ goto err1;
+ }
+ obj1.free();
+ lineEnd1 = lineEnd2 = annotLineEndNone;
+ if (annotObj.dictLookup("LE", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ lineEnd1 = parseLineEndType(obj1.arrayGet(0, &obj2));
+ obj2.free();
+ lineEnd2 = parseLineEndType(obj1.arrayGet(1, &obj2));
+ obj2.free();
+ }
+ obj1.free();
+ if (annotObj.dictLookup("LL", &obj1)->isNum()) {
+ leaderLen = obj1.getNum();
+ } else {
+ leaderLen = 0;
+ }
+ obj1.free();
+ if (annotObj.dictLookup("LLE", &obj1)->isNum()) {
+ leaderExtLen = obj1.getNum();
+ } else {
+ leaderExtLen = 0;
+ }
+ obj1.free();
+ if (annotObj.dictLookup("LLO", &obj1)->isNum()) {
+ leaderOffLen = obj1.getNum();
+ } else {
+ leaderOffLen = 0;
+ }
+ obj1.free();
+
+ //----- compute positions
+ x1 -= xMin;
+ y1 -= yMin;
+ x2 -= xMin;
+ y2 -= yMin;
+ dx = x2 - x1;
+ dy = y2 - y1;
+ len = sqrt(dx*dx + dy*dy);
+ if (len > 0) {
+ dx /= len;
+ dy /= len;
+ }
+ if (leaderLen != 0) {
+ ax1 = x1 + leaderOffLen * dy;
+ ay1 = y1 - leaderOffLen * dx;
+ lx1 = ax1 + leaderLen * dy;
+ ly1 = ay1 - leaderLen * dx;
+ bx1 = lx1 + leaderExtLen * dy;
+ by1 = ly1 - leaderExtLen * dx;
+ ax2 = x2 + leaderOffLen * dy;
+ ay2 = y2 - leaderOffLen * dx;
+ lx2 = ax2 + leaderLen * dy;
+ ly2 = ay2 - leaderLen * dx;
+ bx2 = lx2 + leaderExtLen * dy;
+ by2 = ly2 - leaderExtLen * dx;
+ } else {
+ lx1 = x1;
+ ly1 = y1;
+ lx2 = x2;
+ ly2 = y2;
+ ax1 = ay1 = ax2 = ay2 = 0; // make gcc happy
+ bx1 = by1 = bx2 = by2 = 0;
+ }
+ adjustLineEndpoint(lineEnd1, lx1, ly1, dx, dy, w, &tx1, &ty1);
+ adjustLineEndpoint(lineEnd2, lx2, ly2, -dx, -dy, w, &tx2, &ty2);
+
+ //----- draw leaders
+ if (leaderLen != 0) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
+ ax1, ay1, bx1, by1);
+ appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
+ ax2, ay2 , bx2, by2);
+ }
+
+ //----- draw the line
+ appearBuf->appendf("{0:.4f} {1:.4f} m {2:.4f} {3:.4f} l\n",
+ tx1, ty1, tx2, ty2);
+ appearBuf->append("S\n");
+
+ //----- draw the arrows
+ if (borderStyle->getType() == annotBorderDashed) {
+ appearBuf->append("[] 0 d\n");
+ }
+ drawLineArrow(lineEnd1, lx1, ly1, dx, dy, w, fill);
+ drawLineArrow(lineEnd2, lx2, ly2, -dx, -dy, w, fill);
+
+ //----- build the appearance stream dictionary
+ appearDict.initDict(doc->getXRef());
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(doc->getXRef());
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(xMax - xMin));
+ obj1.arrayAdd(obj2.initReal(yMax - yMin));
+ appearDict.dictAdd(copyString("BBox"), &obj1);
+ if (gfxStateDict.isDict()) {
+ obj1.initDict(doc->getXRef());
+ obj2.initDict(doc->getXRef());
+ obj2.dictAdd(copyString("GS1"), &gfxStateDict);
+ obj1.dictAdd(copyString("ExtGState"), &obj2);
+ appearDict.dictAdd(copyString("Resources"), &obj1);
+ }
+
+ //----- build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.free();
+ appearance.initStream(appearStream);
+
+ err1:
+ annotObj.free();
+}
+
+//~ this doesn't handle line ends (arrows)
+void Annot::generatePolyLineAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ MemStream *appearStream;
+ double x1, y1, w;
+ int i;
+
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
+ return;
+ }
+
+ appearBuf = new GString();
+
+ //----- check for transparency
+ if (annotObj.dictLookup("CA", &obj1)->isNum()) {
+ gfxStateDict.initDict(doc->getXRef());
+ gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
+ appearBuf->append("/GS1 gs\n");
+ }
+ obj1.free();
+
+ //----- set line style, colors
+ setLineStyle(borderStyle, &w);
+ setStrokeColor(borderStyle->getColor(), borderStyle->getNumColorComps());
+ // fill = gFalse;
+ // if (annotObj.dictLookup("IC", &obj1)->isArray()) {
+ // if (setFillColor(&obj1)) {
+ // fill = gTrue;
+ // }
+ // }
+ // obj1.free();
+
+ //----- draw line
+ if (!annotObj.dictLookup("Vertices", &obj1)->isArray()) {
+ obj1.free();
+ goto err1;
+ }
+ for (i = 0; i+1 < obj1.arrayGetLength(); i += 2) {
+ if (!obj1.arrayGet(i, &obj2)->isNum()) {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ x1 = obj2.getNum();
+ obj2.free();
+ if (!obj1.arrayGet(i+1, &obj2)->isNum()) {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ y1 = obj2.getNum();
+ obj2.free();
+ x1 -= xMin;
+ y1 -= yMin;
+ if (i == 0) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n", x1, y1);
+ } else {
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1);
+ }
+ }
+ appearBuf->append("S\n");
+ obj1.free();
+
+ //----- build the appearance stream dictionary
+ appearDict.initDict(doc->getXRef());
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(doc->getXRef());
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(xMax - xMin));
+ obj1.arrayAdd(obj2.initReal(yMax - yMin));
+ appearDict.dictAdd(copyString("BBox"), &obj1);
+ if (gfxStateDict.isDict()) {
+ obj1.initDict(doc->getXRef());
+ obj2.initDict(doc->getXRef());
+ obj2.dictAdd(copyString("GS1"), &gfxStateDict);
+ obj1.dictAdd(copyString("ExtGState"), &obj2);
+ appearDict.dictAdd(copyString("Resources"), &obj1);
+ }
+
+ //----- build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.free();
+ appearance.initStream(appearStream);
+
+ err1:
+ annotObj.free();
+}
+
+void Annot::generatePolygonAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ MemStream *appearStream;
+ double x1, y1;
+ int i;
+
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
+ return;
+ }
+
+ appearBuf = new GString();
+
+ //----- check for transparency
+ if (annotObj.dictLookup("CA", &obj1)->isNum()) {
+ gfxStateDict.initDict(doc->getXRef());
+ gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
+ appearBuf->append("/GS1 gs\n");
+ }
+ obj1.free();
+
+ //----- set fill color
+ if (!annotObj.dictLookup("IC", &obj1)->isArray() ||
+ !setFillColor(&obj1)) {
+ obj1.free();
+ goto err1;
+ }
+ obj1.free();
+
+ //----- fill polygon
+ if (!annotObj.dictLookup("Vertices", &obj1)->isArray()) {
+ obj1.free();
+ goto err1;
+ }
+ for (i = 0; i+1 < obj1.arrayGetLength(); i += 2) {
+ if (!obj1.arrayGet(i, &obj2)->isNum()) {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ x1 = obj2.getNum();
+ obj2.free();
+ if (!obj1.arrayGet(i+1, &obj2)->isNum()) {
+ obj2.free();
+ obj1.free();
+ goto err1;
+ }
+ y1 = obj2.getNum();
+ obj2.free();
+ x1 -= xMin;
+ y1 -= yMin;
+ if (i == 0) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n", x1, y1);
+ } else {
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x1, y1);
+ }
+ }
+ appearBuf->append("f\n");
+ obj1.free();
+
+ //----- build the appearance stream dictionary
+ appearDict.initDict(doc->getXRef());
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(doc->getXRef());
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(xMax - xMin));
+ obj1.arrayAdd(obj2.initReal(yMax - yMin));
+ appearDict.dictAdd(copyString("BBox"), &obj1);
+ if (gfxStateDict.isDict()) {
+ obj1.initDict(doc->getXRef());
+ obj2.initDict(doc->getXRef());
+ obj2.dictAdd(copyString("GS1"), &gfxStateDict);
+ obj1.dictAdd(copyString("ExtGState"), &obj2);
+ appearDict.dictAdd(copyString("Resources"), &obj1);
+ }
+
+ //----- build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.free();
+ appearance.initStream(appearStream);
+
+ err1:
+ annotObj.free();
+}
+
+//~ this doesn't handle rich text
+//~ this doesn't handle the callout
+//~ this doesn't handle the RD field
+void Annot::generateFreeTextAppearance() {
+ Object annotObj, gfxStateDict, appearDict, obj1, obj2;
+ Object resources, gsResources, fontResources, defaultFont;
+ GString *text, *da;
+ double lineWidth;
+ int quadding, rot;
+ MemStream *appearStream;
+
+ if (!getObject(&annotObj)->isDict()) {
+ annotObj.free();
+ return;
+ }
+
+ appearBuf = new GString();
+
+ //----- check for transparency
+ if (annotObj.dictLookup("CA", &obj1)->isNum()) {
+ gfxStateDict.initDict(doc->getXRef());
+ gfxStateDict.dictAdd(copyString("ca"), obj1.copy(&obj2));
+ appearBuf->append("/GS1 gs\n");
+ }
+ obj1.free();
+
+ //----- draw the text
+ if (annotObj.dictLookup("Contents", &obj1)->isString()) {
+ text = obj1.getString()->copy();
+ } else {
+ text = new GString();
+ }
+ obj1.free();
+ if (annotObj.dictLookup("Q", &obj1)->isInt()) {
+ quadding = obj1.getInt();
+ } else {
+ quadding = 0;
+ }
+ obj1.free();
+ if (annotObj.dictLookup("DA", &obj1)->isString()) {
+ da = obj1.getString()->copy();
+ } else {
+ da = new GString();
+ }
+ obj1.free();
+ // the "Rotate" field is not defined in the PDF spec, but Acrobat
+ // looks at it
+ if (annotObj.dictLookup("Rotate", &obj1)->isInt()) {
+ rot = obj1.getInt();
+ } else {
+ rot = 0;
+ }
+ obj1.free();
+ drawText(text, da, quadding, 0, rot);
+ delete text;
+ delete da;
+
+ //----- draw the border
+ if (borderStyle->getWidth() != 0) {
+ setLineStyle(borderStyle, &lineWidth);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} re s\n",
+ 0.5 * lineWidth, 0.5 * lineWidth,
+ xMax - xMin - lineWidth, yMax - yMin - lineWidth);
+ }
+
+ //----- build the appearance stream dictionary
+ appearDict.initDict(doc->getXRef());
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(doc->getXRef());
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(xMax - xMin));
+ obj1.arrayAdd(obj2.initReal(yMax - yMin));
+ appearDict.dictAdd(copyString("BBox"), &obj1);
+ resources.initDict(doc->getXRef());
+ defaultFont.initDict(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.initDict(doc->getXRef());
+ fontResources.dictAdd(copyString("xpdf_default_font"), &defaultFont);
+ resources.dictAdd(copyString("Font"), &fontResources);
+ if (gfxStateDict.isDict()) {
+ gsResources.initDict(doc->getXRef());
+ gsResources.dictAdd(copyString("GS1"), &gfxStateDict);
+ resources.dictAdd(copyString("ExtGState"), &gsResources);
+ }
+ appearDict.dictAdd(copyString("Resources"), &resources);
+
+ //----- build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.free();
+ appearance.initStream(appearStream);
+
+ annotObj.free();
+}
+
+void Annot::setLineStyle(AnnotBorderStyle *bs, double *lineWidth) {
+ double *dash;
+ double w;
+ int dashLength, i;
+
+ if ((w = borderStyle->getWidth()) <= 0) {
+ w = 0.1;
+ }
+ *lineWidth = w;
+ appearBuf->appendf("{0:.4f} w\n", w);
+ // this treats beveled/inset/underline as solid
+ if (borderStyle->getType() == annotBorderDashed) {
+ borderStyle->getDash(&dash, &dashLength);
+ appearBuf->append("[");
+ for (i = 0; i < dashLength; ++i) {
+ appearBuf->appendf(" {0:.4f}", dash[i]);
+ }
+ appearBuf->append("] 0 d\n");
+ }
+ appearBuf->append("0 j\n0 J\n");
+}
+
+void Annot::setStrokeColor(double *color, int nComps) {
+ switch (nComps) {
+ case 0:
+ appearBuf->append("0 G\n");
+ break;
+ case 1:
+ appearBuf->appendf("{0:.2f} G\n", color[0]);
+ break;
+ case 3:
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} RG\n",
+ color[0], color[1], color[2]);
+ break;
+ case 4:
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} K\n",
+ color[0], color[1], color[2], color[3]);
+ break;
+ }
+}
+
+GBool Annot::setFillColor(Object *colorObj) {
+ Object obj;
+ double color[4];
+ int i;
+
+ if (!colorObj->isArray()) {
+ return gFalse;
+ }
+ for (i = 0; i < colorObj->arrayGetLength() && i < 4; ++i) {
+ if (colorObj->arrayGet(i, &obj)->isNum()) {
+ color[i] = obj.getNum();
+ } else {
+ color[i] = 0;
+ }
+ obj.free();
+ }
+ switch (colorObj->arrayGetLength()) {
+ case 1:
+ appearBuf->appendf("{0:.2f} g\n", color[0]);
+ return gTrue;
+ case 3:
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} rg\n",
+ color[0], color[1], color[2]);
+ return gTrue;
+ case 4:
+ appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.3f} k\n",
+ color[0], color[1],
+ color[2], color[3]);
+ return gTrue;
+ }
+ return gFalse;
+}
+
+AnnotLineEndType Annot::parseLineEndType(Object *obj) {
+ if (obj->isName("None")) {
+ return annotLineEndNone;
+ } else if (obj->isName("Square")) {
+ return annotLineEndSquare;
+ } else if (obj->isName("Circle")) {
+ return annotLineEndCircle;
+ } else if (obj->isName("Diamond")) {
+ return annotLineEndDiamond;
+ } else if (obj->isName("OpenArrow")) {
+ return annotLineEndOpenArrow;
+ } else if (obj->isName("ClosedArrow")) {
+ return annotLineEndClosedArrow;
+ } else if (obj->isName("Butt")) {
+ return annotLineEndButt;
+ } else if (obj->isName("ROpenArrow")) {
+ return annotLineEndROpenArrow;
+ } else if (obj->isName("RClosedArrow")) {
+ return annotLineEndRClosedArrow;
+ } else if (obj->isName("Slash")) {
+ return annotLineEndSlash;
+ } else {
+ return annotLineEndNone;
+ }
+}
+
+void Annot::adjustLineEndpoint(AnnotLineEndType lineEnd,
+ double x, double y, double dx, double dy,
+ double w, double *tx, double *ty) {
+ switch (lineEnd) {
+ case annotLineEndNone:
+ w = 0;
+ break;
+ case annotLineEndSquare:
+ w *= lineEndSize1;
+ break;
+ case annotLineEndCircle:
+ w *= lineEndSize1;
+ break;
+ case annotLineEndDiamond:
+ w *= lineEndSize1;
+ break;
+ case annotLineEndOpenArrow:
+ w = 0;
+ break;
+ case annotLineEndClosedArrow:
+ w *= lineEndSize2 * cos(lineArrowAngle);
+ break;
+ case annotLineEndButt:
+ w = 0;
+ break;
+ case annotLineEndROpenArrow:
+ w *= lineEndSize2 * cos(lineArrowAngle);
+ break;
+ case annotLineEndRClosedArrow:
+ w *= lineEndSize2 * cos(lineArrowAngle);
+ break;
+ case annotLineEndSlash:
+ w = 0;
+ break;
+ }
+ *tx = x + w * dx;
+ *ty = y + w * dy;
+}
+
+void Annot::drawLineArrow(AnnotLineEndType lineEnd,
+ double x, double y, double dx, double dy,
+ double w, GBool fill) {
+ switch (lineEnd) {
+ case annotLineEndNone:
+ break;
+ case annotLineEndSquare:
+ w *= lineEndSize1;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*dx + 0.5*w*dy,
+ y + w*dy - 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + 0.5*w*dy,
+ y - 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - 0.5*w*dy,
+ y + 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*dx - 0.5*w*dy,
+ y + w*dy + 0.5*w*dx);
+ appearBuf->append(fill ? "b\n" : "s\n");
+ break;
+ case annotLineEndCircle:
+ w *= lineEndSize1;
+ drawCircle(x + 0.5*w*dx, y + 0.5*w*dy, 0.5*w, fill ? "b" : "s");
+ break;
+ case annotLineEndDiamond:
+ w *= lineEndSize1;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n", x, y);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + 0.5*w*dx - 0.5*w*dy,
+ y + 0.5*w*dy + 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*dx,
+ y + w*dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + 0.5*w*dx + 0.5*w*dy,
+ y + 0.5*w*dy - 0.5*w*dx);
+ appearBuf->append(fill ? "b\n" : "s\n");
+ break;
+ case annotLineEndOpenArrow:
+ w *= lineEndSize2;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*cos(lineArrowAngle)*dx + w*sin(lineArrowAngle)*dy,
+ y + w*cos(lineArrowAngle)*dy - w*sin(lineArrowAngle)*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x, y);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*cos(lineArrowAngle)*dx - w*sin(lineArrowAngle)*dy,
+ y + w*cos(lineArrowAngle)*dy + w*sin(lineArrowAngle)*dx);
+ appearBuf->append("S\n");
+ break;
+ case annotLineEndClosedArrow:
+ w *= lineEndSize2;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*cos(lineArrowAngle)*dx + w*sin(lineArrowAngle)*dy,
+ y + w*cos(lineArrowAngle)*dy - w*sin(lineArrowAngle)*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n", x, y);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*cos(lineArrowAngle)*dx - w*sin(lineArrowAngle)*dy,
+ y + w*cos(lineArrowAngle)*dy + w*sin(lineArrowAngle)*dx);
+ appearBuf->append(fill ? "b\n" : "s\n");
+ break;
+ case annotLineEndButt:
+ w *= lineEndSize1;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + 0.5*w*dy,
+ y - 0.5*w*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - 0.5*w*dy,
+ y + 0.5*w*dx);
+ appearBuf->append("S\n");
+ break;
+ case annotLineEndROpenArrow:
+ w *= lineEndSize2;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*sin(lineArrowAngle)*dy,
+ y - w*sin(lineArrowAngle)*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*cos(lineArrowAngle)*dx,
+ y + w*cos(lineArrowAngle)*dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - w*sin(lineArrowAngle)*dy,
+ y + w*sin(lineArrowAngle)*dx);
+ appearBuf->append("S\n");
+ break;
+ case annotLineEndRClosedArrow:
+ w *= lineEndSize2;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + w*sin(lineArrowAngle)*dy,
+ y - w*sin(lineArrowAngle)*dx);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x + w*cos(lineArrowAngle)*dx,
+ y + w*cos(lineArrowAngle)*dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - w*sin(lineArrowAngle)*dy,
+ y + w*sin(lineArrowAngle)*dx);
+ appearBuf->append(fill ? "b\n" : "s\n");
+ break;
+ case annotLineEndSlash:
+ w *= lineEndSize1;
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ x + 0.5*w*cos(lineArrowAngle)*dy
+ - 0.5*w*sin(lineArrowAngle)*dx,
+ y - 0.5*w*cos(lineArrowAngle)*dx
+ - 0.5*w*sin(lineArrowAngle)*dy);
+ appearBuf->appendf("{0:.4f} {1:.4f} l\n",
+ x - 0.5*w*cos(lineArrowAngle)*dy
+ + 0.5*w*sin(lineArrowAngle)*dx,
+ y + 0.5*w*cos(lineArrowAngle)*dx
+ + 0.5*w*sin(lineArrowAngle)*dy);
+ appearBuf->append("S\n");
+ break;
+ }
+}
+
+// Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
+// <cmd> is used to draw the circle ("f", "s", or "b").
+void Annot::drawCircle(double cx, double cy, double r, const char *cmd) {
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ cx + r, cy);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + r, cy + bezierCircle * r,
+ cx + bezierCircle * r, cy + r,
+ cx, cy + r);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - bezierCircle * r, cy + r,
+ cx - r, cy + bezierCircle * r,
+ cx - r, cy);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - r, cy - bezierCircle * r,
+ cx - bezierCircle * r, cy - r,
+ cx, cy - r);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + bezierCircle * r, cy - r,
+ cx + r, cy - bezierCircle * r,
+ cx + r, cy);
+ appearBuf->appendf("{0:s}\n", cmd);
+}
+
+// Draw the top-left half of an (approximate) circle of radius <r>
+// centered at (<cx>, <cy>).
+void Annot::drawCircleTopLeft(double cx, double cy, double r) {
+ double r2;
+
+ r2 = r / sqrt(2.0);
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ cx + r2, cy + r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + (1 - bezierCircle) * r2,
+ cy + (1 + bezierCircle) * r2,
+ cx - (1 - bezierCircle) * r2,
+ cy + (1 + bezierCircle) * r2,
+ cx - r2,
+ cy + r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - (1 + bezierCircle) * r2,
+ cy + (1 - bezierCircle) * r2,
+ cx - (1 + bezierCircle) * r2,
+ cy - (1 - bezierCircle) * r2,
+ cx - r2,
+ cy - r2);
+ appearBuf->append("S\n");
+}
+
+// Draw the bottom-right half of an (approximate) circle of radius <r>
+// centered at (<cx>, <cy>).
+void Annot::drawCircleBottomRight(double cx, double cy, double r) {
+ double r2;
+
+ r2 = r / sqrt(2.0);
+ appearBuf->appendf("{0:.4f} {1:.4f} m\n",
+ cx - r2, cy - r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx - (1 - bezierCircle) * r2,
+ cy - (1 + bezierCircle) * r2,
+ cx + (1 - bezierCircle) * r2,
+ cy - (1 + bezierCircle) * r2,
+ cx + r2,
+ cy - r2);
+ appearBuf->appendf("{0:.4f} {1:.4f} {2:.4f} {3:.4f} {4:.4f} {5:.4f} c\n",
+ cx + (1 + bezierCircle) * r2,
+ cy - (1 - bezierCircle) * r2,
+ cx + (1 + bezierCircle) * r2,
+ cy + (1 - bezierCircle) * r2,
+ cx + r2,
+ cy + r2);
+ appearBuf->append("S\n");
+}
+
+void Annot::drawText(GString *text, GString *da, int quadding, double margin,
+ int rot) {
+ GString *text2, *tok;
+ GList *daToks;
+ const char *charName;
+ double dx, dy, fontSize, fontSize2, x, y, w;
+ Gushort charWidth;
+ int tfPos, tmPos, i, j, c;
+
+ // check for a Unicode string
+ //~ this currently drops all non-Latin1 characters
+ if (text->getLength() >= 2 &&
+ text->getChar(0) == '\xfe' && text->getChar(1) == '\xff') {
+ text2 = new GString();
+ for (i = 2; i+1 < text->getLength(); i += 2) {
+ c = ((text->getChar(i) & 0xff) << 8) + (text->getChar(i+1) & 0xff);
+ if (c <= 0xff) {
+ text2->append((char)c);
+ } else {
+ text2->append('?');
+ }
+ }
+ } else {
+ text2 = text;
+ }
+
+ // parse the default appearance string
+ tfPos = tmPos = -1;
+ if (da) {
+ daToks = new GList();
+ i = 0;
+ while (i < da->getLength()) {
+ while (i < da->getLength() && Lexer::isSpace(da->getChar(i))) {
+ ++i;
+ }
+ if (i < da->getLength()) {
+ for (j = i + 1;
+ j < da->getLength() && !Lexer::isSpace(da->getChar(j));
+ ++j) ;
+ daToks->append(new GString(da, i, j - i));
+ i = j;
+ }
+ }
+ for (i = 2; i < daToks->getLength(); ++i) {
+ if (i >= 2 && !((GString *)daToks->get(i))->cmp("Tf")) {
+ tfPos = i - 2;
+ } else if (i >= 6 && !((GString *)daToks->get(i))->cmp("Tm")) {
+ tmPos = i - 6;
+ }
+ }
+ } else {
+ daToks = NULL;
+ }
+
+ // get the font and font size
+ fontSize = 0;
+ if (tfPos >= 0) {
+ //~ where do we look up the font?
+ tok = (GString *)daToks->get(tfPos);
+ tok->clear();
+ tok->append("/xpdf_default_font");
+ tok = (GString *)daToks->get(tfPos + 1);
+ fontSize = atof(tok->getCString());
+ } else {
+ error(errSyntaxError, -1,
+ "Missing 'Tf' operator in annotation's DA string");
+ daToks->append(new GString("/xpdf_default_font"));
+ daToks->append(new GString("10"));
+ daToks->append(new GString("Tf"));
+ }
+
+ // setup
+ 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;
+ } 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;
+ } else if (rot == 270) {
+ appearBuf->appendf("0 -1 1 0 0 {0:.4f} cm\n", yMax - yMin);
+ dx = yMax - yMin;
+ dy = xMax - xMin;
+ } else { // assume rot == 0
+ dx = xMax - xMin;
+ dy = yMax - yMin;
+ }
+ appearBuf->append("BT\n");
+
+ // compute string width
+ //~ this assumes we're substituting Helvetica/WinAnsiEncoding for everything
+ w = 0;
+ for (i = 0; i < text2->getLength(); ++i) {
+ charName = winAnsiEncoding[text->getChar(i) & 0xff];
+ if (charName && builtinFonts[4].widths->getWidth(charName, &charWidth)) {
+ w += charWidth;
+ } else {
+ w += 0.5;
+ }
+ }
+
+ // compute font autosize
+ if (fontSize == 0) {
+ fontSize = dy - 2 * margin;
+ fontSize2 = (dx - 2 * margin) / w;
+ if (fontSize2 < fontSize) {
+ fontSize = fontSize2;
+ }
+ fontSize = floor(fontSize);
+ if (tfPos >= 0) {
+ tok = (GString *)daToks->get(tfPos + 1);
+ tok->clear();
+ tok->appendf("{0:.4f}", fontSize);
+ }
+ }
+
+ // compute text start position
+ w *= fontSize;
+ switch (quadding) {
+ case 0:
+ default:
+ x = margin + 2;
+ break;
+ case 1:
+ x = (dx - w) / 2;
+ break;
+ case 2:
+ x = dx - margin - 2 - w;
+ break;
+ }
+ y = 0.5 * dy - 0.4 * fontSize;
+
+ // set the font matrix
+ if (tmPos >= 0) {
+ tok = (GString *)daToks->get(tmPos + 4);
+ tok->clear();
+ tok->appendf("{0:.4f}", x);
+ tok = (GString *)daToks->get(tmPos + 5);
+ tok->clear();
+ tok->appendf("{0:.4f}", y);
+ }
+
+ // write the DA string
+ if (daToks) {
+ for (i = 0; i < daToks->getLength(); ++i) {
+ appearBuf->append((GString *)daToks->get(i))->append(' ');
+ }
+ }
+
+ // 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);
+ }
+
+ // 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");
+ appearBuf->append("Q\n");
+
+ if (daToks) {
+ deleteGList(daToks, GString);
+ }
+ if (text2 != text) {
+ delete text2;
+ }
+}
+
+void Annot::draw(Gfx *gfx, GBool printing) {
+ GBool oc, isLink;
+
+ // check the flags
+ if ((flags & annotFlagHidden) ||
+ (printing && !(flags & annotFlagPrint)) ||
+ (!printing && (flags & annotFlagNoView))) {
+ return;
+ }
+
+ // check the optional content entry
+ if (doc->getOptionalContent()->evalOCObject(&ocObj, &oc) && !oc) {
+ return;
+ }
+
+ // draw the appearance stream
+ isLink = type && !type->cmp("Link");
+ gfx->drawAnnot(&appearance, isLink ? borderStyle : (AnnotBorderStyle *)NULL,
+ xMin, yMin, xMax, yMax);
+}
+
+Object *Annot::getObject(Object *obj) {
+ if (ref.num >= 0) {
+ xref->fetch(ref.num, ref.gen, obj);
+ } else {
+ obj->initNull();
+ }
+ return obj;
+}
+
+//------------------------------------------------------------------------
+// Annots
+//------------------------------------------------------------------------
+
+Annots::Annots(PDFDoc *docA, Object *annotsObj) {
+ Annot *annot;
+ Object obj1, obj2;
+ Ref ref;
+ GBool drawWidgetAnnots;
+ int size;
+ int i;
+
+ doc = docA;
+ annots = NULL;
+ size = 0;
+ nAnnots = 0;
+
+ if (annotsObj->isArray()) {
+ // Kludge: some PDF files define an empty AcroForm, but still
+ // include Widget-type annotations -- in that case, we want to
+ // draw the widgets (since the form code won't). This really
+ // ought to look for Widget-type annotations that are not included
+ // in any form field.
+ drawWidgetAnnots = !doc->getCatalog()->getForm() ||
+ doc->getCatalog()->getForm()->getNumFields() == 0;
+ for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
+ if (annotsObj->arrayGetNF(i, &obj1)->isRef()) {
+ ref = obj1.getRef();
+ obj1.free();
+ annotsObj->arrayGet(i, &obj1);
+ } else {
+ ref.num = ref.gen = -1;
+ }
+ if (obj1.isDict()) {
+ if (drawWidgetAnnots ||
+ !obj1.dictLookup("Subtype", &obj2)->isName("Widget")) {
+ annot = new Annot(doc, obj1.getDict(), &ref);
+ if (annot->isOk()) {
+ if (nAnnots >= size) {
+ size += 16;
+ annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
+ }
+ annots[nAnnots++] = annot;
+ } else {
+ delete annot;
+ }
+ }
+ obj2.free();
+ }
+ obj1.free();
+ }
+ }
+}
+
+Annots::~Annots() {
+ int i;
+
+ for (i = 0; i < nAnnots; ++i) {
+ delete annots[i];
+ }
+ gfree(annots);
+}
+
+Annot *Annots::find(double x, double y) {
+ int i;
+
+ for (i = nAnnots - 1; i >= 0; --i) {
+ if (annots[i]->inRect(x, y)) {
+ return annots[i];
+ }
+ }
+ return NULL;
+}
+
+int Annots::findIdx(double x, double y) {
+ int i;
+
+ for (i = nAnnots - 1; i >= 0; --i) {
+ if (annots[i]->inRect(x, y)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void Annots::generateAnnotAppearances() {
+ int i;
+
+ for (i = 0; i < nAnnots; ++i) {
+ annots[i]->generateAnnotAppearance();
+ }
+}
+
+Annot *Annots::findAnnot(Ref *ref) {
+ int i;
+
+ for (i = 0; i < nAnnots; ++i) {
+ if (annots[i]->match(ref)) {
+ return annots[i];
+ }
+ }
+ return NULL;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.h
new file mode 100644
index 00000000000..6ba5ff490da
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Annot.h
@@ -0,0 +1,185 @@
+//========================================================================
+//
+// Annot.h
+//
+// Copyright 2000-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ANNOT_H
+#define ANNOT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class XRef;
+class Catalog;
+class Gfx;
+class GfxFontDict;
+class PDFDoc;
+
+//------------------------------------------------------------------------
+// AnnotBorderStyle
+//------------------------------------------------------------------------
+
+enum AnnotBorderType {
+ annotBorderSolid,
+ annotBorderDashed,
+ annotBorderBeveled,
+ annotBorderInset,
+ annotBorderUnderlined
+};
+
+class AnnotBorderStyle {
+public:
+
+ AnnotBorderStyle(AnnotBorderType typeA, double widthA,
+ double *dashA, int dashLengthA,
+ double *colorA, int nColorCompsA);
+ ~AnnotBorderStyle();
+
+ AnnotBorderType getType() { return type; }
+ double getWidth() { return width; }
+ void getDash(double **dashA, int *dashLengthA)
+ { *dashA = dash; *dashLengthA = dashLength; }
+ int getNumColorComps() { return nColorComps; }
+ double *getColor() { return color; }
+
+private:
+
+ AnnotBorderType type;
+ double width;
+ double *dash;
+ int dashLength;
+ double color[4];
+ int nColorComps;
+};
+
+//------------------------------------------------------------------------
+
+enum AnnotLineEndType {
+ annotLineEndNone,
+ annotLineEndSquare,
+ annotLineEndCircle,
+ annotLineEndDiamond,
+ annotLineEndOpenArrow,
+ annotLineEndClosedArrow,
+ annotLineEndButt,
+ annotLineEndROpenArrow,
+ annotLineEndRClosedArrow,
+ annotLineEndSlash
+};
+
+//------------------------------------------------------------------------
+// Annot
+//------------------------------------------------------------------------
+
+class Annot {
+public:
+
+ Annot(PDFDoc *docA, Dict *dict, Ref *refA);
+ ~Annot();
+ GBool isOk() { return ok; }
+
+ void draw(Gfx *gfx, GBool printing);
+
+ GString *getType() { return type; }
+ double getXMin() { return xMin; }
+ double getYMin() { return yMin; }
+ double getXMax() { return xMax; }
+ double getYMax() { return yMax; }
+ Object *getObject(Object *obj);
+
+ // Check if point is inside the annotation rectangle.
+ GBool inRect(double x, double y)
+ { return xMin <= x && x <= xMax && yMin <= y && y <= yMax; }
+
+ // Get appearance object.
+ Object *getAppearance(Object *obj) { return appearance.fetch(xref, obj); }
+
+ AnnotBorderStyle *getBorderStyle() { return borderStyle; }
+
+ GBool match(Ref *refA)
+ { return ref.num == refA->num && ref.gen == refA->gen; }
+
+ void generateAnnotAppearance();
+
+private:
+
+ void generateLineAppearance();
+ void generatePolyLineAppearance();
+ void generatePolygonAppearance();
+ void generateFreeTextAppearance();
+ void setLineStyle(AnnotBorderStyle *bs, double *lineWidth);
+ void setStrokeColor(double *color, int nComps);
+ GBool setFillColor(Object *colorObj);
+ AnnotLineEndType parseLineEndType(Object *obj);
+ void adjustLineEndpoint(AnnotLineEndType lineEnd,
+ double x, double y, double dx, double dy,
+ double w, double *tx, double *ty);
+ void drawLineArrow(AnnotLineEndType lineEnd,
+ double x, double y, double dx, double dy,
+ double w, GBool fill);
+ void drawCircle(double cx, double cy, double r, const char *cmd);
+ void drawCircleTopLeft(double cx, double cy, double r);
+ void drawCircleBottomRight(double cx, double cy, double r);
+ void drawText(GString *text, GString *da, int quadding, double margin,
+ int rot);
+
+ PDFDoc *doc;
+ XRef *xref; // the xref table for this PDF file
+ Ref ref; // object ref identifying this annotation
+ GString *type; // annotation type
+ GString *appearanceState; // appearance state name
+ Object appearance; // a reference to the Form XObject stream
+ // for the normal appearance
+ GString *appearBuf;
+ double xMin, yMin, // annotation rectangle
+ xMax, yMax;
+ Guint flags;
+ AnnotBorderStyle *borderStyle;
+ Object ocObj; // optional content entry
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// Annots
+//------------------------------------------------------------------------
+
+class Annots {
+public:
+
+ // Build a list of Annot objects.
+ Annots(PDFDoc *docA, Object *annotsObj);
+
+ ~Annots();
+
+ // Iterate through list of annotations.
+ int getNumAnnots() { return nAnnots; }
+ Annot *getAnnot(int i) { return annots[i]; }
+
+ // If point <x>,<y> is in an annotation, return the associated
+ // annotation; else return NULL.
+ Annot *find(double x, double y);
+ int findIdx(double x, double y);
+
+ // Generate an appearance stream for any non-form-field annotation
+ // that is missing it.
+ void generateAnnotAppearances();
+
+private:
+
+ void scanFieldAppearances(Dict *node, Ref *ref, Dict *parent,
+ Dict *acroForm);
+
+ Annot *findAnnot(Ref *ref);
+
+ PDFDoc *doc;
+ Annot **annots;
+ int nAnnots;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Array.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Array.cc
new file mode 100644
index 00000000000..2bf948ec65c
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Array.cc
@@ -0,0 +1,74 @@
+//========================================================================
+//
+// Array.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "Object.h"
+#include "Array.h"
+
+//------------------------------------------------------------------------
+// Array
+//------------------------------------------------------------------------
+
+Array::Array(XRef *xrefA) {
+ xref = xrefA;
+ elems = NULL;
+ size = length = 0;
+ ref = 1;
+}
+
+Array::~Array() {
+ int i;
+
+ for (i = 0; i < length; ++i)
+ elems[i].free();
+ gfree(elems);
+}
+
+void Array::add(Object *elem) {
+ if (length == size) {
+ if (length == 0) {
+ size = 8;
+ } else {
+ size *= 2;
+ }
+ elems = (Object *)greallocn(elems, size, sizeof(Object));
+ }
+ elems[length] = *elem;
+ ++length;
+}
+
+Object *Array::get(int i, Object *obj, int recursion) {
+ if (i < 0 || i >= length) {
+#ifdef DEBUG_MEM
+ abort();
+#else
+ return obj->initNull();
+#endif
+ }
+ return elems[i].fetch(xref, obj, recursion);
+}
+
+Object *Array::getNF(int i, Object *obj) {
+ if (i < 0 || i >= length) {
+#ifdef DEBUG_MEM
+ abort();
+#else
+ return obj->initNull();
+#endif
+ }
+ return elems[i].copy(obj);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Array.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Array.h
new file mode 100644
index 00000000000..8d10eadc8dd
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Array.h
@@ -0,0 +1,70 @@
+//========================================================================
+//
+// Array.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ARRAY_H
+#define ARRAY_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+#include "Object.h"
+
+class XRef;
+
+//------------------------------------------------------------------------
+// Array
+//------------------------------------------------------------------------
+
+class Array {
+public:
+
+ // Constructor.
+ Array(XRef *xrefA);
+
+ // Destructor.
+ ~Array();
+
+ // Reference counting.
+#if MULTITHREADED
+ long incRef() { return gAtomicIncrement(&ref); }
+ long decRef() { return gAtomicDecrement(&ref); }
+#else
+ long incRef() { return ++ref; }
+ long decRef() { return --ref; }
+#endif
+
+ // Get number of elements.
+ int getLength() { return length; }
+
+ // Add an element.
+ void add(Object *elem);
+
+ // Accessors.
+ Object *get(int i, Object *obj, int recursion = 0);
+ Object *getNF(int i, Object *obj);
+
+private:
+
+ XRef *xref; // the xref table for this PDF file
+ Object *elems; // array of elements
+ int size; // size of <elems> array
+ int length; // number of elements in array
+#if MULTITHREADED
+ GAtomicCounter ref; // reference count
+#else
+ long ref; // reference count
+#endif
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFont.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFont.cc
new file mode 100644
index 00000000000..d2f875e45fd
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFont.cc
@@ -0,0 +1,67 @@
+//========================================================================
+//
+// BuiltinFont.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "FontEncodingTables.h"
+#include "BuiltinFont.h"
+
+//------------------------------------------------------------------------
+
+BuiltinFontWidths::BuiltinFontWidths(BuiltinFontWidth *widths, int sizeA) {
+ int i, h;
+
+ size = sizeA;
+ tab = (BuiltinFontWidth **)gmallocn(size, sizeof(BuiltinFontWidth *));
+ for (i = 0; i < size; ++i) {
+ tab[i] = NULL;
+ }
+ for (i = 0; i < sizeA; ++i) {
+ h = hash(widths[i].name);
+ widths[i].next = tab[h];
+ tab[h] = &widths[i];
+ }
+}
+
+BuiltinFontWidths::~BuiltinFontWidths() {
+ gfree(tab);
+}
+
+GBool BuiltinFontWidths::getWidth(const char *name, Gushort *width) {
+ int h;
+ BuiltinFontWidth *p;
+
+ h = hash(name);
+ for (p = tab[h]; p; p = p->next) {
+ if (!strcmp(p->name, name)) {
+ *width = p->width;
+ return gTrue;
+ }
+ }
+ *width = 0;
+ return gFalse;
+}
+
+int BuiltinFontWidths::hash(const char *name) {
+ const char *p;
+ unsigned int h;
+
+ h = 0;
+ for (p = name; *p; ++p) {
+ h = 17 * h + (int)(*p & 0xff);
+ }
+ return (int)(h % size);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFont.h b/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFont.h
new file mode 100644
index 00000000000..f24cbf98f9f
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFont.h
@@ -0,0 +1,58 @@
+//========================================================================
+//
+// BuiltinFont.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef BUILTINFONT_H
+#define BUILTINFONT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+struct BuiltinFont;
+class BuiltinFontWidths;
+
+//------------------------------------------------------------------------
+
+struct BuiltinFont {
+ const char *name;
+ const char **defaultBaseEnc;
+ short missingWidth;
+ short ascent;
+ short descent;
+ short bbox[4];
+ BuiltinFontWidths *widths;
+};
+
+//------------------------------------------------------------------------
+
+struct BuiltinFontWidth {
+ const char *name;
+ Gushort width;
+ BuiltinFontWidth *next;
+};
+
+class BuiltinFontWidths {
+public:
+
+ BuiltinFontWidths(BuiltinFontWidth *widths, int sizeA);
+ ~BuiltinFontWidths();
+ GBool getWidth(const char *name, Gushort *width);
+
+private:
+
+ int hash(const char *name);
+
+ BuiltinFontWidth **tab;
+ int size;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFontTables.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFontTables.cc
new file mode 100644
index 00000000000..51a468cfb14
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFontTables.cc
@@ -0,0 +1,4285 @@
+//========================================================================
+//
+// BuiltinFontTables.cc
+//
+// Copyright 2001-2017 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdlib.h>
+#include "gmempp.h"
+#include "FontEncodingTables.h"
+#include "BuiltinFontTables.h"
+
+static BuiltinFontWidth courierWidthsTab[] = {
+ { "odieresis", 600, NULL },
+ { "M", 600, NULL },
+ { "Ograve", 600, NULL },
+ { "T", 600, NULL },
+ { "amacron", 600, NULL },
+ { "Ncommaaccent", 600, NULL },
+ { "numbersign", 600, NULL },
+ { "emacron", 600, NULL },
+ { "divide", 600, NULL },
+ { "fl", 600, NULL },
+ { "E", 600, NULL },
+ { "quotedblbase", 600, NULL },
+ { "guillemotleft", 600, NULL },
+ { "endash", 600, NULL },
+ { "w", 600, NULL },
+ { "twosuperior", 600, NULL },
+ { "eth", 600, NULL },
+ { "space", 600, NULL },
+ { "Thorn", 600, NULL },
+ { "ecaron", 600, NULL },
+ { "brokenbar", 600, NULL },
+ { "Aogonek", 600, NULL },
+ { "commaaccent", 600, NULL },
+ { "W", 600, NULL },
+ { "bracketright", 600, NULL },
+ { "germandbls", 600, NULL },
+ { "Ugrave", 600, NULL },
+ { "thorn", 600, NULL },
+ { "zero", 600, NULL },
+ { "udieresis", 600, NULL },
+ { "period", 600, NULL },
+ { "scedilla", 600, NULL },
+ { "Ccedilla", 600, NULL },
+ { "Igrave", 600, NULL },
+ { "Ocircumflex", 600, NULL },
+ { "F", 600, NULL },
+ { "Uring", 600, NULL },
+ { "perthousand", 600, NULL },
+ { "Edotaccent", 600, NULL },
+ { "acircumflex", 600, NULL },
+ { "G", 600, NULL },
+ { "circumflex", 600, NULL },
+ { "Zcaron", 600, NULL },
+ { "ogonek", 600, NULL },
+ { "idieresis", 600, NULL },
+ { "cent", 600, NULL },
+ { "ae", 600, NULL },
+ { "less", 600, NULL },
+ { "ocircumflex", 600, NULL },
+ { "minus", 600, NULL },
+ { "four", 600, NULL },
+ { "dollar", 600, NULL },
+ { "Tcommaaccent", 600, NULL },
+ { "dotaccent", 600, NULL },
+ { "Nacute", 600, NULL },
+ { "v", 600, NULL },
+ { "Kcommaaccent", 600, NULL },
+ { "imacron", 600, NULL },
+ { "rcommaaccent", 600, NULL },
+ { "tilde", 600, NULL },
+ { "underscore", 600, NULL },
+ { "racute", 600, NULL },
+ { "mu", 600, NULL },
+ { "D", 600, NULL },
+ { "Ccaron", 600, NULL },
+ { "j", 600, NULL },
+ { "hungarumlaut", 600, NULL },
+ { "Y", 600, NULL },
+ { "yen", 600, NULL },
+ { "ccaron", 600, NULL },
+ { "lessequal", 600, NULL },
+ { "Ucircumflex", 600, NULL },
+ { "Omacron", 600, NULL },
+ { "quotesinglbase", 600, NULL },
+ { "Scedilla", 600, NULL },
+ { "grave", 600, NULL },
+ { "parenleft", 600, NULL },
+ { "nine", 600, NULL },
+ { "currency", 600, NULL },
+ { "radical", 600, NULL },
+ { "Ntilde", 600, NULL },
+ { "p", 600, NULL },
+ { "dotlessi", 600, NULL },
+ { "cedilla", 600, NULL },
+ { "Idotaccent", 600, NULL },
+ { "plusminus", 600, NULL },
+ { "Eth", 600, NULL },
+ { "quotedblright", 600, NULL },
+ { "ccedilla", 600, NULL },
+ { "colon", 600, NULL },
+ { "g", 600, NULL },
+ { "oe", 600, NULL },
+ { "Odieresis", 600, NULL },
+ { "five", 600, NULL },
+ { "asciicircum", 600, NULL },
+ { "X", 600, NULL },
+ { "emdash", 600, NULL },
+ { "Egrave", 600, NULL },
+ { "aogonek", 600, NULL },
+ { "ring", 600, NULL },
+ { "Dcaron", 600, NULL },
+ { "ecircumflex", 600, NULL },
+ { "Z", 600, NULL },
+ { "breve", 600, NULL },
+ { "Amacron", 600, NULL },
+ { "zcaron", 600, NULL },
+ { "paragraph", 600, NULL },
+ { "lcaron", 600, NULL },
+ { "Ydieresis", 600, NULL },
+ { "aring", 600, NULL },
+ { "iogonek", 600, NULL },
+ { "florin", 600, NULL },
+ { "Euro", 600, NULL },
+ { "Scaron", 600, NULL },
+ { "degree", 600, NULL },
+ { "section", 600, NULL },
+ { "K", 600, NULL },
+ { "Aacute", 600, NULL },
+ { "Scommaaccent", 600, NULL },
+ { "b", 600, NULL },
+ { "greater", 600, NULL },
+ { "asterisk", 600, NULL },
+ { "scommaaccent", 600, NULL },
+ { "Emacron", 600, NULL },
+ { "onesuperior", 600, NULL },
+ { "percent", 600, NULL },
+ { "o", 600, NULL },
+ { "icircumflex", 600, NULL },
+ { "iacute", 600, NULL },
+ { "e", 600, NULL },
+ { "Imacron", 600, NULL },
+ { "abreve", 600, NULL },
+ { "N", 600, NULL },
+ { "equal", 600, NULL },
+ { "dagger", 600, NULL },
+ { "u", 600, NULL },
+ { "C", 600, NULL },
+ { "l", 600, NULL },
+ { "q", 600, NULL },
+ { "quoteleft", 600, NULL },
+ { "registered", 600, NULL },
+ { "Gcommaaccent", 600, NULL },
+ { "Racute", 600, NULL },
+ { "k", 600, NULL },
+ { "copyright", 600, NULL },
+ { "onehalf", 600, NULL },
+ { "lcommaaccent", 600, NULL },
+ { "uhungarumlaut", 600, NULL },
+ { "gbreve", 600, NULL },
+ { "n", 600, NULL },
+ { "Ecaron", 600, NULL },
+ { "zdotaccent", 600, NULL },
+ { "threesuperior", 600, NULL },
+ { "Uacute", 600, NULL },
+ { "Zacute", 600, NULL },
+ { "atilde", 600, NULL },
+ { "fraction", 600, NULL },
+ { "Adieresis", 600, NULL },
+ { "Sacute", 600, NULL },
+ { "asciitilde", 600, NULL },
+ { "lacute", 600, NULL },
+ { "Uogonek", 600, NULL },
+ { "caron", 600, NULL },
+ { "Lslash", 600, NULL },
+ { "sterling", 600, NULL },
+ { "H", 600, NULL },
+ { "at", 600, NULL },
+ { "greaterequal", 600, NULL },
+ { "J", 600, NULL },
+ { "threequarters", 600, NULL },
+ { "ohungarumlaut", 600, NULL },
+ { "Ecircumflex", 600, NULL },
+ { "L", 600, NULL },
+ { "Agrave", 600, NULL },
+ { "exclam", 600, NULL },
+ { "AE", 600, NULL },
+ { "notequal", 600, NULL },
+ { "oslash", 600, NULL },
+ { "Acircumflex", 600, NULL },
+ { "ograve", 600, NULL },
+ { "Rcommaaccent", 600, NULL },
+ { "Lacute", 600, NULL },
+ { "Idieresis", 600, NULL },
+ { "sacute", 600, NULL },
+ { "f", 600, NULL },
+ { "gcommaaccent", 600, NULL },
+ { "bracketleft", 600, NULL },
+ { "Ohungarumlaut", 600, NULL },
+ { "otilde", 600, NULL },
+ { "parenright", 600, NULL },
+ { "questiondown", 600, NULL },
+ { "Lcaron", 600, NULL },
+ { "ordmasculine", 600, NULL },
+ { "backslash", 600, NULL },
+ { "Eogonek", 600, NULL },
+ { "macron", 600, NULL },
+ { "three", 600, NULL },
+ { "slash", 600, NULL },
+ { "r", 600, NULL },
+ { "ucircumflex", 600, NULL },
+ { "zacute", 600, NULL },
+ { "Umacron", 600, NULL },
+ { "adieresis", 600, NULL },
+ { "ncaron", 600, NULL },
+ { "V", 600, NULL },
+ { "lozenge", 600, NULL },
+ { "exclamdown", 600, NULL },
+ { "Q", 600, NULL },
+ { "uogonek", 600, NULL },
+ { "quoteright", 600, NULL },
+ { "guilsinglright", 600, NULL },
+ { "semicolon", 600, NULL },
+ { "egrave", 600, NULL },
+ { "s", 600, NULL },
+ { "uring", 600, NULL },
+ { "z", 600, NULL },
+ { "Uhungarumlaut", 600, NULL },
+ { "I", 600, NULL },
+ { "one", 600, NULL },
+ { "eight", 600, NULL },
+ { "periodcentered", 600, NULL },
+ { "d", 600, NULL },
+ { "c", 600, NULL },
+ { "agrave", 600, NULL },
+ { "ellipsis", 600, NULL },
+ { "Aring", 600, NULL },
+ { "t", 600, NULL },
+ { "quotedbl", 600, NULL },
+ { "Oacute", 600, NULL },
+ { "edotaccent", 600, NULL },
+ { "six", 600, NULL },
+ { "lslash", 600, NULL },
+ { "P", 600, NULL },
+ { "i", 600, NULL },
+ { "multiply", 600, NULL },
+ { "bar", 600, NULL },
+ { "quotesingle", 600, NULL },
+ { "acute", 600, NULL },
+ { "braceright", 600, NULL },
+ { "braceleft", 600, NULL },
+ { "onequarter", 600, NULL },
+ { "Atilde", 600, NULL },
+ { "Edieresis", 600, NULL },
+ { "oacute", 600, NULL },
+ { "Yacute", 600, NULL },
+ { "daggerdbl", 600, NULL },
+ { "ncommaaccent", 600, NULL },
+ { "logicalnot", 600, NULL },
+ { "tcommaaccent", 600, NULL },
+ { "Dcroat", 600, NULL },
+ { "cacute", 600, NULL },
+ { "tcaron", 600, NULL },
+ { "rcaron", 600, NULL },
+ { "Iogonek", 600, NULL },
+ { "y", 600, NULL },
+ { "edieresis", 600, NULL },
+ { "x", 600, NULL },
+ { "Ncaron", 600, NULL },
+ { "OE", 600, NULL },
+ { "m", 600, NULL },
+ { "U", 600, NULL },
+ { "partialdiff", 600, NULL },
+ { "dcroat", 600, NULL },
+ { "igrave", 600, NULL },
+ { "summation", 600, NULL },
+ { "question", 600, NULL },
+ { "A", 600, NULL },
+ { "Cacute", 600, NULL },
+ { "h", 600, NULL },
+ { "guilsinglleft", 600, NULL },
+ { "plus", 600, NULL },
+ { "Otilde", 600, NULL },
+ { "umacron", 600, NULL },
+ { "Tcaron", 600, NULL },
+ { "aacute", 600, NULL },
+ { "B", 600, NULL },
+ { "R", 600, NULL },
+ { "ydieresis", 600, NULL },
+ { "Rcaron", 600, NULL },
+ { "kcommaaccent", 600, NULL },
+ { "yacute", 600, NULL },
+ { "dcaron", 600, NULL },
+ { "ugrave", 600, NULL },
+ { "a", 600, NULL },
+ { "hyphen", 600, NULL },
+ { "Oslash", 600, NULL },
+ { "comma", 600, NULL },
+ { "Delta", 600, NULL },
+ { "omacron", 600, NULL },
+ { "Gbreve", 600, NULL },
+ { "S", 600, NULL },
+ { "quotedblleft", 600, NULL },
+ { "ordfeminine", 600, NULL },
+ { "ampersand", 600, NULL },
+ { "trademark", 600, NULL },
+ { "Lcommaaccent", 600, NULL },
+ { "ntilde", 600, NULL },
+ { "fi", 600, NULL },
+ { "dieresis", 600, NULL },
+ { "Eacute", 600, NULL },
+ { "nacute", 600, NULL },
+ { "O", 600, NULL },
+ { "Icircumflex", 600, NULL },
+ { "Udieresis", 600, NULL },
+ { "Abreve", 600, NULL },
+ { "Iacute", 600, NULL },
+ { "uacute", 600, NULL },
+ { "two", 600, NULL },
+ { "seven", 600, NULL },
+ { "scaron", 600, NULL },
+ { "guillemotright", 600, NULL },
+ { "bullet", 600, NULL },
+ { "eogonek", 600, NULL },
+ { "eacute", 600, NULL },
+ { "Zdotaccent", 600, NULL }
+};
+
+static BuiltinFontWidth courierBoldWidthsTab[] = {
+ { "t", 600, NULL },
+ { "quotedbl", 600, NULL },
+ { "Oacute", 600, NULL },
+ { "agrave", 600, NULL },
+ { "ellipsis", 600, NULL },
+ { "Aring", 600, NULL },
+ { "eight", 600, NULL },
+ { "I", 600, NULL },
+ { "one", 600, NULL },
+ { "periodcentered", 600, NULL },
+ { "c", 600, NULL },
+ { "d", 600, NULL },
+ { "egrave", 600, NULL },
+ { "uring", 600, NULL },
+ { "s", 600, NULL },
+ { "z", 600, NULL },
+ { "Uhungarumlaut", 600, NULL },
+ { "uogonek", 600, NULL },
+ { "quoteright", 600, NULL },
+ { "semicolon", 600, NULL },
+ { "guilsinglright", 600, NULL },
+ { "Umacron", 600, NULL },
+ { "zacute", 600, NULL },
+ { "ncaron", 600, NULL },
+ { "adieresis", 600, NULL },
+ { "lozenge", 600, NULL },
+ { "V", 600, NULL },
+ { "Q", 600, NULL },
+ { "exclamdown", 600, NULL },
+ { "three", 600, NULL },
+ { "slash", 600, NULL },
+ { "r", 600, NULL },
+ { "ucircumflex", 600, NULL },
+ { "macron", 600, NULL },
+ { "Eogonek", 600, NULL },
+ { "Lcaron", 600, NULL },
+ { "ordmasculine", 600, NULL },
+ { "questiondown", 600, NULL },
+ { "backslash", 600, NULL },
+ { "gcommaaccent", 600, NULL },
+ { "Ohungarumlaut", 600, NULL },
+ { "bracketleft", 600, NULL },
+ { "otilde", 600, NULL },
+ { "parenright", 600, NULL },
+ { "f", 600, NULL },
+ { "Lacute", 600, NULL },
+ { "Idieresis", 600, NULL },
+ { "sacute", 600, NULL },
+ { "Rcommaaccent", 600, NULL },
+ { "ograve", 600, NULL },
+ { "threequarters", 600, NULL },
+ { "J", 600, NULL },
+ { "ohungarumlaut", 600, NULL },
+ { "Ecircumflex", 600, NULL },
+ { "L", 600, NULL },
+ { "notequal", 600, NULL },
+ { "exclam", 600, NULL },
+ { "Agrave", 600, NULL },
+ { "AE", 600, NULL },
+ { "Acircumflex", 600, NULL },
+ { "oslash", 600, NULL },
+ { "Uogonek", 600, NULL },
+ { "caron", 600, NULL },
+ { "Lslash", 600, NULL },
+ { "lacute", 600, NULL },
+ { "sterling", 600, NULL },
+ { "H", 600, NULL },
+ { "at", 600, NULL },
+ { "greaterequal", 600, NULL },
+ { "asciitilde", 600, NULL },
+ { "Uacute", 600, NULL },
+ { "atilde", 600, NULL },
+ { "Zacute", 600, NULL },
+ { "fraction", 600, NULL },
+ { "Adieresis", 600, NULL },
+ { "Sacute", 600, NULL },
+ { "scaron", 600, NULL },
+ { "guillemotright", 600, NULL },
+ { "bullet", 600, NULL },
+ { "eogonek", 600, NULL },
+ { "eacute", 600, NULL },
+ { "Zdotaccent", 600, NULL },
+ { "Abreve", 600, NULL },
+ { "Udieresis", 600, NULL },
+ { "Iacute", 600, NULL },
+ { "two", 600, NULL },
+ { "uacute", 600, NULL },
+ { "seven", 600, NULL },
+ { "dieresis", 600, NULL },
+ { "Eacute", 600, NULL },
+ { "O", 600, NULL },
+ { "Icircumflex", 600, NULL },
+ { "nacute", 600, NULL },
+ { "trademark", 600, NULL },
+ { "ampersand", 600, NULL },
+ { "Lcommaaccent", 600, NULL },
+ { "ntilde", 600, NULL },
+ { "fi", 600, NULL },
+ { "quotedblleft", 600, NULL },
+ { "ordfeminine", 600, NULL },
+ { "omacron", 600, NULL },
+ { "comma", 600, NULL },
+ { "Delta", 600, NULL },
+ { "Gbreve", 600, NULL },
+ { "S", 600, NULL },
+ { "B", 600, NULL },
+ { "kcommaaccent", 600, NULL },
+ { "Rcaron", 600, NULL },
+ { "yacute", 600, NULL },
+ { "ydieresis", 600, NULL },
+ { "R", 600, NULL },
+ { "ugrave", 600, NULL },
+ { "dcaron", 600, NULL },
+ { "Oslash", 600, NULL },
+ { "hyphen", 600, NULL },
+ { "a", 600, NULL },
+ { "Otilde", 600, NULL },
+ { "umacron", 600, NULL },
+ { "Tcaron", 600, NULL },
+ { "aacute", 600, NULL },
+ { "guilsinglleft", 600, NULL },
+ { "Cacute", 600, NULL },
+ { "h", 600, NULL },
+ { "plus", 600, NULL },
+ { "igrave", 600, NULL },
+ { "dcroat", 600, NULL },
+ { "question", 600, NULL },
+ { "summation", 600, NULL },
+ { "A", 600, NULL },
+ { "x", 600, NULL },
+ { "edieresis", 600, NULL },
+ { "Iogonek", 600, NULL },
+ { "y", 600, NULL },
+ { "Ncaron", 600, NULL },
+ { "m", 600, NULL },
+ { "OE", 600, NULL },
+ { "partialdiff", 600, NULL },
+ { "U", 600, NULL },
+ { "ncommaaccent", 600, NULL },
+ { "tcommaaccent", 600, NULL },
+ { "logicalnot", 600, NULL },
+ { "cacute", 600, NULL },
+ { "Dcroat", 600, NULL },
+ { "tcaron", 600, NULL },
+ { "rcaron", 600, NULL },
+ { "Atilde", 600, NULL },
+ { "Edieresis", 600, NULL },
+ { "Yacute", 600, NULL },
+ { "oacute", 600, NULL },
+ { "daggerdbl", 600, NULL },
+ { "braceleft", 600, NULL },
+ { "braceright", 600, NULL },
+ { "acute", 600, NULL },
+ { "onequarter", 600, NULL },
+ { "i", 600, NULL },
+ { "multiply", 600, NULL },
+ { "quotesingle", 600, NULL },
+ { "bar", 600, NULL },
+ { "six", 600, NULL },
+ { "edotaccent", 600, NULL },
+ { "lslash", 600, NULL },
+ { "P", 600, NULL },
+ { "parenleft", 600, NULL },
+ { "currency", 600, NULL },
+ { "nine", 600, NULL },
+ { "quotesinglbase", 600, NULL },
+ { "grave", 600, NULL },
+ { "Scedilla", 600, NULL },
+ { "ccaron", 600, NULL },
+ { "Ucircumflex", 600, NULL },
+ { "lessequal", 600, NULL },
+ { "Omacron", 600, NULL },
+ { "hungarumlaut", 600, NULL },
+ { "Ccaron", 600, NULL },
+ { "j", 600, NULL },
+ { "Y", 600, NULL },
+ { "yen", 600, NULL },
+ { "rcommaaccent", 600, NULL },
+ { "Kcommaaccent", 600, NULL },
+ { "imacron", 600, NULL },
+ { "tilde", 600, NULL },
+ { "underscore", 600, NULL },
+ { "mu", 600, NULL },
+ { "D", 600, NULL },
+ { "racute", 600, NULL },
+ { "Tcommaaccent", 600, NULL },
+ { "dollar", 600, NULL },
+ { "Nacute", 600, NULL },
+ { "v", 600, NULL },
+ { "dotaccent", 600, NULL },
+ { "cent", 600, NULL },
+ { "ae", 600, NULL },
+ { "ocircumflex", 600, NULL },
+ { "less", 600, NULL },
+ { "minus", 600, NULL },
+ { "four", 600, NULL },
+ { "circumflex", 600, NULL },
+ { "Zcaron", 600, NULL },
+ { "ogonek", 600, NULL },
+ { "idieresis", 600, NULL },
+ { "G", 600, NULL },
+ { "F", 600, NULL },
+ { "Ocircumflex", 600, NULL },
+ { "Uring", 600, NULL },
+ { "perthousand", 600, NULL },
+ { "Edotaccent", 600, NULL },
+ { "acircumflex", 600, NULL },
+ { "zero", 600, NULL },
+ { "period", 600, NULL },
+ { "scedilla", 600, NULL },
+ { "udieresis", 600, NULL },
+ { "Ccedilla", 600, NULL },
+ { "Igrave", 600, NULL },
+ { "commaaccent", 600, NULL },
+ { "W", 600, NULL },
+ { "bracketright", 600, NULL },
+ { "Ugrave", 600, NULL },
+ { "germandbls", 600, NULL },
+ { "thorn", 600, NULL },
+ { "w", 600, NULL },
+ { "Thorn", 600, NULL },
+ { "space", 600, NULL },
+ { "eth", 600, NULL },
+ { "twosuperior", 600, NULL },
+ { "ecaron", 600, NULL },
+ { "Aogonek", 600, NULL },
+ { "brokenbar", 600, NULL },
+ { "fl", 600, NULL },
+ { "E", 600, NULL },
+ { "quotedblbase", 600, NULL },
+ { "guillemotleft", 600, NULL },
+ { "endash", 600, NULL },
+ { "amacron", 600, NULL },
+ { "Ncommaaccent", 600, NULL },
+ { "numbersign", 600, NULL },
+ { "divide", 600, NULL },
+ { "emacron", 600, NULL },
+ { "M", 600, NULL },
+ { "odieresis", 600, NULL },
+ { "Ograve", 600, NULL },
+ { "T", 600, NULL },
+ { "n", 600, NULL },
+ { "Ecaron", 600, NULL },
+ { "threesuperior", 600, NULL },
+ { "zdotaccent", 600, NULL },
+ { "lcommaaccent", 600, NULL },
+ { "onehalf", 600, NULL },
+ { "gbreve", 600, NULL },
+ { "uhungarumlaut", 600, NULL },
+ { "Racute", 600, NULL },
+ { "k", 600, NULL },
+ { "copyright", 600, NULL },
+ { "quoteleft", 600, NULL },
+ { "registered", 600, NULL },
+ { "Gcommaaccent", 600, NULL },
+ { "equal", 600, NULL },
+ { "dagger", 600, NULL },
+ { "N", 600, NULL },
+ { "C", 600, NULL },
+ { "u", 600, NULL },
+ { "q", 600, NULL },
+ { "l", 600, NULL },
+ { "icircumflex", 600, NULL },
+ { "Imacron", 600, NULL },
+ { "abreve", 600, NULL },
+ { "e", 600, NULL },
+ { "iacute", 600, NULL },
+ { "percent", 600, NULL },
+ { "o", 600, NULL },
+ { "greater", 600, NULL },
+ { "Emacron", 600, NULL },
+ { "asterisk", 600, NULL },
+ { "scommaaccent", 600, NULL },
+ { "onesuperior", 600, NULL },
+ { "K", 600, NULL },
+ { "Scommaaccent", 600, NULL },
+ { "Aacute", 600, NULL },
+ { "b", 600, NULL },
+ { "Euro", 600, NULL },
+ { "florin", 600, NULL },
+ { "iogonek", 600, NULL },
+ { "section", 600, NULL },
+ { "Scaron", 600, NULL },
+ { "degree", 600, NULL },
+ { "lcaron", 600, NULL },
+ { "paragraph", 600, NULL },
+ { "zcaron", 600, NULL },
+ { "aring", 600, NULL },
+ { "Ydieresis", 600, NULL },
+ { "Dcaron", 600, NULL },
+ { "Z", 600, NULL },
+ { "ecircumflex", 600, NULL },
+ { "Amacron", 600, NULL },
+ { "breve", 600, NULL },
+ { "emdash", 600, NULL },
+ { "Egrave", 600, NULL },
+ { "aogonek", 600, NULL },
+ { "ring", 600, NULL },
+ { "Odieresis", 600, NULL },
+ { "oe", 600, NULL },
+ { "five", 600, NULL },
+ { "X", 600, NULL },
+ { "asciicircum", 600, NULL },
+ { "plusminus", 600, NULL },
+ { "Idotaccent", 600, NULL },
+ { "cedilla", 600, NULL },
+ { "quotedblright", 600, NULL },
+ { "Eth", 600, NULL },
+ { "colon", 600, NULL },
+ { "g", 600, NULL },
+ { "ccedilla", 600, NULL },
+ { "radical", 600, NULL },
+ { "Ntilde", 600, NULL },
+ { "dotlessi", 600, NULL },
+ { "p", 600, NULL }
+};
+
+static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
+ { "greater", 600, NULL },
+ { "asterisk", 600, NULL },
+ { "Emacron", 600, NULL },
+ { "scommaaccent", 600, NULL },
+ { "onesuperior", 600, NULL },
+ { "percent", 600, NULL },
+ { "o", 600, NULL },
+ { "icircumflex", 600, NULL },
+ { "iacute", 600, NULL },
+ { "e", 600, NULL },
+ { "Imacron", 600, NULL },
+ { "abreve", 600, NULL },
+ { "N", 600, NULL },
+ { "equal", 600, NULL },
+ { "dagger", 600, NULL },
+ { "u", 600, NULL },
+ { "C", 600, NULL },
+ { "l", 600, NULL },
+ { "q", 600, NULL },
+ { "quoteleft", 600, NULL },
+ { "registered", 600, NULL },
+ { "Gcommaaccent", 600, NULL },
+ { "Racute", 600, NULL },
+ { "k", 600, NULL },
+ { "copyright", 600, NULL },
+ { "onehalf", 600, NULL },
+ { "lcommaaccent", 600, NULL },
+ { "uhungarumlaut", 600, NULL },
+ { "gbreve", 600, NULL },
+ { "n", 600, NULL },
+ { "Ecaron", 600, NULL },
+ { "zdotaccent", 600, NULL },
+ { "threesuperior", 600, NULL },
+ { "radical", 600, NULL },
+ { "Ntilde", 600, NULL },
+ { "p", 600, NULL },
+ { "dotlessi", 600, NULL },
+ { "cedilla", 600, NULL },
+ { "Idotaccent", 600, NULL },
+ { "plusminus", 600, NULL },
+ { "Eth", 600, NULL },
+ { "quotedblright", 600, NULL },
+ { "ccedilla", 600, NULL },
+ { "g", 600, NULL },
+ { "colon", 600, NULL },
+ { "oe", 600, NULL },
+ { "Odieresis", 600, NULL },
+ { "five", 600, NULL },
+ { "asciicircum", 600, NULL },
+ { "X", 600, NULL },
+ { "emdash", 600, NULL },
+ { "aogonek", 600, NULL },
+ { "Egrave", 600, NULL },
+ { "ring", 600, NULL },
+ { "Dcaron", 600, NULL },
+ { "ecircumflex", 600, NULL },
+ { "Z", 600, NULL },
+ { "breve", 600, NULL },
+ { "Amacron", 600, NULL },
+ { "paragraph", 600, NULL },
+ { "zcaron", 600, NULL },
+ { "lcaron", 600, NULL },
+ { "Ydieresis", 600, NULL },
+ { "aring", 600, NULL },
+ { "iogonek", 600, NULL },
+ { "florin", 600, NULL },
+ { "Euro", 600, NULL },
+ { "Scaron", 600, NULL },
+ { "degree", 600, NULL },
+ { "section", 600, NULL },
+ { "K", 600, NULL },
+ { "Aacute", 600, NULL },
+ { "Scommaaccent", 600, NULL },
+ { "b", 600, NULL },
+ { "circumflex", 600, NULL },
+ { "Zcaron", 600, NULL },
+ { "ogonek", 600, NULL },
+ { "idieresis", 600, NULL },
+ { "ae", 600, NULL },
+ { "cent", 600, NULL },
+ { "less", 600, NULL },
+ { "ocircumflex", 600, NULL },
+ { "minus", 600, NULL },
+ { "four", 600, NULL },
+ { "Tcommaaccent", 600, NULL },
+ { "dollar", 600, NULL },
+ { "dotaccent", 600, NULL },
+ { "v", 600, NULL },
+ { "Nacute", 600, NULL },
+ { "Kcommaaccent", 600, NULL },
+ { "imacron", 600, NULL },
+ { "rcommaaccent", 600, NULL },
+ { "tilde", 600, NULL },
+ { "underscore", 600, NULL },
+ { "racute", 600, NULL },
+ { "mu", 600, NULL },
+ { "D", 600, NULL },
+ { "Ccaron", 600, NULL },
+ { "j", 600, NULL },
+ { "hungarumlaut", 600, NULL },
+ { "Y", 600, NULL },
+ { "yen", 600, NULL },
+ { "ccaron", 600, NULL },
+ { "Ucircumflex", 600, NULL },
+ { "lessequal", 600, NULL },
+ { "Omacron", 600, NULL },
+ { "quotesinglbase", 600, NULL },
+ { "Scedilla", 600, NULL },
+ { "grave", 600, NULL },
+ { "parenleft", 600, NULL },
+ { "nine", 600, NULL },
+ { "currency", 600, NULL },
+ { "odieresis", 600, NULL },
+ { "M", 600, NULL },
+ { "Ograve", 600, NULL },
+ { "T", 600, NULL },
+ { "amacron", 600, NULL },
+ { "Ncommaaccent", 600, NULL },
+ { "numbersign", 600, NULL },
+ { "emacron", 600, NULL },
+ { "divide", 600, NULL },
+ { "fl", 600, NULL },
+ { "quotedblbase", 600, NULL },
+ { "E", 600, NULL },
+ { "guillemotleft", 600, NULL },
+ { "endash", 600, NULL },
+ { "w", 600, NULL },
+ { "eth", 600, NULL },
+ { "twosuperior", 600, NULL },
+ { "space", 600, NULL },
+ { "Thorn", 600, NULL },
+ { "ecaron", 600, NULL },
+ { "brokenbar", 600, NULL },
+ { "Aogonek", 600, NULL },
+ { "commaaccent", 600, NULL },
+ { "bracketright", 600, NULL },
+ { "W", 600, NULL },
+ { "germandbls", 600, NULL },
+ { "Ugrave", 600, NULL },
+ { "thorn", 600, NULL },
+ { "zero", 600, NULL },
+ { "udieresis", 600, NULL },
+ { "scedilla", 600, NULL },
+ { "period", 600, NULL },
+ { "Ccedilla", 600, NULL },
+ { "Igrave", 600, NULL },
+ { "F", 600, NULL },
+ { "Ocircumflex", 600, NULL },
+ { "Uring", 600, NULL },
+ { "perthousand", 600, NULL },
+ { "Edotaccent", 600, NULL },
+ { "acircumflex", 600, NULL },
+ { "G", 600, NULL },
+ { "Otilde", 600, NULL },
+ { "umacron", 600, NULL },
+ { "Tcaron", 600, NULL },
+ { "aacute", 600, NULL },
+ { "B", 600, NULL },
+ { "ydieresis", 600, NULL },
+ { "R", 600, NULL },
+ { "kcommaaccent", 600, NULL },
+ { "yacute", 600, NULL },
+ { "Rcaron", 600, NULL },
+ { "dcaron", 600, NULL },
+ { "ugrave", 600, NULL },
+ { "a", 600, NULL },
+ { "hyphen", 600, NULL },
+ { "Oslash", 600, NULL },
+ { "comma", 600, NULL },
+ { "Delta", 600, NULL },
+ { "omacron", 600, NULL },
+ { "Gbreve", 600, NULL },
+ { "S", 600, NULL },
+ { "quotedblleft", 600, NULL },
+ { "ordfeminine", 600, NULL },
+ { "ampersand", 600, NULL },
+ { "trademark", 600, NULL },
+ { "Lcommaaccent", 600, NULL },
+ { "ntilde", 600, NULL },
+ { "fi", 600, NULL },
+ { "dieresis", 600, NULL },
+ { "Eacute", 600, NULL },
+ { "nacute", 600, NULL },
+ { "O", 600, NULL },
+ { "Icircumflex", 600, NULL },
+ { "Udieresis", 600, NULL },
+ { "Abreve", 600, NULL },
+ { "Iacute", 600, NULL },
+ { "uacute", 600, NULL },
+ { "two", 600, NULL },
+ { "seven", 600, NULL },
+ { "scaron", 600, NULL },
+ { "guillemotright", 600, NULL },
+ { "eogonek", 600, NULL },
+ { "eacute", 600, NULL },
+ { "bullet", 600, NULL },
+ { "Zdotaccent", 600, NULL },
+ { "edotaccent", 600, NULL },
+ { "six", 600, NULL },
+ { "lslash", 600, NULL },
+ { "P", 600, NULL },
+ { "multiply", 600, NULL },
+ { "i", 600, NULL },
+ { "bar", 600, NULL },
+ { "quotesingle", 600, NULL },
+ { "acute", 600, NULL },
+ { "braceright", 600, NULL },
+ { "braceleft", 600, NULL },
+ { "onequarter", 600, NULL },
+ { "Atilde", 600, NULL },
+ { "Edieresis", 600, NULL },
+ { "Yacute", 600, NULL },
+ { "oacute", 600, NULL },
+ { "daggerdbl", 600, NULL },
+ { "ncommaaccent", 600, NULL },
+ { "logicalnot", 600, NULL },
+ { "tcommaaccent", 600, NULL },
+ { "Dcroat", 600, NULL },
+ { "cacute", 600, NULL },
+ { "rcaron", 600, NULL },
+ { "tcaron", 600, NULL },
+ { "y", 600, NULL },
+ { "Iogonek", 600, NULL },
+ { "edieresis", 600, NULL },
+ { "x", 600, NULL },
+ { "Ncaron", 600, NULL },
+ { "OE", 600, NULL },
+ { "m", 600, NULL },
+ { "U", 600, NULL },
+ { "partialdiff", 600, NULL },
+ { "dcroat", 600, NULL },
+ { "igrave", 600, NULL },
+ { "summation", 600, NULL },
+ { "question", 600, NULL },
+ { "A", 600, NULL },
+ { "h", 600, NULL },
+ { "Cacute", 600, NULL },
+ { "guilsinglleft", 600, NULL },
+ { "plus", 600, NULL },
+ { "ordmasculine", 600, NULL },
+ { "Lcaron", 600, NULL },
+ { "backslash", 600, NULL },
+ { "questiondown", 600, NULL },
+ { "macron", 600, NULL },
+ { "Eogonek", 600, NULL },
+ { "three", 600, NULL },
+ { "slash", 600, NULL },
+ { "ucircumflex", 600, NULL },
+ { "r", 600, NULL },
+ { "zacute", 600, NULL },
+ { "Umacron", 600, NULL },
+ { "adieresis", 600, NULL },
+ { "ncaron", 600, NULL },
+ { "V", 600, NULL },
+ { "lozenge", 600, NULL },
+ { "exclamdown", 600, NULL },
+ { "Q", 600, NULL },
+ { "quoteright", 600, NULL },
+ { "uogonek", 600, NULL },
+ { "guilsinglright", 600, NULL },
+ { "semicolon", 600, NULL },
+ { "egrave", 600, NULL },
+ { "s", 600, NULL },
+ { "uring", 600, NULL },
+ { "Uhungarumlaut", 600, NULL },
+ { "z", 600, NULL },
+ { "I", 600, NULL },
+ { "one", 600, NULL },
+ { "eight", 600, NULL },
+ { "periodcentered", 600, NULL },
+ { "d", 600, NULL },
+ { "c", 600, NULL },
+ { "agrave", 600, NULL },
+ { "ellipsis", 600, NULL },
+ { "Aring", 600, NULL },
+ { "t", 600, NULL },
+ { "quotedbl", 600, NULL },
+ { "Oacute", 600, NULL },
+ { "Uacute", 600, NULL },
+ { "Zacute", 600, NULL },
+ { "atilde", 600, NULL },
+ { "fraction", 600, NULL },
+ { "Adieresis", 600, NULL },
+ { "Sacute", 600, NULL },
+ { "asciitilde", 600, NULL },
+ { "lacute", 600, NULL },
+ { "Uogonek", 600, NULL },
+ { "Lslash", 600, NULL },
+ { "caron", 600, NULL },
+ { "sterling", 600, NULL },
+ { "H", 600, NULL },
+ { "greaterequal", 600, NULL },
+ { "at", 600, NULL },
+ { "J", 600, NULL },
+ { "threequarters", 600, NULL },
+ { "ohungarumlaut", 600, NULL },
+ { "Ecircumflex", 600, NULL },
+ { "L", 600, NULL },
+ { "AE", 600, NULL },
+ { "Agrave", 600, NULL },
+ { "exclam", 600, NULL },
+ { "notequal", 600, NULL },
+ { "oslash", 600, NULL },
+ { "Acircumflex", 600, NULL },
+ { "Rcommaaccent", 600, NULL },
+ { "ograve", 600, NULL },
+ { "Lacute", 600, NULL },
+ { "Idieresis", 600, NULL },
+ { "sacute", 600, NULL },
+ { "f", 600, NULL },
+ { "gcommaaccent", 600, NULL },
+ { "bracketleft", 600, NULL },
+ { "Ohungarumlaut", 600, NULL },
+ { "otilde", 600, NULL },
+ { "parenright", 600, NULL }
+};
+
+static BuiltinFontWidth courierObliqueWidthsTab[] = {
+ { "ecaron", 600, NULL },
+ { "brokenbar", 600, NULL },
+ { "Aogonek", 600, NULL },
+ { "w", 600, NULL },
+ { "Thorn", 600, NULL },
+ { "space", 600, NULL },
+ { "eth", 600, NULL },
+ { "twosuperior", 600, NULL },
+ { "guillemotleft", 600, NULL },
+ { "endash", 600, NULL },
+ { "fl", 600, NULL },
+ { "E", 600, NULL },
+ { "quotedblbase", 600, NULL },
+ { "numbersign", 600, NULL },
+ { "divide", 600, NULL },
+ { "emacron", 600, NULL },
+ { "amacron", 600, NULL },
+ { "Ncommaaccent", 600, NULL },
+ { "T", 600, NULL },
+ { "M", 600, NULL },
+ { "odieresis", 600, NULL },
+ { "Ograve", 600, NULL },
+ { "G", 600, NULL },
+ { "perthousand", 600, NULL },
+ { "Edotaccent", 600, NULL },
+ { "acircumflex", 600, NULL },
+ { "Ocircumflex", 600, NULL },
+ { "F", 600, NULL },
+ { "Uring", 600, NULL },
+ { "Ccedilla", 600, NULL },
+ { "Igrave", 600, NULL },
+ { "zero", 600, NULL },
+ { "scedilla", 600, NULL },
+ { "period", 600, NULL },
+ { "udieresis", 600, NULL },
+ { "Ugrave", 600, NULL },
+ { "germandbls", 600, NULL },
+ { "thorn", 600, NULL },
+ { "commaaccent", 600, NULL },
+ { "W", 600, NULL },
+ { "bracketright", 600, NULL },
+ { "underscore", 600, NULL },
+ { "D", 600, NULL },
+ { "mu", 600, NULL },
+ { "racute", 600, NULL },
+ { "rcommaaccent", 600, NULL },
+ { "imacron", 600, NULL },
+ { "Kcommaaccent", 600, NULL },
+ { "tilde", 600, NULL },
+ { "Tcommaaccent", 600, NULL },
+ { "dollar", 600, NULL },
+ { "v", 600, NULL },
+ { "Nacute", 600, NULL },
+ { "dotaccent", 600, NULL },
+ { "ocircumflex", 600, NULL },
+ { "less", 600, NULL },
+ { "minus", 600, NULL },
+ { "four", 600, NULL },
+ { "ae", 600, NULL },
+ { "cent", 600, NULL },
+ { "ogonek", 600, NULL },
+ { "idieresis", 600, NULL },
+ { "circumflex", 600, NULL },
+ { "Zcaron", 600, NULL },
+ { "currency", 600, NULL },
+ { "nine", 600, NULL },
+ { "parenleft", 600, NULL },
+ { "grave", 600, NULL },
+ { "Scedilla", 600, NULL },
+ { "quotesinglbase", 600, NULL },
+ { "lessequal", 600, NULL },
+ { "Omacron", 600, NULL },
+ { "Ucircumflex", 600, NULL },
+ { "ccaron", 600, NULL },
+ { "yen", 600, NULL },
+ { "Y", 600, NULL },
+ { "hungarumlaut", 600, NULL },
+ { "Ccaron", 600, NULL },
+ { "j", 600, NULL },
+ { "aogonek", 600, NULL },
+ { "Egrave", 600, NULL },
+ { "ring", 600, NULL },
+ { "emdash", 600, NULL },
+ { "five", 600, NULL },
+ { "X", 600, NULL },
+ { "asciicircum", 600, NULL },
+ { "Odieresis", 600, NULL },
+ { "oe", 600, NULL },
+ { "colon", 600, NULL },
+ { "g", 600, NULL },
+ { "ccedilla", 600, NULL },
+ { "plusminus", 600, NULL },
+ { "cedilla", 600, NULL },
+ { "Idotaccent", 600, NULL },
+ { "quotedblright", 600, NULL },
+ { "Eth", 600, NULL },
+ { "dotlessi", 600, NULL },
+ { "p", 600, NULL },
+ { "radical", 600, NULL },
+ { "Ntilde", 600, NULL },
+ { "Scommaaccent", 600, NULL },
+ { "Aacute", 600, NULL },
+ { "b", 600, NULL },
+ { "K", 600, NULL },
+ { "section", 600, NULL },
+ { "degree", 600, NULL },
+ { "Scaron", 600, NULL },
+ { "Euro", 600, NULL },
+ { "florin", 600, NULL },
+ { "iogonek", 600, NULL },
+ { "lcaron", 600, NULL },
+ { "paragraph", 600, NULL },
+ { "zcaron", 600, NULL },
+ { "aring", 600, NULL },
+ { "Ydieresis", 600, NULL },
+ { "Amacron", 600, NULL },
+ { "breve", 600, NULL },
+ { "Dcaron", 600, NULL },
+ { "Z", 600, NULL },
+ { "ecircumflex", 600, NULL },
+ { "q", 600, NULL },
+ { "l", 600, NULL },
+ { "dagger", 600, NULL },
+ { "equal", 600, NULL },
+ { "N", 600, NULL },
+ { "C", 600, NULL },
+ { "u", 600, NULL },
+ { "abreve", 600, NULL },
+ { "Imacron", 600, NULL },
+ { "iacute", 600, NULL },
+ { "e", 600, NULL },
+ { "icircumflex", 600, NULL },
+ { "percent", 600, NULL },
+ { "o", 600, NULL },
+ { "asterisk", 600, NULL },
+ { "Emacron", 600, NULL },
+ { "scommaaccent", 600, NULL },
+ { "onesuperior", 600, NULL },
+ { "greater", 600, NULL },
+ { "Ecaron", 600, NULL },
+ { "threesuperior", 600, NULL },
+ { "zdotaccent", 600, NULL },
+ { "n", 600, NULL },
+ { "lcommaaccent", 600, NULL },
+ { "onehalf", 600, NULL },
+ { "gbreve", 600, NULL },
+ { "uhungarumlaut", 600, NULL },
+ { "k", 600, NULL },
+ { "copyright", 600, NULL },
+ { "Racute", 600, NULL },
+ { "Gcommaaccent", 600, NULL },
+ { "quoteleft", 600, NULL },
+ { "registered", 600, NULL },
+ { "notequal", 600, NULL },
+ { "Agrave", 600, NULL },
+ { "AE", 600, NULL },
+ { "exclam", 600, NULL },
+ { "Acircumflex", 600, NULL },
+ { "oslash", 600, NULL },
+ { "threequarters", 600, NULL },
+ { "J", 600, NULL },
+ { "Ecircumflex", 600, NULL },
+ { "ohungarumlaut", 600, NULL },
+ { "L", 600, NULL },
+ { "H", 600, NULL },
+ { "sterling", 600, NULL },
+ { "greaterequal", 600, NULL },
+ { "at", 600, NULL },
+ { "caron", 600, NULL },
+ { "Uogonek", 600, NULL },
+ { "Lslash", 600, NULL },
+ { "lacute", 600, NULL },
+ { "asciitilde", 600, NULL },
+ { "fraction", 600, NULL },
+ { "Sacute", 600, NULL },
+ { "Adieresis", 600, NULL },
+ { "Uacute", 600, NULL },
+ { "atilde", 600, NULL },
+ { "Zacute", 600, NULL },
+ { "otilde", 600, NULL },
+ { "parenright", 600, NULL },
+ { "gcommaaccent", 600, NULL },
+ { "Ohungarumlaut", 600, NULL },
+ { "bracketleft", 600, NULL },
+ { "f", 600, NULL },
+ { "Lacute", 600, NULL },
+ { "Idieresis", 600, NULL },
+ { "sacute", 600, NULL },
+ { "Rcommaaccent", 600, NULL },
+ { "ograve", 600, NULL },
+ { "quoteright", 600, NULL },
+ { "uogonek", 600, NULL },
+ { "semicolon", 600, NULL },
+ { "guilsinglright", 600, NULL },
+ { "lozenge", 600, NULL },
+ { "V", 600, NULL },
+ { "Q", 600, NULL },
+ { "exclamdown", 600, NULL },
+ { "Umacron", 600, NULL },
+ { "zacute", 600, NULL },
+ { "ncaron", 600, NULL },
+ { "adieresis", 600, NULL },
+ { "slash", 600, NULL },
+ { "ucircumflex", 600, NULL },
+ { "r", 600, NULL },
+ { "three", 600, NULL },
+ { "Eogonek", 600, NULL },
+ { "macron", 600, NULL },
+ { "Lcaron", 600, NULL },
+ { "ordmasculine", 600, NULL },
+ { "backslash", 600, NULL },
+ { "questiondown", 600, NULL },
+ { "Oacute", 600, NULL },
+ { "t", 600, NULL },
+ { "quotedbl", 600, NULL },
+ { "ellipsis", 600, NULL },
+ { "Aring", 600, NULL },
+ { "agrave", 600, NULL },
+ { "c", 600, NULL },
+ { "d", 600, NULL },
+ { "one", 600, NULL },
+ { "I", 600, NULL },
+ { "eight", 600, NULL },
+ { "periodcentered", 600, NULL },
+ { "z", 600, NULL },
+ { "Uhungarumlaut", 600, NULL },
+ { "egrave", 600, NULL },
+ { "uring", 600, NULL },
+ { "s", 600, NULL },
+ { "oacute", 600, NULL },
+ { "Yacute", 600, NULL },
+ { "daggerdbl", 600, NULL },
+ { "Atilde", 600, NULL },
+ { "Edieresis", 600, NULL },
+ { "onequarter", 600, NULL },
+ { "braceleft", 600, NULL },
+ { "braceright", 600, NULL },
+ { "acute", 600, NULL },
+ { "quotesingle", 600, NULL },
+ { "bar", 600, NULL },
+ { "i", 600, NULL },
+ { "multiply", 600, NULL },
+ { "lslash", 600, NULL },
+ { "P", 600, NULL },
+ { "six", 600, NULL },
+ { "edotaccent", 600, NULL },
+ { "guilsinglleft", 600, NULL },
+ { "h", 600, NULL },
+ { "Cacute", 600, NULL },
+ { "plus", 600, NULL },
+ { "question", 600, NULL },
+ { "summation", 600, NULL },
+ { "A", 600, NULL },
+ { "igrave", 600, NULL },
+ { "dcroat", 600, NULL },
+ { "m", 600, NULL },
+ { "OE", 600, NULL },
+ { "partialdiff", 600, NULL },
+ { "U", 600, NULL },
+ { "x", 600, NULL },
+ { "y", 600, NULL },
+ { "Iogonek", 600, NULL },
+ { "edieresis", 600, NULL },
+ { "Ncaron", 600, NULL },
+ { "cacute", 600, NULL },
+ { "Dcroat", 600, NULL },
+ { "rcaron", 600, NULL },
+ { "tcaron", 600, NULL },
+ { "ncommaaccent", 600, NULL },
+ { "tcommaaccent", 600, NULL },
+ { "logicalnot", 600, NULL },
+ { "ordfeminine", 600, NULL },
+ { "quotedblleft", 600, NULL },
+ { "S", 600, NULL },
+ { "omacron", 600, NULL },
+ { "Delta", 600, NULL },
+ { "comma", 600, NULL },
+ { "Gbreve", 600, NULL },
+ { "ugrave", 600, NULL },
+ { "dcaron", 600, NULL },
+ { "hyphen", 600, NULL },
+ { "Oslash", 600, NULL },
+ { "a", 600, NULL },
+ { "B", 600, NULL },
+ { "Rcaron", 600, NULL },
+ { "kcommaaccent", 600, NULL },
+ { "yacute", 600, NULL },
+ { "ydieresis", 600, NULL },
+ { "R", 600, NULL },
+ { "aacute", 600, NULL },
+ { "umacron", 600, NULL },
+ { "Otilde", 600, NULL },
+ { "Tcaron", 600, NULL },
+ { "eogonek", 600, NULL },
+ { "bullet", 600, NULL },
+ { "eacute", 600, NULL },
+ { "Zdotaccent", 600, NULL },
+ { "scaron", 600, NULL },
+ { "guillemotright", 600, NULL },
+ { "two", 600, NULL },
+ { "uacute", 600, NULL },
+ { "seven", 600, NULL },
+ { "Abreve", 600, NULL },
+ { "Udieresis", 600, NULL },
+ { "Iacute", 600, NULL },
+ { "Icircumflex", 600, NULL },
+ { "O", 600, NULL },
+ { "nacute", 600, NULL },
+ { "dieresis", 600, NULL },
+ { "Eacute", 600, NULL },
+ { "ntilde", 600, NULL },
+ { "fi", 600, NULL },
+ { "trademark", 600, NULL },
+ { "ampersand", 600, NULL },
+ { "Lcommaaccent", 600, NULL }
+};
+
+static BuiltinFontWidth helveticaWidthsTab[] = {
+ { "lcommaaccent", 222, NULL },
+ { "onehalf", 834, NULL },
+ { "uhungarumlaut", 556, NULL },
+ { "gbreve", 556, NULL },
+ { "n", 556, NULL },
+ { "Ecaron", 667, NULL },
+ { "threesuperior", 333, NULL },
+ { "zdotaccent", 500, NULL },
+ { "quoteleft", 222, NULL },
+ { "registered", 737, NULL },
+ { "Gcommaaccent", 778, NULL },
+ { "Racute", 722, NULL },
+ { "k", 500, NULL },
+ { "copyright", 737, NULL },
+ { "icircumflex", 278, NULL },
+ { "abreve", 556, NULL },
+ { "Imacron", 278, NULL },
+ { "iacute", 278, NULL },
+ { "e", 556, NULL },
+ { "equal", 584, NULL },
+ { "dagger", 556, NULL },
+ { "N", 722, NULL },
+ { "C", 722, NULL },
+ { "u", 556, NULL },
+ { "q", 556, NULL },
+ { "l", 222, NULL },
+ { "greater", 584, NULL },
+ { "asterisk", 389, NULL },
+ { "Emacron", 667, NULL },
+ { "scommaaccent", 500, NULL },
+ { "onesuperior", 333, NULL },
+ { "percent", 889, NULL },
+ { "o", 556, NULL },
+ { "Euro", 556, NULL },
+ { "florin", 556, NULL },
+ { "iogonek", 222, NULL },
+ { "section", 556, NULL },
+ { "Scaron", 667, NULL },
+ { "degree", 400, NULL },
+ { "K", 667, NULL },
+ { "Scommaaccent", 667, NULL },
+ { "Aacute", 667, NULL },
+ { "b", 556, NULL },
+ { "Dcaron", 722, NULL },
+ { "Z", 611, NULL },
+ { "ecircumflex", 556, NULL },
+ { "Amacron", 667, NULL },
+ { "breve", 333, NULL },
+ { "lcaron", 299, NULL },
+ { "paragraph", 537, NULL },
+ { "zcaron", 500, NULL },
+ { "aring", 556, NULL },
+ { "Ydieresis", 667, NULL },
+ { "Odieresis", 778, NULL },
+ { "oe", 944, NULL },
+ { "five", 556, NULL },
+ { "X", 667, NULL },
+ { "asciicircum", 469, NULL },
+ { "emdash", 1000, NULL },
+ { "Egrave", 667, NULL },
+ { "aogonek", 556, NULL },
+ { "ring", 333, NULL },
+ { "Ntilde", 722, NULL },
+ { "radical", 453, NULL },
+ { "dotlessi", 278, NULL },
+ { "p", 556, NULL },
+ { "plusminus", 584, NULL },
+ { "cedilla", 333, NULL },
+ { "Idotaccent", 278, NULL },
+ { "quotedblright", 333, NULL },
+ { "Eth", 722, NULL },
+ { "g", 556, NULL },
+ { "colon", 278, NULL },
+ { "ccedilla", 500, NULL },
+ { "quotesinglbase", 222, NULL },
+ { "grave", 333, NULL },
+ { "Scedilla", 667, NULL },
+ { "parenleft", 333, NULL },
+ { "currency", 556, NULL },
+ { "nine", 556, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "Ccaron", 722, NULL },
+ { "j", 222, NULL },
+ { "yen", 556, NULL },
+ { "Y", 667, NULL },
+ { "ccaron", 500, NULL },
+ { "Omacron", 778, NULL },
+ { "lessequal", 549, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "dollar", 556, NULL },
+ { "Tcommaaccent", 611, NULL },
+ { "Nacute", 722, NULL },
+ { "v", 500, NULL },
+ { "dotaccent", 333, NULL },
+ { "rcommaaccent", 333, NULL },
+ { "Kcommaaccent", 667, NULL },
+ { "imacron", 278, NULL },
+ { "tilde", 333, NULL },
+ { "underscore", 556, NULL },
+ { "mu", 556, NULL },
+ { "D", 722, NULL },
+ { "racute", 333, NULL },
+ { "circumflex", 333, NULL },
+ { "Zcaron", 611, NULL },
+ { "ogonek", 333, NULL },
+ { "idieresis", 278, NULL },
+ { "cent", 556, NULL },
+ { "ae", 889, NULL },
+ { "ocircumflex", 556, NULL },
+ { "less", 584, NULL },
+ { "minus", 584, NULL },
+ { "four", 556, NULL },
+ { "F", 611, NULL },
+ { "Ocircumflex", 778, NULL },
+ { "Uring", 722, NULL },
+ { "Edotaccent", 667, NULL },
+ { "perthousand", 1000, NULL },
+ { "acircumflex", 556, NULL },
+ { "G", 778, NULL },
+ { "commaaccent", 250, NULL },
+ { "W", 944, NULL },
+ { "bracketright", 278, NULL },
+ { "Ugrave", 722, NULL },
+ { "germandbls", 611, NULL },
+ { "thorn", 556, NULL },
+ { "zero", 556, NULL },
+ { "period", 278, NULL },
+ { "scedilla", 500, NULL },
+ { "udieresis", 556, NULL },
+ { "Ccedilla", 722, NULL },
+ { "Igrave", 278, NULL },
+ { "fl", 500, NULL },
+ { "quotedblbase", 333, NULL },
+ { "E", 667, NULL },
+ { "endash", 556, NULL },
+ { "guillemotleft", 556, NULL },
+ { "w", 722, NULL },
+ { "space", 278, NULL },
+ { "Thorn", 667, NULL },
+ { "eth", 556, NULL },
+ { "twosuperior", 333, NULL },
+ { "ecaron", 556, NULL },
+ { "brokenbar", 260, NULL },
+ { "Aogonek", 667, NULL },
+ { "M", 833, NULL },
+ { "odieresis", 556, NULL },
+ { "Ograve", 778, NULL },
+ { "T", 611, NULL },
+ { "amacron", 556, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "numbersign", 556, NULL },
+ { "divide", 584, NULL },
+ { "emacron", 556, NULL },
+ { "Abreve", 667, NULL },
+ { "Udieresis", 722, NULL },
+ { "Iacute", 278, NULL },
+ { "two", 556, NULL },
+ { "uacute", 556, NULL },
+ { "seven", 556, NULL },
+ { "scaron", 500, NULL },
+ { "guillemotright", 556, NULL },
+ { "bullet", 350, NULL },
+ { "eogonek", 556, NULL },
+ { "eacute", 556, NULL },
+ { "Zdotaccent", 611, NULL },
+ { "trademark", 1000, NULL },
+ { "ampersand", 667, NULL },
+ { "Lcommaaccent", 556, NULL },
+ { "ntilde", 556, NULL },
+ { "fi", 500, NULL },
+ { "dieresis", 333, NULL },
+ { "Eacute", 667, NULL },
+ { "O", 778, NULL },
+ { "Icircumflex", 278, NULL },
+ { "nacute", 556, NULL },
+ { "omacron", 556, NULL },
+ { "comma", 278, NULL },
+ { "Delta", 612, NULL },
+ { "Gbreve", 778, NULL },
+ { "S", 667, NULL },
+ { "quotedblleft", 333, NULL },
+ { "ordfeminine", 370, NULL },
+ { "Otilde", 778, NULL },
+ { "umacron", 556, NULL },
+ { "Tcaron", 611, NULL },
+ { "aacute", 556, NULL },
+ { "B", 667, NULL },
+ { "kcommaaccent", 500, NULL },
+ { "Rcaron", 722, NULL },
+ { "yacute", 500, NULL },
+ { "ydieresis", 500, NULL },
+ { "R", 722, NULL },
+ { "ugrave", 556, NULL },
+ { "dcaron", 643, NULL },
+ { "hyphen", 333, NULL },
+ { "Oslash", 778, NULL },
+ { "a", 556, NULL },
+ { "igrave", 278, NULL },
+ { "dcroat", 556, NULL },
+ { "question", 556, NULL },
+ { "summation", 600, NULL },
+ { "A", 667, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "h", 556, NULL },
+ { "Cacute", 722, NULL },
+ { "plus", 584, NULL },
+ { "ncommaaccent", 556, NULL },
+ { "tcommaaccent", 278, NULL },
+ { "logicalnot", 584, NULL },
+ { "cacute", 500, NULL },
+ { "Dcroat", 722, NULL },
+ { "tcaron", 317, NULL },
+ { "rcaron", 333, NULL },
+ { "x", 500, NULL },
+ { "Iogonek", 278, NULL },
+ { "y", 500, NULL },
+ { "edieresis", 556, NULL },
+ { "Ncaron", 722, NULL },
+ { "m", 833, NULL },
+ { "OE", 1000, NULL },
+ { "U", 722, NULL },
+ { "partialdiff", 476, NULL },
+ { "braceright", 334, NULL },
+ { "braceleft", 334, NULL },
+ { "acute", 333, NULL },
+ { "onequarter", 834, NULL },
+ { "Atilde", 667, NULL },
+ { "Edieresis", 667, NULL },
+ { "Yacute", 667, NULL },
+ { "oacute", 556, NULL },
+ { "daggerdbl", 556, NULL },
+ { "six", 556, NULL },
+ { "edotaccent", 556, NULL },
+ { "P", 667, NULL },
+ { "lslash", 222, NULL },
+ { "multiply", 584, NULL },
+ { "i", 222, NULL },
+ { "quotesingle", 191, NULL },
+ { "bar", 260, NULL },
+ { "agrave", 556, NULL },
+ { "ellipsis", 1000, NULL },
+ { "Aring", 667, NULL },
+ { "t", 278, NULL },
+ { "quotedbl", 355, NULL },
+ { "Oacute", 778, NULL },
+ { "egrave", 556, NULL },
+ { "uring", 556, NULL },
+ { "s", 500, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "z", 500, NULL },
+ { "eight", 556, NULL },
+ { "I", 278, NULL },
+ { "one", 556, NULL },
+ { "periodcentered", 278, NULL },
+ { "c", 500, NULL },
+ { "d", 556, NULL },
+ { "zacute", 500, NULL },
+ { "Umacron", 722, NULL },
+ { "ncaron", 556, NULL },
+ { "adieresis", 556, NULL },
+ { "lozenge", 471, NULL },
+ { "V", 667, NULL },
+ { "Q", 778, NULL },
+ { "exclamdown", 333, NULL },
+ { "quoteright", 222, NULL },
+ { "uogonek", 556, NULL },
+ { "semicolon", 278, NULL },
+ { "guilsinglright", 333, NULL },
+ { "Eogonek", 667, NULL },
+ { "macron", 333, NULL },
+ { "ordmasculine", 365, NULL },
+ { "Lcaron", 556, NULL },
+ { "backslash", 278, NULL },
+ { "questiondown", 611, NULL },
+ { "three", 556, NULL },
+ { "slash", 278, NULL },
+ { "ucircumflex", 556, NULL },
+ { "r", 333, NULL },
+ { "f", 278, NULL },
+ { "gcommaaccent", 556, NULL },
+ { "Ohungarumlaut", 778, NULL },
+ { "bracketleft", 278, NULL },
+ { "otilde", 556, NULL },
+ { "parenright", 333, NULL },
+ { "Rcommaaccent", 722, NULL },
+ { "ograve", 556, NULL },
+ { "Lacute", 556, NULL },
+ { "sacute", 500, NULL },
+ { "Idieresis", 278, NULL },
+ { "Uogonek", 722, NULL },
+ { "Lslash", 556, NULL },
+ { "caron", 333, NULL },
+ { "lacute", 222, NULL },
+ { "sterling", 556, NULL },
+ { "H", 722, NULL },
+ { "at", 1015, NULL },
+ { "greaterequal", 549, NULL },
+ { "threequarters", 834, NULL },
+ { "J", 500, NULL },
+ { "L", 556, NULL },
+ { "ohungarumlaut", 556, NULL },
+ { "Ecircumflex", 667, NULL },
+ { "notequal", 549, NULL },
+ { "exclam", 278, NULL },
+ { "Agrave", 667, NULL },
+ { "AE", 1000, NULL },
+ { "Acircumflex", 667, NULL },
+ { "oslash", 611, NULL },
+ { "Uacute", 722, NULL },
+ { "atilde", 556, NULL },
+ { "Zacute", 611, NULL },
+ { "fraction", 167, NULL },
+ { "Sacute", 667, NULL },
+ { "Adieresis", 667, NULL },
+ { "asciitilde", 584, NULL }
+};
+
+static BuiltinFontWidth helveticaBoldWidthsTab[] = {
+ { "uogonek", 611, NULL },
+ { "quoteright", 278, NULL },
+ { "guilsinglright", 333, NULL },
+ { "semicolon", 333, NULL },
+ { "V", 667, NULL },
+ { "lozenge", 494, NULL },
+ { "exclamdown", 333, NULL },
+ { "Q", 778, NULL },
+ { "zacute", 500, NULL },
+ { "Umacron", 722, NULL },
+ { "adieresis", 556, NULL },
+ { "ncaron", 611, NULL },
+ { "slash", 278, NULL },
+ { "r", 389, NULL },
+ { "ucircumflex", 611, NULL },
+ { "three", 556, NULL },
+ { "ordmasculine", 365, NULL },
+ { "Lcaron", 611, NULL },
+ { "backslash", 278, NULL },
+ { "questiondown", 611, NULL },
+ { "macron", 333, NULL },
+ { "Eogonek", 667, NULL },
+ { "Oacute", 778, NULL },
+ { "t", 333, NULL },
+ { "quotedbl", 474, NULL },
+ { "ellipsis", 1000, NULL },
+ { "Aring", 722, NULL },
+ { "agrave", 556, NULL },
+ { "d", 611, NULL },
+ { "c", 556, NULL },
+ { "I", 278, NULL },
+ { "one", 556, NULL },
+ { "eight", 556, NULL },
+ { "periodcentered", 278, NULL },
+ { "z", 500, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "egrave", 556, NULL },
+ { "s", 556, NULL },
+ { "uring", 611, NULL },
+ { "Agrave", 722, NULL },
+ { "AE", 1000, NULL },
+ { "exclam", 333, NULL },
+ { "notequal", 549, NULL },
+ { "oslash", 611, NULL },
+ { "Acircumflex", 722, NULL },
+ { "J", 556, NULL },
+ { "threequarters", 834, NULL },
+ { "ohungarumlaut", 611, NULL },
+ { "L", 611, NULL },
+ { "Ecircumflex", 667, NULL },
+ { "H", 722, NULL },
+ { "sterling", 556, NULL },
+ { "greaterequal", 549, NULL },
+ { "at", 975, NULL },
+ { "lacute", 278, NULL },
+ { "Uogonek", 722, NULL },
+ { "caron", 333, NULL },
+ { "Lslash", 611, NULL },
+ { "asciitilde", 584, NULL },
+ { "fraction", 167, NULL },
+ { "Adieresis", 722, NULL },
+ { "Sacute", 667, NULL },
+ { "Uacute", 722, NULL },
+ { "Zacute", 611, NULL },
+ { "atilde", 556, NULL },
+ { "otilde", 611, NULL },
+ { "parenright", 333, NULL },
+ { "gcommaaccent", 611, NULL },
+ { "bracketleft", 333, NULL },
+ { "Ohungarumlaut", 778, NULL },
+ { "f", 333, NULL },
+ { "Lacute", 611, NULL },
+ { "sacute", 556, NULL },
+ { "Idieresis", 278, NULL },
+ { "ograve", 611, NULL },
+ { "Rcommaaccent", 722, NULL },
+ { "ordfeminine", 370, NULL },
+ { "quotedblleft", 500, NULL },
+ { "S", 667, NULL },
+ { "comma", 278, NULL },
+ { "Delta", 612, NULL },
+ { "omacron", 611, NULL },
+ { "Gbreve", 778, NULL },
+ { "dcaron", 743, NULL },
+ { "ugrave", 611, NULL },
+ { "a", 556, NULL },
+ { "hyphen", 333, NULL },
+ { "Oslash", 778, NULL },
+ { "B", 722, NULL },
+ { "R", 722, NULL },
+ { "ydieresis", 556, NULL },
+ { "yacute", 556, NULL },
+ { "kcommaaccent", 556, NULL },
+ { "Rcaron", 722, NULL },
+ { "aacute", 556, NULL },
+ { "Otilde", 778, NULL },
+ { "umacron", 611, NULL },
+ { "Tcaron", 611, NULL },
+ { "bullet", 350, NULL },
+ { "eogonek", 556, NULL },
+ { "eacute", 556, NULL },
+ { "Zdotaccent", 611, NULL },
+ { "scaron", 556, NULL },
+ { "guillemotright", 556, NULL },
+ { "uacute", 611, NULL },
+ { "two", 556, NULL },
+ { "seven", 556, NULL },
+ { "Abreve", 722, NULL },
+ { "Udieresis", 722, NULL },
+ { "Iacute", 278, NULL },
+ { "nacute", 611, NULL },
+ { "O", 778, NULL },
+ { "Icircumflex", 278, NULL },
+ { "dieresis", 333, NULL },
+ { "Eacute", 667, NULL },
+ { "ntilde", 611, NULL },
+ { "fi", 611, NULL },
+ { "ampersand", 722, NULL },
+ { "trademark", 1000, NULL },
+ { "Lcommaaccent", 611, NULL },
+ { "Yacute", 667, NULL },
+ { "oacute", 611, NULL },
+ { "daggerdbl", 556, NULL },
+ { "Atilde", 722, NULL },
+ { "Edieresis", 667, NULL },
+ { "onequarter", 834, NULL },
+ { "acute", 333, NULL },
+ { "braceright", 389, NULL },
+ { "braceleft", 389, NULL },
+ { "quotesingle", 238, NULL },
+ { "bar", 280, NULL },
+ { "i", 278, NULL },
+ { "multiply", 584, NULL },
+ { "lslash", 278, NULL },
+ { "P", 667, NULL },
+ { "edotaccent", 556, NULL },
+ { "six", 556, NULL },
+ { "Cacute", 722, NULL },
+ { "h", 611, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "plus", 584, NULL },
+ { "summation", 600, NULL },
+ { "question", 611, NULL },
+ { "A", 722, NULL },
+ { "dcroat", 611, NULL },
+ { "igrave", 278, NULL },
+ { "OE", 1000, NULL },
+ { "m", 889, NULL },
+ { "U", 722, NULL },
+ { "partialdiff", 494, NULL },
+ { "y", 556, NULL },
+ { "Iogonek", 278, NULL },
+ { "edieresis", 556, NULL },
+ { "x", 556, NULL },
+ { "Ncaron", 722, NULL },
+ { "Dcroat", 722, NULL },
+ { "cacute", 556, NULL },
+ { "tcaron", 389, NULL },
+ { "rcaron", 389, NULL },
+ { "ncommaaccent", 611, NULL },
+ { "logicalnot", 584, NULL },
+ { "tcommaaccent", 333, NULL },
+ { "underscore", 556, NULL },
+ { "racute", 389, NULL },
+ { "D", 722, NULL },
+ { "mu", 611, NULL },
+ { "Kcommaaccent", 722, NULL },
+ { "imacron", 278, NULL },
+ { "rcommaaccent", 389, NULL },
+ { "tilde", 333, NULL },
+ { "dollar", 556, NULL },
+ { "Tcommaaccent", 611, NULL },
+ { "dotaccent", 333, NULL },
+ { "Nacute", 722, NULL },
+ { "v", 556, NULL },
+ { "less", 584, NULL },
+ { "ocircumflex", 611, NULL },
+ { "four", 556, NULL },
+ { "minus", 584, NULL },
+ { "ae", 889, NULL },
+ { "cent", 556, NULL },
+ { "ogonek", 333, NULL },
+ { "idieresis", 278, NULL },
+ { "circumflex", 333, NULL },
+ { "Zcaron", 611, NULL },
+ { "nine", 556, NULL },
+ { "currency", 556, NULL },
+ { "parenleft", 333, NULL },
+ { "Scedilla", 667, NULL },
+ { "grave", 333, NULL },
+ { "quotesinglbase", 278, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "lessequal", 549, NULL },
+ { "Omacron", 778, NULL },
+ { "ccaron", 556, NULL },
+ { "Y", 667, NULL },
+ { "yen", 556, NULL },
+ { "Ccaron", 722, NULL },
+ { "j", 278, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "ecaron", 556, NULL },
+ { "brokenbar", 280, NULL },
+ { "Aogonek", 722, NULL },
+ { "w", 778, NULL },
+ { "twosuperior", 333, NULL },
+ { "eth", 611, NULL },
+ { "Thorn", 667, NULL },
+ { "space", 278, NULL },
+ { "guillemotleft", 556, NULL },
+ { "endash", 556, NULL },
+ { "fl", 611, NULL },
+ { "E", 667, NULL },
+ { "quotedblbase", 500, NULL },
+ { "numbersign", 556, NULL },
+ { "emacron", 556, NULL },
+ { "divide", 584, NULL },
+ { "amacron", 556, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "T", 611, NULL },
+ { "odieresis", 611, NULL },
+ { "M", 833, NULL },
+ { "Ograve", 778, NULL },
+ { "G", 778, NULL },
+ { "perthousand", 1000, NULL },
+ { "Edotaccent", 667, NULL },
+ { "acircumflex", 556, NULL },
+ { "F", 611, NULL },
+ { "Ocircumflex", 778, NULL },
+ { "Uring", 722, NULL },
+ { "Ccedilla", 722, NULL },
+ { "Igrave", 278, NULL },
+ { "zero", 556, NULL },
+ { "udieresis", 611, NULL },
+ { "period", 278, NULL },
+ { "scedilla", 556, NULL },
+ { "germandbls", 611, NULL },
+ { "Ugrave", 722, NULL },
+ { "thorn", 611, NULL },
+ { "commaaccent", 250, NULL },
+ { "W", 944, NULL },
+ { "bracketright", 333, NULL },
+ { "l", 278, NULL },
+ { "q", 611, NULL },
+ { "N", 722, NULL },
+ { "dagger", 556, NULL },
+ { "equal", 584, NULL },
+ { "u", 611, NULL },
+ { "C", 722, NULL },
+ { "iacute", 278, NULL },
+ { "e", 556, NULL },
+ { "Imacron", 278, NULL },
+ { "abreve", 556, NULL },
+ { "icircumflex", 278, NULL },
+ { "percent", 889, NULL },
+ { "o", 611, NULL },
+ { "asterisk", 389, NULL },
+ { "Emacron", 667, NULL },
+ { "scommaaccent", 556, NULL },
+ { "onesuperior", 333, NULL },
+ { "greater", 584, NULL },
+ { "Ecaron", 667, NULL },
+ { "zdotaccent", 500, NULL },
+ { "threesuperior", 333, NULL },
+ { "n", 611, NULL },
+ { "dbldaggerumlaut", 556, NULL },
+ { "onehalf", 834, NULL },
+ { "lcommaaccent", 278, NULL },
+ { "uhungarumlaut", 611, NULL },
+ { "gbreve", 611, NULL },
+ { "k", 556, NULL },
+ { "copyright", 737, NULL },
+ { "Racute", 722, NULL },
+ { "Gcommaaccent", 778, NULL },
+ { "quoteleft", 278, NULL },
+ { "registered", 737, NULL },
+ { "aogonek", 556, NULL },
+ { "Egrave", 667, NULL },
+ { "ring", 333, NULL },
+ { "emdash", 1000, NULL },
+ { "five", 556, NULL },
+ { "asciicircum", 584, NULL },
+ { "X", 667, NULL },
+ { "oe", 944, NULL },
+ { "Odieresis", 778, NULL },
+ { "ccedilla", 556, NULL },
+ { "g", 611, NULL },
+ { "colon", 333, NULL },
+ { "Idotaccent", 278, NULL },
+ { "cedilla", 333, NULL },
+ { "plusminus", 584, NULL },
+ { "Eth", 722, NULL },
+ { "quotedblright", 500, NULL },
+ { "p", 611, NULL },
+ { "dotlessi", 278, NULL },
+ { "radical", 549, NULL },
+ { "Ntilde", 722, NULL },
+ { "Aacute", 722, NULL },
+ { "Scommaaccent", 667, NULL },
+ { "b", 611, NULL },
+ { "K", 722, NULL },
+ { "Scaron", 667, NULL },
+ { "degree", 400, NULL },
+ { "section", 556, NULL },
+ { "florin", 556, NULL },
+ { "iogonek", 278, NULL },
+ { "Euro", 556, NULL },
+ { "paragraph", 556, NULL },
+ { "zcaron", 500, NULL },
+ { "lcaron", 400, NULL },
+ { "Ydieresis", 667, NULL },
+ { "aring", 556, NULL },
+ { "breve", 333, NULL },
+ { "Amacron", 722, NULL },
+ { "Dcaron", 722, NULL },
+ { "ecircumflex", 556, NULL },
+ { "Z", 611, NULL }
+};
+
+static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
+ { "divide", 584, NULL },
+ { "emacron", 556, NULL },
+ { "numbersign", 556, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "amacron", 556, NULL },
+ { "T", 611, NULL },
+ { "Ograve", 778, NULL },
+ { "M", 833, NULL },
+ { "odieresis", 611, NULL },
+ { "Aogonek", 722, NULL },
+ { "brokenbar", 280, NULL },
+ { "ecaron", 556, NULL },
+ { "Thorn", 667, NULL },
+ { "space", 278, NULL },
+ { "twosuperior", 333, NULL },
+ { "eth", 611, NULL },
+ { "w", 778, NULL },
+ { "endash", 556, NULL },
+ { "guillemotleft", 556, NULL },
+ { "E", 667, NULL },
+ { "quotedblbase", 500, NULL },
+ { "fl", 611, NULL },
+ { "Igrave", 278, NULL },
+ { "Ccedilla", 722, NULL },
+ { "period", 278, NULL },
+ { "scedilla", 556, NULL },
+ { "udieresis", 611, NULL },
+ { "zero", 556, NULL },
+ { "thorn", 611, NULL },
+ { "Ugrave", 722, NULL },
+ { "germandbls", 611, NULL },
+ { "bracketright", 333, NULL },
+ { "W", 944, NULL },
+ { "commaaccent", 250, NULL },
+ { "G", 778, NULL },
+ { "acircumflex", 556, NULL },
+ { "Edotaccent", 667, NULL },
+ { "perthousand", 1000, NULL },
+ { "Uring", 722, NULL },
+ { "F", 611, NULL },
+ { "Ocircumflex", 778, NULL },
+ { "minus", 584, NULL },
+ { "four", 556, NULL },
+ { "ocircumflex", 611, NULL },
+ { "less", 584, NULL },
+ { "ae", 889, NULL },
+ { "cent", 556, NULL },
+ { "idieresis", 278, NULL },
+ { "ogonek", 333, NULL },
+ { "Zcaron", 611, NULL },
+ { "circumflex", 333, NULL },
+ { "D", 722, NULL },
+ { "mu", 611, NULL },
+ { "racute", 389, NULL },
+ { "underscore", 556, NULL },
+ { "tilde", 333, NULL },
+ { "rcommaaccent", 389, NULL },
+ { "Kcommaaccent", 722, NULL },
+ { "imacron", 278, NULL },
+ { "Nacute", 722, NULL },
+ { "v", 556, NULL },
+ { "dotaccent", 333, NULL },
+ { "Tcommaaccent", 611, NULL },
+ { "dollar", 556, NULL },
+ { "lessequal", 549, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "Omacron", 778, NULL },
+ { "ccaron", 556, NULL },
+ { "yen", 556, NULL },
+ { "Y", 667, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "Ccaron", 722, NULL },
+ { "j", 278, NULL },
+ { "currency", 556, NULL },
+ { "nine", 556, NULL },
+ { "parenleft", 333, NULL },
+ { "grave", 333, NULL },
+ { "Scedilla", 667, NULL },
+ { "quotesinglbase", 278, NULL },
+ { "g", 611, NULL },
+ { "colon", 333, NULL },
+ { "ccedilla", 556, NULL },
+ { "quotedblright", 500, NULL },
+ { "Eth", 722, NULL },
+ { "plusminus", 584, NULL },
+ { "cedilla", 333, NULL },
+ { "Idotaccent", 278, NULL },
+ { "dotlessi", 278, NULL },
+ { "p", 611, NULL },
+ { "Ntilde", 722, NULL },
+ { "radical", 549, NULL },
+ { "ring", 333, NULL },
+ { "Egrave", 667, NULL },
+ { "aogonek", 556, NULL },
+ { "emdash", 1000, NULL },
+ { "X", 667, NULL },
+ { "asciicircum", 584, NULL },
+ { "five", 556, NULL },
+ { "Odieresis", 778, NULL },
+ { "oe", 944, NULL },
+ { "aring", 556, NULL },
+ { "Ydieresis", 667, NULL },
+ { "lcaron", 400, NULL },
+ { "zcaron", 500, NULL },
+ { "paragraph", 556, NULL },
+ { "Amacron", 722, NULL },
+ { "breve", 333, NULL },
+ { "Z", 611, NULL },
+ { "ecircumflex", 556, NULL },
+ { "Dcaron", 722, NULL },
+ { "b", 611, NULL },
+ { "Scommaaccent", 667, NULL },
+ { "Aacute", 722, NULL },
+ { "K", 722, NULL },
+ { "section", 556, NULL },
+ { "Scaron", 667, NULL },
+ { "degree", 400, NULL },
+ { "Euro", 556, NULL },
+ { "florin", 556, NULL },
+ { "iogonek", 278, NULL },
+ { "o", 611, NULL },
+ { "percent", 889, NULL },
+ { "onesuperior", 333, NULL },
+ { "scommaaccent", 556, NULL },
+ { "Emacron", 667, NULL },
+ { "asterisk", 389, NULL },
+ { "greater", 584, NULL },
+ { "q", 611, NULL },
+ { "l", 278, NULL },
+ { "C", 722, NULL },
+ { "u", 611, NULL },
+ { "dagger", 556, NULL },
+ { "equal", 584, NULL },
+ { "N", 722, NULL },
+ { "Imacron", 278, NULL },
+ { "abreve", 556, NULL },
+ { "iacute", 278, NULL },
+ { "e", 556, NULL },
+ { "icircumflex", 278, NULL },
+ { "copyright", 737, NULL },
+ { "k", 556, NULL },
+ { "Racute", 722, NULL },
+ { "Gcommaaccent", 778, NULL },
+ { "registered", 737, NULL },
+ { "quoteleft", 278, NULL },
+ { "threesuperior", 333, NULL },
+ { "zdotaccent", 500, NULL },
+ { "Ecaron", 667, NULL },
+ { "n", 611, NULL },
+ { "gbreve", 611, NULL },
+ { "uhungarumlaut", 611, NULL },
+ { "lcommaaccent", 278, NULL },
+ { "onehalf", 834, NULL },
+ { "asciitilde", 584, NULL },
+ { "Adieresis", 722, NULL },
+ { "Sacute", 667, NULL },
+ { "fraction", 167, NULL },
+ { "atilde", 556, NULL },
+ { "Zacute", 611, NULL },
+ { "Uacute", 722, NULL },
+ { "Acircumflex", 722, NULL },
+ { "oslash", 611, NULL },
+ { "notequal", 549, NULL },
+ { "AE", 1000, NULL },
+ { "Agrave", 722, NULL },
+ { "exclam", 333, NULL },
+ { "L", 611, NULL },
+ { "ohungarumlaut", 611, NULL },
+ { "Ecircumflex", 667, NULL },
+ { "threequarters", 834, NULL },
+ { "J", 556, NULL },
+ { "greaterequal", 549, NULL },
+ { "at", 975, NULL },
+ { "sterling", 556, NULL },
+ { "H", 722, NULL },
+ { "Lslash", 611, NULL },
+ { "Uogonek", 722, NULL },
+ { "caron", 333, NULL },
+ { "lacute", 278, NULL },
+ { "sacute", 556, NULL },
+ { "Idieresis", 278, NULL },
+ { "Lacute", 611, NULL },
+ { "Rcommaaccent", 722, NULL },
+ { "ograve", 611, NULL },
+ { "parenright", 333, NULL },
+ { "otilde", 611, NULL },
+ { "Ohungarumlaut", 778, NULL },
+ { "bracketleft", 333, NULL },
+ { "gcommaaccent", 611, NULL },
+ { "f", 333, NULL },
+ { "r", 389, NULL },
+ { "ucircumflex", 611, NULL },
+ { "slash", 278, NULL },
+ { "three", 556, NULL },
+ { "Eogonek", 667, NULL },
+ { "macron", 333, NULL },
+ { "Lcaron", 611, NULL },
+ { "ordmasculine", 365, NULL },
+ { "backslash", 278, NULL },
+ { "questiondown", 611, NULL },
+ { "semicolon", 333, NULL },
+ { "guilsinglright", 333, NULL },
+ { "uogonek", 611, NULL },
+ { "quoteright", 278, NULL },
+ { "Q", 778, NULL },
+ { "exclamdown", 333, NULL },
+ { "lozenge", 494, NULL },
+ { "V", 667, NULL },
+ { "ncaron", 611, NULL },
+ { "adieresis", 556, NULL },
+ { "zacute", 500, NULL },
+ { "Umacron", 722, NULL },
+ { "c", 556, NULL },
+ { "d", 611, NULL },
+ { "periodcentered", 278, NULL },
+ { "eight", 556, NULL },
+ { "I", 278, NULL },
+ { "one", 556, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "z", 500, NULL },
+ { "uring", 611, NULL },
+ { "s", 556, NULL },
+ { "egrave", 556, NULL },
+ { "Oacute", 778, NULL },
+ { "quotedbl", 474, NULL },
+ { "t", 333, NULL },
+ { "Aring", 722, NULL },
+ { "ellipsis", 1000, NULL },
+ { "agrave", 556, NULL },
+ { "bar", 280, NULL },
+ { "quotesingle", 238, NULL },
+ { "multiply", 584, NULL },
+ { "i", 278, NULL },
+ { "P", 667, NULL },
+ { "lslash", 278, NULL },
+ { "six", 556, NULL },
+ { "edotaccent", 556, NULL },
+ { "daggerdbl", 556, NULL },
+ { "Yacute", 667, NULL },
+ { "oacute", 611, NULL },
+ { "Edieresis", 667, NULL },
+ { "Atilde", 722, NULL },
+ { "onequarter", 834, NULL },
+ { "braceright", 389, NULL },
+ { "braceleft", 389, NULL },
+ { "acute", 333, NULL },
+ { "partialdiff", 494, NULL },
+ { "U", 722, NULL },
+ { "m", 889, NULL },
+ { "OE", 1000, NULL },
+ { "Ncaron", 722, NULL },
+ { "x", 556, NULL },
+ { "edieresis", 556, NULL },
+ { "Iogonek", 278, NULL },
+ { "y", 556, NULL },
+ { "tcaron", 389, NULL },
+ { "rcaron", 389, NULL },
+ { "cacute", 556, NULL },
+ { "Dcroat", 722, NULL },
+ { "tcommaaccent", 333, NULL },
+ { "logicalnot", 584, NULL },
+ { "ncommaaccent", 611, NULL },
+ { "plus", 584, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "h", 611, NULL },
+ { "Cacute", 722, NULL },
+ { "A", 722, NULL },
+ { "question", 611, NULL },
+ { "summation", 600, NULL },
+ { "igrave", 278, NULL },
+ { "dcroat", 611, NULL },
+ { "Oslash", 778, NULL },
+ { "hyphen", 333, NULL },
+ { "a", 556, NULL },
+ { "ugrave", 611, NULL },
+ { "dcaron", 743, NULL },
+ { "Rcaron", 722, NULL },
+ { "kcommaaccent", 556, NULL },
+ { "yacute", 556, NULL },
+ { "ydieresis", 556, NULL },
+ { "R", 722, NULL },
+ { "B", 722, NULL },
+ { "aacute", 556, NULL },
+ { "Tcaron", 611, NULL },
+ { "Otilde", 778, NULL },
+ { "umacron", 611, NULL },
+ { "ordfeminine", 370, NULL },
+ { "quotedblleft", 500, NULL },
+ { "S", 667, NULL },
+ { "Gbreve", 778, NULL },
+ { "omacron", 611, NULL },
+ { "Delta", 612, NULL },
+ { "comma", 278, NULL },
+ { "O", 778, NULL },
+ { "Icircumflex", 278, NULL },
+ { "nacute", 611, NULL },
+ { "Eacute", 667, NULL },
+ { "dieresis", 333, NULL },
+ { "fi", 611, NULL },
+ { "ntilde", 611, NULL },
+ { "Lcommaaccent", 611, NULL },
+ { "trademark", 1000, NULL },
+ { "ampersand", 722, NULL },
+ { "Zdotaccent", 611, NULL },
+ { "bullet", 350, NULL },
+ { "eogonek", 556, NULL },
+ { "eacute", 556, NULL },
+ { "guillemotright", 556, NULL },
+ { "scaron", 556, NULL },
+ { "seven", 556, NULL },
+ { "two", 556, NULL },
+ { "uacute", 611, NULL },
+ { "Iacute", 278, NULL },
+ { "Udieresis", 722, NULL },
+ { "Abreve", 722, NULL }
+};
+
+static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
+ { "dieresis", 333, NULL },
+ { "Eacute", 667, NULL },
+ { "Icircumflex", 278, NULL },
+ { "O", 778, NULL },
+ { "nacute", 556, NULL },
+ { "trademark", 1000, NULL },
+ { "ampersand", 667, NULL },
+ { "Lcommaaccent", 556, NULL },
+ { "ntilde", 556, NULL },
+ { "fi", 500, NULL },
+ { "scaron", 500, NULL },
+ { "guillemotright", 556, NULL },
+ { "eacute", 556, NULL },
+ { "eogonek", 556, NULL },
+ { "bullet", 350, NULL },
+ { "Zdotaccent", 611, NULL },
+ { "Abreve", 667, NULL },
+ { "Udieresis", 722, NULL },
+ { "Iacute", 278, NULL },
+ { "two", 556, NULL },
+ { "uacute", 556, NULL },
+ { "seven", 556, NULL },
+ { "B", 667, NULL },
+ { "Rcaron", 722, NULL },
+ { "kcommaaccent", 500, NULL },
+ { "yacute", 500, NULL },
+ { "R", 722, NULL },
+ { "ydieresis", 500, NULL },
+ { "ugrave", 556, NULL },
+ { "dcaron", 643, NULL },
+ { "Oslash", 778, NULL },
+ { "hyphen", 333, NULL },
+ { "a", 556, NULL },
+ { "umacron", 556, NULL },
+ { "Otilde", 778, NULL },
+ { "Tcaron", 611, NULL },
+ { "aacute", 556, NULL },
+ { "quotedblleft", 333, NULL },
+ { "ordfeminine", 370, NULL },
+ { "omacron", 556, NULL },
+ { "Delta", 612, NULL },
+ { "comma", 278, NULL },
+ { "Gbreve", 778, NULL },
+ { "S", 667, NULL },
+ { "x", 500, NULL },
+ { "y", 500, NULL },
+ { "Iogonek", 278, NULL },
+ { "edieresis", 556, NULL },
+ { "Ncaron", 722, NULL },
+ { "m", 833, NULL },
+ { "OE", 1000, NULL },
+ { "partialdiff", 476, NULL },
+ { "U", 722, NULL },
+ { "ncommaaccent", 556, NULL },
+ { "tcommaaccent", 278, NULL },
+ { "logicalnot", 584, NULL },
+ { "cacute", 500, NULL },
+ { "Dcroat", 722, NULL },
+ { "tcaron", 317, NULL },
+ { "rcaron", 333, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "Cacute", 722, NULL },
+ { "h", 556, NULL },
+ { "plus", 584, NULL },
+ { "igrave", 278, NULL },
+ { "dcroat", 556, NULL },
+ { "question", 556, NULL },
+ { "summation", 600, NULL },
+ { "A", 667, NULL },
+ { "multiply", 584, NULL },
+ { "i", 222, NULL },
+ { "quotesingle", 191, NULL },
+ { "bar", 260, NULL },
+ { "six", 556, NULL },
+ { "edotaccent", 556, NULL },
+ { "lslash", 222, NULL },
+ { "P", 667, NULL },
+ { "Atilde", 667, NULL },
+ { "Edieresis", 667, NULL },
+ { "oacute", 556, NULL },
+ { "Yacute", 667, NULL },
+ { "daggerdbl", 556, NULL },
+ { "braceleft", 334, NULL },
+ { "braceright", 334, NULL },
+ { "acute", 333, NULL },
+ { "onequarter", 834, NULL },
+ { "one", 556, NULL },
+ { "I", 278, NULL },
+ { "eight", 556, NULL },
+ { "periodcentered", 278, NULL },
+ { "c", 500, NULL },
+ { "d", 556, NULL },
+ { "egrave", 556, NULL },
+ { "uring", 556, NULL },
+ { "s", 500, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "z", 500, NULL },
+ { "t", 278, NULL },
+ { "quotedbl", 355, NULL },
+ { "Oacute", 778, NULL },
+ { "agrave", 556, NULL },
+ { "ellipsis", 1000, NULL },
+ { "Aring", 667, NULL },
+ { "three", 556, NULL },
+ { "slash", 278, NULL },
+ { "ucircumflex", 556, NULL },
+ { "r", 333, NULL },
+ { "macron", 333, NULL },
+ { "Eogonek", 667, NULL },
+ { "Lcaron", 556, NULL },
+ { "questiondown", 611, NULL },
+ { "ordmasculine", 365, NULL },
+ { "backslash", 278, NULL },
+ { "uogonek", 556, NULL },
+ { "quoteright", 222, NULL },
+ { "semicolon", 278, NULL },
+ { "guilsinglright", 333, NULL },
+ { "Umacron", 722, NULL },
+ { "zacute", 500, NULL },
+ { "ncaron", 556, NULL },
+ { "adieresis", 556, NULL },
+ { "lozenge", 471, NULL },
+ { "V", 667, NULL },
+ { "Q", 778, NULL },
+ { "exclamdown", 333, NULL },
+ { "Lacute", 556, NULL },
+ { "sacute", 500, NULL },
+ { "Idieresis", 278, NULL },
+ { "ograve", 556, NULL },
+ { "Rcommaaccent", 722, NULL },
+ { "gcommaaccent", 556, NULL },
+ { "Ohungarumlaut", 778, NULL },
+ { "bracketleft", 278, NULL },
+ { "otilde", 556, NULL },
+ { "parenright", 333, NULL },
+ { "f", 278, NULL },
+ { "asciitilde", 584, NULL },
+ { "Uacute", 722, NULL },
+ { "atilde", 556, NULL },
+ { "Zacute", 611, NULL },
+ { "fraction", 167, NULL },
+ { "Sacute", 667, NULL },
+ { "Adieresis", 667, NULL },
+ { "threequarters", 834, NULL },
+ { "J", 500, NULL },
+ { "ohungarumlaut", 556, NULL },
+ { "Ecircumflex", 667, NULL },
+ { "L", 556, NULL },
+ { "notequal", 549, NULL },
+ { "exclam", 278, NULL },
+ { "Agrave", 667, NULL },
+ { "AE", 1000, NULL },
+ { "Acircumflex", 667, NULL },
+ { "oslash", 611, NULL },
+ { "Uogonek", 722, NULL },
+ { "caron", 333, NULL },
+ { "Lslash", 556, NULL },
+ { "lacute", 222, NULL },
+ { "sterling", 556, NULL },
+ { "H", 722, NULL },
+ { "at", 1015, NULL },
+ { "greaterequal", 549, NULL },
+ { "Racute", 722, NULL },
+ { "k", 500, NULL },
+ { "copyright", 737, NULL },
+ { "quoteleft", 222, NULL },
+ { "registered", 737, NULL },
+ { "Gcommaaccent", 778, NULL },
+ { "n", 556, NULL },
+ { "Ecaron", 667, NULL },
+ { "threesuperior", 333, NULL },
+ { "zdotaccent", 500, NULL },
+ { "lcommaaccent", 222, NULL },
+ { "onehalf", 834, NULL },
+ { "gbreve", 556, NULL },
+ { "uhungarumlaut", 556, NULL },
+ { "percent", 889, NULL },
+ { "o", 556, NULL },
+ { "greater", 584, NULL },
+ { "asterisk", 389, NULL },
+ { "Emacron", 667, NULL },
+ { "scommaaccent", 500, NULL },
+ { "onesuperior", 333, NULL },
+ { "dagger", 556, NULL },
+ { "equal", 584, NULL },
+ { "N", 722, NULL },
+ { "C", 722, NULL },
+ { "u", 556, NULL },
+ { "q", 556, NULL },
+ { "l", 222, NULL },
+ { "icircumflex", 278, NULL },
+ { "Imacron", 278, NULL },
+ { "abreve", 556, NULL },
+ { "iacute", 278, NULL },
+ { "e", 556, NULL },
+ { "lcaron", 299, NULL },
+ { "paragraph", 537, NULL },
+ { "zcaron", 500, NULL },
+ { "aring", 556, NULL },
+ { "Ydieresis", 667, NULL },
+ { "Dcaron", 722, NULL },
+ { "Z", 611, NULL },
+ { "ecircumflex", 556, NULL },
+ { "Amacron", 667, NULL },
+ { "breve", 333, NULL },
+ { "K", 667, NULL },
+ { "Scommaaccent", 667, NULL },
+ { "Aacute", 667, NULL },
+ { "b", 556, NULL },
+ { "Euro", 556, NULL },
+ { "iogonek", 222, NULL },
+ { "florin", 556, NULL },
+ { "section", 556, NULL },
+ { "Scaron", 667, NULL },
+ { "degree", 400, NULL },
+ { "plusminus", 584, NULL },
+ { "cedilla", 333, NULL },
+ { "Idotaccent", 278, NULL },
+ { "quotedblright", 333, NULL },
+ { "Eth", 722, NULL },
+ { "colon", 278, NULL },
+ { "g", 556, NULL },
+ { "ccedilla", 500, NULL },
+ { "Ntilde", 722, NULL },
+ { "radical", 453, NULL },
+ { "dotlessi", 278, NULL },
+ { "p", 556, NULL },
+ { "emdash", 1000, NULL },
+ { "aogonek", 556, NULL },
+ { "Egrave", 667, NULL },
+ { "ring", 333, NULL },
+ { "Odieresis", 778, NULL },
+ { "oe", 944, NULL },
+ { "five", 556, NULL },
+ { "X", 667, NULL },
+ { "asciicircum", 469, NULL },
+ { "ccaron", 500, NULL },
+ { "Omacron", 778, NULL },
+ { "lessequal", 549, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "j", 222, NULL },
+ { "Ccaron", 722, NULL },
+ { "yen", 556, NULL },
+ { "Y", 667, NULL },
+ { "parenleft", 333, NULL },
+ { "currency", 556, NULL },
+ { "nine", 556, NULL },
+ { "quotesinglbase", 222, NULL },
+ { "grave", 333, NULL },
+ { "Scedilla", 667, NULL },
+ { "ae", 889, NULL },
+ { "cent", 556, NULL },
+ { "ocircumflex", 556, NULL },
+ { "less", 584, NULL },
+ { "minus", 584, NULL },
+ { "four", 556, NULL },
+ { "circumflex", 333, NULL },
+ { "Zcaron", 611, NULL },
+ { "ogonek", 333, NULL },
+ { "idieresis", 278, NULL },
+ { "rcommaaccent", 333, NULL },
+ { "Kcommaaccent", 667, NULL },
+ { "imacron", 278, NULL },
+ { "tilde", 333, NULL },
+ { "underscore", 556, NULL },
+ { "D", 722, NULL },
+ { "mu", 556, NULL },
+ { "racute", 333, NULL },
+ { "Tcommaaccent", 611, NULL },
+ { "dollar", 556, NULL },
+ { "v", 500, NULL },
+ { "Nacute", 722, NULL },
+ { "dotaccent", 333, NULL },
+ { "zero", 556, NULL },
+ { "scedilla", 500, NULL },
+ { "period", 278, NULL },
+ { "udieresis", 556, NULL },
+ { "Ccedilla", 722, NULL },
+ { "Igrave", 278, NULL },
+ { "commaaccent", 250, NULL },
+ { "W", 944, NULL },
+ { "bracketright", 278, NULL },
+ { "Ugrave", 722, NULL },
+ { "germandbls", 611, NULL },
+ { "thorn", 556, NULL },
+ { "G", 778, NULL },
+ { "Ocircumflex", 778, NULL },
+ { "F", 611, NULL },
+ { "Uring", 722, NULL },
+ { "perthousand", 1000, NULL },
+ { "Edotaccent", 667, NULL },
+ { "acircumflex", 556, NULL },
+ { "amacron", 556, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "numbersign", 556, NULL },
+ { "divide", 584, NULL },
+ { "emacron", 556, NULL },
+ { "M", 833, NULL },
+ { "odieresis", 556, NULL },
+ { "Ograve", 778, NULL },
+ { "T", 611, NULL },
+ { "w", 722, NULL },
+ { "Thorn", 667, NULL },
+ { "space", 278, NULL },
+ { "twosuperior", 333, NULL },
+ { "eth", 556, NULL },
+ { "ecaron", 556, NULL },
+ { "brokenbar", 260, NULL },
+ { "Aogonek", 667, NULL },
+ { "fl", 500, NULL },
+ { "E", 667, NULL },
+ { "quotedblbase", 333, NULL },
+ { "guillemotleft", 556, NULL },
+ { "endash", 556, NULL }
+};
+
+static BuiltinFontWidth symbolWidthsTab[] = {
+ { "copyrightsans", 790, NULL },
+ { "Upsilon", 690, NULL },
+ { "sigma", 603, NULL },
+ { "kappa", 549, NULL },
+ { "circleplus", 768, NULL },
+ { "degree", 400, NULL },
+ { "florin", 500, NULL },
+ { "Euro", 750, NULL },
+ { "phi1", 603, NULL },
+ { "club", 753, NULL },
+ { "radicalex", 500, NULL },
+ { "theta", 521, NULL },
+ { "Upsilon1", 620, NULL },
+ { "radical", 549, NULL },
+ { "angle", 768, NULL },
+ { "colon", 278, NULL },
+ { "plusminus", 549, NULL },
+ { "five", 500, NULL },
+ { "weierstrass", 987, NULL },
+ { "gamma", 411, NULL },
+ { "theta1", 631, NULL },
+ { "Zeta", 611, NULL },
+ { "angleleft", 329, NULL },
+ { "copyrightserif", 790, NULL },
+ { "logicaland", 603, NULL },
+ { "arrowvertex", 603, NULL },
+ { "arrowleft", 987, NULL },
+ { "Omicron", 722, NULL },
+ { "propersubset", 713, NULL },
+ { "equivalence", 549, NULL },
+ { "circlemultiply", 768, NULL },
+ { "greater", 549, NULL },
+ { "minute", 247, NULL },
+ { "integral", 274, NULL },
+ { "percent", 833, NULL },
+ { "omega", 686, NULL },
+ { "Beta", 667, NULL },
+ { "equal", 549, NULL },
+ { "therefore", 863, NULL },
+ { "braceleftmid", 494, NULL },
+ { "parenrighttp", 384, NULL },
+ { "bracketright", 333, NULL },
+ { "omicron", 549, NULL },
+ { "Epsilon", 611, NULL },
+ { "Nu", 722, NULL },
+ { "zero", 500, NULL },
+ { "period", 250, NULL },
+ { "Gamma", 603, NULL },
+ { "product", 823, NULL },
+ { "arrowboth", 1042, NULL },
+ { "Ifraktur", 686, NULL },
+ { "infinity", 713, NULL },
+ { "heart", 753, NULL },
+ { "parenrightex", 384, NULL },
+ { "arrowdown", 603, NULL },
+ { "bracketleftbt", 384, NULL },
+ { "logicalor", 603, NULL },
+ { "numbersign", 500, NULL },
+ { "divide", 549, NULL },
+ { "nu", 521, NULL },
+ { "spade", 753, NULL },
+ { "rho", 549, NULL },
+ { "Sigma", 592, NULL },
+ { "integralex", 686, NULL },
+ { "space", 250, NULL },
+ { "reflexsuperset", 713, NULL },
+ { "alpha", 631, NULL },
+ { "parenleftex", 384, NULL },
+ { "lessequal", 549, NULL },
+ { "second", 411, NULL },
+ { "integraltp", 686, NULL },
+ { "approxequal", 549, NULL },
+ { "existential", 549, NULL },
+ { "diamond", 753, NULL },
+ { "phi", 521, NULL },
+ { "nine", 500, NULL },
+ { "Iota", 333, NULL },
+ { "parenleft", 333, NULL },
+ { "tau", 439, NULL },
+ { "Tau", 611, NULL },
+ { "less", 549, NULL },
+ { "Eta", 722, NULL },
+ { "minus", 549, NULL },
+ { "four", 500, NULL },
+ { "bracketleftex", 384, NULL },
+ { "arrowdblboth", 1042, NULL },
+ { "arrowup", 603, NULL },
+ { "arrowright", 987, NULL },
+ { "integralbt", 686, NULL },
+ { "similar", 549, NULL },
+ { "underscore", 500, NULL },
+ { "dotmath", 250, NULL },
+ { "mu", 576, NULL },
+ { "trademarkserif", 890, NULL },
+ { "Chi", 722, NULL },
+ { "angleright", 329, NULL },
+ { "logicalnot", 713, NULL },
+ { "parenlefttp", 384, NULL },
+ { "partialdiff", 494, NULL },
+ { "arrowdblright", 987, NULL },
+ { "summation", 713, NULL },
+ { "question", 444, NULL },
+ { "apple", 790, NULL },
+ { "plus", 549, NULL },
+ { "epsilon", 439, NULL },
+ { "reflexsubset", 713, NULL },
+ { "six", 500, NULL },
+ { "arrowdbldown", 603, NULL },
+ { "Omega", 768, NULL },
+ { "bar", 200, NULL },
+ { "multiply", 549, NULL },
+ { "braceright", 480, NULL },
+ { "braceleft", 480, NULL },
+ { "braceleftbt", 494, NULL },
+ { "delta", 494, NULL },
+ { "arrowdblleft", 987, NULL },
+ { "ampersand", 778, NULL },
+ { "arrowdblup", 603, NULL },
+ { "aleph", 823, NULL },
+ { "two", 500, NULL },
+ { "bracerighttp", 494, NULL },
+ { "seven", 500, NULL },
+ { "propersuperset", 713, NULL },
+ { "bullet", 460, NULL },
+ { "perpendicular", 658, NULL },
+ { "Lambda", 686, NULL },
+ { "parenrightbt", 384, NULL },
+ { "bracerightmid", 494, NULL },
+ { "comma", 250, NULL },
+ { "Delta", 612, NULL },
+ { "bracerightbt", 494, NULL },
+ { "union", 768, NULL },
+ { "arrowhorizex", 1000, NULL },
+ { "omega1", 713, NULL },
+ { "proportional", 713, NULL },
+ { "sigma1", 439, NULL },
+ { "carriagereturn", 658, NULL },
+ { "suchthat", 439, NULL },
+ { "notelement", 713, NULL },
+ { "beta", 549, NULL },
+ { "parenright", 333, NULL },
+ { "bracketleft", 333, NULL },
+ { "fraction", 167, NULL },
+ { "Xi", 645, NULL },
+ { "pi", 549, NULL },
+ { "braceex", 494, NULL },
+ { "bracketlefttp", 384, NULL },
+ { "notsubset", 713, NULL },
+ { "asteriskmath", 500, NULL },
+ { "greaterequal", 549, NULL },
+ { "emptyset", 823, NULL },
+ { "bracketrightex", 384, NULL },
+ { "gradient", 713, NULL },
+ { "exclam", 333, NULL },
+ { "notequal", 549, NULL },
+ { "congruent", 549, NULL },
+ { "Pi", 768, NULL },
+ { "Theta", 741, NULL },
+ { "Rfraktur", 795, NULL },
+ { "bracketrighttp", 384, NULL },
+ { "parenleftbt", 384, NULL },
+ { "universal", 713, NULL },
+ { "upsilon", 576, NULL },
+ { "psi", 686, NULL },
+ { "eight", 500, NULL },
+ { "one", 500, NULL },
+ { "Kappa", 722, NULL },
+ { "ellipsis", 1000, NULL },
+ { "chi", 549, NULL },
+ { "trademarksans", 786, NULL },
+ { "Rho", 556, NULL },
+ { "Alpha", 722, NULL },
+ { "bracketrightbt", 384, NULL },
+ { "intersection", 768, NULL },
+ { "xi", 493, NULL },
+ { "slash", 278, NULL },
+ { "registerserif", 790, NULL },
+ { "iota", 329, NULL },
+ { "Psi", 795, NULL },
+ { "three", 500, NULL },
+ { "lozenge", 494, NULL },
+ { "Phi", 763, NULL },
+ { "zeta", 494, NULL },
+ { "lambda", 549, NULL },
+ { "Mu", 889, NULL },
+ { "bracelefttp", 494, NULL },
+ { "eta", 603, NULL },
+ { "registersans", 790, NULL },
+ { "element", 713, NULL },
+ { "semicolon", 278, NULL }
+};
+
+static BuiltinFontWidth timesBoldWidthsTab[] = {
+ { "cacute", 444, NULL },
+ { "Dcroat", 722, NULL },
+ { "tcaron", 416, NULL },
+ { "rcaron", 444, NULL },
+ { "ncommaaccent", 556, NULL },
+ { "tcommaaccent", 333, NULL },
+ { "logicalnot", 570, NULL },
+ { "m", 833, NULL },
+ { "OE", 1000, NULL },
+ { "partialdiff", 494, NULL },
+ { "U", 722, NULL },
+ { "x", 500, NULL },
+ { "y", 500, NULL },
+ { "Iogonek", 389, NULL },
+ { "edieresis", 444, NULL },
+ { "Ncaron", 722, NULL },
+ { "question", 500, NULL },
+ { "summation", 600, NULL },
+ { "A", 722, NULL },
+ { "igrave", 278, NULL },
+ { "dcroat", 556, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "h", 556, NULL },
+ { "Cacute", 722, NULL },
+ { "plus", 570, NULL },
+ { "P", 611, NULL },
+ { "lslash", 278, NULL },
+ { "six", 500, NULL },
+ { "edotaccent", 444, NULL },
+ { "bar", 220, NULL },
+ { "quotesingle", 278, NULL },
+ { "multiply", 570, NULL },
+ { "i", 278, NULL },
+ { "onequarter", 750, NULL },
+ { "braceright", 394, NULL },
+ { "braceleft", 394, NULL },
+ { "acute", 333, NULL },
+ { "oacute", 500, NULL },
+ { "Yacute", 722, NULL },
+ { "daggerdbl", 500, NULL },
+ { "Atilde", 722, NULL },
+ { "Edieresis", 667, NULL },
+ { "ntilde", 556, NULL },
+ { "fi", 556, NULL },
+ { "trademark", 1000, NULL },
+ { "ampersand", 833, NULL },
+ { "Lcommaaccent", 667, NULL },
+ { "O", 778, NULL },
+ { "Icircumflex", 389, NULL },
+ { "nacute", 556, NULL },
+ { "dieresis", 333, NULL },
+ { "Eacute", 667, NULL },
+ { "two", 500, NULL },
+ { "uacute", 556, NULL },
+ { "seven", 500, NULL },
+ { "Udieresis", 722, NULL },
+ { "Abreve", 722, NULL },
+ { "Iacute", 389, NULL },
+ { "bullet", 350, NULL },
+ { "eogonek", 444, NULL },
+ { "eacute", 444, NULL },
+ { "Zdotaccent", 667, NULL },
+ { "scaron", 389, NULL },
+ { "guillemotright", 500, NULL },
+ { "aacute", 500, NULL },
+ { "Otilde", 778, NULL },
+ { "umacron", 556, NULL },
+ { "Tcaron", 667, NULL },
+ { "ugrave", 556, NULL },
+ { "dcaron", 672, NULL },
+ { "hyphen", 333, NULL },
+ { "Oslash", 778, NULL },
+ { "a", 500, NULL },
+ { "B", 667, NULL },
+ { "yacute", 500, NULL },
+ { "kcommaaccent", 556, NULL },
+ { "Rcaron", 722, NULL },
+ { "R", 722, NULL },
+ { "ydieresis", 500, NULL },
+ { "S", 556, NULL },
+ { "omacron", 500, NULL },
+ { "comma", 250, NULL },
+ { "Delta", 612, NULL },
+ { "Gbreve", 778, NULL },
+ { "ordfeminine", 300, NULL },
+ { "quotedblleft", 500, NULL },
+ { "Rcommaaccent", 722, NULL },
+ { "ograve", 500, NULL },
+ { "Lacute", 667, NULL },
+ { "Idieresis", 389, NULL },
+ { "sacute", 389, NULL },
+ { "f", 333, NULL },
+ { "otilde", 500, NULL },
+ { "parenright", 333, NULL },
+ { "gcommaaccent", 500, NULL },
+ { "Ohungarumlaut", 778, NULL },
+ { "bracketleft", 333, NULL },
+ { "fraction", 167, NULL },
+ { "Sacute", 556, NULL },
+ { "Adieresis", 722, NULL },
+ { "Uacute", 722, NULL },
+ { "atilde", 500, NULL },
+ { "Zacute", 667, NULL },
+ { "asciitilde", 520, NULL },
+ { "H", 778, NULL },
+ { "sterling", 500, NULL },
+ { "greaterequal", 549, NULL },
+ { "at", 930, NULL },
+ { "Lslash", 667, NULL },
+ { "Uogonek", 722, NULL },
+ { "caron", 333, NULL },
+ { "lacute", 278, NULL },
+ { "notequal", 549, NULL },
+ { "Agrave", 722, NULL },
+ { "AE", 1000, NULL },
+ { "exclam", 333, NULL },
+ { "Acircumflex", 722, NULL },
+ { "oslash", 500, NULL },
+ { "threequarters", 750, NULL },
+ { "J", 500, NULL },
+ { "ohungarumlaut", 500, NULL },
+ { "Ecircumflex", 667, NULL },
+ { "L", 667, NULL },
+ { "z", 444, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "egrave", 444, NULL },
+ { "uring", 556, NULL },
+ { "s", 389, NULL },
+ { "c", 444, NULL },
+ { "d", 556, NULL },
+ { "eight", 500, NULL },
+ { "I", 389, NULL },
+ { "one", 500, NULL },
+ { "periodcentered", 250, NULL },
+ { "ellipsis", 1000, NULL },
+ { "Aring", 722, NULL },
+ { "agrave", 500, NULL },
+ { "Oacute", 778, NULL },
+ { "t", 333, NULL },
+ { "quotedbl", 555, NULL },
+ { "Eogonek", 667, NULL },
+ { "macron", 333, NULL },
+ { "Lcaron", 667, NULL },
+ { "ordmasculine", 330, NULL },
+ { "questiondown", 500, NULL },
+ { "backslash", 278, NULL },
+ { "slash", 278, NULL },
+ { "r", 444, NULL },
+ { "ucircumflex", 556, NULL },
+ { "three", 500, NULL },
+ { "lozenge", 494, NULL },
+ { "V", 722, NULL },
+ { "Q", 778, NULL },
+ { "exclamdown", 333, NULL },
+ { "Umacron", 722, NULL },
+ { "zacute", 444, NULL },
+ { "ncaron", 556, NULL },
+ { "adieresis", 500, NULL },
+ { "quoteright", 333, NULL },
+ { "uogonek", 556, NULL },
+ { "semicolon", 333, NULL },
+ { "guilsinglright", 333, NULL },
+ { "Amacron", 722, NULL },
+ { "breve", 333, NULL },
+ { "Dcaron", 722, NULL },
+ { "Z", 667, NULL },
+ { "ecircumflex", 444, NULL },
+ { "lcaron", 394, NULL },
+ { "paragraph", 540, NULL },
+ { "zcaron", 444, NULL },
+ { "aring", 500, NULL },
+ { "Ydieresis", 722, NULL },
+ { "section", 500, NULL },
+ { "Scaron", 556, NULL },
+ { "degree", 400, NULL },
+ { "Euro", 500, NULL },
+ { "florin", 500, NULL },
+ { "iogonek", 278, NULL },
+ { "Scommaaccent", 556, NULL },
+ { "Aacute", 722, NULL },
+ { "b", 556, NULL },
+ { "K", 778, NULL },
+ { "dotlessi", 278, NULL },
+ { "p", 556, NULL },
+ { "Ntilde", 722, NULL },
+ { "radical", 549, NULL },
+ { "g", 500, NULL },
+ { "colon", 333, NULL },
+ { "ccedilla", 444, NULL },
+ { "plusminus", 570, NULL },
+ { "cedilla", 333, NULL },
+ { "Idotaccent", 389, NULL },
+ { "quotedblright", 500, NULL },
+ { "Eth", 722, NULL },
+ { "five", 500, NULL },
+ { "X", 722, NULL },
+ { "asciicircum", 581, NULL },
+ { "Odieresis", 778, NULL },
+ { "oe", 722, NULL },
+ { "aogonek", 500, NULL },
+ { "Egrave", 667, NULL },
+ { "ring", 333, NULL },
+ { "emdash", 1000, NULL },
+ { "Gcommaaccent", 778, NULL },
+ { "quoteleft", 333, NULL },
+ { "registered", 747, NULL },
+ { "k", 556, NULL },
+ { "copyright", 747, NULL },
+ { "Racute", 722, NULL },
+ { "lcommaaccent", 278, NULL },
+ { "onehalf", 750, NULL },
+ { "uhungarumlaut", 556, NULL },
+ { "gbreve", 500, NULL },
+ { "Ecaron", 667, NULL },
+ { "threesuperior", 300, NULL },
+ { "zdotaccent", 444, NULL },
+ { "n", 556, NULL },
+ { "Emacron", 667, NULL },
+ { "asterisk", 500, NULL },
+ { "scommaaccent", 389, NULL },
+ { "onesuperior", 300, NULL },
+ { "greater", 570, NULL },
+ { "percent", 1000, NULL },
+ { "o", 500, NULL },
+ { "abreve", 500, NULL },
+ { "Imacron", 389, NULL },
+ { "iacute", 278, NULL },
+ { "e", 444, NULL },
+ { "icircumflex", 278, NULL },
+ { "q", 556, NULL },
+ { "l", 278, NULL },
+ { "dagger", 500, NULL },
+ { "equal", 570, NULL },
+ { "N", 722, NULL },
+ { "C", 722, NULL },
+ { "u", 556, NULL },
+ { "Ugrave", 722, NULL },
+ { "germandbls", 556, NULL },
+ { "thorn", 556, NULL },
+ { "commaaccent", 250, NULL },
+ { "bracketright", 333, NULL },
+ { "W", 1000, NULL },
+ { "Ccedilla", 722, NULL },
+ { "Igrave", 389, NULL },
+ { "zero", 500, NULL },
+ { "period", 250, NULL },
+ { "scedilla", 389, NULL },
+ { "udieresis", 556, NULL },
+ { "perthousand", 1000, NULL },
+ { "Edotaccent", 667, NULL },
+ { "acircumflex", 500, NULL },
+ { "Ocircumflex", 778, NULL },
+ { "F", 611, NULL },
+ { "Uring", 722, NULL },
+ { "G", 778, NULL },
+ { "T", 667, NULL },
+ { "M", 944, NULL },
+ { "odieresis", 500, NULL },
+ { "Ograve", 778, NULL },
+ { "numbersign", 500, NULL },
+ { "divide", 570, NULL },
+ { "emacron", 444, NULL },
+ { "amacron", 500, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "endash", 500, NULL },
+ { "guillemotleft", 500, NULL },
+ { "fl", 556, NULL },
+ { "E", 667, NULL },
+ { "quotedblbase", 500, NULL },
+ { "ecaron", 444, NULL },
+ { "Aogonek", 722, NULL },
+ { "brokenbar", 220, NULL },
+ { "w", 722, NULL },
+ { "Thorn", 611, NULL },
+ { "space", 250, NULL },
+ { "eth", 500, NULL },
+ { "twosuperior", 300, NULL },
+ { "Y", 722, NULL },
+ { "yen", 500, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "Ccaron", 722, NULL },
+ { "j", 333, NULL },
+ { "Omacron", 778, NULL },
+ { "lessequal", 549, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "ccaron", 444, NULL },
+ { "grave", 333, NULL },
+ { "Scedilla", 556, NULL },
+ { "quotesinglbase", 333, NULL },
+ { "currency", 500, NULL },
+ { "nine", 500, NULL },
+ { "parenleft", 333, NULL },
+ { "ogonek", 333, NULL },
+ { "idieresis", 278, NULL },
+ { "circumflex", 333, NULL },
+ { "Zcaron", 667, NULL },
+ { "ocircumflex", 500, NULL },
+ { "less", 570, NULL },
+ { "minus", 570, NULL },
+ { "four", 500, NULL },
+ { "ae", 722, NULL },
+ { "cent", 500, NULL },
+ { "Tcommaaccent", 667, NULL },
+ { "dollar", 500, NULL },
+ { "v", 500, NULL },
+ { "Nacute", 722, NULL },
+ { "dotaccent", 333, NULL },
+ { "underscore", 500, NULL },
+ { "D", 722, NULL },
+ { "mu", 556, NULL },
+ { "racute", 444, NULL },
+ { "rcommaaccent", 444, NULL },
+ { "imacron", 278, NULL },
+ { "Kcommaaccent", 778, NULL },
+ { "tilde", 333, NULL }
+};
+
+static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
+ { "M", 889, NULL },
+ { "odieresis", 500, NULL },
+ { "Ograve", 722, NULL },
+ { "T", 611, NULL },
+ { "amacron", 500, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "numbersign", 500, NULL },
+ { "divide", 570, NULL },
+ { "emacron", 444, NULL },
+ { "fl", 556, NULL },
+ { "E", 667, NULL },
+ { "quotedblbase", 500, NULL },
+ { "endash", 500, NULL },
+ { "guillemotleft", 500, NULL },
+ { "w", 667, NULL },
+ { "Thorn", 611, NULL },
+ { "space", 250, NULL },
+ { "eth", 500, NULL },
+ { "twosuperior", 300, NULL },
+ { "ecaron", 444, NULL },
+ { "Aogonek", 667, NULL },
+ { "brokenbar", 220, NULL },
+ { "commaaccent", 250, NULL },
+ { "bracketright", 333, NULL },
+ { "W", 889, NULL },
+ { "Ugrave", 722, NULL },
+ { "germandbls", 500, NULL },
+ { "thorn", 500, NULL },
+ { "zero", 500, NULL },
+ { "period", 250, NULL },
+ { "scedilla", 389, NULL },
+ { "udieresis", 556, NULL },
+ { "Ccedilla", 667, NULL },
+ { "Igrave", 389, NULL },
+ { "Ocircumflex", 722, NULL },
+ { "F", 667, NULL },
+ { "Uring", 722, NULL },
+ { "Edotaccent", 667, NULL },
+ { "perthousand", 1000, NULL },
+ { "acircumflex", 500, NULL },
+ { "G", 722, NULL },
+ { "circumflex", 333, NULL },
+ { "Zcaron", 611, NULL },
+ { "ogonek", 333, NULL },
+ { "idieresis", 278, NULL },
+ { "ae", 722, NULL },
+ { "cent", 500, NULL },
+ { "ocircumflex", 500, NULL },
+ { "less", 570, NULL },
+ { "minus", 606, NULL },
+ { "four", 500, NULL },
+ { "dollar", 500, NULL },
+ { "Tcommaaccent", 611, NULL },
+ { "v", 444, NULL },
+ { "Nacute", 722, NULL },
+ { "dotaccent", 333, NULL },
+ { "rcommaaccent", 389, NULL },
+ { "Kcommaaccent", 667, NULL },
+ { "imacron", 278, NULL },
+ { "tilde", 333, NULL },
+ { "underscore", 500, NULL },
+ { "mu", 576, NULL },
+ { "D", 722, NULL },
+ { "racute", 389, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "j", 278, NULL },
+ { "Ccaron", 667, NULL },
+ { "yen", 500, NULL },
+ { "Y", 611, NULL },
+ { "ccaron", 444, NULL },
+ { "lessequal", 549, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "Omacron", 722, NULL },
+ { "quotesinglbase", 333, NULL },
+ { "grave", 333, NULL },
+ { "Scedilla", 556, NULL },
+ { "parenleft", 333, NULL },
+ { "currency", 500, NULL },
+ { "nine", 500, NULL },
+ { "Ntilde", 722, NULL },
+ { "radical", 549, NULL },
+ { "dotlessi", 278, NULL },
+ { "p", 500, NULL },
+ { "plusminus", 570, NULL },
+ { "Idotaccent", 389, NULL },
+ { "cedilla", 333, NULL },
+ { "quotedblright", 500, NULL },
+ { "Eth", 722, NULL },
+ { "colon", 333, NULL },
+ { "g", 500, NULL },
+ { "ccedilla", 444, NULL },
+ { "Odieresis", 722, NULL },
+ { "oe", 722, NULL },
+ { "five", 500, NULL },
+ { "X", 667, NULL },
+ { "asciicircum", 570, NULL },
+ { "emdash", 1000, NULL },
+ { "aogonek", 500, NULL },
+ { "Egrave", 667, NULL },
+ { "ring", 333, NULL },
+ { "Dcaron", 722, NULL },
+ { "Z", 611, NULL },
+ { "ecircumflex", 444, NULL },
+ { "Amacron", 667, NULL },
+ { "breve", 333, NULL },
+ { "lcaron", 382, NULL },
+ { "paragraph", 500, NULL },
+ { "zcaron", 389, NULL },
+ { "aring", 500, NULL },
+ { "Ydieresis", 611, NULL },
+ { "Euro", 500, NULL },
+ { "iogonek", 278, NULL },
+ { "florin", 500, NULL },
+ { "section", 500, NULL },
+ { "Scaron", 556, NULL },
+ { "degree", 400, NULL },
+ { "K", 667, NULL },
+ { "Scommaaccent", 556, NULL },
+ { "Aacute", 667, NULL },
+ { "b", 500, NULL },
+ { "greater", 570, NULL },
+ { "asterisk", 500, NULL },
+ { "Emacron", 667, NULL },
+ { "scommaaccent", 389, NULL },
+ { "onesuperior", 300, NULL },
+ { "percent", 833, NULL },
+ { "o", 500, NULL },
+ { "icircumflex", 278, NULL },
+ { "abreve", 500, NULL },
+ { "Imacron", 389, NULL },
+ { "e", 444, NULL },
+ { "iacute", 278, NULL },
+ { "dagger", 500, NULL },
+ { "equal", 570, NULL },
+ { "N", 722, NULL },
+ { "C", 667, NULL },
+ { "u", 556, NULL },
+ { "q", 500, NULL },
+ { "l", 278, NULL },
+ { "quoteleft", 333, NULL },
+ { "registered", 747, NULL },
+ { "Gcommaaccent", 722, NULL },
+ { "Racute", 667, NULL },
+ { "k", 500, NULL },
+ { "copyright", 747, NULL },
+ { "lcommaaccent", 278, NULL },
+ { "onehalf", 750, NULL },
+ { "gbreve", 500, NULL },
+ { "uhungarumlaut", 556, NULL },
+ { "n", 556, NULL },
+ { "Ecaron", 667, NULL },
+ { "threesuperior", 300, NULL },
+ { "zdotaccent", 389, NULL },
+ { "Uacute", 722, NULL },
+ { "atilde", 500, NULL },
+ { "Zacute", 611, NULL },
+ { "fraction", 167, NULL },
+ { "Sacute", 556, NULL },
+ { "Adieresis", 667, NULL },
+ { "asciitilde", 570, NULL },
+ { "Uogonek", 722, NULL },
+ { "Lslash", 611, NULL },
+ { "caron", 333, NULL },
+ { "lacute", 278, NULL },
+ { "H", 778, NULL },
+ { "sterling", 500, NULL },
+ { "greaterequal", 549, NULL },
+ { "at", 832, NULL },
+ { "threequarters", 750, NULL },
+ { "J", 500, NULL },
+ { "ohungarumlaut", 500, NULL },
+ { "Ecircumflex", 667, NULL },
+ { "L", 611, NULL },
+ { "notequal", 549, NULL },
+ { "Agrave", 667, NULL },
+ { "exclam", 389, NULL },
+ { "AE", 944, NULL },
+ { "Acircumflex", 667, NULL },
+ { "oslash", 500, NULL },
+ { "ograve", 500, NULL },
+ { "Rcommaaccent", 667, NULL },
+ { "Lacute", 611, NULL },
+ { "sacute", 389, NULL },
+ { "Idieresis", 389, NULL },
+ { "f", 333, NULL },
+ { "gcommaaccent", 500, NULL },
+ { "Ohungarumlaut", 722, NULL },
+ { "bracketleft", 333, NULL },
+ { "otilde", 500, NULL },
+ { "parenright", 333, NULL },
+ { "Eogonek", 667, NULL },
+ { "macron", 333, NULL },
+ { "backslash", 278, NULL },
+ { "Lcaron", 611, NULL },
+ { "ordmasculine", 300, NULL },
+ { "questiondown", 500, NULL },
+ { "three", 500, NULL },
+ { "slash", 278, NULL },
+ { "r", 389, NULL },
+ { "ucircumflex", 556, NULL },
+ { "zacute", 389, NULL },
+ { "Umacron", 722, NULL },
+ { "ncaron", 556, NULL },
+ { "adieresis", 500, NULL },
+ { "lozenge", 494, NULL },
+ { "V", 667, NULL },
+ { "Q", 722, NULL },
+ { "exclamdown", 389, NULL },
+ { "quoteright", 333, NULL },
+ { "uogonek", 556, NULL },
+ { "semicolon", 333, NULL },
+ { "guilsinglright", 333, NULL },
+ { "egrave", 444, NULL },
+ { "uring", 556, NULL },
+ { "s", 389, NULL },
+ { "z", 389, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "one", 500, NULL },
+ { "I", 389, NULL },
+ { "eight", 500, NULL },
+ { "periodcentered", 250, NULL },
+ { "c", 444, NULL },
+ { "d", 500, NULL },
+ { "agrave", 500, NULL },
+ { "ellipsis", 1000, NULL },
+ { "Aring", 667, NULL },
+ { "t", 278, NULL },
+ { "quotedbl", 555, NULL },
+ { "Oacute", 722, NULL },
+ { "six", 500, NULL },
+ { "edotaccent", 444, NULL },
+ { "lslash", 278, NULL },
+ { "P", 611, NULL },
+ { "i", 278, NULL },
+ { "multiply", 570, NULL },
+ { "bar", 220, NULL },
+ { "quotesingle", 278, NULL },
+ { "braceleft", 348, NULL },
+ { "braceright", 348, NULL },
+ { "acute", 333, NULL },
+ { "onequarter", 750, NULL },
+ { "Atilde", 667, NULL },
+ { "Edieresis", 667, NULL },
+ { "oacute", 500, NULL },
+ { "Yacute", 611, NULL },
+ { "daggerdbl", 500, NULL },
+ { "ncommaaccent", 556, NULL },
+ { "tcommaaccent", 278, NULL },
+ { "logicalnot", 606, NULL },
+ { "cacute", 444, NULL },
+ { "Dcroat", 722, NULL },
+ { "tcaron", 366, NULL },
+ { "rcaron", 389, NULL },
+ { "x", 500, NULL },
+ { "Iogonek", 389, NULL },
+ { "edieresis", 444, NULL },
+ { "y", 444, NULL },
+ { "Ncaron", 722, NULL },
+ { "m", 778, NULL },
+ { "OE", 944, NULL },
+ { "U", 722, NULL },
+ { "partialdiff", 494, NULL },
+ { "igrave", 278, NULL },
+ { "dcroat", 500, NULL },
+ { "question", 500, NULL },
+ { "summation", 600, NULL },
+ { "A", 667, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "Cacute", 667, NULL },
+ { "h", 556, NULL },
+ { "plus", 570, NULL },
+ { "Otilde", 722, NULL },
+ { "umacron", 556, NULL },
+ { "Tcaron", 611, NULL },
+ { "aacute", 500, NULL },
+ { "B", 667, NULL },
+ { "kcommaaccent", 500, NULL },
+ { "Rcaron", 667, NULL },
+ { "yacute", 444, NULL },
+ { "ydieresis", 444, NULL },
+ { "R", 667, NULL },
+ { "ugrave", 556, NULL },
+ { "dcaron", 608, NULL },
+ { "Oslash", 722, NULL },
+ { "hyphen", 333, NULL },
+ { "a", 500, NULL },
+ { "omacron", 500, NULL },
+ { "Delta", 612, NULL },
+ { "comma", 250, NULL },
+ { "Gbreve", 722, NULL },
+ { "S", 556, NULL },
+ { "quotedblleft", 500, NULL },
+ { "ordfeminine", 266, NULL },
+ { "trademark", 1000, NULL },
+ { "ampersand", 778, NULL },
+ { "Lcommaaccent", 611, NULL },
+ { "ntilde", 556, NULL },
+ { "fi", 556, NULL },
+ { "dieresis", 333, NULL },
+ { "Eacute", 667, NULL },
+ { "O", 722, NULL },
+ { "Icircumflex", 389, NULL },
+ { "nacute", 556, NULL },
+ { "Abreve", 667, NULL },
+ { "Udieresis", 722, NULL },
+ { "Iacute", 389, NULL },
+ { "two", 500, NULL },
+ { "uacute", 556, NULL },
+ { "seven", 500, NULL },
+ { "scaron", 389, NULL },
+ { "guillemotright", 500, NULL },
+ { "bullet", 350, NULL },
+ { "eogonek", 444, NULL },
+ { "eacute", 444, NULL },
+ { "Zdotaccent", 611, NULL }
+};
+
+static BuiltinFontWidth timesItalicWidthsTab[] = {
+ { "quoteright", 333, NULL },
+ { "uogonek", 500, NULL },
+ { "semicolon", 333, NULL },
+ { "guilsinglright", 333, NULL },
+ { "Umacron", 722, NULL },
+ { "zacute", 389, NULL },
+ { "ncaron", 500, NULL },
+ { "adieresis", 500, NULL },
+ { "lozenge", 471, NULL },
+ { "V", 611, NULL },
+ { "Q", 722, NULL },
+ { "exclamdown", 389, NULL },
+ { "three", 500, NULL },
+ { "slash", 278, NULL },
+ { "r", 389, NULL },
+ { "ucircumflex", 500, NULL },
+ { "Eogonek", 611, NULL },
+ { "macron", 333, NULL },
+ { "ordmasculine", 310, NULL },
+ { "Lcaron", 611, NULL },
+ { "questiondown", 500, NULL },
+ { "backslash", 278, NULL },
+ { "t", 278, NULL },
+ { "quotedbl", 420, NULL },
+ { "Oacute", 722, NULL },
+ { "agrave", 500, NULL },
+ { "ellipsis", 889, NULL },
+ { "Aring", 611, NULL },
+ { "I", 333, NULL },
+ { "one", 500, NULL },
+ { "eight", 500, NULL },
+ { "periodcentered", 250, NULL },
+ { "c", 444, NULL },
+ { "d", 500, NULL },
+ { "egrave", 444, NULL },
+ { "uring", 500, NULL },
+ { "s", 389, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "z", 389, NULL },
+ { "threequarters", 750, NULL },
+ { "J", 444, NULL },
+ { "ohungarumlaut", 500, NULL },
+ { "Ecircumflex", 611, NULL },
+ { "L", 556, NULL },
+ { "notequal", 549, NULL },
+ { "Agrave", 611, NULL },
+ { "AE", 889, NULL },
+ { "exclam", 333, NULL },
+ { "Acircumflex", 611, NULL },
+ { "oslash", 500, NULL },
+ { "caron", 333, NULL },
+ { "Uogonek", 722, NULL },
+ { "Lslash", 556, NULL },
+ { "lacute", 278, NULL },
+ { "H", 722, NULL },
+ { "sterling", 500, NULL },
+ { "at", 920, NULL },
+ { "greaterequal", 549, NULL },
+ { "asciitilde", 541, NULL },
+ { "Uacute", 722, NULL },
+ { "atilde", 500, NULL },
+ { "Zacute", 556, NULL },
+ { "fraction", 167, NULL },
+ { "Adieresis", 611, NULL },
+ { "Sacute", 500, NULL },
+ { "gcommaaccent", 500, NULL },
+ { "Ohungarumlaut", 722, NULL },
+ { "bracketleft", 389, NULL },
+ { "otilde", 500, NULL },
+ { "parenright", 333, NULL },
+ { "f", 278, NULL },
+ { "Lacute", 556, NULL },
+ { "sacute", 389, NULL },
+ { "Idieresis", 333, NULL },
+ { "ograve", 500, NULL },
+ { "Rcommaaccent", 611, NULL },
+ { "quotedblleft", 556, NULL },
+ { "ordfeminine", 276, NULL },
+ { "omacron", 500, NULL },
+ { "Delta", 612, NULL },
+ { "comma", 250, NULL },
+ { "Gbreve", 722, NULL },
+ { "S", 500, NULL },
+ { "B", 611, NULL },
+ { "kcommaaccent", 444, NULL },
+ { "yacute", 444, NULL },
+ { "Rcaron", 611, NULL },
+ { "R", 611, NULL },
+ { "ydieresis", 444, NULL },
+ { "ugrave", 500, NULL },
+ { "dcaron", 544, NULL },
+ { "Oslash", 722, NULL },
+ { "hyphen", 333, NULL },
+ { "a", 500, NULL },
+ { "umacron", 500, NULL },
+ { "Otilde", 722, NULL },
+ { "Tcaron", 556, NULL },
+ { "aacute", 500, NULL },
+ { "scaron", 389, NULL },
+ { "guillemotright", 500, NULL },
+ { "eogonek", 444, NULL },
+ { "bullet", 350, NULL },
+ { "eacute", 444, NULL },
+ { "Zdotaccent", 556, NULL },
+ { "Udieresis", 722, NULL },
+ { "Abreve", 611, NULL },
+ { "Iacute", 333, NULL },
+ { "two", 500, NULL },
+ { "uacute", 500, NULL },
+ { "seven", 500, NULL },
+ { "dieresis", 333, NULL },
+ { "Eacute", 611, NULL },
+ { "O", 722, NULL },
+ { "Icircumflex", 333, NULL },
+ { "nacute", 500, NULL },
+ { "trademark", 980, NULL },
+ { "ampersand", 778, NULL },
+ { "Lcommaaccent", 556, NULL },
+ { "ntilde", 500, NULL },
+ { "fi", 500, NULL },
+ { "Atilde", 611, NULL },
+ { "Edieresis", 611, NULL },
+ { "Yacute", 556, NULL },
+ { "oacute", 500, NULL },
+ { "daggerdbl", 500, NULL },
+ { "braceleft", 400, NULL },
+ { "braceright", 400, NULL },
+ { "acute", 333, NULL },
+ { "onequarter", 750, NULL },
+ { "multiply", 675, NULL },
+ { "i", 278, NULL },
+ { "bar", 275, NULL },
+ { "quotesingle", 214, NULL },
+ { "six", 500, NULL },
+ { "edotaccent", 444, NULL },
+ { "P", 611, NULL },
+ { "lslash", 278, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "h", 500, NULL },
+ { "Cacute", 667, NULL },
+ { "plus", 675, NULL },
+ { "igrave", 278, NULL },
+ { "dcroat", 500, NULL },
+ { "question", 500, NULL },
+ { "summation", 600, NULL },
+ { "A", 611, NULL },
+ { "x", 444, NULL },
+ { "Iogonek", 333, NULL },
+ { "y", 444, NULL },
+ { "edieresis", 444, NULL },
+ { "Ncaron", 667, NULL },
+ { "m", 722, NULL },
+ { "OE", 944, NULL },
+ { "U", 722, NULL },
+ { "partialdiff", 476, NULL },
+ { "ncommaaccent", 500, NULL },
+ { "tcommaaccent", 278, NULL },
+ { "logicalnot", 675, NULL },
+ { "cacute", 444, NULL },
+ { "Dcroat", 722, NULL },
+ { "tcaron", 300, NULL },
+ { "rcaron", 389, NULL },
+ { "rcommaaccent", 389, NULL },
+ { "Kcommaaccent", 667, NULL },
+ { "imacron", 278, NULL },
+ { "tilde", 333, NULL },
+ { "underscore", 500, NULL },
+ { "mu", 500, NULL },
+ { "D", 722, NULL },
+ { "racute", 389, NULL },
+ { "Tcommaaccent", 556, NULL },
+ { "dollar", 500, NULL },
+ { "v", 444, NULL },
+ { "Nacute", 667, NULL },
+ { "dotaccent", 333, NULL },
+ { "ae", 667, NULL },
+ { "cent", 500, NULL },
+ { "ocircumflex", 500, NULL },
+ { "less", 675, NULL },
+ { "four", 500, NULL },
+ { "minus", 675, NULL },
+ { "circumflex", 333, NULL },
+ { "Zcaron", 556, NULL },
+ { "ogonek", 333, NULL },
+ { "idieresis", 278, NULL },
+ { "parenleft", 333, NULL },
+ { "currency", 500, NULL },
+ { "nine", 500, NULL },
+ { "quotesinglbase", 333, NULL },
+ { "grave", 333, NULL },
+ { "Scedilla", 500, NULL },
+ { "ccaron", 444, NULL },
+ { "lessequal", 549, NULL },
+ { "Omacron", 722, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "j", 278, NULL },
+ { "Ccaron", 667, NULL },
+ { "Y", 556, NULL },
+ { "yen", 500, NULL },
+ { "w", 667, NULL },
+ { "space", 250, NULL },
+ { "Thorn", 611, NULL },
+ { "eth", 500, NULL },
+ { "twosuperior", 300, NULL },
+ { "ecaron", 444, NULL },
+ { "brokenbar", 275, NULL },
+ { "Aogonek", 611, NULL },
+ { "fl", 500, NULL },
+ { "quotedblbase", 556, NULL },
+ { "E", 611, NULL },
+ { "endash", 500, NULL },
+ { "guillemotleft", 500, NULL },
+ { "amacron", 500, NULL },
+ { "Ncommaaccent", 667, NULL },
+ { "numbersign", 500, NULL },
+ { "divide", 675, NULL },
+ { "emacron", 444, NULL },
+ { "M", 833, NULL },
+ { "odieresis", 500, NULL },
+ { "Ograve", 722, NULL },
+ { "T", 556, NULL },
+ { "G", 722, NULL },
+ { "F", 611, NULL },
+ { "Ocircumflex", 722, NULL },
+ { "Uring", 722, NULL },
+ { "Edotaccent", 611, NULL },
+ { "perthousand", 1000, NULL },
+ { "acircumflex", 500, NULL },
+ { "zero", 500, NULL },
+ { "period", 250, NULL },
+ { "scedilla", 389, NULL },
+ { "udieresis", 500, NULL },
+ { "Ccedilla", 667, NULL },
+ { "Igrave", 333, NULL },
+ { "commaaccent", 250, NULL },
+ { "bracketright", 389, NULL },
+ { "W", 833, NULL },
+ { "Ugrave", 722, NULL },
+ { "germandbls", 500, NULL },
+ { "thorn", 500, NULL },
+ { "dagger", 500, NULL },
+ { "equal", 675, NULL },
+ { "N", 667, NULL },
+ { "C", 667, NULL },
+ { "u", 500, NULL },
+ { "q", 500, NULL },
+ { "l", 278, NULL },
+ { "icircumflex", 278, NULL },
+ { "abreve", 500, NULL },
+ { "Imacron", 333, NULL },
+ { "iacute", 278, NULL },
+ { "e", 444, NULL },
+ { "percent", 833, NULL },
+ { "o", 500, NULL },
+ { "greater", 675, NULL },
+ { "Emacron", 611, NULL },
+ { "scommaaccent", 389, NULL },
+ { "asterisk", 500, NULL },
+ { "onesuperior", 300, NULL },
+ { "n", 500, NULL },
+ { "Ecaron", 611, NULL },
+ { "threesuperior", 300, NULL },
+ { "zdotaccent", 389, NULL },
+ { "lcommaaccent", 278, NULL },
+ { "onehalf", 750, NULL },
+ { "gbreve", 500, NULL },
+ { "uhungarumlaut", 500, NULL },
+ { "Racute", 611, NULL },
+ { "k", 444, NULL },
+ { "copyright", 760, NULL },
+ { "quoteleft", 333, NULL },
+ { "registered", 760, NULL },
+ { "Gcommaaccent", 722, NULL },
+ { "emdash", 889, NULL },
+ { "aogonek", 500, NULL },
+ { "Egrave", 611, NULL },
+ { "ring", 333, NULL },
+ { "Odieresis", 722, NULL },
+ { "oe", 667, NULL },
+ { "five", 500, NULL },
+ { "X", 611, NULL },
+ { "asciicircum", 422, NULL },
+ { "plusminus", 675, NULL },
+ { "Idotaccent", 333, NULL },
+ { "cedilla", 333, NULL },
+ { "quotedblright", 556, NULL },
+ { "Eth", 722, NULL },
+ { "g", 500, NULL },
+ { "colon", 333, NULL },
+ { "ccedilla", 444, NULL },
+ { "Ntilde", 667, NULL },
+ { "radical", 453, NULL },
+ { "dotlessi", 278, NULL },
+ { "p", 500, NULL },
+ { "K", 667, NULL },
+ { "Scommaaccent", 500, NULL },
+ { "Aacute", 611, NULL },
+ { "b", 500, NULL },
+ { "Euro", 500, NULL },
+ { "iogonek", 278, NULL },
+ { "florin", 500, NULL },
+ { "section", 500, NULL },
+ { "Scaron", 500, NULL },
+ { "degree", 400, NULL },
+ { "lcaron", 300, NULL },
+ { "paragraph", 523, NULL },
+ { "zcaron", 389, NULL },
+ { "aring", 500, NULL },
+ { "Ydieresis", 556, NULL },
+ { "Dcaron", 722, NULL },
+ { "Z", 556, NULL },
+ { "ecircumflex", 444, NULL },
+ { "Amacron", 611, NULL },
+ { "breve", 333, NULL }
+};
+
+static BuiltinFontWidth timesRomanWidthsTab[] = {
+ { "greaterequal", 549, NULL },
+ { "at", 921, NULL },
+ { "sterling", 500, NULL },
+ { "H", 722, NULL },
+ { "caron", 333, NULL },
+ { "Uogonek", 722, NULL },
+ { "Lslash", 611, NULL },
+ { "lacute", 278, NULL },
+ { "Acircumflex", 722, NULL },
+ { "oslash", 500, NULL },
+ { "notequal", 549, NULL },
+ { "Agrave", 722, NULL },
+ { "AE", 889, NULL },
+ { "exclam", 333, NULL },
+ { "ohungarumlaut", 500, NULL },
+ { "L", 611, NULL },
+ { "Ecircumflex", 611, NULL },
+ { "threequarters", 750, NULL },
+ { "J", 389, NULL },
+ { "Sacute", 556, NULL },
+ { "Adieresis", 722, NULL },
+ { "fraction", 167, NULL },
+ { "atilde", 444, NULL },
+ { "Zacute", 611, NULL },
+ { "Uacute", 722, NULL },
+ { "asciitilde", 541, NULL },
+ { "f", 333, NULL },
+ { "parenright", 333, NULL },
+ { "otilde", 500, NULL },
+ { "Ohungarumlaut", 722, NULL },
+ { "bracketleft", 333, NULL },
+ { "gcommaaccent", 500, NULL },
+ { "Rcommaaccent", 667, NULL },
+ { "ograve", 500, NULL },
+ { "sacute", 389, NULL },
+ { "Idieresis", 333, NULL },
+ { "Lacute", 611, NULL },
+ { "Q", 722, NULL },
+ { "exclamdown", 333, NULL },
+ { "lozenge", 471, NULL },
+ { "V", 722, NULL },
+ { "ncaron", 500, NULL },
+ { "adieresis", 444, NULL },
+ { "zacute", 444, NULL },
+ { "Umacron", 722, NULL },
+ { "semicolon", 278, NULL },
+ { "guilsinglright", 333, NULL },
+ { "quoteright", 333, NULL },
+ { "uogonek", 500, NULL },
+ { "Eogonek", 611, NULL },
+ { "macron", 333, NULL },
+ { "ordmasculine", 310, NULL },
+ { "Lcaron", 611, NULL },
+ { "backslash", 278, NULL },
+ { "questiondown", 444, NULL },
+ { "ucircumflex", 500, NULL },
+ { "r", 333, NULL },
+ { "slash", 278, NULL },
+ { "three", 500, NULL },
+ { "Aring", 722, NULL },
+ { "ellipsis", 1000, NULL },
+ { "agrave", 444, NULL },
+ { "Oacute", 722, NULL },
+ { "quotedbl", 408, NULL },
+ { "t", 278, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "z", 444, NULL },
+ { "uring", 500, NULL },
+ { "s", 389, NULL },
+ { "egrave", 444, NULL },
+ { "c", 444, NULL },
+ { "d", 500, NULL },
+ { "periodcentered", 250, NULL },
+ { "I", 333, NULL },
+ { "eight", 500, NULL },
+ { "one", 500, NULL },
+ { "onequarter", 750, NULL },
+ { "braceright", 480, NULL },
+ { "braceleft", 480, NULL },
+ { "acute", 333, NULL },
+ { "daggerdbl", 500, NULL },
+ { "oacute", 500, NULL },
+ { "Yacute", 722, NULL },
+ { "Edieresis", 611, NULL },
+ { "Atilde", 722, NULL },
+ { "P", 556, NULL },
+ { "lslash", 278, NULL },
+ { "six", 500, NULL },
+ { "edotaccent", 444, NULL },
+ { "quotesingle", 180, NULL },
+ { "bar", 200, NULL },
+ { "i", 278, NULL },
+ { "multiply", 564, NULL },
+ { "A", 722, NULL },
+ { "question", 444, NULL },
+ { "summation", 600, NULL },
+ { "igrave", 278, NULL },
+ { "dcroat", 500, NULL },
+ { "plus", 564, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "h", 500, NULL },
+ { "Cacute", 667, NULL },
+ { "rcaron", 333, NULL },
+ { "tcaron", 326, NULL },
+ { "cacute", 444, NULL },
+ { "Dcroat", 722, NULL },
+ { "tcommaaccent", 278, NULL },
+ { "logicalnot", 564, NULL },
+ { "ncommaaccent", 500, NULL },
+ { "partialdiff", 476, NULL },
+ { "U", 722, NULL },
+ { "m", 778, NULL },
+ { "OE", 889, NULL },
+ { "Ncaron", 722, NULL },
+ { "x", 500, NULL },
+ { "edieresis", 444, NULL },
+ { "Iogonek", 333, NULL },
+ { "y", 500, NULL },
+ { "S", 556, NULL },
+ { "Gbreve", 722, NULL },
+ { "omacron", 500, NULL },
+ { "comma", 250, NULL },
+ { "Delta", 612, NULL },
+ { "ordfeminine", 276, NULL },
+ { "quotedblleft", 444, NULL },
+ { "aacute", 444, NULL },
+ { "Tcaron", 611, NULL },
+ { "Otilde", 722, NULL },
+ { "umacron", 500, NULL },
+ { "Oslash", 722, NULL },
+ { "hyphen", 333, NULL },
+ { "a", 444, NULL },
+ { "ugrave", 500, NULL },
+ { "dcaron", 588, NULL },
+ { "yacute", 500, NULL },
+ { "kcommaaccent", 500, NULL },
+ { "Rcaron", 667, NULL },
+ { "R", 667, NULL },
+ { "ydieresis", 500, NULL },
+ { "B", 667, NULL },
+ { "seven", 500, NULL },
+ { "two", 500, NULL },
+ { "uacute", 500, NULL },
+ { "Iacute", 333, NULL },
+ { "Abreve", 722, NULL },
+ { "Udieresis", 722, NULL },
+ { "Zdotaccent", 611, NULL },
+ { "eacute", 444, NULL },
+ { "eogonek", 444, NULL },
+ { "bullet", 350, NULL },
+ { "guillemotright", 500, NULL },
+ { "scaron", 389, NULL },
+ { "fi", 556, NULL },
+ { "ntilde", 500, NULL },
+ { "Lcommaaccent", 611, NULL },
+ { "trademark", 980, NULL },
+ { "ampersand", 778, NULL },
+ { "O", 722, NULL },
+ { "Icircumflex", 333, NULL },
+ { "nacute", 500, NULL },
+ { "Eacute", 611, NULL },
+ { "dieresis", 333, NULL },
+ { "endash", 500, NULL },
+ { "guillemotleft", 500, NULL },
+ { "E", 611, NULL },
+ { "quotedblbase", 444, NULL },
+ { "fl", 556, NULL },
+ { "brokenbar", 200, NULL },
+ { "Aogonek", 722, NULL },
+ { "ecaron", 444, NULL },
+ { "Thorn", 556, NULL },
+ { "space", 250, NULL },
+ { "twosuperior", 300, NULL },
+ { "eth", 500, NULL },
+ { "w", 722, NULL },
+ { "T", 611, NULL },
+ { "Ograve", 722, NULL },
+ { "M", 889, NULL },
+ { "odieresis", 500, NULL },
+ { "divide", 564, NULL },
+ { "emacron", 444, NULL },
+ { "numbersign", 500, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "amacron", 444, NULL },
+ { "acircumflex", 444, NULL },
+ { "Edotaccent", 611, NULL },
+ { "perthousand", 1000, NULL },
+ { "Uring", 722, NULL },
+ { "Ocircumflex", 722, NULL },
+ { "F", 556, NULL },
+ { "G", 722, NULL },
+ { "thorn", 500, NULL },
+ { "Ugrave", 722, NULL },
+ { "germandbls", 500, NULL },
+ { "bracketright", 333, NULL },
+ { "W", 944, NULL },
+ { "commaaccent", 250, NULL },
+ { "Igrave", 333, NULL },
+ { "Ccedilla", 667, NULL },
+ { "period", 250, NULL },
+ { "scedilla", 389, NULL },
+ { "udieresis", 500, NULL },
+ { "zero", 500, NULL },
+ { "v", 500, NULL },
+ { "Nacute", 722, NULL },
+ { "dotaccent", 333, NULL },
+ { "Tcommaaccent", 611, NULL },
+ { "dollar", 500, NULL },
+ { "mu", 500, NULL },
+ { "D", 722, NULL },
+ { "racute", 333, NULL },
+ { "underscore", 500, NULL },
+ { "tilde", 333, NULL },
+ { "rcommaaccent", 333, NULL },
+ { "Kcommaaccent", 722, NULL },
+ { "imacron", 278, NULL },
+ { "idieresis", 278, NULL },
+ { "ogonek", 333, NULL },
+ { "Zcaron", 611, NULL },
+ { "circumflex", 333, NULL },
+ { "four", 500, NULL },
+ { "minus", 564, NULL },
+ { "ocircumflex", 500, NULL },
+ { "less", 564, NULL },
+ { "cent", 500, NULL },
+ { "ae", 667, NULL },
+ { "grave", 333, NULL },
+ { "Scedilla", 556, NULL },
+ { "quotesinglbase", 333, NULL },
+ { "currency", 500, NULL },
+ { "nine", 500, NULL },
+ { "parenleft", 333, NULL },
+ { "yen", 500, NULL },
+ { "Y", 722, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "Ccaron", 667, NULL },
+ { "j", 278, NULL },
+ { "lessequal", 549, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "Omacron", 722, NULL },
+ { "ccaron", 444, NULL },
+ { "X", 722, NULL },
+ { "asciicircum", 469, NULL },
+ { "five", 500, NULL },
+ { "Odieresis", 722, NULL },
+ { "oe", 722, NULL },
+ { "ring", 333, NULL },
+ { "Egrave", 611, NULL },
+ { "aogonek", 444, NULL },
+ { "emdash", 1000, NULL },
+ { "dotlessi", 278, NULL },
+ { "p", 500, NULL },
+ { "radical", 453, NULL },
+ { "Ntilde", 722, NULL },
+ { "g", 500, NULL },
+ { "colon", 278, NULL },
+ { "ccedilla", 444, NULL },
+ { "quotedblright", 444, NULL },
+ { "Eth", 722, NULL },
+ { "plusminus", 564, NULL },
+ { "Idotaccent", 333, NULL },
+ { "cedilla", 333, NULL },
+ { "section", 500, NULL },
+ { "Scaron", 556, NULL },
+ { "degree", 400, NULL },
+ { "Euro", 500, NULL },
+ { "florin", 500, NULL },
+ { "iogonek", 278, NULL },
+ { "b", 500, NULL },
+ { "Scommaaccent", 556, NULL },
+ { "Aacute", 722, NULL },
+ { "K", 722, NULL },
+ { "Amacron", 722, NULL },
+ { "breve", 333, NULL },
+ { "Z", 611, NULL },
+ { "ecircumflex", 444, NULL },
+ { "Dcaron", 722, NULL },
+ { "aring", 444, NULL },
+ { "Ydieresis", 722, NULL },
+ { "lcaron", 344, NULL },
+ { "paragraph", 453, NULL },
+ { "zcaron", 444, NULL },
+ { "Imacron", 333, NULL },
+ { "abreve", 444, NULL },
+ { "e", 444, NULL },
+ { "iacute", 278, NULL },
+ { "icircumflex", 278, NULL },
+ { "q", 500, NULL },
+ { "l", 278, NULL },
+ { "C", 667, NULL },
+ { "u", 500, NULL },
+ { "dagger", 500, NULL },
+ { "equal", 564, NULL },
+ { "N", 722, NULL },
+ { "onesuperior", 300, NULL },
+ { "asterisk", 500, NULL },
+ { "Emacron", 611, NULL },
+ { "scommaaccent", 389, NULL },
+ { "greater", 564, NULL },
+ { "o", 500, NULL },
+ { "percent", 833, NULL },
+ { "gbreve", 500, NULL },
+ { "uhungarumlaut", 500, NULL },
+ { "lcommaaccent", 278, NULL },
+ { "onehalf", 750, NULL },
+ { "threesuperior", 300, NULL },
+ { "zdotaccent", 444, NULL },
+ { "Ecaron", 611, NULL },
+ { "n", 500, NULL },
+ { "Gcommaaccent", 722, NULL },
+ { "registered", 760, NULL },
+ { "quoteleft", 333, NULL },
+ { "copyright", 760, NULL },
+ { "k", 500, NULL },
+ { "Racute", 667, NULL }
+};
+
+static BuiltinFontWidth zapfDingbatsWidthsTab[] = {
+ { "a198", 888, NULL },
+ { "a107", 760, NULL },
+ { "a167", 927, NULL },
+ { "a97", 392, NULL },
+ { "a137", 788, NULL },
+ { "a175", 930, NULL },
+ { "a55", 708, NULL },
+ { "a206", 410, NULL },
+ { "a146", 788, NULL },
+ { "a27", 759, NULL },
+ { "a71", 791, NULL },
+ { "a79", 784, NULL },
+ { "a136", 788, NULL },
+ { "a43", 923, NULL },
+ { "a131", 788, NULL },
+ { "a154", 788, NULL },
+ { "a123", 788, NULL },
+ { "a8", 537, NULL },
+ { "a87", 234, NULL },
+ { "a72", 873, NULL },
+ { "a199", 867, NULL },
+ { "a151", 788, NULL },
+ { "a99", 668, NULL },
+ { "a126", 788, NULL },
+ { "a46", 749, NULL },
+ { "a34", 794, NULL },
+ { "a186", 967, NULL },
+ { "a102", 544, NULL },
+ { "a205", 509, NULL },
+ { "a62", 707, NULL },
+ { "a140", 788, NULL },
+ { "a90", 390, NULL },
+ { "a98", 392, NULL },
+ { "a9", 577, NULL },
+ { "a178", 883, NULL },
+ { "a26", 760, NULL },
+ { "a40", 833, NULL },
+ { "a125", 788, NULL },
+ { "a101", 732, NULL },
+ { "a103", 544, NULL },
+ { "a150", 788, NULL },
+ { "a57", 701, NULL },
+ { "a18", 974, NULL },
+ { "a184", 946, NULL },
+ { "space", 278, NULL },
+ { "a74", 762, NULL },
+ { "a168", 928, NULL },
+ { "a147", 788, NULL },
+ { "a187", 831, NULL },
+ { "a110", 694, NULL },
+ { "a143", 788, NULL },
+ { "a36", 823, NULL },
+ { "a41", 816, NULL },
+ { "a86", 410, NULL },
+ { "a164", 458, NULL },
+ { "a152", 788, NULL },
+ { "a163", 1016, NULL },
+ { "a3", 980, NULL },
+ { "a160", 894, NULL },
+ { "a166", 918, NULL },
+ { "a117", 690, NULL },
+ { "a83", 277, NULL },
+ { "a29", 786, NULL },
+ { "a22", 761, NULL },
+ { "a112", 776, NULL },
+ { "a133", 788, NULL },
+ { "a156", 788, NULL },
+ { "a200", 696, NULL },
+ { "a81", 438, NULL },
+ { "a106", 667, NULL },
+ { "a100", 668, NULL },
+ { "a132", 788, NULL },
+ { "a95", 334, NULL },
+ { "a37", 789, NULL },
+ { "a58", 826, NULL },
+ { "a191", 918, NULL },
+ { "a56", 682, NULL },
+ { "a70", 785, NULL },
+ { "a157", 788, NULL },
+ { "a171", 873, NULL },
+ { "a188", 873, NULL },
+ { "a47", 790, NULL },
+ { "a14", 855, NULL },
+ { "a23", 571, NULL },
+ { "a2", 961, NULL },
+ { "a77", 892, NULL },
+ { "a105", 911, NULL },
+ { "a25", 763, NULL },
+ { "a202", 974, NULL },
+ { "a61", 789, NULL },
+ { "a10", 692, NULL },
+ { "a44", 744, NULL },
+ { "a159", 788, NULL },
+ { "a48", 792, NULL },
+ { "a124", 788, NULL },
+ { "a190", 970, NULL },
+ { "a20", 846, NULL },
+ { "a185", 865, NULL },
+ { "a16", 933, NULL },
+ { "a11", 960, NULL },
+ { "a134", 788, NULL },
+ { "a108", 760, NULL },
+ { "a28", 754, NULL },
+ { "a149", 788, NULL },
+ { "a172", 828, NULL },
+ { "a1", 974, NULL },
+ { "a144", 788, NULL },
+ { "a92", 276, NULL },
+ { "a66", 786, NULL },
+ { "a189", 927, NULL },
+ { "a54", 707, NULL },
+ { "a59", 815, NULL },
+ { "a13", 549, NULL },
+ { "a4", 719, NULL },
+ { "a82", 138, NULL },
+ { "a109", 626, NULL },
+ { "a170", 834, NULL },
+ { "a201", 874, NULL },
+ { "a204", 759, NULL },
+ { "a39", 823, NULL },
+ { "a197", 771, NULL },
+ { "a49", 695, NULL },
+ { "a139", 788, NULL },
+ { "a96", 334, NULL },
+ { "a7", 552, NULL },
+ { "a183", 760, NULL },
+ { "a118", 791, NULL },
+ { "a155", 788, NULL },
+ { "a148", 788, NULL },
+ { "a73", 761, NULL },
+ { "a75", 759, NULL },
+ { "a76", 892, NULL },
+ { "a135", 788, NULL },
+ { "a177", 463, NULL },
+ { "a104", 910, NULL },
+ { "a88", 234, NULL },
+ { "a142", 788, NULL },
+ { "a179", 836, NULL },
+ { "a17", 945, NULL },
+ { "a203", 762, NULL },
+ { "a141", 788, NULL },
+ { "a31", 788, NULL },
+ { "a129", 788, NULL },
+ { "a35", 816, NULL },
+ { "a182", 874, NULL },
+ { "a15", 911, NULL },
+ { "a38", 841, NULL },
+ { "a169", 928, NULL },
+ { "a67", 787, NULL },
+ { "a45", 723, NULL },
+ { "a93", 317, NULL },
+ { "a195", 888, NULL },
+ { "a68", 713, NULL },
+ { "a6", 494, NULL },
+ { "a24", 677, NULL },
+ { "a21", 762, NULL },
+ { "a192", 748, NULL },
+ { "a138", 788, NULL },
+ { "a65", 689, NULL },
+ { "a84", 415, NULL },
+ { "a52", 792, NULL },
+ { "a32", 790, NULL },
+ { "a33", 793, NULL },
+ { "a60", 789, NULL },
+ { "a130", 788, NULL },
+ { "a180", 867, NULL },
+ { "a78", 788, NULL },
+ { "a42", 831, NULL },
+ { "a196", 748, NULL },
+ { "a111", 595, NULL },
+ { "a53", 759, NULL },
+ { "a161", 838, NULL },
+ { "a94", 317, NULL },
+ { "a85", 509, NULL },
+ { "a51", 768, NULL },
+ { "a181", 696, NULL },
+ { "a176", 931, NULL },
+ { "a50", 776, NULL },
+ { "a173", 924, NULL },
+ { "a153", 788, NULL },
+ { "a120", 788, NULL },
+ { "a122", 788, NULL },
+ { "a193", 836, NULL },
+ { "a165", 924, NULL },
+ { "a19", 755, NULL },
+ { "a30", 788, NULL },
+ { "a89", 390, NULL },
+ { "a158", 788, NULL },
+ { "a145", 788, NULL },
+ { "a121", 788, NULL },
+ { "a194", 771, NULL },
+ { "a64", 696, NULL },
+ { "a127", 788, NULL },
+ { "a174", 917, NULL },
+ { "a69", 791, NULL },
+ { "a128", 788, NULL },
+ { "a91", 276, NULL },
+ { "a63", 687, NULL },
+ { "a5", 789, NULL },
+ { "a119", 790, NULL },
+ { "a12", 939, NULL },
+ { "a162", 924, NULL }
+};
+
+BuiltinFont builtinFonts[] = {
+ { "Courier", standardEncoding, 600, 629, -157, { -23, -250, 715, 805}, NULL },
+ { "Courier-Bold", standardEncoding, 600, 629, -157, {-113, -250, 749, 801}, NULL },
+ { "Courier-BoldOblique", standardEncoding, 600, 629, -157, { -57, -250, 869, 801}, NULL },
+ { "Courier-Oblique", standardEncoding, 600, 629, -157, { -27, -250, 849, 805}, NULL },
+ { "Helvetica", standardEncoding, 0, 718, -207, {-166, -225, 1000, 931}, NULL },
+ { "Helvetica-Bold", standardEncoding, 0, 718, -207, {-170, -228, 1003, 962}, NULL },
+ { "Helvetica-BoldOblique", standardEncoding, 0, 718, -207, {-174, -228, 1114, 962}, NULL },
+ { "Helvetica-Oblique", standardEncoding, 0, 718, -207, {-170, -225, 1116, 931}, NULL },
+ { "Symbol", symbolEncoding, 0, 1010, -293, {-180, -293, 1090, 1010}, NULL },
+ { "Times-Bold", standardEncoding, 0, 683, -217, {-168, -218, 1000, 935}, NULL },
+ { "Times-BoldItalic", standardEncoding, 0, 683, -217, {-200, -218, 996, 921}, NULL },
+ { "Times-Italic", standardEncoding, 0, 683, -217, {-169, -217, 1010, 883}, NULL },
+ { "Times-Roman", standardEncoding, 0, 683, -217, {-168, -218, 1000, 898}, NULL },
+ { "ZapfDingbats", zapfDingbatsEncoding, 0, 820, -143, { -1, -143, 981, 820}, NULL }
+};
+
+BuiltinFont *builtinFontSubst[] = {
+ &builtinFonts[0],
+ &builtinFonts[3],
+ &builtinFonts[1],
+ &builtinFonts[2],
+ &builtinFonts[4],
+ &builtinFonts[7],
+ &builtinFonts[5],
+ &builtinFonts[6],
+ &builtinFonts[12],
+ &builtinFonts[11],
+ &builtinFonts[9],
+ &builtinFonts[10]
+};
+
+void initBuiltinFontTables() {
+ builtinFonts[0].widths = new BuiltinFontWidths(courierWidthsTab, 315);
+ builtinFonts[1].widths = new BuiltinFontWidths(courierBoldWidthsTab, 315);
+ builtinFonts[2].widths = new BuiltinFontWidths(courierBoldObliqueWidthsTab, 315);
+ builtinFonts[3].widths = new BuiltinFontWidths(courierObliqueWidthsTab, 315);
+ builtinFonts[4].widths = new BuiltinFontWidths(helveticaWidthsTab, 315);
+ builtinFonts[5].widths = new BuiltinFontWidths(helveticaBoldWidthsTab, 316);
+ builtinFonts[6].widths = new BuiltinFontWidths(helveticaBoldObliqueWidthsTab, 315);
+ builtinFonts[7].widths = new BuiltinFontWidths(helveticaObliqueWidthsTab, 315);
+ builtinFonts[8].widths = new BuiltinFontWidths(symbolWidthsTab, 190);
+ builtinFonts[9].widths = new BuiltinFontWidths(timesBoldWidthsTab, 315);
+ builtinFonts[10].widths = new BuiltinFontWidths(timesBoldItalicWidthsTab, 315);
+ builtinFonts[11].widths = new BuiltinFontWidths(timesItalicWidthsTab, 315);
+ builtinFonts[12].widths = new BuiltinFontWidths(timesRomanWidthsTab, 315);
+ builtinFonts[13].widths = new BuiltinFontWidths(zapfDingbatsWidthsTab, 202);
+}
+
+void freeBuiltinFontTables() {
+ int i;
+
+ for (i = 0; i < 14; ++i) {
+ delete builtinFonts[i].widths;
+ }
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFontTables.h b/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFontTables.h
new file mode 100644
index 00000000000..eb45549efd2
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/BuiltinFontTables.h
@@ -0,0 +1,23 @@
+//========================================================================
+//
+// BuiltinFontTables.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef BUILTINFONTTABLES_H
+#define BUILTINFONTTABLES_H
+
+#include "BuiltinFont.h"
+
+#define nBuiltinFonts 14
+#define nBuiltinFontSubsts 12
+
+extern BuiltinFont builtinFonts[nBuiltinFonts];
+extern BuiltinFont *builtinFontSubst[nBuiltinFontSubsts];
+
+extern void initBuiltinFontTables();
+extern void freeBuiltinFontTables();
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt b/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt
new file mode 100644
index 00000000000..7b515d1d036
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CMakeLists.txt
@@ -0,0 +1,273 @@
+#========================================================================
+#
+# xpdf/cmake-xpdf.txt
+#
+# CMake script for the Xpdf tools.
+#
+# Copyright 2015 Glyph & Cog, LLC
+#
+#========================================================================
+
+include_directories("${PROJECT_SOURCE_DIR}")
+include_directories("${PROJECT_BINARY_DIR}")
+include_directories("${PROJECT_SOURCE_DIR}/goo")
+include_directories("${PROJECT_SOURCE_DIR}/fofi")
+include_directories("${PROJECT_SOURCE_DIR}/splash")
+if (PNG_FOUND)
+ include_directories("${PNG_INCLUDE_DIRS}")
+ add_definitions("${PNG_DEFINITIONS}")
+endif ()
+if (HAVE_LCMS)
+ include_directories("${LCMS_INCLUDE_DIR}")
+ set(COLOR_MANAGER_SOURCE "ColorManager.cc")
+else ()
+ set(COLOR_MANAGER_SOURCE "")
+endif ()
+
+add_library(xpdf_objs OBJECT
+ AcroForm.cc
+ Annot.cc
+ Array.cc
+ BuiltinFont.cc
+ BuiltinFontTables.cc
+ Catalog.cc
+ CharCodeToUnicode.cc
+ CMap.cc
+ ${COLOR_MANAGER_SOURCE}
+ Decrypt.cc
+ Dict.cc
+ Error.cc
+ FontEncodingTables.cc
+ Function.cc
+ Gfx.cc
+ GfxFont.cc
+ GfxState.cc
+ GlobalParams.cc
+ JArithmeticDecoder.cc
+ JBIG2Stream.cc
+ JPXStream.cc
+ Lexer.cc
+ Link.cc
+ NameToCharCode.cc
+ Object.cc
+ OptionalContent.cc
+ Outline.cc
+ OutputDev.cc
+ Page.cc
+ Parser.cc
+ PDF417Barcode.cc
+ PDFDoc.cc
+ PDFDocEncoding.cc
+ PSTokenizer.cc
+ SecurityHandler.cc
+ Stream.cc
+ TextString.cc
+ UnicodeMap.cc
+ UnicodeRemapping.cc
+ UnicodeTypeTable.cc
+ UTF8.cc
+ XFAScanner.cc
+ XRef.cc
+ Zoox.cc
+)
+
+#--- object files needed by XpdfWidget
+
+if ((QT4_FOUND OR Qt5Widgets_FOUND)
+ AND HAVE_SPLASH AND MULTITHREADED AND USE_EXCEPTIONS)
+
+ if (HIGHLIGHTED_REGIONS)
+ set(HIGHLIGHT_SRC "HighlightFile.cc")
+ else ()
+ set(HIGHLIGHT_SRC "")
+ endif ()
+
+ add_library(xpdf_widget_objs OBJECT
+ DisplayState.cc
+ PDFCore.cc
+ PreScanOutputDev.cc
+ PSOutputDev.cc
+ ShadingImage.cc
+ SplashOutputDev.cc
+ TextOutputDev.cc
+ TileCache.cc
+ TileCompositor.cc
+ TileMap.cc
+ ${HIGHLIGHT_SRC}
+ )
+endif ()
+
+#--- pdftops
+
+if (HAVE_SPLASH)
+ add_executable(pdftops
+ $<TARGET_OBJECTS:xpdf_objs>
+ PreScanOutputDev.cc
+ PSOutputDev.cc
+ ShadingImage.cc
+ SplashOutputDev.cc
+ pdftops.cc
+ )
+ target_link_libraries(pdftops goo fofi splash
+ ${PAPER_LIBRARY}
+ ${FREETYPE_LIBRARY} ${FREETYPE_OTHER_LIBS}
+ ${DTYPE_LIBRARY}
+ ${LCMS_LIBRARY}
+ ${FONTCONFIG_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT})
+else ()
+ add_executable(pdftops
+ $<TARGET_OBJECTS:xpdf_objs>
+ PreScanOutputDev.cc
+ PSOutputDev.cc
+ pdftops.cc
+ )
+ target_link_libraries(pdftops goo fofi
+ ${PAPER_LIBRARY}
+ ${LCMS_LIBRARY}
+ ${FONTCONFIG_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT})
+endif ()
+install(TARGETS pdftops RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(FILES ${PROJECT_SOURCE_DIR}/doc/pdftops.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+
+#--- pdftotext
+
+add_executable(pdftotext
+ $<TARGET_OBJECTS:xpdf_objs>
+ TextOutputDev.cc
+ pdftotext.cc
+)
+target_link_libraries(pdftotext goo fofi
+ ${PAPER_LIBRARY}
+ ${LCMS_LIBRARY}
+ ${FONTCONFIG_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT})
+install(TARGETS pdftotext RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(FILES ${PROJECT_SOURCE_DIR}/doc/pdftotext.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+
+#--- pdftohtml
+
+if (HAVE_SPLASH AND PNG_FOUND)
+ add_executable(pdftohtml
+ $<TARGET_OBJECTS:xpdf_objs>
+ HTMLGen.cc
+ ShadingImage.cc
+ SplashOutputDev.cc
+ TextOutputDev.cc
+ WebFont.cc
+ pdftohtml.cc
+ )
+ target_link_libraries(pdftohtml goo fofi splash
+ ${PAPER_LIBRARY}
+ ${FREETYPE_LIBRARY} ${FREETYPE_OTHER_LIBS}
+ ${DTYPE_LIBRARY}
+ ${LCMS_LIBRARY}
+ ${PNG_LIBRARIES}
+ ${FONTCONFIG_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT})
+ install(TARGETS pdftohtml RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+ install(FILES ${PROJECT_SOURCE_DIR}/doc/pdftohtml.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+endif ()
+
+#--- pdfinfo
+
+add_executable(pdfinfo
+ $<TARGET_OBJECTS:xpdf_objs>
+ pdfinfo.cc
+)
+target_link_libraries(pdfinfo goo fofi
+ ${PAPER_LIBRARY}
+ ${LCMS_LIBRARY}
+ ${FONTCONFIG_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT})
+install(TARGETS pdfinfo RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(FILES ${PROJECT_SOURCE_DIR}/doc/pdfinfo.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+
+#--- pdffonts
+
+add_executable(pdffonts
+ $<TARGET_OBJECTS:xpdf_objs>
+ pdffonts.cc
+)
+target_link_libraries(pdffonts goo fofi
+ ${PAPER_LIBRARY}
+ ${LCMS_LIBRARY}
+ ${FONTCONFIG_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT})
+install(TARGETS pdffonts RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(FILES ${PROJECT_SOURCE_DIR}/doc/pdffonts.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+
+#--- pdfdetach
+
+add_executable(pdfdetach
+ $<TARGET_OBJECTS:xpdf_objs>
+ pdfdetach.cc
+)
+target_link_libraries(pdfdetach goo fofi
+ ${PAPER_LIBRARY}
+ ${LCMS_LIBRARY}
+ ${FONTCONFIG_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT})
+install(TARGETS pdfdetach RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(FILES ${PROJECT_SOURCE_DIR}/doc/pdfdetach.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+
+#--- pdftoppm
+
+if (HAVE_SPLASH)
+ add_executable(pdftoppm
+ $<TARGET_OBJECTS:xpdf_objs>
+ ShadingImage.cc
+ SplashOutputDev.cc
+ pdftoppm.cc
+ )
+ target_link_libraries(pdftoppm goo fofi splash
+ ${PAPER_LIBRARY}
+ ${FREETYPE_LIBRARY} ${FREETYPE_OTHER_LIBS}
+ ${DTYPE_LIBRARY}
+ ${LCMS_LIBRARY}
+ ${FONTCONFIG_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT})
+ install(TARGETS pdftoppm RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+ install(FILES ${PROJECT_SOURCE_DIR}/doc/pdftoppm.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+endif ()
+
+#--- pdftopng
+
+if (HAVE_SPLASH AND PNG_FOUND)
+ add_executable(pdftopng
+ $<TARGET_OBJECTS:xpdf_objs>
+ ShadingImage.cc
+ SplashOutputDev.cc
+ pdftopng.cc
+ )
+ target_link_libraries(pdftopng goo fofi splash
+ ${PAPER_LIBRARY}
+ ${FREETYPE_LIBRARY} ${FREETYPE_OTHER_LIBS}
+ ${DTYPE_LIBRARY}
+ ${LCMS_LIBRARY}
+ ${PNG_LIBRARIES}
+ ${FONTCONFIG_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT})
+ install(TARGETS pdftopng RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+ install(FILES ${PROJECT_SOURCE_DIR}/doc/pdftopng.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+endif ()
+
+#--- pdfimages
+
+add_executable(pdfimages
+ $<TARGET_OBJECTS:xpdf_objs>
+ ImageOutputDev.cc
+ pdfimages.cc
+)
+target_link_libraries(pdfimages goo fofi
+ ${PAPER_LIBRARY}
+ ${LCMS_LIBRARY}
+ ${FONTCONFIG_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT})
+install(TARGETS pdfimages RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+install(FILES ${PROJECT_SOURCE_DIR}/doc/pdfimages.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+
+#--- xpdfrc man page
+
+install(FILES ${PROJECT_SOURCE_DIR}/doc/xpdfrc.5 DESTINATION ${CMAKE_INSTALL_MANDIR}/man5)
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CMap.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/CMap.cc
new file mode 100644
index 00000000000..12632c5992c
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CMap.cc
@@ -0,0 +1,440 @@
+//========================================================================
+//
+// CMap.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "gfile.h"
+#include "GString.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "PSTokenizer.h"
+#include "Object.h"
+#include "Stream.h"
+#include "CMap.h"
+
+//------------------------------------------------------------------------
+
+struct CMapVectorEntry {
+ GBool isVector;
+ union {
+ CMapVectorEntry *vector;
+ CID cid;
+ };
+};
+
+//------------------------------------------------------------------------
+
+static int getCharFromFile(void *data) {
+ return fgetc((FILE *)data);
+}
+
+static int getCharFromStream(void *data) {
+ return ((Stream *)data)->getChar();
+}
+
+//------------------------------------------------------------------------
+
+CMap *CMap::parse(CMapCache *cache, GString *collectionA, Object *obj) {
+ CMap *cMap;
+ GString *cMapNameA;
+
+ if (obj->isName()) {
+ cMapNameA = new GString(obj->getName());
+ if (!(cMap = globalParams->getCMap(collectionA, cMapNameA))) {
+ error(errSyntaxError, -1,
+ "Unknown CMap '{0:t}' for character collection '{1:t}'",
+ cMapNameA, collectionA);
+ }
+ delete cMapNameA;
+ } else if (obj->isStream()) {
+ if (!(cMap = CMap::parse(NULL, collectionA, obj->getStream()))) {
+ error(errSyntaxError, -1, "Invalid CMap in Type 0 font");
+ }
+ } else {
+ error(errSyntaxError, -1, "Invalid Encoding in Type 0 font");
+ return NULL;
+ }
+ return cMap;
+}
+
+CMap *CMap::parse(CMapCache *cache, GString *collectionA,
+ GString *cMapNameA) {
+ FILE *f;
+ CMap *cMap;
+
+ if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) {
+
+ // Check for an identity CMap.
+ if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) {
+ return new CMap(collectionA->copy(), cMapNameA->copy(), 0);
+ }
+ if (!cMapNameA->cmp("Identity-V")) {
+ return new CMap(collectionA->copy(), cMapNameA->copy(), 1);
+ }
+
+ error(errSyntaxError, -1,
+ "Couldn't find '{0:t}' CMap file for '{1:t}' collection",
+ cMapNameA, collectionA);
+ return NULL;
+ }
+
+ cMap = new CMap(collectionA->copy(), cMapNameA->copy());
+ cMap->parse2(cache, &getCharFromFile, f);
+
+ fclose(f);
+
+ return cMap;
+}
+
+CMap *CMap::parse(CMapCache *cache, GString *collectionA, Stream *str) {
+ Object obj1;
+ CMap *cMap;
+
+ cMap = new CMap(collectionA->copy(), NULL);
+
+ if (!str->getDict()->lookup("UseCMap", &obj1)->isNull()) {
+ cMap->useCMap(cache, &obj1);
+ }
+ obj1.free();
+
+ str->reset();
+ cMap->parse2(cache, &getCharFromStream, str);
+ str->close();
+ return cMap;
+}
+
+void CMap::parse2(CMapCache *cache, int (*getCharFunc)(void *), void *data) {
+ PSTokenizer *pst;
+ char tok1[256], tok2[256], tok3[256];
+ int n1, n2, n3;
+ Guint start, end, code;
+
+ pst = new PSTokenizer(getCharFunc, data);
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ while (pst->getToken(tok2, sizeof(tok2), &n2)) {
+ if (!strcmp(tok2, "usecmap")) {
+ if (tok1[0] == '/') {
+ useCMap(cache, tok1 + 1);
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok1, "/WMode")) {
+ wMode = atoi(tok2);
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok2, "begincidchar")) {
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+ if (!strcmp(tok1, "endcidchar")) {
+ break;
+ }
+ if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+ !strcmp(tok2, "endcidchar")) {
+ error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
+ break;
+ }
+ if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
+ n1 >= 4 && (n1 & 1) == 0)) {
+ error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
+ continue;
+ }
+ tok1[n1 - 1] = '\0';
+ if (sscanf(tok1 + 1, "%x", &code) != 1) {
+ error(errSyntaxError, -1, "Illegal entry in cidchar block in CMap");
+ continue;
+ }
+ n1 = (n1 - 2) / 2;
+ addCIDs(code, code, n1, (CID)atoi(tok2));
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok2, "begincidrange")) {
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+ if (!strcmp(tok1, "endcidrange")) {
+ break;
+ }
+ if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+ !strcmp(tok2, "endcidrange") ||
+ !pst->getToken(tok3, sizeof(tok3), &n3) ||
+ !strcmp(tok3, "endcidrange")) {
+ error(errSyntaxError, -1, "Illegal entry in cidrange block in CMap");
+ break;
+ }
+ if (tok1[0] == '<' && tok2[0] == '<' &&
+ n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
+ tok1[n1 - 1] = tok2[n1 - 1] = '\0';
+ sscanf(tok1 + 1, "%x", &start);
+ sscanf(tok2 + 1, "%x", &end);
+ n1 = (n1 - 2) / 2;
+ addCIDs(start, end, n1, (CID)atoi(tok3));
+ }
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else {
+ strcpy(tok1, tok2);
+ }
+ }
+ delete pst;
+}
+
+CMap::CMap(GString *collectionA, GString *cMapNameA) {
+ int i;
+
+ collection = collectionA;
+ cMapName = cMapNameA;
+ isIdent = gFalse;
+ wMode = 0;
+ vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
+ for (i = 0; i < 256; ++i) {
+ vector[i].isVector = gFalse;
+ vector[i].cid = 0;
+ }
+ refCnt = 1;
+}
+
+CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
+ collection = collectionA;
+ cMapName = cMapNameA;
+ isIdent = gTrue;
+ wMode = wModeA;
+ vector = NULL;
+ refCnt = 1;
+}
+
+void CMap::useCMap(CMapCache *cache, char *useName) {
+ GString *useNameStr;
+ CMap *subCMap;
+
+ useNameStr = new GString(useName);
+ // if cache is non-NULL, we already have a lock, and we can use
+ // CMapCache::getCMap() directly; otherwise, we need to use
+ // GlobalParams::getCMap() in order to acqure the lock need to use
+ // GlobalParams::getCMap
+ if (cache) {
+ subCMap = cache->getCMap(collection, useNameStr);
+ } else {
+ subCMap = globalParams->getCMap(collection, useNameStr);
+ }
+ delete useNameStr;
+ if (!subCMap) {
+ return;
+ }
+ isIdent = subCMap->isIdent;
+ if (subCMap->vector) {
+ copyVector(vector, subCMap->vector);
+ }
+ subCMap->decRefCnt();
+}
+
+void CMap::useCMap(CMapCache *cache, Object *obj) {
+ CMap *subCMap;
+
+ subCMap = CMap::parse(cache, collection, obj);
+ if (!subCMap) {
+ return;
+ }
+ isIdent = subCMap->isIdent;
+ if (subCMap->vector) {
+ copyVector(vector, subCMap->vector);
+ }
+ subCMap->decRefCnt();
+}
+
+void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
+ int i, j;
+
+ for (i = 0; i < 256; ++i) {
+ if (src[i].isVector) {
+ if (!dest[i].isVector) {
+ dest[i].isVector = gTrue;
+ dest[i].vector =
+ (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
+ for (j = 0; j < 256; ++j) {
+ dest[i].vector[j].isVector = gFalse;
+ dest[i].vector[j].cid = 0;
+ }
+ }
+ copyVector(dest[i].vector, src[i].vector);
+ } else {
+ if (dest[i].isVector) {
+ error(errSyntaxError, -1, "Collision in usecmap");
+ } else {
+ dest[i].cid = src[i].cid;
+ }
+ }
+ }
+}
+
+void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
+ CMapVectorEntry *vec;
+ int byte, byte0, byte1;
+ Guint start1, end1, i, j, k;
+
+ start1 = start & 0xffffff00;
+ end1 = end & 0xffffff00;
+ for (i = start1; i <= end1; i += 0x100) {
+ vec = vector;
+ for (j = nBytes - 1; j >= 1; --j) {
+ byte = (i >> (8 * j)) & 0xff;
+ if (!vec[byte].isVector) {
+ vec[byte].isVector = gTrue;
+ vec[byte].vector =
+ (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
+ for (k = 0; k < 256; ++k) {
+ vec[byte].vector[k].isVector = gFalse;
+ vec[byte].vector[k].cid = 0;
+ }
+ }
+ vec = vec[byte].vector;
+ }
+ byte0 = (i < start) ? (start & 0xff) : 0;
+ byte1 = (i + 0xff > end) ? (end & 0xff) : 0xff;
+ for (byte = byte0; byte <= byte1; ++byte) {
+ if (vec[byte].isVector) {
+ error(errSyntaxError, -1, "Invalid CID ({0:x} [{1:d} bytes]) in CMap",
+ i, nBytes);
+ } else {
+ vec[byte].cid = firstCID + ((i + byte) - start);
+ }
+ }
+ }
+}
+
+CMap::~CMap() {
+ delete collection;
+ if (cMapName) {
+ delete cMapName;
+ }
+ if (vector) {
+ freeCMapVector(vector);
+ }
+}
+
+void CMap::freeCMapVector(CMapVectorEntry *vec) {
+ int i;
+
+ for (i = 0; i < 256; ++i) {
+ if (vec[i].isVector) {
+ freeCMapVector(vec[i].vector);
+ }
+ }
+ gfree(vec);
+}
+
+void CMap::incRefCnt() {
+#if MULTITHREADED
+ gAtomicIncrement(&refCnt);
+#else
+ ++refCnt;
+#endif
+}
+
+void CMap::decRefCnt() {
+ GBool done;
+
+#if MULTITHREADED
+ done = gAtomicDecrement(&refCnt) == 0;
+#else
+ done = --refCnt == 0;
+#endif
+ if (done) {
+ delete this;
+ }
+}
+
+GBool CMap::match(GString *collectionA, GString *cMapNameA) {
+ return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA);
+}
+
+CID CMap::getCID(char *s, int len, CharCode *c, int *nUsed) {
+ CMapVectorEntry *vec;
+ CharCode cc;
+ int n, i;
+
+ vec = vector;
+ cc = 0;
+ n = 0;
+ while (vec && n < len) {
+ i = s[n++] & 0xff;
+ cc = (cc << 8) | i;
+ if (!vec[i].isVector) {
+ *c = cc;
+ *nUsed = n;
+ return vec[i].cid;
+ }
+ vec = vec[i].vector;
+ }
+ if (isIdent && len >= 2) {
+ // identity CMap
+ *nUsed = 2;
+ *c = cc = ((s[0] & 0xff) << 8) + (s[1] & 0xff);
+ return cc;
+ }
+ *nUsed = 1;
+ *c = s[0] & 0xff;
+ return 0;
+}
+
+//------------------------------------------------------------------------
+
+CMapCache::CMapCache() {
+ int i;
+
+ for (i = 0; i < cMapCacheSize; ++i) {
+ cache[i] = NULL;
+ }
+}
+
+CMapCache::~CMapCache() {
+ int i;
+
+ for (i = 0; i < cMapCacheSize; ++i) {
+ if (cache[i]) {
+ cache[i]->decRefCnt();
+ }
+ }
+}
+
+CMap *CMapCache::getCMap(GString *collection, GString *cMapName) {
+ CMap *cmap;
+ int i, j;
+
+ if (cache[0] && cache[0]->match(collection, cMapName)) {
+ cache[0]->incRefCnt();
+ return cache[0];
+ }
+ for (i = 1; i < cMapCacheSize; ++i) {
+ if (cache[i] && cache[i]->match(collection, cMapName)) {
+ cmap = cache[i];
+ for (j = i; j >= 1; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = cmap;
+ cmap->incRefCnt();
+ return cmap;
+ }
+ }
+ if ((cmap = CMap::parse(this, collection, cMapName))) {
+ if (cache[cMapCacheSize - 1]) {
+ cache[cMapCacheSize - 1]->decRefCnt();
+ }
+ for (j = cMapCacheSize - 1; j >= 1; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = cmap;
+ cmap->incRefCnt();
+ return cmap;
+ }
+ return NULL;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CMap.h b/Build/source/libs/xpdf/xpdf-src/xpdf/CMap.h
new file mode 100644
index 00000000000..55692b4f337
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CMap.h
@@ -0,0 +1,115 @@
+//========================================================================
+//
+// CMap.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CMAP_H
+#define CMAP_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "CharTypes.h"
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+class GString;
+class Object;
+class Stream;
+struct CMapVectorEntry;
+class CMapCache;
+
+//------------------------------------------------------------------------
+
+class CMap {
+public:
+
+ // Parse a CMap from <obj>, which can be a name or a stream. Sets
+ // the initial reference count to 1. Returns NULL on failure.
+ static CMap *parse(CMapCache *cache, GString *collectionA, Object *obj);
+
+ // Create the CMap specified by <collection> and <cMapName>. Sets
+ // the initial reference count to 1. Returns NULL on failure.
+ static CMap *parse(CMapCache *cache, GString *collectionA,
+ GString *cMapNameA);
+
+ // Parse a CMap from <str>. Sets the initial reference count to 1.
+ // Returns NULL on failure.
+ static CMap *parse(CMapCache *cache, GString *collectionA, Stream *str);
+
+ ~CMap();
+
+ void incRefCnt();
+ void decRefCnt();
+
+ // Return collection name (<registry>-<ordering>).
+ GString *getCollection() { return collection; }
+
+ // Return true if this CMap matches the specified <collectionA>, and
+ // <cMapNameA>.
+ GBool match(GString *collectionA, GString *cMapNameA);
+
+ // Return the CID corresponding to the character code starting at
+ // <s>, which contains <len> bytes. Sets *<c> to the char code, and
+ // *<nUsed> to the number of bytes used by the char code.
+ CID getCID(char *s, int len, CharCode *c, int *nUsed);
+
+ // Return the writing mode (0=horizontal, 1=vertical).
+ int getWMode() { return wMode; }
+
+private:
+
+ void parse2(CMapCache *cache, int (*getCharFunc)(void *), void *data);
+ CMap(GString *collectionA, GString *cMapNameA);
+ CMap(GString *collectionA, GString *cMapNameA, int wModeA);
+ void useCMap(CMapCache *cache, char *useName);
+ void useCMap(CMapCache *cache, Object *obj);
+ void copyVector(CMapVectorEntry *dest, CMapVectorEntry *src);
+ void addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID);
+ void freeCMapVector(CMapVectorEntry *vec);
+
+ GString *collection;
+ GString *cMapName;
+ GBool isIdent; // true if this CMap is an identity mapping,
+ // or is based on one (via usecmap)
+ int wMode; // writing mode (0=horizontal, 1=vertical)
+ CMapVectorEntry *vector; // vector for first byte (NULL for
+ // identity CMap)
+#if MULTITHREADED
+ GAtomicCounter refCnt;
+#else
+ int refCnt;
+#endif
+};
+
+//------------------------------------------------------------------------
+
+#define cMapCacheSize 4
+
+class CMapCache {
+public:
+
+ CMapCache();
+ ~CMapCache();
+
+ // Get the <cMapName> CMap for the specified character collection.
+ // Increments its reference count; there will be one reference for
+ // the cache plus one for the caller of this function. Returns NULL
+ // on failure.
+ CMap *getCMap(GString *collection, GString *cMapName);
+
+private:
+
+ CMap *cache[cMapCacheSize];
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc
new file mode 100644
index 00000000000..6ea8fa89ff4
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.cc
@@ -0,0 +1,1197 @@
+//========================================================================
+//
+// Catalog.cc
+//
+// Copyright 1996-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include <stddef.h>
+#include <limits.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "gfile.h"
+#include "GList.h"
+#include "Object.h"
+#include "CharTypes.h"
+#include "PDFDoc.h"
+#include "XRef.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Page.h"
+#include "Error.h"
+#include "Link.h"
+#include "AcroForm.h"
+#include "TextString.h"
+#include "Catalog.h"
+
+//------------------------------------------------------------------------
+// PageTreeNode
+//------------------------------------------------------------------------
+
+class PageTreeNode {
+public:
+
+ PageTreeNode(Ref refA, int countA, PageTreeNode *parentA);
+ ~PageTreeNode();
+
+ Ref ref;
+ int count;
+ PageTreeNode *parent;
+ GList *kids; // [PageTreeNode]
+ PageAttrs *attrs;
+};
+
+PageTreeNode::PageTreeNode(Ref refA, int countA, PageTreeNode *parentA) {
+ ref = refA;
+ count = countA;
+ parent = parentA;
+ kids = NULL;
+ attrs = NULL;
+}
+
+PageTreeNode::~PageTreeNode() {
+ delete attrs;
+ if (kids) {
+ deleteGList(kids, PageTreeNode);
+ }
+}
+
+//------------------------------------------------------------------------
+// EmbeddedFile
+//------------------------------------------------------------------------
+
+class EmbeddedFile {
+public:
+
+ EmbeddedFile(TextString *nameA, Object *streamRefA);
+ ~EmbeddedFile();
+
+ TextString *name;
+ Object streamRef;
+};
+
+EmbeddedFile::EmbeddedFile(TextString *nameA, Object *streamRefA) {
+ name = nameA;
+ streamRefA->copy(&streamRef);
+}
+
+EmbeddedFile::~EmbeddedFile() {
+ delete name;
+ streamRef.free();
+}
+
+//------------------------------------------------------------------------
+// PageLabelNode
+//------------------------------------------------------------------------
+
+class PageLabelNode {
+public:
+
+ PageLabelNode(int firstPageA, Dict *dict);
+ ~PageLabelNode();
+
+ int firstPage; // first page number covered by this node
+ int lastPage; // last page number covered by this node
+ TextString *prefix; // label prefix (may be empty)
+ int start; // value of the numeric portion of this
+ // label for the first page in the range
+ char style; // page label style
+};
+
+PageLabelNode::PageLabelNode(int firstPageA, Dict *dict) {
+ Object prefixObj, styleObj, startObj;
+
+ // convert page index to page number
+ firstPage = firstPageA + 1;
+
+ // lastPage will be filled in later
+ lastPage = -1;
+
+ if (dict->lookup("P", &prefixObj)->isString()) {
+ prefix = new TextString(prefixObj.getString());
+ } else {
+ prefix = new TextString();
+ }
+ prefixObj.free();
+
+ style = '\0';
+ if (dict->lookup("S", &styleObj)->isName()) {
+ if (strlen(styleObj.getName()) == 1) {
+ style = styleObj.getName()[0];
+ }
+ }
+ styleObj.free();
+
+ start = 1;
+ if (dict->lookup("St", &startObj)->isInt()) {
+ start = startObj.getInt();
+ }
+ startObj.free();
+}
+
+PageLabelNode::~PageLabelNode() {
+ delete prefix;
+}
+
+//------------------------------------------------------------------------
+// Catalog
+//------------------------------------------------------------------------
+
+Catalog::Catalog(PDFDoc *docA) {
+ Object catDict;
+ Object obj, obj2;
+
+ ok = gTrue;
+ doc = docA;
+ xref = doc->getXRef();
+ pageTree = NULL;
+ pages = NULL;
+ pageRefs = NULL;
+ numPages = 0;
+ baseURI = NULL;
+ form = NULL;
+ embeddedFiles = NULL;
+ pageLabels = NULL;
+#if MULTITHREADED
+ gInitMutex(&pageMutex);
+#endif
+
+ xref->getCatalog(&catDict);
+ if (!catDict.isDict()) {
+ error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})",
+ catDict.getTypeName());
+ goto err1;
+ }
+
+ // read page tree
+ if (!readPageTree(&catDict)) {
+ goto err1;
+ }
+
+ // read named destination dictionary
+ catDict.dictLookup("Dests", &dests);
+
+ // read root of named destination tree
+ if (catDict.dictLookup("Names", &obj)->isDict())
+ obj.dictLookup("Dests", &nameTree);
+ else
+ nameTree.initNull();
+ obj.free();
+
+ // read base URI
+ if (catDict.dictLookup("URI", &obj)->isDict()) {
+ if (obj.dictLookup("Base", &obj2)->isString()) {
+ baseURI = obj2.getString()->copy();
+ }
+ obj2.free();
+ }
+ obj.free();
+ if (!baseURI || baseURI->getLength() == 0) {
+ if (baseURI) {
+ delete baseURI;
+ }
+ if (doc->getFileName()) {
+ baseURI = makePathAbsolute(grabPath(doc->getFileName()->getCString()));
+ if (baseURI->getChar(0) == '/') {
+ baseURI->insert(0, "file://localhost");
+ } else {
+ baseURI->insert(0, "file://localhost/");
+ }
+ } else {
+ baseURI = new GString("file://localhost/");
+ }
+ }
+
+ // get the metadata stream
+ catDict.dictLookup("Metadata", &metadata);
+
+ // get the structure tree root
+ catDict.dictLookup("StructTreeRoot", &structTreeRoot);
+
+ // get the outline dictionary
+ catDict.dictLookup("Outlines", &outline);
+
+ // get the AcroForm dictionary
+ catDict.dictLookup("AcroForm", &acroForm);
+
+ // get the NeedsRendering flag
+ // NB: AcroForm::load() uses this value
+ needsRendering = catDict.dictLookup("NeedsRendering", &obj)->isBool() &&
+ obj.getBool();
+ obj.free();
+
+ // create the Form
+ // (if acroForm is a null object, this will still create an AcroForm
+ // if there are unattached Widget-type annots)
+ form = AcroForm::load(doc, this, &acroForm);
+
+ // get the OCProperties dictionary
+ catDict.dictLookup("OCProperties", &ocProperties);
+
+ // get the list of embedded files
+ readEmbeddedFileList(catDict.getDict());
+
+ // get the ViewerPreferences object
+ catDict.dictLookupNF("ViewerPreferences", &viewerPrefs);
+
+ if (catDict.dictLookup("PageLabels", &obj)->isDict()) {
+ readPageLabelTree(&obj);
+ }
+ obj.free();
+
+ catDict.free();
+ return;
+
+ err1:
+ catDict.free();
+ dests.initNull();
+ nameTree.initNull();
+ ok = gFalse;
+}
+
+Catalog::~Catalog() {
+ int i;
+
+ if (pageTree) {
+ delete pageTree;
+ }
+ if (pages) {
+ for (i = 0; i < numPages; ++i) {
+ if (pages[i]) {
+ delete pages[i];
+ }
+ }
+ gfree(pages);
+ gfree(pageRefs);
+ }
+#if MULTITHREADED
+ gDestroyMutex(&pageMutex);
+#endif
+ dests.free();
+ nameTree.free();
+ if (baseURI) {
+ delete baseURI;
+ }
+ metadata.free();
+ structTreeRoot.free();
+ outline.free();
+ acroForm.free();
+ if (form) {
+ delete form;
+ }
+ ocProperties.free();
+ if (embeddedFiles) {
+ deleteGList(embeddedFiles, EmbeddedFile);
+ }
+ if (pageLabels) {
+ deleteGList(pageLabels, PageLabelNode);
+ }
+ viewerPrefs.free();
+}
+
+Page *Catalog::getPage(int i) {
+ Page *page;
+
+#if MULTITHREADED
+ gLockMutex(&pageMutex);
+#endif
+ if (!pages[i-1]) {
+ loadPage(i);
+ }
+ page = pages[i-1];
+#if MULTITHREADED
+ gUnlockMutex(&pageMutex);
+#endif
+ return page;
+}
+
+Ref *Catalog::getPageRef(int i) {
+ Ref *pageRef;
+
+#if MULTITHREADED
+ gLockMutex(&pageMutex);
+#endif
+ if (!pages[i-1]) {
+ loadPage(i);
+ }
+ pageRef = &pageRefs[i-1];
+#if MULTITHREADED
+ gUnlockMutex(&pageMutex);
+#endif
+ return pageRef;
+}
+
+void Catalog::doneWithPage(int i) {
+#if MULTITHREADED
+ gLockMutex(&pageMutex);
+#endif
+ if (pages[i-1]) {
+ delete pages[i-1];
+ pages[i-1] = NULL;
+ }
+#if MULTITHREADED
+ gUnlockMutex(&pageMutex);
+#endif
+}
+
+GString *Catalog::readMetadata() {
+ GString *s;
+ Dict *dict;
+ Object obj;
+ char buf[4096];
+ int n;
+
+ if (!metadata.isStream()) {
+ return NULL;
+ }
+ dict = metadata.streamGetDict();
+ if (!dict->lookup("Subtype", &obj)->isName("XML")) {
+ error(errSyntaxWarning, -1, "Unknown Metadata type: '{0:s}'",
+ obj.isName() ? obj.getName() : "???");
+ }
+ obj.free();
+ s = new GString();
+ metadata.streamReset();
+ while ((n = metadata.streamGetBlock(buf, sizeof(buf))) > 0) {
+ s->append(buf, n);
+ }
+ metadata.streamClose();
+ return s;
+}
+
+int Catalog::findPage(int num, int gen) {
+ int i;
+
+#if MULTITHREADED
+ gLockMutex(&pageMutex);
+#endif
+ for (i = 0; i < numPages; ++i) {
+ if (!pages[i]) {
+ loadPage(i+1);
+ }
+ if (pageRefs[i].num == num && pageRefs[i].gen == gen) {
+#if MULTITHREADED
+ gUnlockMutex(&pageMutex);
+#endif
+ return i + 1;
+ }
+ }
+#if MULTITHREADED
+ gUnlockMutex(&pageMutex);
+#endif
+ return 0;
+}
+
+LinkDest *Catalog::findDest(GString *name) {
+ LinkDest *dest;
+ Object obj1, obj2;
+ GBool found;
+
+ // try named destination dictionary then name tree
+ found = gFalse;
+ if (dests.isDict()) {
+ if (!dests.dictLookup(name->getCString(), &obj1)->isNull()) {
+ found = gTrue;
+ } else {
+ obj1.free();
+ }
+ }
+ if (!found && nameTree.isDict()) {
+ if (!findDestInTree(&nameTree, name, &obj1)->isNull()) {
+ found = gTrue;
+ } else {
+ obj1.free();
+ }
+ }
+ if (!found) {
+ return NULL;
+ }
+
+ // construct LinkDest
+ dest = NULL;
+ if (obj1.isArray()) {
+ dest = new LinkDest(obj1.getArray());
+ } else if (obj1.isDict()) {
+ if (obj1.dictLookup("D", &obj2)->isArray()) {
+ dest = new LinkDest(obj2.getArray());
+ } else {
+ error(errSyntaxWarning, -1, "Bad named destination value");
+ }
+ obj2.free();
+ } else {
+ error(errSyntaxWarning, -1, "Bad named destination value");
+ }
+ obj1.free();
+ if (dest && !dest->isOk()) {
+ delete dest;
+ dest = NULL;
+ }
+
+ return dest;
+}
+
+Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
+ Object names, name1;
+ Object kids, kid, limits, low, high;
+ GBool done, found;
+ int cmp, i;
+
+ // leaf node
+ if (tree->dictLookup("Names", &names)->isArray()) {
+ done = found = gFalse;
+ for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
+ if (names.arrayGet(i, &name1)->isString()) {
+ cmp = name->cmp(name1.getString());
+ if (cmp == 0) {
+ names.arrayGet(i+1, obj);
+ found = gTrue;
+ done = gTrue;
+ } else if (cmp < 0) {
+ done = gTrue;
+ }
+ }
+ name1.free();
+ }
+ names.free();
+ if (!found) {
+ obj->initNull();
+ }
+ return obj;
+ }
+ names.free();
+
+ // root or intermediate node
+ done = gFalse;
+ if (tree->dictLookup("Kids", &kids)->isArray()) {
+ for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
+ if (kids.arrayGet(i, &kid)->isDict()) {
+ if (kid.dictLookup("Limits", &limits)->isArray()) {
+ if (limits.arrayGet(0, &low)->isString() &&
+ name->cmp(low.getString()) >= 0) {
+ if (limits.arrayGet(1, &high)->isString() &&
+ name->cmp(high.getString()) <= 0) {
+ findDestInTree(&kid, name, obj);
+ done = gTrue;
+ }
+ high.free();
+ }
+ low.free();
+ }
+ limits.free();
+ }
+ kid.free();
+ }
+ }
+ kids.free();
+
+ // name was outside of ranges of all kids
+ if (!done) {
+ obj->initNull();
+ }
+
+ return obj;
+}
+
+GBool Catalog::readPageTree(Object *catDict) {
+ Object topPagesRef, topPagesObj, countObj;
+ int i;
+
+ if (!catDict->dictLookupNF("Pages", &topPagesRef)->isRef()) {
+ error(errSyntaxError, -1, "Top-level pages reference is wrong type ({0:s})",
+ topPagesRef.getTypeName());
+ topPagesRef.free();
+ return gFalse;
+ }
+ if (!topPagesRef.fetch(xref, &topPagesObj)->isDict()) {
+ error(errSyntaxError, -1, "Top-level pages object is wrong type ({0:s})",
+ topPagesObj.getTypeName());
+ topPagesObj.free();
+ topPagesRef.free();
+ return gFalse;
+ }
+ if (topPagesObj.dictLookup("Count", &countObj)->isInt()) {
+ numPages = countObj.getInt();
+ if (numPages == 0 || numPages > 50000) {
+ // 1. Acrobat apparently scans the page tree if it sees a zero
+ // count.
+ // 2. Absurdly large page counts result in very slow loading,
+ // because other code tries to fetch pages 1 through n.
+ // In both cases: ignore the given page count and scan the tree
+ // instead.
+ numPages = countPageTree(&topPagesObj);
+ }
+ } else {
+ // assume we got a Page node instead of a Pages node
+ numPages = 1;
+ }
+ countObj.free();
+ if (numPages < 0) {
+ error(errSyntaxError, -1, "Invalid page count");
+ topPagesObj.free();
+ topPagesRef.free();
+ numPages = 0;
+ return gFalse;
+ }
+ pageTree = new PageTreeNode(topPagesRef.getRef(), numPages, NULL);
+ topPagesObj.free();
+ topPagesRef.free();
+ pages = (Page **)greallocn(pages, numPages, sizeof(Page *));
+ pageRefs = (Ref *)greallocn(pageRefs, numPages, sizeof(Ref));
+ for (i = 0; i < numPages; ++i) {
+ pages[i] = NULL;
+ pageRefs[i].num = -1;
+ pageRefs[i].gen = -1;
+ }
+ return gTrue;
+}
+
+int Catalog::countPageTree(Object *pagesObj) {
+ Object kids, kid;
+ int n, n2, i;
+
+ if (!pagesObj->isDict()) {
+ return 0;
+ }
+ if (pagesObj->dictLookup("Kids", &kids)->isArray()) {
+ n = 0;
+ for (i = 0; i < kids.arrayGetLength(); ++i) {
+ kids.arrayGet(i, &kid);
+ n2 = countPageTree(&kid);
+ if (n2 < INT_MAX - n) {
+ n += n2;
+ } else {
+ error(errSyntaxError, -1, "Page tree contains too many pages");
+ n = INT_MAX;
+ }
+ kid.free();
+ }
+ } else {
+ n = 1;
+ }
+ kids.free();
+ return n;
+}
+
+void Catalog::loadPage(int pg) {
+ loadPage2(pg, pg - 1, pageTree);
+}
+
+void Catalog::loadPage2(int pg, int relPg, PageTreeNode *node) {
+ Object pageRefObj, pageObj, kidsObj, kidRefObj, kidObj, countObj;
+ PageTreeNode *kidNode, *p;
+ PageAttrs *attrs;
+ int count, i;
+
+ if (relPg >= node->count) {
+ error(errSyntaxError, -1, "Internal error in page tree");
+ pages[pg-1] = new Page(doc, pg);
+ return;
+ }
+
+ // if this node has not been filled in yet, it's either a leaf node
+ // or an unread internal node
+ if (!node->kids) {
+
+ // check for a loop in the page tree
+ for (p = node->parent; p; p = p->parent) {
+ if (node->ref.num == p->ref.num && node->ref.gen == p->ref.gen) {
+ error(errSyntaxError, -1, "Loop in Pages tree");
+ pages[pg-1] = new Page(doc, pg);
+ return;
+ }
+ }
+
+ // fetch the Page/Pages object
+ pageRefObj.initRef(node->ref.num, node->ref.gen);
+ if (!pageRefObj.fetch(xref, &pageObj)->isDict()) {
+ error(errSyntaxError, -1, "Page tree object is wrong type ({0:s})",
+ pageObj.getTypeName());
+ pageObj.free();
+ pageRefObj.free();
+ pages[pg-1] = new Page(doc, pg);
+ return;
+ }
+
+ // merge the PageAttrs
+ attrs = new PageAttrs(node->parent ? node->parent->attrs
+ : (PageAttrs *)NULL,
+ pageObj.getDict());
+
+ // if "Kids" exists, it's an internal node
+ if (pageObj.dictLookup("Kids", &kidsObj)->isArray()) {
+
+ // save the PageAttrs
+ node->attrs = attrs;
+
+ // read the kids
+ node->kids = new GList();
+ for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
+ if (kidsObj.arrayGetNF(i, &kidRefObj)->isRef()) {
+ if (kidRefObj.fetch(xref, &kidObj)->isDict()) {
+ if (kidObj.dictLookup("Count", &countObj)->isInt()) {
+ count = countObj.getInt();
+ } else {
+ count = 1;
+ }
+ countObj.free();
+ node->kids->append(new PageTreeNode(kidRefObj.getRef(), count,
+ node));
+ } else {
+ error(errSyntaxError, -1, "Page tree object is wrong type ({0:s})",
+ kidObj.getTypeName());
+ }
+ kidObj.free();
+ } else {
+ error(errSyntaxError, -1,
+ "Page tree reference is wrong type ({0:s})",
+ kidRefObj.getTypeName());
+ }
+ kidRefObj.free();
+ }
+
+ } else {
+
+ // create the Page object
+ pageRefs[pg-1] = node->ref;
+ pages[pg-1] = new Page(doc, pg, pageObj.getDict(), attrs);
+ if (!pages[pg-1]->isOk()) {
+ delete pages[pg-1];
+ pages[pg-1] = new Page(doc, pg);
+ }
+
+ }
+
+ kidsObj.free();
+ pageObj.free();
+ pageRefObj.free();
+ }
+
+ // recursively descend the tree
+ if (node->kids) {
+ for (i = 0; i < node->kids->getLength(); ++i) {
+ kidNode = (PageTreeNode *)node->kids->get(i);
+ if (relPg < kidNode->count) {
+ loadPage2(pg, relPg, kidNode);
+ break;
+ }
+ relPg -= kidNode->count;
+ }
+
+ // this will only happen if the page tree is invalid
+ // (i.e., parent count > sum of children counts)
+ if (i == node->kids->getLength()) {
+ error(errSyntaxError, -1, "Invalid page count in page tree");
+ pages[pg-1] = new Page(doc, pg);
+ }
+ }
+}
+
+Object *Catalog::getDestOutputProfile(Object *destOutProf) {
+ Object catDict, intents, intent, subtype;
+ int i;
+
+ if (!xref->getCatalog(&catDict)->isDict()) {
+ goto err1;
+ }
+ if (!catDict.dictLookup("OutputIntents", &intents)->isArray()) {
+ goto err2;
+ }
+ for (i = 0; i < intents.arrayGetLength(); ++i) {
+ intents.arrayGet(i, &intent);
+ if (!intent.isDict()) {
+ intent.free();
+ continue;
+ }
+ if (!intent.dictLookup("S", &subtype)->isName("GTS_PDFX")) {
+ subtype.free();
+ intent.free();
+ continue;
+ }
+ subtype.free();
+ if (!intent.dictLookup("DestOutputProfile", destOutProf)->isStream()) {
+ destOutProf->free();
+ intent.free();
+ goto err2;
+ }
+ intent.free();
+ intents.free();
+ catDict.free();
+ return destOutProf;
+ }
+
+ err2:
+ intents.free();
+ err1:
+ catDict.free();
+ return NULL;
+}
+
+void Catalog::readEmbeddedFileList(Dict *catDict) {
+ Object obj1, obj2;
+ char *touchedObjs;
+
+ // read the embedded file name tree
+ if (catDict->lookup("Names", &obj1)->isDict()) {
+ if (obj1.dictLookup("EmbeddedFiles", &obj2)->isDict()) {
+ readEmbeddedFileTree(&obj2);
+ }
+ obj2.free();
+ }
+ obj1.free();
+
+ // look for file attachment annotations
+ touchedObjs = (char *)gmalloc(xref->getNumObjects());
+ memset(touchedObjs, 0, xref->getNumObjects());
+ readFileAttachmentAnnots(catDict->lookupNF("Pages", &obj1), touchedObjs);
+ obj1.free();
+ gfree(touchedObjs);
+}
+
+void Catalog::readEmbeddedFileTree(Object *node) {
+ Object kidsObj, kidObj;
+ Object namesObj, nameObj, fileSpecObj;
+ int i;
+
+ if (node->dictLookup("Kids", &kidsObj)->isArray()) {
+ for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
+ if (kidsObj.arrayGet(i, &kidObj)->isDict()) {
+ readEmbeddedFileTree(&kidObj);
+ }
+ kidObj.free();
+ }
+ } else {
+ if (node->dictLookup("Names", &namesObj)->isArray()) {
+ for (i = 0; i+1 < namesObj.arrayGetLength(); ++i) {
+ namesObj.arrayGet(i, &nameObj);
+ namesObj.arrayGet(i+1, &fileSpecObj);
+ readEmbeddedFile(&fileSpecObj, &nameObj);
+ nameObj.free();
+ fileSpecObj.free();
+ }
+ }
+ namesObj.free();
+ }
+ kidsObj.free();
+}
+
+void Catalog::readFileAttachmentAnnots(Object *pageNodeRef,
+ char *touchedObjs) {
+ Object pageNode, kids, kid, annots, annot, subtype, fileSpec, contents;
+ int i;
+
+ // check for an invalid object reference (e.g., in a damaged PDF file)
+ if (pageNodeRef->getRefNum() < 0 ||
+ pageNodeRef->getRefNum() >= xref->getNumObjects()) {
+ return;
+ }
+
+ // check for a page tree loop
+ if (pageNodeRef->isRef()) {
+ if (touchedObjs[pageNodeRef->getRefNum()]) {
+ return;
+ }
+ touchedObjs[pageNodeRef->getRefNum()] = 1;
+ xref->fetch(pageNodeRef->getRefNum(), pageNodeRef->getRefGen(), &pageNode);
+ } else {
+ pageNodeRef->copy(&pageNode);
+ }
+
+ if (pageNode.isDict()) {
+ if (pageNode.dictLookup("Kids", &kids)->isArray()) {
+ for (i = 0; i < kids.arrayGetLength(); ++i) {
+ readFileAttachmentAnnots(kids.arrayGetNF(i, &kid), touchedObjs);
+ kid.free();
+ }
+ } else {
+ if (pageNode.dictLookup("Annots", &annots)->isArray()) {
+ for (i = 0; i < annots.arrayGetLength(); ++i) {
+ if (annots.arrayGet(i, &annot)->isDict()) {
+ if (annot.dictLookup("Subtype", &subtype)
+ ->isName("FileAttachment")) {
+ if (annot.dictLookup("FS", &fileSpec)) {
+ readEmbeddedFile(&fileSpec,
+ annot.dictLookup("Contents", &contents));
+ contents.free();
+ }
+ fileSpec.free();
+ }
+ subtype.free();
+ }
+ annot.free();
+ }
+ }
+ annots.free();
+ }
+ kids.free();
+ }
+
+ pageNode.free();
+}
+
+void Catalog::readEmbeddedFile(Object *fileSpec, Object *name1) {
+ Object name2, efObj, streamObj;
+ GString *s;
+ TextString *name;
+
+ if (fileSpec->isDict()) {
+ if (fileSpec->dictLookup("UF", &name2)->isString()) {
+ name = new TextString(name2.getString());
+ } else {
+ name2.free();
+ if (fileSpec->dictLookup("F", &name2)->isString()) {
+ name = new TextString(name2.getString());
+ } else if (name1 && name1->isString()) {
+ name = new TextString(name1->getString());
+ } else {
+ s = new GString("?");
+ name = new TextString(s);
+ delete s;
+ }
+ }
+ name2.free();
+ if (fileSpec->dictLookup("EF", &efObj)->isDict()) {
+ if (efObj.dictLookupNF("F", &streamObj)->isRef()) {
+ if (!embeddedFiles) {
+ embeddedFiles = new GList();
+ }
+ embeddedFiles->append(new EmbeddedFile(name, &streamObj));
+ } else {
+ delete name;
+ }
+ streamObj.free();
+ } else {
+ delete name;
+ }
+ efObj.free();
+ }
+}
+
+int Catalog::getNumEmbeddedFiles() {
+ return embeddedFiles ? embeddedFiles->getLength() : 0;
+}
+
+Unicode *Catalog::getEmbeddedFileName(int idx) {
+ return ((EmbeddedFile *)embeddedFiles->get(idx))->name->getUnicode();
+}
+
+int Catalog::getEmbeddedFileNameLength(int idx) {
+ return ((EmbeddedFile *)embeddedFiles->get(idx))->name->getLength();
+}
+
+Object *Catalog::getEmbeddedFileStreamRef(int idx) {
+ return &((EmbeddedFile *)embeddedFiles->get(idx))->streamRef;
+}
+
+Object *Catalog::getEmbeddedFileStreamObj(int idx, Object *strObj) {
+ ((EmbeddedFile *)embeddedFiles->get(idx))->streamRef.fetch(xref, strObj);
+ if (!strObj->isStream()) {
+ strObj->free();
+ return NULL;
+ }
+ return strObj;
+}
+
+void Catalog::readPageLabelTree(Object *root) {
+ PageLabelNode *label0, *label1;
+ int i;
+
+ pageLabels = new GList();
+ readPageLabelTree2(root);
+
+ if (pageLabels->getLength() == 0) {
+ deleteGList(pageLabels, PageLabelNode);
+ pageLabels = NULL;
+ return;
+ }
+
+ // set lastPage in each node
+ label0 = (PageLabelNode *)pageLabels->get(0);
+ for (i = 1; i < pageLabels->getLength(); ++i) {
+ label1 = (PageLabelNode *)pageLabels->get(i);
+ label0->lastPage = label1->firstPage - 1;
+ label0 = label1;
+ }
+ label0->lastPage = numPages;
+}
+
+void Catalog::readPageLabelTree2(Object *node) {
+ Object nums, num, labelObj, kids, kid;
+ int i;
+
+ if (!node->isDict()) {
+ return;
+ }
+
+ if (node->dictLookup("Nums", &nums)->isArray()) {
+ for (i = 0; i < nums.arrayGetLength() - 1; i += 2) {
+ if (nums.arrayGet(i, &num)->isInt()) {
+ if (nums.arrayGet(i+1, &labelObj)->isDict()) {
+ pageLabels->append(new PageLabelNode(num.getInt(),
+ labelObj.getDict()));
+ }
+ labelObj.free();
+ }
+ num.free();
+ }
+ }
+ nums.free();
+
+ if (node->dictLookup("Kids", &kids)->isArray()) {
+ for (i = 0; i < kids.arrayGetLength(); ++i) {
+ kids.arrayGet(i, &kid);
+ readPageLabelTree2(&kid);
+ kid.free();
+ }
+ }
+ kids.free();
+}
+
+TextString *Catalog::getPageLabel(int pageNum) {
+ PageLabelNode *label;
+ TextString *ts;
+ int pageRangeNum;
+ GString *suffix;
+
+ if (!pageLabels || !(label = findPageLabel(pageNum))) {
+ return NULL;
+ }
+
+ ts = new TextString(label->prefix);
+
+ pageRangeNum = label->start + (pageNum - label->firstPage);
+
+ suffix = NULL;
+ if (label->style == 'D') {
+ suffix = GString::format("{0:d}", pageRangeNum);
+ } else if (label->style == 'R') {
+ suffix = makeRomanNumeral(pageRangeNum, gTrue);
+ } else if (label->style == 'r') {
+ suffix = makeRomanNumeral(pageRangeNum, gFalse);
+ } else if (label->style == 'A') {
+ suffix = makeLetterLabel(pageRangeNum, gTrue);
+ } else if (label->style == 'a') {
+ suffix = makeLetterLabel(pageRangeNum, gFalse);
+ }
+ if (suffix) {
+ ts->append(suffix);
+ delete suffix;
+ }
+
+ return ts;
+}
+
+PageLabelNode *Catalog::findPageLabel(int pageNum) {
+ PageLabelNode *label;
+ int i;
+
+ //~ this could use a binary search
+ for (i = 0; i < pageLabels->getLength(); ++i) {
+ label = (PageLabelNode *)pageLabels->get(i);
+ if (pageNum >= label->firstPage && pageNum <= label->lastPage) {
+ return label;
+ }
+ }
+ return NULL;
+}
+
+GString *Catalog::makeRomanNumeral(int num, GBool uppercase) {
+ GString *s;
+
+ s = new GString();
+ while (num >= 1000) {
+ s->append(uppercase ? 'M' : 'm');
+ num -= 1000;
+ }
+ if (num >= 900) {
+ s->append(uppercase ? "CM" : "cm");
+ num -= 900;
+ } else if (num >= 500) {
+ s->append(uppercase ? 'D' : 'd');
+ num -= 500;
+ } else if (num >= 400) {
+ s->append(uppercase ? "CD" : "cd");
+ num -= 400;
+ }
+ while (num >= 100) {
+ s->append(uppercase ? 'C' : 'c');
+ num -= 100;
+ }
+ if (num >= 90) {
+ s->append(uppercase ? "XC" : "xc");
+ num -= 90;
+ } else if (num >= 50) {
+ s->append(uppercase ? 'L' : 'l');
+ num -= 50;
+ } else if (num >= 40) {
+ s->append(uppercase ? "XL" : "xl");
+ num -= 40;
+ }
+ while (num >= 10) {
+ s->append(uppercase ? 'X' : 'x');
+ num -= 10;
+ }
+ if (num >= 9) {
+ s->append(uppercase ? "IX" : "ix");
+ num -= 9;
+ } else if (num >= 5) {
+ s->append(uppercase ? 'V' : 'v');
+ num -= 5;
+ } else if (num >= 4) {
+ s->append(uppercase ? "IV" : "iv");
+ num -= 4;
+ }
+ while (num >= 1) {
+ s->append(uppercase ? 'I' : 'i');
+ num -= 1;
+ }
+ return s;
+}
+
+GString *Catalog::makeLetterLabel(int num, GBool uppercase) {
+ GString *s;
+ int m, n, i;
+
+ m = (num - 1) / 26 + 1;
+ n = (num - 1) % 26;
+ s = new GString();
+ for (i = 0; i < m; ++i) {
+ s->append((char)((uppercase ? 'A' : 'a') + n));
+ }
+ return s;
+}
+
+int Catalog::getPageNumFromPageLabel(TextString *pageLabel) {
+ PageLabelNode *label;
+ int pageNum, prefixLength, i, n;
+
+ if (!pageLabels) {
+ return -1;
+ }
+ for (i = 0; i < pageLabels->getLength(); ++i) {
+ label = (PageLabelNode *)pageLabels->get(i);
+ prefixLength = label->prefix->getLength();
+ if (pageLabel->getLength() < prefixLength ||
+ memcmp(pageLabel->getUnicode(), label->prefix->getUnicode(),
+ prefixLength * sizeof(Unicode))) {
+ continue;
+ }
+ if (label->style == '\0' && pageLabel->getLength() == prefixLength) {
+ return label->firstPage;
+ }
+ if (!convertPageLabelToInt(pageLabel, prefixLength, label->style, &n)) {
+ continue;
+ }
+ if (n < label->start) {
+ continue;
+ }
+ pageNum = label->firstPage + n - label->start;
+ if (pageNum <= label->lastPage) {
+ return pageNum;
+ }
+ }
+ return -1;
+}
+
+// Attempts to convert pageLabel[prefixLength .. end] to an integer,
+// following the specified page label style. If successful, sets *n
+// and returns true; else returns false.
+GBool Catalog::convertPageLabelToInt(TextString *pageLabel, int prefixLength,
+ char style, int *n) {
+ Unicode *u;
+ Unicode delta;
+ int len, i;
+
+ len = pageLabel->getLength();
+ if (len <= prefixLength) {
+ return gFalse;
+ }
+ u = pageLabel->getUnicode();
+ if (style == 'D') {
+ *n = 0;
+ for (i = prefixLength; i < len; ++i) {
+ if (u[i] < (Unicode)'0' || u[i] > (Unicode)'9') {
+ return gFalse;
+ }
+ *n = *n * 10 + (u[i] - (Unicode)'0');
+ }
+ return gTrue;
+ } else if (style == 'R' || style == 'r') {
+ delta = style - 'R';
+ *n = 0;
+ i = prefixLength;
+ while (i < len && u[i] == (Unicode)'M' + delta) {
+ *n += 1000;
+ ++i;
+ }
+ if (i+1 < len && u[i] == (Unicode)'C' + delta &&
+ u[i+1] == (Unicode)'M' + delta) {
+ *n += 900;
+ i += 2;
+ } else if (i < len && u[i] == (Unicode)'D' + delta) {
+ *n += 500;
+ ++i;
+ } else if (i+1 < len && u[i] == (Unicode)'C' + delta &&
+ u[i+1] == (Unicode)'D' + delta) {
+ *n += 400;
+ i += 2;
+ }
+ while (i < len && u[i] == (Unicode)'C' + delta) {
+ *n += 100;
+ ++i;
+ }
+ if (i+1 < len && u[i] == (Unicode)'X' + delta &&
+ u[i+1] == (Unicode)'C' + delta) {
+ *n += 90;
+ i += 2;
+ } else if (i < len && u[i] == (Unicode)'L' + delta) {
+ *n += 50;
+ ++i;
+ } else if (i+1 < len && u[i] == (Unicode)'X' + delta &&
+ u[i+1] == (Unicode)'L' + delta) {
+ *n += 40;
+ i += 2;
+ }
+ while (i < len && u[i] == (Unicode)'X' + delta) {
+ *n += 10;
+ ++i;
+ }
+ if (i+1 < len && u[i] == (Unicode)'I' + delta &&
+ u[i+1] == (Unicode)'X' + delta) {
+ *n += 9;
+ i += 2;
+ } else if (i < len && u[i] == (Unicode)'V' + delta) {
+ *n += 5;
+ ++i;
+ } else if (i+1 < len && u[i] == (Unicode)'I' + delta &&
+ u[i+1] == (Unicode)'V' + delta) {
+ *n += 4;
+ i += 2;
+ }
+ while (i < len && u[i] == (Unicode)'I' + delta) {
+ *n += 1;
+ ++i;
+ }
+ return i == len;
+ } else if (style == 'A' || style == 'a') {
+ if (u[prefixLength] < (Unicode)style ||
+ u[prefixLength] > (Unicode)style + 25) {
+ return gFalse;
+ }
+ for (i = prefixLength + 1; i < len; ++i) {
+ if (u[i] != u[prefixLength]) {
+ return gFalse;
+ }
+ }
+ *n = (len - prefixLength - 1) * 26 + (u[prefixLength] - (Unicode)style) + 1;
+ return gTrue;
+ }
+ return gFalse;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h
new file mode 100644
index 00000000000..0acdd2d1574
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Catalog.h
@@ -0,0 +1,166 @@
+//========================================================================
+//
+// Catalog.h
+//
+// Copyright 1996-2007 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CATALOG_H
+#define CATALOG_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+#include "CharTypes.h"
+
+class GList;
+class PDFDoc;
+class XRef;
+class Object;
+class Page;
+class PageAttrs;
+struct Ref;
+class LinkDest;
+class PageTreeNode;
+class PageLabelNode;
+class AcroForm;
+class TextString;
+
+//------------------------------------------------------------------------
+// Catalog
+//------------------------------------------------------------------------
+
+class Catalog {
+public:
+
+ // Constructor.
+ Catalog(PDFDoc *docA);
+
+ // Destructor.
+ ~Catalog();
+
+ // Is catalog valid?
+ GBool isOk() { return ok; }
+
+ // Get number of pages.
+ int getNumPages() { return numPages; }
+
+ // Get a page.
+ Page *getPage(int i);
+
+ // Get the reference for a page object.
+ Ref *getPageRef(int i);
+
+ // Remove a page from the catalog. (It can be reloaded later by
+ // calling getPage).
+ void doneWithPage(int i);
+
+ // Return base URI, or NULL if none.
+ GString *getBaseURI() { return baseURI; }
+
+ // Return the contents of the metadata stream, or NULL if there is
+ // no metadata.
+ GString *readMetadata();
+
+ // Return the structure tree root object.
+ Object *getStructTreeRoot() { return &structTreeRoot; }
+
+ // Find a page, given its object ID. Returns page number, or 0 if
+ // not found.
+ int findPage(int num, int gen);
+
+ // Find a named destination. Returns the link destination, or
+ // NULL if <name> is not a destination.
+ LinkDest *findDest(GString *name);
+
+ Object *getDests() { return &dests; }
+
+ Object *getNameTree() { return &nameTree; }
+
+ Object *getOutline() { return &outline; }
+
+ Object *getAcroForm() { return &acroForm; }
+
+ AcroForm *getForm() { return form; }
+
+ GBool getNeedsRendering() { return needsRendering; }
+
+ Object *getOCProperties() { return &ocProperties; }
+
+ // Return the DestOutputProfile stream, or NULL if there isn't one.
+ Object *getDestOutputProfile(Object *destOutProf);
+
+ // Get the list of embedded files.
+ int getNumEmbeddedFiles();
+ Unicode *getEmbeddedFileName(int idx);
+ int getEmbeddedFileNameLength(int idx);
+ Object *getEmbeddedFileStreamRef(int idx);
+ Object *getEmbeddedFileStreamObj(int idx, Object *strObj);
+
+ // Return true if the document has page labels.
+ GBool hasPageLabels() { return pageLabels != NULL; }
+
+ // Get the page label for page number [pageNum]. Returns NULL if
+ // the PDF file doesn't have page labels.
+ TextString *getPageLabel(int pageNum);
+
+ // Returns the page number corresponding to [pageLabel]. Returns -1
+ // if there is no matching page label, or if the document doesn't
+ // have page labels.
+ int getPageNumFromPageLabel(TextString *pageLabel);
+
+ Object *getViewerPreferences() { return &viewerPrefs; }
+
+private:
+
+ PDFDoc *doc;
+ XRef *xref; // the xref table for this PDF file
+ PageTreeNode *pageTree; // the page tree
+ Page **pages; // array of pages
+ Ref *pageRefs; // object ID for each page
+#if MULTITHREADED
+ GMutex pageMutex;
+#endif
+ int numPages; // number of pages
+ Object dests; // named destination dictionary
+ Object nameTree; // name tree
+ GString *baseURI; // base URI for URI-type links
+ Object metadata; // metadata stream
+ Object structTreeRoot; // structure tree root dictionary
+ Object outline; // outline dictionary
+ Object acroForm; // AcroForm dictionary
+ GBool needsRendering; // NeedsRendering flag
+ AcroForm *form; // parsed form
+ Object ocProperties; // OCProperties dictionary
+ GList *embeddedFiles; // embedded file list [EmbeddedFile]
+ GList *pageLabels; // page labels [PageLabelNode]
+ Object viewerPrefs; // ViewerPreferences object
+ GBool ok; // true if catalog is valid
+
+ Object *findDestInTree(Object *tree, GString *name, Object *obj);
+ GBool readPageTree(Object *catDict);
+ int countPageTree(Object *pagesObj);
+ void loadPage(int pg);
+ void loadPage2(int pg, int relPg, PageTreeNode *node);
+ void readEmbeddedFileList(Dict *catDict);
+ void readEmbeddedFileTree(Object *node);
+ void readFileAttachmentAnnots(Object *pageNodeRef,
+ char *touchedObjs);
+ void readEmbeddedFile(Object *fileSpec, Object *name1);
+ void readPageLabelTree(Object *root);
+ void readPageLabelTree2(Object *node);
+ PageLabelNode *findPageLabel(int pageNum);
+ GString *makeRomanNumeral(int num, GBool uppercase);
+ GString *makeLetterLabel(int num, GBool uppercase);
+ GBool convertPageLabelToInt(TextString *pageLabel, int prefixLength,
+ char style, int *n);
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc
new file mode 100644
index 00000000000..5eec7a9fc6c
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.cc
@@ -0,0 +1,787 @@
+//========================================================================
+//
+// CharCodeToUnicode.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "gfile.h"
+#include "GString.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "PSTokenizer.h"
+#include "CharCodeToUnicode.h"
+
+//------------------------------------------------------------------------
+
+#define maxUnicodeString 8
+
+struct CharCodeToUnicodeString {
+ CharCode c;
+ Unicode u[maxUnicodeString];
+ int len;
+};
+
+//------------------------------------------------------------------------
+
+struct GStringIndex {
+ GString *s;
+ int i;
+};
+
+static int getCharFromGString(void *data) {
+ GStringIndex *idx = (GStringIndex *)data;
+ if (idx->i >= idx->s->getLength()) {
+ return EOF;
+ }
+ return idx->s->getChar(idx->i++) & 0xff;
+}
+
+static int getCharFromFile(void *data) {
+ return fgetc((FILE *)data);
+}
+
+//------------------------------------------------------------------------
+
+static int hexCharVals[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2x
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 3x
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5x
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 8x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 9x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ax
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Bx
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Cx
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Dx
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ex
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // Fx
+};
+
+// Parse a <len>-byte hex string <s> into *<val>. Returns false on
+// error.
+static GBool parseHex(char *s, int len, Guint *val) {
+ int i, x;
+
+ *val = 0;
+ for (i = 0; i < len; ++i) {
+ x = hexCharVals[s[i] & 0xff];
+ if (x < 0) {
+ return gFalse;
+ }
+ *val = (*val << 4) + x;
+ }
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+
+CharCodeToUnicode *CharCodeToUnicode::makeIdentityMapping() {
+ return new CharCodeToUnicode();
+}
+
+CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *fileName,
+ GString *collection) {
+ FILE *f;
+ Unicode *mapA;
+ CharCode size, mapLenA;
+ char buf[64];
+ Unicode u;
+ CharCodeToUnicode *ctu;
+
+ if (!(f = openFile(fileName->getCString(), "r"))) {
+ error(errSyntaxError, -1, "Couldn't open cidToUnicode file '{0:t}'",
+ fileName);
+ return NULL;
+ }
+
+ size = 32768;
+ mapA = (Unicode *)gmallocn(size, sizeof(Unicode));
+ mapLenA = 0;
+
+ while (getLine(buf, sizeof(buf), f)) {
+ if (mapLenA == size) {
+ size *= 2;
+ mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode));
+ }
+ if (sscanf(buf, "%x", &u) == 1) {
+ mapA[mapLenA] = u;
+ } else {
+ error(errSyntaxWarning, -1,
+ "Bad line ({0:d}) in cidToUnicode file '{1:t}'",
+ (int)(mapLenA + 1), fileName);
+ mapA[mapLenA] = 0;
+ }
+ ++mapLenA;
+ }
+ fclose(f);
+
+ ctu = new CharCodeToUnicode(collection->copy(), mapA, mapLenA, gTrue,
+ NULL, 0, 0);
+ gfree(mapA);
+ return ctu;
+}
+
+CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(
+ GString *fileName) {
+ FILE *f;
+ Unicode *mapA;
+ CharCodeToUnicodeString *sMapA;
+ CharCode size, oldSize, len, sMapSizeA, sMapLenA;
+ char buf[256];
+ char *tok;
+ Unicode u0;
+ Unicode uBuf[maxUnicodeString];
+ CharCodeToUnicode *ctu;
+ int line, n, i;
+
+ if (!(f = openFile(fileName->getCString(), "r"))) {
+ error(errSyntaxError, -1, "Couldn't open unicodeToUnicode file '{0:t}'",
+ fileName);
+ return NULL;
+ }
+
+ size = 4096;
+ mapA = (Unicode *)gmallocn(size, sizeof(Unicode));
+ memset(mapA, 0, size * sizeof(Unicode));
+ len = 0;
+ sMapA = NULL;
+ sMapSizeA = sMapLenA = 0;
+
+ line = 0;
+ while (getLine(buf, sizeof(buf), f)) {
+ ++line;
+ if (!(tok = strtok(buf, " \t\r\n")) ||
+ !parseHex(tok, (int)strlen(tok), &u0)) {
+ error(errSyntaxWarning, -1,
+ "Bad line ({0:d}) in unicodeToUnicode file '{1:t}'",
+ line, fileName);
+ continue;
+ }
+ n = 0;
+ while (n < maxUnicodeString) {
+ if (!(tok = strtok(NULL, " \t\r\n"))) {
+ break;
+ }
+ if (!parseHex(tok, (int)strlen(tok), &uBuf[n])) {
+ error(errSyntaxWarning, -1,
+ "Bad line ({0:d}) in unicodeToUnicode file '{1:t}'",
+ line, fileName);
+ break;
+ }
+ ++n;
+ }
+ if (n < 1) {
+ error(errSyntaxWarning, -1,
+ "Bad line ({0:d}) in unicodeToUnicode file '{1:t}'",
+ line, fileName);
+ continue;
+ }
+ if (u0 >= size) {
+ oldSize = size;
+ while (u0 >= size) {
+ size *= 2;
+ }
+ mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode));
+ memset(mapA + oldSize, 0, (size - oldSize) * sizeof(Unicode));
+ }
+ if (n == 1) {
+ mapA[u0] = uBuf[0];
+ } else {
+ mapA[u0] = 0;
+ if (sMapLenA == sMapSizeA) {
+ sMapSizeA += 16;
+ sMapA = (CharCodeToUnicodeString *)
+ greallocn(sMapA, sMapSizeA, sizeof(CharCodeToUnicodeString));
+ }
+ sMapA[sMapLenA].c = u0;
+ for (i = 0; i < n; ++i) {
+ sMapA[sMapLenA].u[i] = uBuf[i];
+ }
+ sMapA[sMapLenA].len = n;
+ ++sMapLenA;
+ }
+ if (u0 >= len) {
+ len = u0 + 1;
+ }
+ }
+ fclose(f);
+
+ ctu = new CharCodeToUnicode(fileName->copy(), mapA, len, gTrue,
+ sMapA, sMapLenA, sMapSizeA);
+ gfree(mapA);
+ return ctu;
+}
+
+CharCodeToUnicode *CharCodeToUnicode::make8BitToUnicode(Unicode *toUnicode) {
+ return new CharCodeToUnicode(NULL, toUnicode, 256, gTrue, NULL, 0, 0);
+}
+
+CharCodeToUnicode *CharCodeToUnicode::parseCMap(GString *buf, int nBits) {
+ CharCodeToUnicode *ctu;
+ GStringIndex idx;
+
+ ctu = new CharCodeToUnicode(NULL);
+ idx.s = buf;
+ idx.i = 0;
+ if (!ctu->parseCMap1(&getCharFromGString, &idx, nBits)) {
+ delete ctu;
+ return NULL;
+ }
+ return ctu;
+}
+
+void CharCodeToUnicode::mergeCMap(GString *buf, int nBits) {
+ GStringIndex idx;
+
+ idx.s = buf;
+ idx.i = 0;
+ parseCMap1(&getCharFromGString, &idx, nBits);
+}
+
+GBool CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
+ int nBits) {
+ PSTokenizer *pst;
+ char tok1[256], tok2[256], tok3[256];
+ int n1, n2, n3;
+ CharCode i;
+ CharCode maxCode, code1, code2;
+ GString *name;
+ FILE *f;
+ GBool ok;
+
+ ok = gFalse;
+ maxCode = (nBits == 8) ? 0xff : (nBits == 16) ? 0xffff : 0xffffffff;
+ pst = new PSTokenizer(getCharFunc, data);
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ while (pst->getToken(tok2, sizeof(tok2), &n2)) {
+ if (!strcmp(tok2, "usecmap")) {
+ if (tok1[0] == '/') {
+ name = new GString(tok1 + 1);
+ if ((f = globalParams->findToUnicodeFile(name))) {
+ if (parseCMap1(&getCharFromFile, f, nBits)) {
+ ok = gTrue;
+ }
+ fclose(f);
+ } else {
+ error(errSyntaxError, -1,
+ "Couldn't find ToUnicode CMap file for '{1:t}'",
+ name);
+ }
+ delete name;
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok2, "beginbfchar")) {
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+ if (!strcmp(tok1, "endbfchar")) {
+ break;
+ }
+ if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+ !strcmp(tok2, "endbfchar")) {
+ error(errSyntaxWarning, -1,
+ "Illegal entry in bfchar block in ToUnicode CMap");
+ break;
+ }
+ if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
+ tok2[0] == '<' && tok2[n2 - 1] == '>')) {
+ error(errSyntaxWarning, -1,
+ "Illegal entry in bfchar block in ToUnicode CMap");
+ continue;
+ }
+ tok1[n1 - 1] = tok2[n2 - 1] = '\0';
+ if (!parseHex(tok1 + 1, n1 - 2, &code1)) {
+ error(errSyntaxWarning, -1,
+ "Illegal entry in bfchar block in ToUnicode CMap");
+ continue;
+ }
+ if (code1 > maxCode) {
+ error(errSyntaxWarning, -1,
+ "Invalid entry in bfchar block in ToUnicode CMap");
+ }
+ addMapping(code1, tok2 + 1, n2 - 2, 0);
+ ok = gTrue;
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok2, "beginbfrange")) {
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+ if (!strcmp(tok1, "endbfrange")) {
+ break;
+ }
+ if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+ !strcmp(tok2, "endbfrange") ||
+ !pst->getToken(tok3, sizeof(tok3), &n3) ||
+ !strcmp(tok3, "endbfrange")) {
+ error(errSyntaxWarning, -1,
+ "Illegal entry in bfrange block in ToUnicode CMap");
+ break;
+ }
+ if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
+ tok2[0] == '<' && tok2[n2 - 1] == '>')) {
+ error(errSyntaxWarning,
+ -1, "Illegal entry in bfrange block in ToUnicode CMap");
+ continue;
+ }
+ tok1[n1 - 1] = tok2[n2 - 1] = '\0';
+ if (!parseHex(tok1 + 1, n1 - 2, &code1) ||
+ !parseHex(tok2 + 1, n2 - 2, &code2)) {
+ error(errSyntaxWarning, -1,
+ "Illegal entry in bfrange block in ToUnicode CMap");
+ continue;
+ }
+ if (code1 > maxCode || code2 > maxCode) {
+ error(errSyntaxWarning, -1,
+ "Invalid entry in bfrange block in ToUnicode CMap");
+ if (code2 > maxCode) {
+ code2 = maxCode;
+ }
+ }
+ if (!strcmp(tok3, "[")) {
+ i = 0;
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+ if (!strcmp(tok1, "]")) {
+ break;
+ }
+ if (tok1[0] == '<' && tok1[n1 - 1] == '>') {
+ if (code1 + i <= code2) {
+ tok1[n1 - 1] = '\0';
+ addMapping(code1 + i, tok1 + 1, n1 - 2, 0);
+ ok = gTrue;
+ }
+ } else {
+ error(errSyntaxWarning, -1,
+ "Illegal entry in bfrange block in ToUnicode CMap");
+ }
+ ++i;
+ }
+ } else if (tok3[0] == '<' && tok3[n3 - 1] == '>') {
+ tok3[n3 - 1] = '\0';
+ for (i = 0; code1 <= code2; ++code1, ++i) {
+ addMapping(code1, tok3 + 1, n3 - 2, i);
+ ok = gTrue;
+ }
+ } else {
+ error(errSyntaxWarning, -1,
+ "Illegal entry in bfrange block in ToUnicode CMap");
+ }
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok2, "begincidchar")) {
+ // the begincidchar operator is not allowed in ToUnicode CMaps,
+ // but some buggy PDF generators incorrectly use
+ // code-to-CID-type CMaps here
+ error(errSyntaxWarning, -1,
+ "Invalid 'begincidchar' operator in ToUnicode CMap");
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+ if (!strcmp(tok1, "endcidchar")) {
+ break;
+ }
+ if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+ !strcmp(tok2, "endcidchar")) {
+ error(errSyntaxWarning, -1,
+ "Illegal entry in cidchar block in ToUnicode CMap");
+ break;
+ }
+ if (!(tok1[0] == '<' && tok1[n1 - 1] == '>')) {
+ error(errSyntaxWarning, -1,
+ "Illegal entry in cidchar block in ToUnicode CMap");
+ continue;
+ }
+ tok1[n1 - 1] = '\0';
+ if (!parseHex(tok1 + 1, n1 - 2, &code1)) {
+ error(errSyntaxWarning, -1,
+ "Illegal entry in cidchar block in ToUnicode CMap");
+ continue;
+ }
+ if (code1 > maxCode) {
+ error(errSyntaxWarning, -1,
+ "Invalid entry in cidchar block in ToUnicode CMap");
+ }
+ addMappingInt(code1, atoi(tok2));
+ ok = gTrue;
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok2, "begincidrange")) {
+ // the begincidrange operator is not allowed in ToUnicode CMaps,
+ // but some buggy PDF generators incorrectly use
+ // code-to-CID-type CMaps here
+ error(errSyntaxWarning, -1,
+ "Invalid 'begincidrange' operator in ToUnicode CMap");
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+ if (!strcmp(tok1, "endcidrange")) {
+ break;
+ }
+ if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+ !strcmp(tok2, "endcidrange") ||
+ !pst->getToken(tok3, sizeof(tok3), &n3) ||
+ !strcmp(tok3, "endcidrange")) {
+ error(errSyntaxWarning, -1,
+ "Illegal entry in cidrange block in ToUnicode CMap");
+ break;
+ }
+ if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
+ tok2[0] == '<' && tok2[n2 - 1] == '>')) {
+ error(errSyntaxWarning,
+ -1, "Illegal entry in cidrange block in ToUnicode CMap");
+ continue;
+ }
+ tok1[n1 - 1] = tok2[n2 - 1] = '\0';
+ if (!parseHex(tok1 + 1, n1 - 2, &code1) ||
+ !parseHex(tok2 + 1, n2 - 2, &code2)) {
+ error(errSyntaxWarning, -1,
+ "Illegal entry in cidrange block in ToUnicode CMap");
+ continue;
+ }
+ if (code1 > maxCode || code2 > maxCode) {
+ error(errSyntaxWarning, -1,
+ "Invalid entry in cidrange block in ToUnicode CMap");
+ if (code2 > maxCode) {
+ code2 = maxCode;
+ }
+ }
+ for (i = atoi(tok3); code1 <= code2; ++code1, ++i) {
+ addMappingInt(code1, i);
+ ok = gTrue;
+ }
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else {
+ strcpy(tok1, tok2);
+ }
+ }
+ delete pst;
+ return ok;
+}
+
+void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n,
+ int offset) {
+ CharCode oldLen, i;
+#if 1 //~utf16
+ Unicode u[maxUnicodeString];
+ int uLen, j;
+#else
+ Unicode u;
+ int j;
+#endif
+
+ if (code > 0xffffff) {
+ // This is an arbitrary limit to avoid integer overflow issues.
+ // (I've seen CMaps with mappings for <ffffffff>.)
+ return;
+ }
+#if 1 //~utf16
+ if ((uLen = parseUTF16String(uStr, n, u)) == 0) {
+ return;
+ }
+#endif
+ if (code >= mapLen) {
+ oldLen = mapLen;
+ mapLen = mapLen ? 2 * mapLen : 256;
+ if (code >= mapLen) {
+ mapLen = (code + 256) & ~255;
+ }
+ map = (Unicode *)greallocn(map, mapLen, sizeof(Unicode));
+ for (i = oldLen; i < mapLen; ++i) {
+ 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");
+ return;
+ }
+ map[code] = u + offset;
+ } else {
+ if (sMapLen >= sMapSize) {
+ sMapSize = sMapSize + 16;
+ sMap = (CharCodeToUnicodeString *)
+ greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
+ }
+ map[code] = 0;
+ sMap[sMapLen].c = code;
+ if ((sMap[sMapLen].len = n / 4) > maxUnicodeString) {
+ sMap[sMapLen].len = maxUnicodeString;
+ }
+ for (j = 0; j < sMap[sMapLen].len; ++j) {
+ if (!parseHex(uStr + j*4, 4, &sMap[sMapLen].u[j])) {
+ error(errSyntaxWarning, -1, "Illegal entry in ToUnicode CMap");
+ return;
+ }
+ }
+ 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;
+
+ if (code > 0xffffff) {
+ // This is an arbitrary limit to avoid integer overflow issues.
+ // (I've seen CMaps with mappings for <ffffffff>.)
+ return;
+ }
+ if (code >= mapLen) {
+ oldLen = mapLen;
+ mapLen = mapLen ? 2 * mapLen : 256;
+ if (code >= mapLen) {
+ mapLen = (code + 256) & ~255;
+ }
+ map = (Unicode *)greallocn(map, mapLen, sizeof(Unicode));
+ for (i = oldLen; i < mapLen; ++i) {
+ map[i] = 0;
+ }
+ }
+ map[code] = u;
+}
+
+CharCodeToUnicode::CharCodeToUnicode() {
+ tag = NULL;
+ map = NULL;
+ mapLen = 0;
+ sMap = NULL;
+ sMapLen = sMapSize = 0;
+ refCnt = 1;
+}
+
+CharCodeToUnicode::CharCodeToUnicode(GString *tagA) {
+ CharCode i;
+
+ tag = tagA;
+ mapLen = 256;
+ map = (Unicode *)gmallocn(mapLen, sizeof(Unicode));
+ for (i = 0; i < mapLen; ++i) {
+ map[i] = 0;
+ }
+ sMap = NULL;
+ sMapLen = sMapSize = 0;
+ refCnt = 1;
+}
+
+CharCodeToUnicode::CharCodeToUnicode(GString *tagA, Unicode *mapA,
+ CharCode mapLenA, GBool copyMap,
+ CharCodeToUnicodeString *sMapA,
+ int sMapLenA, int sMapSizeA) {
+ tag = tagA;
+ mapLen = mapLenA;
+ if (copyMap) {
+ map = (Unicode *)gmallocn(mapLen, sizeof(Unicode));
+ memcpy(map, mapA, mapLen * sizeof(Unicode));
+ } else {
+ map = mapA;
+ }
+ sMap = sMapA;
+ sMapLen = sMapLenA;
+ sMapSize = sMapSizeA;
+ refCnt = 1;
+}
+
+CharCodeToUnicode::~CharCodeToUnicode() {
+ if (tag) {
+ delete tag;
+ }
+ gfree(map);
+ gfree(sMap);
+}
+
+void CharCodeToUnicode::incRefCnt() {
+#if MULTITHREADED
+ gAtomicIncrement(&refCnt);
+#else
+ ++refCnt;
+#endif
+}
+
+void CharCodeToUnicode::decRefCnt() {
+ GBool done;
+
+#if MULTITHREADED
+ done = gAtomicDecrement(&refCnt) == 0;
+#else
+ done = --refCnt == 0;
+#endif
+ if (done) {
+ delete this;
+ }
+}
+
+GBool CharCodeToUnicode::match(GString *tagA) {
+ return tag && !tag->cmp(tagA);
+}
+
+void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len) {
+ int i, j;
+
+ if (!map) {
+ return;
+ }
+ if (len == 1) {
+ map[c] = u[0];
+ } else {
+ for (i = 0; i < sMapLen; ++i) {
+ if (sMap[i].c == c) {
+ break;
+ }
+ }
+ if (i == sMapLen) {
+ if (sMapLen == sMapSize) {
+ sMapSize += 8;
+ sMap = (CharCodeToUnicodeString *)
+ greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
+ }
+ ++sMapLen;
+ }
+ map[c] = 0;
+ sMap[i].c = c;
+ sMap[i].len = len;
+ for (j = 0; j < len && j < maxUnicodeString; ++j) {
+ sMap[i].u[j] = u[j];
+ }
+ }
+}
+
+int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode *u, int size) {
+ int i, j;
+
+ if (!map) {
+ u[0] = (Unicode)c;
+ return 1;
+ }
+ if (c >= mapLen) {
+ return 0;
+ }
+ if (map[c]) {
+ u[0] = map[c];
+ return 1;
+ }
+ for (i = 0; i < sMapLen; ++i) {
+ if (sMap[i].c == c) {
+ for (j = 0; j < sMap[i].len && j < size; ++j) {
+ u[j] = sMap[i].u[j];
+ }
+ return j;
+ }
+ }
+ return 0;
+}
+
+//------------------------------------------------------------------------
+
+CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) {
+ int i;
+
+ size = sizeA;
+ cache = (CharCodeToUnicode **)gmallocn(size, sizeof(CharCodeToUnicode *));
+ for (i = 0; i < size; ++i) {
+ cache[i] = NULL;
+ }
+}
+
+CharCodeToUnicodeCache::~CharCodeToUnicodeCache() {
+ int i;
+
+ for (i = 0; i < size; ++i) {
+ if (cache[i]) {
+ cache[i]->decRefCnt();
+ }
+ }
+ gfree(cache);
+}
+
+CharCodeToUnicode *CharCodeToUnicodeCache::getCharCodeToUnicode(GString *tag) {
+ CharCodeToUnicode *ctu;
+ int i, j;
+
+ if (cache[0] && cache[0]->match(tag)) {
+ cache[0]->incRefCnt();
+ return cache[0];
+ }
+ for (i = 1; i < size; ++i) {
+ if (cache[i] && cache[i]->match(tag)) {
+ ctu = cache[i];
+ for (j = i; j >= 1; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = ctu;
+ ctu->incRefCnt();
+ return ctu;
+ }
+ }
+ return NULL;
+}
+
+void CharCodeToUnicodeCache::add(CharCodeToUnicode *ctu) {
+ int i;
+
+ if (cache[size - 1]) {
+ cache[size - 1]->decRefCnt();
+ }
+ for (i = size - 1; i >= 1; --i) {
+ cache[i] = cache[i - 1];
+ }
+ cache[0] = ctu;
+ ctu->incRefCnt();
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h b/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h
new file mode 100644
index 00000000000..c390a8c5c50
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CharCodeToUnicode.h
@@ -0,0 +1,126 @@
+//========================================================================
+//
+// CharCodeToUnicode.h
+//
+// Mapping from character codes to Unicode.
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CHARCODETOUNICODE_H
+#define CHARCODETOUNICODE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "CharTypes.h"
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+struct CharCodeToUnicodeString;
+
+//------------------------------------------------------------------------
+
+class CharCodeToUnicode {
+public:
+
+ // Create an identity mapping (Unicode = CharCode).
+ static CharCodeToUnicode *makeIdentityMapping();
+
+ // Read the CID-to-Unicode mapping for <collection> from the file
+ // specified by <fileName>. Sets the initial reference count to 1.
+ // Returns NULL on failure.
+ static CharCodeToUnicode *parseCIDToUnicode(GString *fileName,
+ GString *collection);
+
+ // Create a Unicode-to-Unicode mapping from the file specified by
+ // <fileName>. Sets the initial reference count to 1. Returns NULL
+ // on failure.
+ static CharCodeToUnicode *parseUnicodeToUnicode(GString *fileName);
+
+ // Create the CharCode-to-Unicode mapping for an 8-bit font.
+ // <toUnicode> is an array of 256 Unicode indexes. Sets the initial
+ // reference count to 1.
+ static CharCodeToUnicode *make8BitToUnicode(Unicode *toUnicode);
+
+ // Parse a ToUnicode CMap for an 8- or 16-bit font.
+ static CharCodeToUnicode *parseCMap(GString *buf, int nBits);
+
+ // Parse a ToUnicode CMap for an 8- or 16-bit font, merging it into
+ // <this>.
+ void mergeCMap(GString *buf, int nBits);
+
+ ~CharCodeToUnicode();
+
+ void incRefCnt();
+ void decRefCnt();
+
+ // Return true if this mapping matches the specified <tagA>.
+ GBool match(GString *tagA);
+
+ // Set the mapping for <c>.
+ void setMapping(CharCode c, Unicode *u, int len);
+
+ // Map a CharCode to Unicode.
+ int mapToUnicode(CharCode c, Unicode *u, int size);
+
+ // Return the mapping's length, i.e., one more than the max char
+ // code supported by the mapping.
+ CharCode getLength() { return mapLen; }
+
+ GBool isIdentity() { return !map; }
+
+private:
+
+ GBool parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);
+ void addMapping(CharCode code, char *uStr, int n, int offset);
+ int parseUTF16String(char *uStr, int n, Unicode *uOut);
+ void addMappingInt(CharCode code, Unicode u);
+ CharCodeToUnicode();
+ CharCodeToUnicode(GString *tagA);
+ CharCodeToUnicode(GString *tagA, Unicode *mapA,
+ CharCode mapLenA, GBool copyMap,
+ CharCodeToUnicodeString *sMapA,
+ int sMapLenA, int sMapSizeA);
+
+ GString *tag;
+ Unicode *map;
+ CharCode mapLen;
+ CharCodeToUnicodeString *sMap;
+ int sMapLen, sMapSize;
+#if MULTITHREADED
+ GAtomicCounter refCnt;
+#else
+ int refCnt;
+#endif
+};
+
+//------------------------------------------------------------------------
+
+class CharCodeToUnicodeCache {
+public:
+
+ CharCodeToUnicodeCache(int sizeA);
+ ~CharCodeToUnicodeCache();
+
+ // Get the CharCodeToUnicode object for <tag>. Increments its
+ // reference count; there will be one reference for the cache plus
+ // one for the caller of this function. Returns NULL on failure.
+ CharCodeToUnicode *getCharCodeToUnicode(GString *tag);
+
+ // Insert <ctu> into the cache, in the most-recently-used position.
+ void add(CharCodeToUnicode *ctu);
+
+private:
+
+ CharCodeToUnicode **cache;
+ int size;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CharTypes.h b/Build/source/libs/xpdf/xpdf-src/xpdf/CharTypes.h
new file mode 100644
index 00000000000..d0df630d0c9
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CharTypes.h
@@ -0,0 +1,24 @@
+//========================================================================
+//
+// CharTypes.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CHARTYPES_H
+#define CHARTYPES_H
+
+// Unicode character.
+typedef unsigned int Unicode;
+
+// Character ID for CID character collections.
+typedef unsigned int CID;
+
+// This is large enough to hold any of the following:
+// - 8-bit char code
+// - 16-bit CID
+// - Unicode
+typedef unsigned int CharCode;
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/CompactFontTables.h b/Build/source/libs/xpdf/xpdf-src/xpdf/CompactFontTables.h
new file mode 100644
index 00000000000..28e16e775ea
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/CompactFontTables.h
@@ -0,0 +1,464 @@
+//========================================================================
+//
+// CompactFontTables.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef COMPACTFONTINFO_H
+#define COMPACTFONTINFO_H
+
+static char *type1CStdStrings[391] = {
+ ".notdef",
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quoteright",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "quoteleft",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ "exclamdown",
+ "cent",
+ "sterling",
+ "fraction",
+ "yen",
+ "florin",
+ "section",
+ "currency",
+ "quotesingle",
+ "quotedblleft",
+ "guillemotleft",
+ "guilsinglleft",
+ "guilsinglright",
+ "fi",
+ "fl",
+ "endash",
+ "dagger",
+ "daggerdbl",
+ "periodcentered",
+ "paragraph",
+ "bullet",
+ "quotesinglbase",
+ "quotedblbase",
+ "quotedblright",
+ "guillemotright",
+ "ellipsis",
+ "perthousand",
+ "questiondown",
+ "grave",
+ "acute",
+ "circumflex",
+ "tilde",
+ "macron",
+ "breve",
+ "dotaccent",
+ "dieresis",
+ "ring",
+ "cedilla",
+ "hungarumlaut",
+ "ogonek",
+ "caron",
+ "emdash",
+ "AE",
+ "ordfeminine",
+ "Lslash",
+ "Oslash",
+ "OE",
+ "ordmasculine",
+ "ae",
+ "dotlessi",
+ "lslash",
+ "oslash",
+ "oe",
+ "germandbls",
+ "onesuperior",
+ "logicalnot",
+ "mu",
+ "trademark",
+ "Eth",
+ "onehalf",
+ "plusminus",
+ "Thorn",
+ "onequarter",
+ "divide",
+ "brokenbar",
+ "degree",
+ "thorn",
+ "threequarters",
+ "twosuperior",
+ "registered",
+ "minus",
+ "eth",
+ "multiply",
+ "threesuperior",
+ "copyright",
+ "Aacute",
+ "Acircumflex",
+ "Adieresis",
+ "Agrave",
+ "Aring",
+ "Atilde",
+ "Ccedilla",
+ "Eacute",
+ "Ecircumflex",
+ "Edieresis",
+ "Egrave",
+ "Iacute",
+ "Icircumflex",
+ "Idieresis",
+ "Igrave",
+ "Ntilde",
+ "Oacute",
+ "Ocircumflex",
+ "Odieresis",
+ "Ograve",
+ "Otilde",
+ "Scaron",
+ "Uacute",
+ "Ucircumflex",
+ "Udieresis",
+ "Ugrave",
+ "Yacute",
+ "Ydieresis",
+ "Zcaron",
+ "aacute",
+ "acircumflex",
+ "adieresis",
+ "agrave",
+ "aring",
+ "atilde",
+ "ccedilla",
+ "eacute",
+ "ecircumflex",
+ "edieresis",
+ "egrave",
+ "iacute",
+ "icircumflex",
+ "idieresis",
+ "igrave",
+ "ntilde",
+ "oacute",
+ "ocircumflex",
+ "odieresis",
+ "ograve",
+ "otilde",
+ "scaron",
+ "uacute",
+ "ucircumflex",
+ "udieresis",
+ "ugrave",
+ "yacute",
+ "ydieresis",
+ "zcaron",
+ "exclamsmall",
+ "Hungarumlautsmall",
+ "dollaroldstyle",
+ "dollarsuperior",
+ "ampersandsmall",
+ "Acutesmall",
+ "parenleftsuperior",
+ "parenrightsuperior",
+ "twodotenleader",
+ "onedotenleader",
+ "zerooldstyle",
+ "oneoldstyle",
+ "twooldstyle",
+ "threeoldstyle",
+ "fouroldstyle",
+ "fiveoldstyle",
+ "sixoldstyle",
+ "sevenoldstyle",
+ "eightoldstyle",
+ "nineoldstyle",
+ "commasuperior",
+ "threequartersemdash",
+ "periodsuperior",
+ "questionsmall",
+ "asuperior",
+ "bsuperior",
+ "centsuperior",
+ "dsuperior",
+ "esuperior",
+ "isuperior",
+ "lsuperior",
+ "msuperior",
+ "nsuperior",
+ "osuperior",
+ "rsuperior",
+ "ssuperior",
+ "tsuperior",
+ "ff",
+ "ffi",
+ "ffl",
+ "parenleftinferior",
+ "parenrightinferior",
+ "Circumflexsmall",
+ "hyphensuperior",
+ "Gravesmall",
+ "Asmall",
+ "Bsmall",
+ "Csmall",
+ "Dsmall",
+ "Esmall",
+ "Fsmall",
+ "Gsmall",
+ "Hsmall",
+ "Ismall",
+ "Jsmall",
+ "Ksmall",
+ "Lsmall",
+ "Msmall",
+ "Nsmall",
+ "Osmall",
+ "Psmall",
+ "Qsmall",
+ "Rsmall",
+ "Ssmall",
+ "Tsmall",
+ "Usmall",
+ "Vsmall",
+ "Wsmall",
+ "Xsmall",
+ "Ysmall",
+ "Zsmall",
+ "colonmonetary",
+ "onefitted",
+ "rupiah",
+ "Tildesmall",
+ "exclamdownsmall",
+ "centoldstyle",
+ "Lslashsmall",
+ "Scaronsmall",
+ "Zcaronsmall",
+ "Dieresissmall",
+ "Brevesmall",
+ "Caronsmall",
+ "Dotaccentsmall",
+ "Macronsmall",
+ "figuredash",
+ "hypheninferior",
+ "Ogoneksmall",
+ "Ringsmall",
+ "Cedillasmall",
+ "questiondownsmall",
+ "oneeighth",
+ "threeeighths",
+ "fiveeighths",
+ "seveneighths",
+ "onethird",
+ "twothirds",
+ "zerosuperior",
+ "foursuperior",
+ "fivesuperior",
+ "sixsuperior",
+ "sevensuperior",
+ "eightsuperior",
+ "ninesuperior",
+ "zeroinferior",
+ "oneinferior",
+ "twoinferior",
+ "threeinferior",
+ "fourinferior",
+ "fiveinferior",
+ "sixinferior",
+ "seveninferior",
+ "eightinferior",
+ "nineinferior",
+ "centinferior",
+ "dollarinferior",
+ "periodinferior",
+ "commainferior",
+ "Agravesmall",
+ "Aacutesmall",
+ "Acircumflexsmall",
+ "Atildesmall",
+ "Adieresissmall",
+ "Aringsmall",
+ "AEsmall",
+ "Ccedillasmall",
+ "Egravesmall",
+ "Eacutesmall",
+ "Ecircumflexsmall",
+ "Edieresissmall",
+ "Igravesmall",
+ "Iacutesmall",
+ "Icircumflexsmall",
+ "Idieresissmall",
+ "Ethsmall",
+ "Ntildesmall",
+ "Ogravesmall",
+ "Oacutesmall",
+ "Ocircumflexsmall",
+ "Otildesmall",
+ "Odieresissmall",
+ "OEsmall",
+ "Oslashsmall",
+ "Ugravesmall",
+ "Uacutesmall",
+ "Ucircumflexsmall",
+ "Udieresissmall",
+ "Yacutesmall",
+ "Thornsmall",
+ "Ydieresissmall",
+ "001.000",
+ "001.001",
+ "001.002",
+ "001.003",
+ "Black",
+ "Bold",
+ "Book",
+ "Light",
+ "Medium",
+ "Regular",
+ "Roman",
+ "Semibold"
+};
+
+static Gushort type1CISOAdobeCharset[229] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228
+};
+
+static Gushort type1CExpertCharset[166] = {
+ 0, 1, 229, 230, 231, 232, 233, 234, 235, 236,
+ 237, 238, 13, 14, 15, 99, 239, 240, 241, 242,
+ 243, 244, 245, 246, 247, 248, 27, 28, 249, 250,
+ 251, 252, 253, 254, 255, 256, 257, 258, 259, 260,
+ 261, 262, 263, 264, 265, 266, 109, 110, 267, 268,
+ 269, 270, 271, 272, 273, 274, 275, 276, 277, 278,
+ 279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, 318,
+ 158, 155, 163, 319, 320, 321, 322, 323, 324, 325,
+ 326, 150, 164, 169, 327, 328, 329, 330, 331, 332,
+ 333, 334, 335, 336, 337, 338, 339, 340, 341, 342,
+ 343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
+ 353, 354, 355, 356, 357, 358, 359, 360, 361, 362,
+ 363, 364, 365, 366, 367, 368, 369, 370, 371, 372,
+ 373, 374, 375, 376, 377, 378
+};
+
+static Gushort type1CExpertSubsetCharset[87] = {
+ 0, 1, 231, 232, 235, 236, 237, 238, 13, 14,
+ 15, 99, 239, 240, 241, 242, 243, 244, 245, 246,
+ 247, 248, 27, 28, 249, 250, 251, 253, 254, 255,
+ 256, 257, 258, 259, 260, 261, 262, 263, 264, 265,
+ 266, 109, 110, 267, 268, 269, 270, 272, 300, 301,
+ 302, 305, 314, 315, 158, 155, 163, 320, 321, 322,
+ 323, 324, 325, 326, 150, 164, 169, 327, 328, 329,
+ 330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
+ 340, 341, 342, 343, 344, 345, 346
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Decrypt.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Decrypt.cc
new file mode 100644
index 00000000000..d4f8d4ec336
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Decrypt.cc
@@ -0,0 +1,1620 @@
+//========================================================================
+//
+// Decrypt.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "Decrypt.h"
+
+static void aes256KeyExpansion(DecryptAES256State *s,
+ Guchar *objKey, int objKeyLen);
+static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, GBool last);
+static void sha256(Guchar *msg, int msgLen, Guchar *hash);
+static void sha384(Guchar *msg, int msgLen, Guchar *hash);
+static void sha512(Guchar *msg, int msgLen, Guchar *hash);
+
+static Guchar passwordPad[32] = {
+ 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41,
+ 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08,
+ 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80,
+ 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a
+};
+
+//------------------------------------------------------------------------
+// Decrypt
+//------------------------------------------------------------------------
+
+GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
+ GString *ownerKey, GString *userKey,
+ GString *ownerEnc, GString *userEnc,
+ int permissions, GString *fileID,
+ GString *ownerPassword, GString *userPassword,
+ Guchar *fileKey, GBool encryptMetadata,
+ GBool *ownerPasswordOk) {
+ DecryptAES256State state;
+ Guchar test[127 + 56], test2[32];
+ GString *userPassword2;
+ const char *userPW;
+ Guchar fState[256];
+ Guchar tmpKey[16];
+ Guchar fx, fy;
+ int len, i, j;
+
+ *ownerPasswordOk = gFalse;
+
+ if (encRevision == 5 || encRevision == 6) {
+
+ // check the owner password
+ if (ownerPassword) {
+ //~ this is supposed to convert the password to UTF-8 using "SASLprep"
+ len = ownerPassword->getLength();
+ if (len > 127) {
+ len = 127;
+ }
+ memcpy(test, ownerPassword->getCString(), len);
+ memcpy(test + len, ownerKey->getCString() + 32, 8);
+ memcpy(test + len + 8, userKey->getCString(), 48);
+ sha256(test, len + 56, test);
+ if (encRevision == 6) {
+ r6Hash(test, 32, ownerPassword->getCString(), len,
+ userKey->getCString());
+ }
+ if (!memcmp(test, ownerKey->getCString(), 32)) {
+
+ // compute the file key from the owner password
+ memcpy(test, ownerPassword->getCString(), len);
+ memcpy(test + len, ownerKey->getCString() + 40, 8);
+ memcpy(test + len + 8, userKey->getCString(), 48);
+ sha256(test, len + 56, test);
+ if (encRevision == 6) {
+ r6Hash(test, 32, ownerPassword->getCString(), len,
+ userKey->getCString());
+ }
+ aes256KeyExpansion(&state, test, 32);
+ for (i = 0; i < 16; ++i) {
+ state.cbc[i] = 0;
+ }
+ aes256DecryptBlock(&state, (Guchar *)ownerEnc->getCString(), gFalse);
+ memcpy(fileKey, state.buf, 16);
+ aes256DecryptBlock(&state, (Guchar *)ownerEnc->getCString() + 16,
+ gFalse);
+ memcpy(fileKey + 16, state.buf, 16);
+
+ *ownerPasswordOk = gTrue;
+ return gTrue;
+ }
+ }
+
+ // check the user password
+ if (userPassword) {
+ //~ this is supposed to convert the password to UTF-8 using "SASLprep"
+ userPW = userPassword->getCString();
+ len = userPassword->getLength();
+ if (len > 127) {
+ len = 127;
+ }
+ } else {
+ userPW = "";
+ len = 0;
+ }
+ memcpy(test, userPW, len);
+ memcpy(test + len, userKey->getCString() + 32, 8);
+ sha256(test, len + 8, test);
+ if (encRevision == 6) {
+ r6Hash(test, 32, userPW, len, NULL);
+ }
+ if (!memcmp(test, userKey->getCString(), 32)) {
+
+ // compute the file key from the user password
+ memcpy(test, userPW, len);
+ memcpy(test + len, userKey->getCString() + 40, 8);
+ sha256(test, len + 8, test);
+ if (encRevision == 6) {
+ r6Hash(test, 32, userPW, len, NULL);
+ }
+ aes256KeyExpansion(&state, test, 32);
+ for (i = 0; i < 16; ++i) {
+ state.cbc[i] = 0;
+ }
+ aes256DecryptBlock(&state, (Guchar *)userEnc->getCString(), gFalse);
+ memcpy(fileKey, state.buf, 16);
+ aes256DecryptBlock(&state, (Guchar *)userEnc->getCString() + 16,
+ gFalse);
+ memcpy(fileKey + 16, state.buf, 16);
+
+ return gTrue;
+ }
+
+ return gFalse;
+
+ } else {
+
+ // try using the supplied owner password to generate the user password
+ if (ownerPassword) {
+ len = ownerPassword->getLength();
+ if (len < 32) {
+ memcpy(test, ownerPassword->getCString(), len);
+ memcpy(test + len, passwordPad, 32 - len);
+ } else {
+ memcpy(test, ownerPassword->getCString(), 32);
+ }
+ md5(test, 32, test);
+ if (encRevision == 3) {
+ for (i = 0; i < 50; ++i) {
+ md5(test, keyLength, test);
+ }
+ }
+ if (encRevision == 2) {
+ rc4InitKey(test, keyLength, fState);
+ fx = fy = 0;
+ for (i = 0; i < 32; ++i) {
+ test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
+ }
+ } else {
+ memcpy(test2, ownerKey->getCString(), 32);
+ for (i = 19; i >= 0; --i) {
+ for (j = 0; j < keyLength; ++j) {
+ tmpKey[j] = (Guchar)(test[j] ^ i);
+ }
+ rc4InitKey(tmpKey, keyLength, fState);
+ fx = fy = 0;
+ for (j = 0; j < 32; ++j) {
+ test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]);
+ }
+ }
+ }
+ userPassword2 = new GString((char *)test2, 32);
+ if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
+ permissions, fileID, userPassword2, fileKey,
+ encryptMetadata)) {
+ *ownerPasswordOk = gTrue;
+ delete userPassword2;
+ return gTrue;
+ }
+ delete userPassword2;
+ }
+
+ // try using the supplied user password
+ return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
+ permissions, fileID, userPassword, fileKey,
+ encryptMetadata);
+ }
+}
+
+void Decrypt::r6Hash(Guchar *key, int keyLen, const char *pwd, int pwdLen,
+ char *userKey) {
+ Guchar key1[64*(127+64+48)];
+ DecryptAESState state128;
+ int n, i, j, k;
+
+ i = 0;
+ while (1) {
+ memcpy(key1, pwd, pwdLen);
+ memcpy(key1 + pwdLen, key, keyLen);
+ n = pwdLen + keyLen;
+ if (userKey) {
+ memcpy(key1 + pwdLen + keyLen, userKey, 48);
+ n += 48;
+ }
+ for (j = 1; j < 64; ++j) {
+ memcpy(key1 + j * n, key1, n);
+ }
+ n *= 64;
+ aesKeyExpansion(&state128, key, 16, gFalse);
+ for (j = 0; j < 16; ++j) {
+ state128.cbc[j] = key[16+j];
+ }
+ for (j = 0; j < n; j += 16) {
+ aesEncryptBlock(&state128, key1 + j);
+ memcpy(key1 + j, state128.buf, 16);
+ }
+ k = 0;
+ for (j = 0; j < 16; ++j) {
+ k += key1[j] % 3;
+ }
+ k %= 3;
+ switch (k) {
+ case 0:
+ sha256(key1, n, key);
+ keyLen = 32;
+ break;
+ case 1:
+ sha384(key1, n, key);
+ keyLen = 48;
+ break;
+ case 2:
+ sha512(key1, n, key);
+ keyLen = 64;
+ break;
+ }
+ // from the spec, it appears that i should be incremented after
+ // the test, but that doesn't match what Adobe does
+ ++i;
+ if (i >= 64 && key1[n - 1] <= i - 32) {
+ break;
+ }
+ }
+}
+
+GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength,
+ GString *ownerKey, GString *userKey,
+ int permissions, GString *fileID,
+ GString *userPassword, Guchar *fileKey,
+ GBool encryptMetadata) {
+ Guchar *buf;
+ Guchar test[32];
+ Guchar fState[256];
+ Guchar tmpKey[16];
+ Guchar fx, fy;
+ int len, i, j;
+ GBool ok;
+
+ // generate file key
+ buf = (Guchar *)gmalloc(72 + fileID->getLength());
+ if (userPassword) {
+ len = userPassword->getLength();
+ if (len < 32) {
+ memcpy(buf, userPassword->getCString(), len);
+ memcpy(buf + len, passwordPad, 32 - len);
+ } else {
+ memcpy(buf, userPassword->getCString(), 32);
+ }
+ } else {
+ memcpy(buf, passwordPad, 32);
+ }
+ memcpy(buf + 32, ownerKey->getCString(), 32);
+ buf[64] = (Guchar)(permissions & 0xff);
+ buf[65] = (Guchar)((permissions >> 8) & 0xff);
+ buf[66] = (Guchar)((permissions >> 16) & 0xff);
+ buf[67] = (Guchar)((permissions >> 24) & 0xff);
+ memcpy(buf + 68, fileID->getCString(), fileID->getLength());
+ len = 68 + fileID->getLength();
+ if (!encryptMetadata) {
+ buf[len++] = 0xff;
+ buf[len++] = 0xff;
+ buf[len++] = 0xff;
+ buf[len++] = 0xff;
+ }
+ md5(buf, len, fileKey);
+ if (encRevision == 3) {
+ for (i = 0; i < 50; ++i) {
+ md5(fileKey, keyLength, fileKey);
+ }
+ }
+
+ // test user password
+ if (encRevision == 2) {
+ rc4InitKey(fileKey, keyLength, fState);
+ fx = fy = 0;
+ for (i = 0; i < 32; ++i) {
+ test[i] = rc4DecryptByte(fState, &fx, &fy, userKey->getChar(i));
+ }
+ ok = memcmp(test, passwordPad, 32) == 0;
+ } else if (encRevision == 3) {
+ memcpy(test, userKey->getCString(), 32);
+ for (i = 19; i >= 0; --i) {
+ for (j = 0; j < keyLength; ++j) {
+ tmpKey[j] = (Guchar)(fileKey[j] ^ i);
+ }
+ rc4InitKey(tmpKey, keyLength, fState);
+ fx = fy = 0;
+ for (j = 0; j < 32; ++j) {
+ test[j] = rc4DecryptByte(fState, &fx, &fy, test[j]);
+ }
+ }
+ memcpy(buf, passwordPad, 32);
+ memcpy(buf + 32, fileID->getCString(), fileID->getLength());
+ md5(buf, 32 + fileID->getLength(), buf);
+ ok = memcmp(test, buf, 16) == 0;
+ } else {
+ ok = gFalse;
+ }
+
+ gfree(buf);
+ return ok;
+}
+
+//------------------------------------------------------------------------
+// DecryptStream
+//------------------------------------------------------------------------
+
+DecryptStream::DecryptStream(Stream *strA, Guchar *fileKeyA,
+ CryptAlgorithm algoA, int keyLengthA,
+ int objNumA, int objGenA):
+ FilterStream(strA)
+{
+ int i;
+
+ memcpy(fileKey, fileKeyA, keyLengthA);
+ algo = algoA;
+ keyLength = keyLengthA;
+ objNum = objNumA;
+ objGen = objGenA;
+
+ // construct object key
+ for (i = 0; i < keyLength; ++i) {
+ objKey[i] = fileKey[i];
+ }
+ switch (algo) {
+ case cryptRC4:
+ objKey[keyLength] = (Guchar)(objNum & 0xff);
+ objKey[keyLength + 1] = (Guchar)((objNum >> 8) & 0xff);
+ objKey[keyLength + 2] = (Guchar)((objNum >> 16) & 0xff);
+ objKey[keyLength + 3] = (Guchar)(objGen & 0xff);
+ objKey[keyLength + 4] = (Guchar)((objGen >> 8) & 0xff);
+ md5(objKey, keyLength + 5, objKey);
+ if ((objKeyLength = keyLength + 5) > 16) {
+ objKeyLength = 16;
+ }
+ break;
+ case cryptAES:
+ objKey[keyLength] = (Guchar)(objNum & 0xff);
+ objKey[keyLength + 1] = (Guchar)((objNum >> 8) & 0xff);
+ objKey[keyLength + 2] = (Guchar)((objNum >> 16) & 0xff);
+ objKey[keyLength + 3] = (Guchar)(objGen & 0xff);
+ objKey[keyLength + 4] = (Guchar)((objGen >> 8) & 0xff);
+ objKey[keyLength + 5] = 0x73; // 's'
+ objKey[keyLength + 6] = 0x41; // 'A'
+ objKey[keyLength + 7] = 0x6c; // 'l'
+ objKey[keyLength + 8] = 0x54; // 'T'
+ md5(objKey, keyLength + 9, objKey);
+ if ((objKeyLength = keyLength + 5) > 16) {
+ objKeyLength = 16;
+ }
+ break;
+ case cryptAES256:
+ objKeyLength = keyLength;
+ break;
+ }
+}
+
+DecryptStream::~DecryptStream() {
+ delete str;
+}
+
+Stream *DecryptStream::copy() {
+ return new DecryptStream(str->copy(), fileKey, algo, keyLength,
+ objNum, objGen);
+}
+
+void DecryptStream::reset() {
+ str->reset();
+ switch (algo) {
+ case cryptRC4:
+ state.rc4.x = state.rc4.y = 0;
+ rc4InitKey(objKey, objKeyLength, state.rc4.state);
+ state.rc4.buf = EOF;
+ break;
+ case cryptAES:
+ aesKeyExpansion(&state.aes, objKey, objKeyLength, gTrue);
+ str->getBlock((char *)state.aes.cbc, 16);
+ state.aes.bufIdx = 16;
+ break;
+ case cryptAES256:
+ aes256KeyExpansion(&state.aes256, objKey, objKeyLength);
+ str->getBlock((char *)state.aes256.cbc, 16);
+ state.aes256.bufIdx = 16;
+ break;
+ }
+}
+
+int DecryptStream::getChar() {
+ Guchar in[16];
+ int c;
+
+ c = EOF; // make gcc happy
+ switch (algo) {
+ case cryptRC4:
+ if (state.rc4.buf == EOF) {
+ c = str->getChar();
+ if (c != EOF) {
+ state.rc4.buf = rc4DecryptByte(state.rc4.state, &state.rc4.x,
+ &state.rc4.y, (Guchar)c);
+ }
+ }
+ c = state.rc4.buf;
+ state.rc4.buf = EOF;
+ break;
+ case cryptAES:
+ if (state.aes.bufIdx == 16) {
+ if (str->getBlock((char *)in, 16) != 16) {
+ return EOF;
+ }
+ aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
+ }
+ if (state.aes.bufIdx == 16) {
+ c = EOF;
+ } else {
+ c = state.aes.buf[state.aes.bufIdx++];
+ }
+ break;
+ case cryptAES256:
+ if (state.aes256.bufIdx == 16) {
+ if (str->getBlock((char *)in, 16) != 16) {
+ return EOF;
+ }
+ aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
+ }
+ if (state.aes256.bufIdx == 16) {
+ c = EOF;
+ } else {
+ c = state.aes256.buf[state.aes256.bufIdx++];
+ }
+ break;
+ }
+ return c;
+}
+
+int DecryptStream::lookChar() {
+ Guchar in[16];
+ int c;
+
+ c = EOF; // make gcc happy
+ switch (algo) {
+ case cryptRC4:
+ if (state.rc4.buf == EOF) {
+ c = str->getChar();
+ if (c != EOF) {
+ state.rc4.buf = rc4DecryptByte(state.rc4.state, &state.rc4.x,
+ &state.rc4.y, (Guchar)c);
+ }
+ }
+ c = state.rc4.buf;
+ break;
+ case cryptAES:
+ if (state.aes.bufIdx == 16) {
+ if (str->getBlock((char *)in, 16) != 16) {
+ return EOF;
+ }
+ aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);
+ }
+ if (state.aes.bufIdx == 16) {
+ c = EOF;
+ } else {
+ c = state.aes.buf[state.aes.bufIdx];
+ }
+ break;
+ case cryptAES256:
+ if (state.aes256.bufIdx == 16) {
+ if (str->getBlock((char *)in, 16) != 16) {
+ return EOF;
+ }
+ aes256DecryptBlock(&state.aes256, in, str->lookChar() == EOF);
+ }
+ if (state.aes256.bufIdx == 16) {
+ c = EOF;
+ } else {
+ c = state.aes256.buf[state.aes256.bufIdx];
+ }
+ break;
+ }
+ return c;
+}
+
+GBool DecryptStream::isBinary(GBool last) {
+ return str->isBinary(last);
+}
+
+//------------------------------------------------------------------------
+// RC4-compatible decryption
+//------------------------------------------------------------------------
+
+void rc4InitKey(Guchar *key, int keyLen, Guchar *state) {
+ Guchar index1, index2;
+ Guchar t;
+ int i;
+
+ for (i = 0; i < 256; ++i)
+ state[i] = (Guchar)i;
+ index1 = index2 = 0;
+ for (i = 0; i < 256; ++i) {
+ index2 = (Guchar)(key[index1] + state[i] + index2);
+ t = state[i];
+ state[i] = state[index2];
+ state[index2] = t;
+ index1 = (Guchar)((index1 + 1) % keyLen);
+ }
+}
+
+Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c) {
+ Guchar x1, y1, tx, ty;
+
+ x1 = *x = (Guchar)(*x + 1);
+ y1 = *y = (Guchar)(state[*x] + *y);
+ tx = state[x1];
+ ty = state[y1];
+ state[x1] = ty;
+ state[y1] = tx;
+ return c ^ state[(tx + ty) % 256];
+}
+
+//------------------------------------------------------------------------
+// AES decryption
+//------------------------------------------------------------------------
+
+static Guchar sbox[256] = {
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+};
+
+static Guchar invSbox[256] = {
+ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+};
+
+static Guint rcon[11] = {
+ 0x00000000, // unused
+ 0x01000000,
+ 0x02000000,
+ 0x04000000,
+ 0x08000000,
+ 0x10000000,
+ 0x20000000,
+ 0x40000000,
+ 0x80000000,
+ 0x1b000000,
+ 0x36000000
+};
+
+static inline Guint subWord(Guint x) {
+ return (sbox[x >> 24] << 24)
+ | (sbox[(x >> 16) & 0xff] << 16)
+ | (sbox[(x >> 8) & 0xff] << 8)
+ | sbox[x & 0xff];
+}
+
+static inline Guint rotWord(Guint x) {
+ return ((x << 8) & 0xffffffff) | (x >> 24);
+}
+
+static inline void subBytes(Guchar *state) {
+ int i;
+
+ for (i = 0; i < 16; ++i) {
+ state[i] = sbox[state[i]];
+ }
+}
+
+static inline void invSubBytes(Guchar *state) {
+ int i;
+
+ for (i = 0; i < 16; ++i) {
+ state[i] = invSbox[state[i]];
+ }
+}
+
+static inline void shiftRows(Guchar *state) {
+ Guchar t;
+
+ t = state[4];
+ state[4] = state[5];
+ state[5] = state[6];
+ state[6] = state[7];
+ state[7] = t;
+
+ t = state[8];
+ state[8] = state[10];
+ state[10] = t;
+ t = state[9];
+ state[9] = state[11];
+ state[11] = t;
+
+ t = state[15];
+ state[15] = state[14];
+ state[14] = state[13];
+ state[13] = state[12];
+ state[12] = t;
+}
+
+static inline void invShiftRows(Guchar *state) {
+ Guchar t;
+
+ t = state[7];
+ state[7] = state[6];
+ state[6] = state[5];
+ state[5] = state[4];
+ state[4] = t;
+
+ t = state[8];
+ state[8] = state[10];
+ state[10] = t;
+ t = state[9];
+ state[9] = state[11];
+ state[11] = t;
+
+ t = state[12];
+ state[12] = state[13];
+ state[13] = state[14];
+ state[14] = state[15];
+ state[15] = t;
+}
+
+// {02} \cdot s
+static inline Guchar mul02(Guchar s) {
+ Guchar s2;
+
+ s2 = (Guchar)((s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1));
+ return s2;
+}
+
+// {03} \cdot s
+static inline Guchar mul03(Guchar s) {
+ Guchar s2;
+
+ s2 = (Guchar)((s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1));
+ return s ^ s2;
+}
+
+// {09} \cdot s
+static inline Guchar mul09(Guchar s) {
+ Guchar s2, s4, s8;
+
+ s2 = (Guchar)((s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1));
+ s4 = (Guchar)((s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1));
+ s8 = (Guchar)((s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1));
+ return s ^ s8;
+}
+
+// {0b} \cdot s
+static inline Guchar mul0b(Guchar s) {
+ Guchar s2, s4, s8;
+
+ s2 = (Guchar)((s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1));
+ s4 = (Guchar)((s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1));
+ s8 = (Guchar)((s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1));
+ return s ^ s2 ^ s8;
+}
+
+// {0d} \cdot s
+static inline Guchar mul0d(Guchar s) {
+ Guchar s2, s4, s8;
+
+ s2 = (Guchar)((s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1));
+ s4 = (Guchar)((s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1));
+ s8 = (Guchar)((s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1));
+ return s ^ s4 ^ s8;
+}
+
+// {0e} \cdot s
+static inline Guchar mul0e(Guchar s) {
+ Guchar s2, s4, s8;
+
+ s2 = (Guchar)((s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1));
+ s4 = (Guchar)((s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1));
+ s8 = (Guchar)((s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1));
+ return s2 ^ s4 ^ s8;
+}
+
+static inline void mixColumns(Guchar *state) {
+ int c;
+ Guchar s0, s1, s2, s3;
+
+ for (c = 0; c < 4; ++c) {
+ s0 = state[c];
+ s1 = state[4+c];
+ s2 = state[8+c];
+ s3 = state[12+c];
+ state[c] = mul02(s0) ^ mul03(s1) ^ s2 ^ s3;
+ state[4+c] = s0 ^ mul02(s1) ^ mul03(s2) ^ s3;
+ state[8+c] = s0 ^ s1 ^ mul02(s2) ^ mul03(s3);
+ state[12+c] = mul03(s0) ^ s1 ^ s2 ^ mul02(s3);
+ }
+}
+
+static inline void invMixColumns(Guchar *state) {
+ int c;
+ Guchar s0, s1, s2, s3;
+
+ for (c = 0; c < 4; ++c) {
+ s0 = state[c];
+ s1 = state[4+c];
+ s2 = state[8+c];
+ s3 = state[12+c];
+ state[c] = mul0e(s0) ^ mul0b(s1) ^ mul0d(s2) ^ mul09(s3);
+ state[4+c] = mul09(s0) ^ mul0e(s1) ^ mul0b(s2) ^ mul0d(s3);
+ state[8+c] = mul0d(s0) ^ mul09(s1) ^ mul0e(s2) ^ mul0b(s3);
+ state[12+c] = mul0b(s0) ^ mul0d(s1) ^ mul09(s2) ^ mul0e(s3);
+ }
+}
+
+static inline void invMixColumnsW(Guint *w) {
+ int c;
+ Guchar s0, s1, s2, s3;
+
+ for (c = 0; c < 4; ++c) {
+ s0 = (Guchar)(w[c] >> 24);
+ s1 = (Guchar)(w[c] >> 16);
+ s2 = (Guchar)(w[c] >> 8);
+ s3 = (Guchar)w[c];
+ w[c] = ((mul0e(s0) ^ mul0b(s1) ^ mul0d(s2) ^ mul09(s3)) << 24)
+ | ((mul09(s0) ^ mul0e(s1) ^ mul0b(s2) ^ mul0d(s3)) << 16)
+ | ((mul0d(s0) ^ mul09(s1) ^ mul0e(s2) ^ mul0b(s3)) << 8)
+ | (mul0b(s0) ^ mul0d(s1) ^ mul09(s2) ^ mul0e(s3));
+ }
+}
+
+static inline void addRoundKey(Guchar *state, Guint *w) {
+ int c;
+
+ for (c = 0; c < 4; ++c) {
+ state[c] ^= (Guchar)(w[c] >> 24);
+ state[4+c] ^= (Guchar)(w[c] >> 16);
+ state[8+c] ^= (Guchar)(w[c] >> 8);
+ state[12+c] ^= (Guchar)w[c];
+ }
+}
+
+void aesKeyExpansion(DecryptAESState *s,
+ Guchar *objKey, int objKeyLen,
+ GBool decrypt) {
+ Guint temp;
+ int i, round;
+
+ //~ this assumes objKeyLen == 16
+
+ for (i = 0; i < 4; ++i) {
+ s->w[i] = (objKey[4*i] << 24) + (objKey[4*i+1] << 16) +
+ (objKey[4*i+2] << 8) + objKey[4*i+3];
+ }
+ for (i = 4; i < 44; ++i) {
+ temp = s->w[i-1];
+ if (!(i & 3)) {
+ temp = subWord(rotWord(temp)) ^ rcon[i/4];
+ }
+ s->w[i] = s->w[i-4] ^ temp;
+ }
+ if (decrypt) {
+ for (round = 1; round <= 9; ++round) {
+ invMixColumnsW(&s->w[round * 4]);
+ }
+ }
+}
+
+void aesEncryptBlock(DecryptAESState *s, Guchar *in) {
+ int c, round;
+
+ // initial state + CBC
+ for (c = 0; c < 4; ++c) {
+ s->state[c] = in[4*c] ^ s->cbc[4*c];
+ s->state[4+c] = in[4*c+1] ^ s->cbc[4*c+1];
+ s->state[8+c] = in[4*c+2] ^ s->cbc[4*c+2];
+ s->state[12+c] = in[4*c+3] ^ s->cbc[4*c+3];
+ }
+
+ // round 0
+ addRoundKey(s->state, &s->w[0]);
+
+ // rounds 1 .. 9
+ for (round = 1; round <= 9; ++round) {
+ subBytes(s->state);
+ shiftRows(s->state);
+ mixColumns(s->state);
+ addRoundKey(s->state, &s->w[round * 4]);
+ }
+
+ // round 10
+ subBytes(s->state);
+ shiftRows(s->state);
+ addRoundKey(s->state, &s->w[10 * 4]);
+
+ // output + save for next CBC
+ for (c = 0; c < 4; ++c) {
+ s->buf[4*c] = s->cbc[4*c] = s->state[c];
+ s->buf[4*c+1] = s->cbc[4*c+1] = s->state[4+c];
+ s->buf[4*c+2] = s->cbc[4*c+2] = s->state[8+c];
+ s->buf[4*c+3] = s->cbc[4*c+3] = s->state[12+c];
+ }
+}
+
+void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last) {
+ int c, round, n, i;
+
+ // initial state
+ for (c = 0; c < 4; ++c) {
+ s->state[c] = in[4*c];
+ s->state[4+c] = in[4*c+1];
+ s->state[8+c] = in[4*c+2];
+ s->state[12+c] = in[4*c+3];
+ }
+
+ // round 0
+ addRoundKey(s->state, &s->w[10 * 4]);
+
+ // rounds 1-9
+ for (round = 9; round >= 1; --round) {
+ invSubBytes(s->state);
+ invShiftRows(s->state);
+ invMixColumns(s->state);
+ addRoundKey(s->state, &s->w[round * 4]);
+ }
+
+ // round 10
+ invSubBytes(s->state);
+ invShiftRows(s->state);
+ addRoundKey(s->state, &s->w[0]);
+
+ // CBC
+ for (c = 0; c < 4; ++c) {
+ s->buf[4*c] = s->state[c] ^ s->cbc[4*c];
+ s->buf[4*c+1] = s->state[4+c] ^ s->cbc[4*c+1];
+ s->buf[4*c+2] = s->state[8+c] ^ s->cbc[4*c+2];
+ s->buf[4*c+3] = s->state[12+c] ^ s->cbc[4*c+3];
+ }
+
+ // save the input block for the next CBC
+ for (i = 0; i < 16; ++i) {
+ s->cbc[i] = in[i];
+ }
+
+ // remove padding
+ s->bufIdx = 0;
+ if (last) {
+ n = s->buf[15];
+ if (n < 1 || n > 16) { // this should never happen
+ n = 16;
+ }
+ for (i = 15; i >= n; --i) {
+ s->buf[i] = s->buf[i-n];
+ }
+ s->bufIdx = n;
+ }
+}
+
+//------------------------------------------------------------------------
+// AES-256 decryption
+//------------------------------------------------------------------------
+
+static void aes256KeyExpansion(DecryptAES256State *s,
+ Guchar *objKey, int objKeyLen) {
+ Guint temp;
+ int i, round;
+
+ //~ this assumes objKeyLen == 32
+
+ for (i = 0; i < 8; ++i) {
+ s->w[i] = (objKey[4*i] << 24) + (objKey[4*i+1] << 16) +
+ (objKey[4*i+2] << 8) + objKey[4*i+3];
+ }
+ for (i = 8; i < 60; ++i) {
+ temp = s->w[i-1];
+ if ((i & 7) == 0) {
+ temp = subWord(rotWord(temp)) ^ rcon[i/8];
+ } else if ((i & 7) == 4) {
+ temp = subWord(temp);
+ }
+ s->w[i] = s->w[i-8] ^ temp;
+ }
+ for (round = 1; round <= 13; ++round) {
+ invMixColumnsW(&s->w[round * 4]);
+ }
+}
+
+static void aes256DecryptBlock(DecryptAES256State *s, Guchar *in, GBool last) {
+ int c, round, n, i;
+
+ // initial state
+ for (c = 0; c < 4; ++c) {
+ s->state[c] = in[4*c];
+ s->state[4+c] = in[4*c+1];
+ s->state[8+c] = in[4*c+2];
+ s->state[12+c] = in[4*c+3];
+ }
+
+ // round 0
+ addRoundKey(s->state, &s->w[14 * 4]);
+
+ // rounds 13-1
+ for (round = 13; round >= 1; --round) {
+ invSubBytes(s->state);
+ invShiftRows(s->state);
+ invMixColumns(s->state);
+ addRoundKey(s->state, &s->w[round * 4]);
+ }
+
+ // round 14
+ invSubBytes(s->state);
+ invShiftRows(s->state);
+ addRoundKey(s->state, &s->w[0]);
+
+ // CBC
+ for (c = 0; c < 4; ++c) {
+ s->buf[4*c] = s->state[c] ^ s->cbc[4*c];
+ s->buf[4*c+1] = s->state[4+c] ^ s->cbc[4*c+1];
+ s->buf[4*c+2] = s->state[8+c] ^ s->cbc[4*c+2];
+ s->buf[4*c+3] = s->state[12+c] ^ s->cbc[4*c+3];
+ }
+
+ // save the input block for the next CBC
+ for (i = 0; i < 16; ++i) {
+ s->cbc[i] = in[i];
+ }
+
+ // remove padding
+ s->bufIdx = 0;
+ if (last) {
+ n = s->buf[15];
+ if (n < 1 || n > 16) { // this should never happen
+ n = 16;
+ }
+ for (i = 15; i >= n; --i) {
+ s->buf[i] = s->buf[i-n];
+ }
+ s->bufIdx = n;
+ }
+}
+
+//------------------------------------------------------------------------
+// MD5 message digest
+//------------------------------------------------------------------------
+
+// this works around a bug in older Sun compilers
+static inline Gulong rotateLeft(Gulong x, int r) {
+ x &= 0xffffffff;
+ return ((x << r) | (x >> (32 - r))) & 0xffffffff;
+}
+
+static inline Gulong md5Round1(Gulong a, Gulong b, Gulong c, Gulong d,
+ Gulong Xk, int s, Gulong Ti) {
+ return b + rotateLeft((a + ((b & c) | (~b & d)) + Xk + Ti), s);
+}
+
+static inline Gulong md5Round2(Gulong a, Gulong b, Gulong c, Gulong d,
+ Gulong Xk, int s, Gulong Ti) {
+ return b + rotateLeft((a + ((b & d) | (c & ~d)) + Xk + Ti), s);
+}
+
+static inline Gulong md5Round3(Gulong a, Gulong b, Gulong c, Gulong d,
+ Gulong Xk, int s, Gulong Ti) {
+ return b + rotateLeft((a + (b ^ c ^ d) + Xk + Ti), s);
+}
+
+static inline Gulong md5Round4(Gulong a, Gulong b, Gulong c, Gulong d,
+ Gulong Xk, int s, Gulong Ti) {
+ return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s);
+}
+
+void md5Start(MD5State *state) {
+ state->a = 0x67452301;
+ state->b = 0xefcdab89;
+ state->c = 0x98badcfe;
+ state->d = 0x10325476;
+ state->bufLen = 0;
+ state->msgLen = 0;
+}
+
+static void md5ProcessBlock(MD5State *state) {
+ Gulong x[16];
+ Gulong a, b, c, d;
+ int i;
+
+ for (i = 0; i < 16; ++i) {
+ x[i] = state->buf[4*i] | (state->buf[4*i+1] << 8) |
+ (state->buf[4*i+2] << 16) | (state->buf[4*i+3] << 24);
+ }
+
+ a = state->a;
+ b = state->b;
+ c = state->c;
+ d = state->d;
+
+ // round 1
+ a = md5Round1(a, b, c, d, x[0], 7, 0xd76aa478);
+ d = md5Round1(d, a, b, c, x[1], 12, 0xe8c7b756);
+ c = md5Round1(c, d, a, b, x[2], 17, 0x242070db);
+ b = md5Round1(b, c, d, a, x[3], 22, 0xc1bdceee);
+ a = md5Round1(a, b, c, d, x[4], 7, 0xf57c0faf);
+ d = md5Round1(d, a, b, c, x[5], 12, 0x4787c62a);
+ c = md5Round1(c, d, a, b, x[6], 17, 0xa8304613);
+ b = md5Round1(b, c, d, a, x[7], 22, 0xfd469501);
+ a = md5Round1(a, b, c, d, x[8], 7, 0x698098d8);
+ d = md5Round1(d, a, b, c, x[9], 12, 0x8b44f7af);
+ c = md5Round1(c, d, a, b, x[10], 17, 0xffff5bb1);
+ b = md5Round1(b, c, d, a, x[11], 22, 0x895cd7be);
+ a = md5Round1(a, b, c, d, x[12], 7, 0x6b901122);
+ d = md5Round1(d, a, b, c, x[13], 12, 0xfd987193);
+ c = md5Round1(c, d, a, b, x[14], 17, 0xa679438e);
+ b = md5Round1(b, c, d, a, x[15], 22, 0x49b40821);
+
+ // round 2
+ a = md5Round2(a, b, c, d, x[1], 5, 0xf61e2562);
+ d = md5Round2(d, a, b, c, x[6], 9, 0xc040b340);
+ c = md5Round2(c, d, a, b, x[11], 14, 0x265e5a51);
+ b = md5Round2(b, c, d, a, x[0], 20, 0xe9b6c7aa);
+ a = md5Round2(a, b, c, d, x[5], 5, 0xd62f105d);
+ d = md5Round2(d, a, b, c, x[10], 9, 0x02441453);
+ c = md5Round2(c, d, a, b, x[15], 14, 0xd8a1e681);
+ b = md5Round2(b, c, d, a, x[4], 20, 0xe7d3fbc8);
+ a = md5Round2(a, b, c, d, x[9], 5, 0x21e1cde6);
+ d = md5Round2(d, a, b, c, x[14], 9, 0xc33707d6);
+ c = md5Round2(c, d, a, b, x[3], 14, 0xf4d50d87);
+ b = md5Round2(b, c, d, a, x[8], 20, 0x455a14ed);
+ a = md5Round2(a, b, c, d, x[13], 5, 0xa9e3e905);
+ d = md5Round2(d, a, b, c, x[2], 9, 0xfcefa3f8);
+ c = md5Round2(c, d, a, b, x[7], 14, 0x676f02d9);
+ b = md5Round2(b, c, d, a, x[12], 20, 0x8d2a4c8a);
+
+ // round 3
+ a = md5Round3(a, b, c, d, x[5], 4, 0xfffa3942);
+ d = md5Round3(d, a, b, c, x[8], 11, 0x8771f681);
+ c = md5Round3(c, d, a, b, x[11], 16, 0x6d9d6122);
+ b = md5Round3(b, c, d, a, x[14], 23, 0xfde5380c);
+ a = md5Round3(a, b, c, d, x[1], 4, 0xa4beea44);
+ d = md5Round3(d, a, b, c, x[4], 11, 0x4bdecfa9);
+ c = md5Round3(c, d, a, b, x[7], 16, 0xf6bb4b60);
+ b = md5Round3(b, c, d, a, x[10], 23, 0xbebfbc70);
+ a = md5Round3(a, b, c, d, x[13], 4, 0x289b7ec6);
+ d = md5Round3(d, a, b, c, x[0], 11, 0xeaa127fa);
+ c = md5Round3(c, d, a, b, x[3], 16, 0xd4ef3085);
+ b = md5Round3(b, c, d, a, x[6], 23, 0x04881d05);
+ a = md5Round3(a, b, c, d, x[9], 4, 0xd9d4d039);
+ d = md5Round3(d, a, b, c, x[12], 11, 0xe6db99e5);
+ c = md5Round3(c, d, a, b, x[15], 16, 0x1fa27cf8);
+ b = md5Round3(b, c, d, a, x[2], 23, 0xc4ac5665);
+
+ // round 4
+ a = md5Round4(a, b, c, d, x[0], 6, 0xf4292244);
+ d = md5Round4(d, a, b, c, x[7], 10, 0x432aff97);
+ c = md5Round4(c, d, a, b, x[14], 15, 0xab9423a7);
+ b = md5Round4(b, c, d, a, x[5], 21, 0xfc93a039);
+ a = md5Round4(a, b, c, d, x[12], 6, 0x655b59c3);
+ d = md5Round4(d, a, b, c, x[3], 10, 0x8f0ccc92);
+ c = md5Round4(c, d, a, b, x[10], 15, 0xffeff47d);
+ b = md5Round4(b, c, d, a, x[1], 21, 0x85845dd1);
+ a = md5Round4(a, b, c, d, x[8], 6, 0x6fa87e4f);
+ d = md5Round4(d, a, b, c, x[15], 10, 0xfe2ce6e0);
+ c = md5Round4(c, d, a, b, x[6], 15, 0xa3014314);
+ b = md5Round4(b, c, d, a, x[13], 21, 0x4e0811a1);
+ a = md5Round4(a, b, c, d, x[4], 6, 0xf7537e82);
+ d = md5Round4(d, a, b, c, x[11], 10, 0xbd3af235);
+ c = md5Round4(c, d, a, b, x[2], 15, 0x2ad7d2bb);
+ b = md5Round4(b, c, d, a, x[9], 21, 0xeb86d391);
+
+ // increment a, b, c, d
+ state->a += a;
+ state->b += b;
+ state->c += c;
+ state->d += d;
+
+ state->bufLen = 0;
+}
+
+void md5Append(MD5State *state, Guchar *data, int dataLen) {
+ Guchar *p;
+ int remain, k;
+
+ p = data;
+ remain = dataLen;
+ while (state->bufLen + remain >= 64) {
+ k = 64 - state->bufLen;
+ memcpy(state->buf + state->bufLen, p, k);
+ state->bufLen = 64;
+ md5ProcessBlock(state);
+ p += k;
+ remain -= k;
+ }
+ if (remain > 0) {
+ memcpy(state->buf + state->bufLen, p, remain);
+ state->bufLen += remain;
+ }
+ state->msgLen += dataLen;
+}
+
+void md5Finish(MD5State *state) {
+ // padding and length
+ state->buf[state->bufLen++] = 0x80;
+ if (state->bufLen > 56) {
+ while (state->bufLen < 64) {
+ state->buf[state->bufLen++] = 0x00;
+ }
+ md5ProcessBlock(state);
+ }
+ while (state->bufLen < 56) {
+ state->buf[state->bufLen++] = 0x00;
+ }
+ state->buf[56] = (Guchar)(state->msgLen << 3);
+ state->buf[57] = (Guchar)(state->msgLen >> 5);
+ state->buf[58] = (Guchar)(state->msgLen >> 13);
+ state->buf[59] = (Guchar)(state->msgLen >> 21);
+ state->buf[60] = (Guchar)(state->msgLen >> 29);
+ state->buf[61] = (Guchar)0;
+ state->buf[62] = (Guchar)0;
+ state->buf[63] = (Guchar)0;
+ state->bufLen = 64;
+ md5ProcessBlock(state);
+
+ // break digest into bytes
+ state->digest[0] = (Guchar)state->a;
+ state->digest[1] = (Guchar)(state->a >> 8);
+ state->digest[2] = (Guchar)(state->a >> 16);
+ state->digest[3] = (Guchar)(state->a >> 24);
+ state->digest[4] = (Guchar)state->b;
+ state->digest[5] = (Guchar)(state->b >> 8);
+ state->digest[6] = (Guchar)(state->b >> 16);
+ state->digest[7] = (Guchar)(state->b >> 24);
+ state->digest[8] = (Guchar)state->c;
+ state->digest[9] = (Guchar)(state->c >> 8);
+ state->digest[10] = (Guchar)(state->c >> 16);
+ state->digest[11] = (Guchar)(state->c >> 24);
+ state->digest[12] = (Guchar)state->d;
+ state->digest[13] = (Guchar)(state->d >> 8);
+ state->digest[14] = (Guchar)(state->d >> 16);
+ state->digest[15] = (Guchar)(state->d >> 24);
+}
+
+void md5(Guchar *msg, int msgLen, Guchar *digest) {
+ MD5State state;
+ int i;
+
+ if (msgLen < 0) {
+ return;
+ }
+ md5Start(&state);
+ md5Append(&state, msg, msgLen);
+ md5Finish(&state);
+ for (i = 0; i < 16; ++i) {
+ digest[i] = state.digest[i];
+ }
+}
+
+//------------------------------------------------------------------------
+// SHA-256 hash
+//------------------------------------------------------------------------
+
+static Guint sha256K[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+static inline Guint rotr(Guint x, Guint n) {
+ return (x >> n) | (x << (32 - n));
+}
+
+static inline Guint sha256Ch(Guint x, Guint y, Guint z) {
+ return (x & y) ^ (~x & z);
+}
+
+static inline Guint sha256Maj(Guint x, Guint y, Guint z) {
+ return (x & y) ^ (x & z) ^ (y & z);
+}
+
+static inline Guint sha256Sigma0(Guint x) {
+ return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
+}
+
+static inline Guint sha256Sigma1(Guint x) {
+ return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
+}
+
+static inline Guint sha256sigma0(Guint x) {
+ return rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3);
+}
+
+static inline Guint sha256sigma1(Guint x) {
+ return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10);
+}
+
+static void sha256HashBlock(Guchar *blk, Guint *H) {
+ Guint W[64];
+ Guint a, b, c, d, e, f, g, h;
+ Guint T1, T2;
+ Guint t;
+
+ // 1. prepare the message schedule
+ for (t = 0; t < 16; ++t) {
+ W[t] = (blk[t*4] << 24) |
+ (blk[t*4 + 1] << 16) |
+ (blk[t*4 + 2] << 8) |
+ blk[t*4 + 3];
+ }
+ for (t = 16; t < 64; ++t) {
+ W[t] = sha256sigma1(W[t-2]) + W[t-7] + sha256sigma0(W[t-15]) + W[t-16];
+ }
+
+ // 2. initialize the eight working variables
+ a = H[0];
+ b = H[1];
+ c = H[2];
+ d = H[3];
+ e = H[4];
+ f = H[5];
+ g = H[6];
+ h = H[7];
+
+ // 3.
+ for (t = 0; t < 64; ++t) {
+ T1 = h + sha256Sigma1(e) + sha256Ch(e,f,g) + sha256K[t] + W[t];
+ T2 = sha256Sigma0(a) + sha256Maj(a,b,c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+ }
+
+ // 4. compute the intermediate hash value
+ H[0] += a;
+ H[1] += b;
+ H[2] += c;
+ H[3] += d;
+ H[4] += e;
+ H[5] += f;
+ H[6] += g;
+ H[7] += h;
+}
+
+static void sha256(Guchar *msg, int msgLen, Guchar *hash) {
+ Guchar blk[64];
+ Guint H[8];
+ int blkLen, i;
+
+ H[0] = 0x6a09e667;
+ H[1] = 0xbb67ae85;
+ H[2] = 0x3c6ef372;
+ H[3] = 0xa54ff53a;
+ H[4] = 0x510e527f;
+ H[5] = 0x9b05688c;
+ H[6] = 0x1f83d9ab;
+ H[7] = 0x5be0cd19;
+
+ blkLen = 0;
+ for (i = 0; i + 64 <= msgLen; i += 64) {
+ sha256HashBlock(msg + i, H);
+ }
+ blkLen = msgLen - i;
+ if (blkLen > 0) {
+ memcpy(blk, msg + i, blkLen);
+ }
+
+ // pad the message
+ blk[blkLen++] = 0x80;
+ if (blkLen > 56) {
+ while (blkLen < 64) {
+ blk[blkLen++] = 0;
+ }
+ sha256HashBlock(blk, H);
+ blkLen = 0;
+ }
+ while (blkLen < 56) {
+ blk[blkLen++] = 0;
+ }
+ blk[56] = 0;
+ blk[57] = 0;
+ blk[58] = 0;
+ blk[59] = 0;
+ blk[60] = (Guchar)(msgLen >> 21);
+ blk[61] = (Guchar)(msgLen >> 13);
+ blk[62] = (Guchar)(msgLen >> 5);
+ blk[63] = (Guchar)(msgLen << 3);
+ sha256HashBlock(blk, H);
+
+ // copy the output into the buffer (convert words to bytes)
+ for (i = 0; i < 8; ++i) {
+ hash[i*4] = (Guchar)(H[i] >> 24);
+ hash[i*4 + 1] = (Guchar)(H[i] >> 16);
+ hash[i*4 + 2] = (Guchar)(H[i] >> 8);
+ hash[i*4 + 3] = (Guchar)H[i];
+ }
+}
+
+//------------------------------------------------------------------------
+// SHA-384 and SHA-512 hashes
+//------------------------------------------------------------------------
+
+typedef unsigned long long SHA512Uint64;
+
+static SHA512Uint64 sha512K[80] = {
+ 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
+ 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
+ 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+ 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
+ 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
+ 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+ 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
+ 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
+ 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+ 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
+ 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
+ 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+ 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
+ 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
+ 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+ 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
+ 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
+ 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+ 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
+ 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
+ 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+ 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
+ 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
+ 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+ 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
+ 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
+ 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+ 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
+ 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
+ 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+ 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
+ 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
+ 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+ 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
+ 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
+ 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+ 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
+ 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
+ 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+ 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
+};
+
+static inline SHA512Uint64 rotr64(SHA512Uint64 x, Guint n) {
+ return (x >> n) | (x << (64 - n));
+}
+
+static inline SHA512Uint64 sha512Ch(SHA512Uint64 x, SHA512Uint64 y,
+ SHA512Uint64 z) {
+ return (x & y) ^ (~x & z);
+}
+
+static inline SHA512Uint64 sha512Maj(SHA512Uint64 x, SHA512Uint64 y,
+ SHA512Uint64 z) {
+ return (x & y) ^ (x & z) ^ (y & z);
+}
+
+static inline SHA512Uint64 sha512Sigma0(SHA512Uint64 x) {
+ return rotr64(x, 28) ^ rotr64(x, 34) ^ rotr64(x, 39);
+}
+
+static inline SHA512Uint64 sha512Sigma1(SHA512Uint64 x) {
+ return rotr64(x, 14) ^ rotr64(x, 18) ^ rotr64(x, 41);
+}
+
+static inline SHA512Uint64 sha512sigma0(SHA512Uint64 x) {
+ return rotr64(x, 1) ^ rotr64(x, 8) ^ (x >> 7);
+}
+
+static inline SHA512Uint64 sha512sigma1(SHA512Uint64 x) {
+ return rotr64(x, 19) ^ rotr64(x, 61) ^ (x >> 6);
+}
+
+static void sha512HashBlock(Guchar *blk, SHA512Uint64 *H) {
+ SHA512Uint64 W[80];
+ SHA512Uint64 a, b, c, d, e, f, g, h;
+ SHA512Uint64 T1, T2;
+ Guint t;
+
+ // 1. prepare the message schedule
+ for (t = 0; t < 16; ++t) {
+ W[t] = ((SHA512Uint64)blk[t*8] << 56) |
+ ((SHA512Uint64)blk[t*8 + 1] << 48) |
+ ((SHA512Uint64)blk[t*8 + 2] << 40) |
+ ((SHA512Uint64)blk[t*8 + 3] << 32) |
+ ((SHA512Uint64)blk[t*8 + 4] << 24) |
+ ((SHA512Uint64)blk[t*8 + 5] << 16) |
+ ((SHA512Uint64)blk[t*8 + 6] << 8) |
+ (SHA512Uint64)blk[t*8 + 7];
+ }
+ for (t = 16; t < 80; ++t) {
+ W[t] = sha512sigma1(W[t-2]) + W[t-7] + sha512sigma0(W[t-15]) + W[t-16];
+ }
+
+ // 2. initialize the eight working variables
+ a = H[0];
+ b = H[1];
+ c = H[2];
+ d = H[3];
+ e = H[4];
+ f = H[5];
+ g = H[6];
+ h = H[7];
+
+ // 3.
+ for (t = 0; t < 80; ++t) {
+ T1 = h + sha512Sigma1(e) + sha512Ch(e,f,g) + sha512K[t] + W[t];
+ T2 = sha512Sigma0(a) + sha512Maj(a,b,c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+ }
+
+ // 4. compute the intermediate hash value
+ H[0] += a;
+ H[1] += b;
+ H[2] += c;
+ H[3] += d;
+ H[4] += e;
+ H[5] += f;
+ H[6] += g;
+ H[7] += h;
+}
+
+static void sha512(Guchar *msg, int msgLen, Guchar *hash) {
+ Guchar blk[128];
+ SHA512Uint64 H[8];
+ int blkLen, i;
+
+ H[0] = 0x6a09e667f3bcc908LL;
+ H[1] = 0xbb67ae8584caa73bLL;
+ H[2] = 0x3c6ef372fe94f82bLL;
+ H[3] = 0xa54ff53a5f1d36f1LL;
+ H[4] = 0x510e527fade682d1LL;
+ H[5] = 0x9b05688c2b3e6c1fLL;
+ H[6] = 0x1f83d9abfb41bd6bLL;
+ H[7] = 0x5be0cd19137e2179LL;
+
+ blkLen = 0;
+ for (i = 0; i + 128 <= msgLen; i += 128) {
+ sha512HashBlock(msg + i, H);
+ }
+ blkLen = msgLen - i;
+ if (blkLen > 0) {
+ memcpy(blk, msg + i, blkLen);
+ }
+
+ // pad the message
+ blk[blkLen++] = 0x80;
+ if (blkLen > 112) {
+ while (blkLen < 128) {
+ blk[blkLen++] = 0;
+ }
+ sha512HashBlock(blk, H);
+ blkLen = 0;
+ }
+ while (blkLen < 112) {
+ blk[blkLen++] = 0;
+ }
+ blk[112] = 0;
+ blk[113] = 0;
+ blk[114] = 0;
+ blk[115] = 0;
+ blk[116] = 0;
+ blk[117] = 0;
+ blk[118] = 0;
+ blk[119] = 0;
+ blk[120] = 0;
+ blk[121] = 0;
+ blk[122] = 0;
+ blk[123] = 0;
+ blk[124] = (Guchar)(msgLen >> 21);
+ blk[125] = (Guchar)(msgLen >> 13);
+ blk[126] = (Guchar)(msgLen >> 5);
+ blk[127] = (Guchar)(msgLen << 3);
+ sha512HashBlock(blk, H);
+
+ // copy the output into the buffer (convert words to bytes)
+ for (i = 0; i < 8; ++i) {
+ hash[i*8] = (Guchar)(H[i] >> 56);
+ hash[i*8 + 1] = (Guchar)(H[i] >> 48);
+ hash[i*8 + 2] = (Guchar)(H[i] >> 40);
+ hash[i*8 + 3] = (Guchar)(H[i] >> 32);
+ hash[i*8 + 4] = (Guchar)(H[i] >> 24);
+ hash[i*8 + 5] = (Guchar)(H[i] >> 16);
+ hash[i*8 + 6] = (Guchar)(H[i] >> 8);
+ hash[i*8 + 7] = (Guchar)H[i];
+ }
+}
+
+static void sha384(Guchar *msg, int msgLen, Guchar *hash) {
+ Guchar blk[128];
+ SHA512Uint64 H[8];
+ int blkLen, i;
+
+ H[0] = 0xcbbb9d5dc1059ed8LL;
+ H[1] = 0x629a292a367cd507LL;
+ H[2] = 0x9159015a3070dd17LL;
+ H[3] = 0x152fecd8f70e5939LL;
+ H[4] = 0x67332667ffc00b31LL;
+ H[5] = 0x8eb44a8768581511LL;
+ H[6] = 0xdb0c2e0d64f98fa7LL;
+ H[7] = 0x47b5481dbefa4fa4LL;
+
+ blkLen = 0;
+ for (i = 0; i + 128 <= msgLen; i += 128) {
+ sha512HashBlock(msg + i, H);
+ }
+ blkLen = msgLen - i;
+ if (blkLen > 0) {
+ memcpy(blk, msg + i, blkLen);
+ }
+
+ // pad the message
+ blk[blkLen++] = 0x80;
+ if (blkLen > 112) {
+ while (blkLen < 128) {
+ blk[blkLen++] = 0;
+ }
+ sha512HashBlock(blk, H);
+ blkLen = 0;
+ }
+ while (blkLen < 112) {
+ blk[blkLen++] = 0;
+ }
+ blk[112] = 0;
+ blk[113] = 0;
+ blk[114] = 0;
+ blk[115] = 0;
+ blk[116] = 0;
+ blk[117] = 0;
+ blk[118] = 0;
+ blk[119] = 0;
+ blk[120] = 0;
+ blk[121] = 0;
+ blk[122] = 0;
+ blk[123] = 0;
+ blk[124] = (Guchar)(msgLen >> 21);
+ blk[125] = (Guchar)(msgLen >> 13);
+ blk[126] = (Guchar)(msgLen >> 5);
+ blk[127] = (Guchar)(msgLen << 3);
+ sha512HashBlock(blk, H);
+
+ // copy the output into the buffer (convert words to bytes)
+ for (i = 0; i < 6; ++i) {
+ hash[i*8] = (Guchar)(H[i] >> 56);
+ hash[i*8 + 1] = (Guchar)(H[i] >> 48);
+ hash[i*8 + 2] = (Guchar)(H[i] >> 40);
+ hash[i*8 + 3] = (Guchar)(H[i] >> 32);
+ hash[i*8 + 4] = (Guchar)(H[i] >> 24);
+ hash[i*8 + 5] = (Guchar)(H[i] >> 16);
+ hash[i*8 + 6] = (Guchar)(H[i] >> 8);
+ hash[i*8 + 7] = (Guchar)H[i];
+ }
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Decrypt.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Decrypt.h
new file mode 100644
index 00000000000..2b10abd050b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Decrypt.h
@@ -0,0 +1,133 @@
+//========================================================================
+//
+// Decrypt.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef DECRYPT_H
+#define DECRYPT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "GString.h"
+#include "Object.h"
+#include "Stream.h"
+
+//------------------------------------------------------------------------
+// Decrypt
+//------------------------------------------------------------------------
+
+class Decrypt {
+public:
+
+ // Generate a file key. The <fileKey> buffer must have space for at
+ // least 16 bytes. Checks <ownerPassword> and then <userPassword>
+ // and returns true if either is correct. Sets <ownerPasswordOk> if
+ // the owner password was correct. Either or both of the passwords
+ // may be NULL, which is treated as an empty string.
+ static GBool makeFileKey(int encVersion, int encRevision, int keyLength,
+ GString *ownerKey, GString *userKey,
+ GString *ownerEnc, GString *userEnc,
+ int permissions, GString *fileID,
+ GString *ownerPassword, GString *userPassword,
+ Guchar *fileKey, GBool encryptMetadata,
+ GBool *ownerPasswordOk);
+
+private:
+
+ static void r6Hash(Guchar *key, int keyLen, const char *pwd, int pwdLen,
+ char *userKey);
+ static GBool makeFileKey2(int encVersion, int encRevision, int keyLength,
+ GString *ownerKey, GString *userKey,
+ int permissions, GString *fileID,
+ GString *userPassword, Guchar *fileKey,
+ GBool encryptMetadata);
+};
+
+//------------------------------------------------------------------------
+// DecryptStream
+//------------------------------------------------------------------------
+
+struct DecryptRC4State {
+ Guchar state[256];
+ Guchar x, y;
+ int buf;
+};
+
+struct DecryptAESState {
+ Guint w[44];
+ Guchar state[16];
+ Guchar cbc[16];
+ Guchar buf[16];
+ int bufIdx;
+};
+
+struct DecryptAES256State {
+ Guint w[60];
+ Guchar state[16];
+ Guchar cbc[16];
+ Guchar buf[16];
+ int bufIdx;
+};
+
+class DecryptStream: public FilterStream {
+public:
+
+ DecryptStream(Stream *strA, Guchar *fileKeyA,
+ CryptAlgorithm algoA, int keyLengthA,
+ int objNumA, int objGenA);
+ virtual ~DecryptStream();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual GBool isBinary(GBool last);
+ virtual Stream *getUndecodedStream() { return this; }
+
+private:
+
+ Guchar fileKey[32];
+ CryptAlgorithm algo;
+ int keyLength;
+ int objNum, objGen;
+ int objKeyLength;
+ Guchar objKey[32];
+
+ union {
+ DecryptRC4State rc4;
+ DecryptAESState aes;
+ DecryptAES256State aes256;
+ } state;
+};
+
+//------------------------------------------------------------------------
+
+struct MD5State {
+ Gulong a, b, c, d;
+ Guchar buf[64];
+ int bufLen;
+ int msgLen;
+ Guchar digest[16];
+};
+
+extern void rc4InitKey(Guchar *key, int keyLen, Guchar *state);
+extern Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c);
+void md5Start(MD5State *state);
+void md5Append(MD5State *state, Guchar *data, int dataLen);
+void md5Finish(MD5State *state);
+extern void md5(Guchar *msg, int msgLen, Guchar *digest);
+extern void aesKeyExpansion(DecryptAESState *s,
+ Guchar *objKey, int objKeyLen,
+ GBool decrypt);
+extern void aesEncryptBlock(DecryptAESState *s, Guchar *in);
+extern void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last);
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Dict.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Dict.cc
new file mode 100644
index 00000000000..c8c29736fe9
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Dict.cc
@@ -0,0 +1,145 @@
+//========================================================================
+//
+// Dict.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "Object.h"
+#include "XRef.h"
+#include "Dict.h"
+
+//------------------------------------------------------------------------
+
+struct DictEntry {
+ char *key;
+ Object val;
+ DictEntry *next;
+};
+
+//------------------------------------------------------------------------
+// Dict
+//------------------------------------------------------------------------
+
+Dict::Dict(XRef *xrefA) {
+ xref = xrefA;
+ size = 8;
+ length = 0;
+ entries = (DictEntry *)gmallocn(size, sizeof(DictEntry));
+ hashTab = (DictEntry **)gmallocn(2 * size - 1, sizeof(DictEntry *));
+ memset(hashTab, 0, (2 * size - 1) * sizeof(DictEntry *));
+ ref = 1;
+}
+
+Dict::~Dict() {
+ int i;
+
+ for (i = 0; i < length; ++i) {
+ gfree(entries[i].key);
+ entries[i].val.free();
+ }
+ gfree(entries);
+ gfree(hashTab);
+}
+
+void Dict::add(char *key, Object *val) {
+ DictEntry *e;
+ int h;
+
+ if ((e = find(key))) {
+ e->val.free();
+ e->val = *val;
+ gfree(key);
+ } else {
+ if (length == size) {
+ expand();
+ }
+ h = hash(key);
+ entries[length].key = key;
+ entries[length].val = *val;
+ entries[length].next = hashTab[h];
+ hashTab[h] = &entries[length];
+ ++length;
+ }
+}
+
+void Dict::expand() {
+ int h, i;
+
+ size *= 2;
+ entries = (DictEntry *)greallocn(entries, size, sizeof(DictEntry));
+ hashTab = (DictEntry **)greallocn(hashTab, 2 * size - 1,
+ sizeof(DictEntry *));
+ memset(hashTab, 0, (2 * size - 1) * sizeof(DictEntry *));
+ for (i = 0; i < length; ++i) {
+ h = hash(entries[i].key);
+ entries[i].next = hashTab[h];
+ hashTab[h] = &entries[i];
+ }
+}
+
+inline DictEntry *Dict::find(const char *key) {
+ DictEntry *e;
+ int h;
+
+ h = hash(key);
+ for (e = hashTab[h]; e; e = e->next) {
+ if (!strcmp(key, e->key)) {
+ return e;
+ }
+ }
+ return NULL;
+}
+
+int Dict::hash(const char *key) {
+ const char *p;
+ unsigned int h;
+
+ h = 0;
+ for (p = key; *p; ++p) {
+ h = 17 * h + (int)(*p & 0xff);
+ }
+ return (int)(h % (2 * size - 1));
+}
+
+GBool Dict::is(const char *type) {
+ DictEntry *e;
+
+ return (e = find("Type")) && e->val.isName(type);
+}
+
+Object *Dict::lookup(const char *key, Object *obj, int recursion) {
+ DictEntry *e;
+
+ return (e = find(key)) ? e->val.fetch(xref, obj, recursion)
+ : obj->initNull();
+}
+
+Object *Dict::lookupNF(const char *key, Object *obj) {
+ DictEntry *e;
+
+ return (e = find(key)) ? e->val.copy(obj) : obj->initNull();
+}
+
+char *Dict::getKey(int i) {
+ return entries[i].key;
+}
+
+Object *Dict::getVal(int i, Object *obj) {
+ return entries[i].val.fetch(xref, obj);
+}
+
+Object *Dict::getValNF(int i, Object *obj) {
+ return entries[i].val.copy(obj);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Dict.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Dict.h
new file mode 100644
index 00000000000..b05f729e0a8
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Dict.h
@@ -0,0 +1,89 @@
+//========================================================================
+//
+// Dict.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef DICT_H
+#define DICT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+#include "Object.h"
+
+struct DictEntry;
+
+//------------------------------------------------------------------------
+// Dict
+//------------------------------------------------------------------------
+
+class Dict {
+public:
+
+ // Constructor.
+ Dict(XRef *xrefA);
+
+ // Destructor.
+ ~Dict();
+
+ // Reference counting.
+#if MULTITHREADED
+ long incRef() { return gAtomicIncrement(&ref); }
+ long decRef() { return gAtomicDecrement(&ref); }
+#else
+ long incRef() { return ++ref; }
+ long decRef() { return --ref; }
+#endif
+
+ // Get number of entries.
+ int getLength() { return length; }
+
+ // Add an entry. NB: does not copy key.
+ void add(char *key, Object *val);
+
+ // Check if dictionary is of specified type.
+ GBool is(const char *type);
+
+ // Look up an entry and return the value. Returns a null object
+ // if <key> is not in the dictionary.
+ Object *lookup(const char *key, Object *obj, int recursion = 0);
+ Object *lookupNF(const char *key, Object *obj);
+
+ // Iterative accessors.
+ char *getKey(int i);
+ Object *getVal(int i, Object *obj);
+ Object *getValNF(int i, Object *obj);
+
+ // Set the xref pointer. This is only used in one special case: the
+ // trailer dictionary, which is read before the xref table is
+ // parsed.
+ void setXRef(XRef *xrefA) { xref = xrefA; }
+
+private:
+
+ XRef *xref; // the xref table for this PDF file
+ DictEntry *entries; // array of entries
+ DictEntry **hashTab; // hash table pointers
+ int size; // size of <entries> array
+ int length; // number of entries in dictionary
+#if MULTITHREADED
+ GAtomicCounter ref; // reference count
+#else
+ long ref; // reference count
+#endif
+
+ DictEntry *find(const char *key);
+ void expand();
+ int hash(const char *key);
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/DisplayState.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/DisplayState.cc
new file mode 100644
index 00000000000..e0d306857ca
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/DisplayState.cc
@@ -0,0 +1,223 @@
+//========================================================================
+//
+// DisplayState.cc
+//
+// Copyright 2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "TileMap.h"
+#include "TileCache.h"
+#include "TileCompositor.h"
+#include "DisplayState.h"
+
+
+//------------------------------------------------------------------------
+// DisplayState
+//------------------------------------------------------------------------
+
+DisplayState::DisplayState(int maxTileWidthA, int maxTileHeightA,
+ int tileCacheSizeA, int nWorkerThreadsA,
+ SplashColorMode colorModeA, int bitmapRowPadA) {
+ int i;
+
+ maxTileWidth = maxTileWidthA;
+ maxTileHeight = maxTileHeightA;
+ tileCacheSize = tileCacheSizeA;
+ nWorkerThreads = nWorkerThreadsA;
+ colorMode = colorModeA;
+ bitmapRowPad = bitmapRowPadA;
+
+ tileMap = NULL;
+ tileCache = NULL;
+ tileCompositor = NULL;
+
+ for (i = 0; i < splashColorModeNComps[colorMode]; ++i) {
+ paperColor[i] = 0xff;
+ matteColor[i] = 0x80;
+ }
+ if (colorMode == splashModeRGB8 || colorMode == splashModeBGR8) {
+ selectColor[0] = 0x80;
+ selectColor[1] = 0x80;
+ selectColor[2] = 0xff;
+ } else {
+ for (i = 0; i < splashColorModeNComps[colorMode]; ++i) {
+ selectColor[i] = 0xa0;
+ }
+ }
+ reverseVideo = gFalse;
+
+ doc = NULL;
+
+ winW = winH = 100;
+ displayMode = displayContinuous;
+ zoom = 100;
+ rotate = 0;
+ scrollPage = 0;
+ scrollX = scrollY = 0;
+
+ selectRects = NULL;
+
+
+}
+
+DisplayState::~DisplayState() {
+ if (selectRects) {
+ deleteGList(selectRects, SelectRect);
+ }
+}
+
+void DisplayState::setPaperColor(SplashColorPtr paperColorA) {
+ splashColorCopy(paperColor, paperColorA);
+ tileCache->paperColorChanged();
+ tileCompositor->paperColorChanged();
+}
+
+void DisplayState::setMatteColor(SplashColorPtr matteColorA) {
+ splashColorCopy(matteColor, matteColorA);
+ tileCompositor->matteColorChanged();
+}
+
+void DisplayState::setSelectColor(SplashColorPtr selectColorA) {
+ splashColorCopy(selectColor, selectColorA);
+ tileCompositor->selectColorChanged();
+}
+
+void DisplayState::setReverseVideo(GBool reverseVideoA) {
+ if (reverseVideo != reverseVideoA) {
+ reverseVideo = reverseVideoA;
+ tileCache->reverseVideoChanged();
+ tileCompositor->reverseVideoChanged();
+ }
+}
+
+void DisplayState::setDoc(PDFDoc *docA) {
+ doc = docA;
+ tileMap->docChanged();
+ tileCache->docChanged();
+ tileCompositor->docChanged();
+}
+
+void DisplayState::setWindowSize(int winWA, int winHA) {
+ if (winW != winWA || winH != winHA) {
+ winW = winWA;
+ winH = winHA;
+ tileMap->windowSizeChanged();
+ tileCompositor->windowSizeChanged();
+ }
+}
+
+void DisplayState::setDisplayMode(DisplayMode displayModeA) {
+ if (displayMode != displayModeA) {
+ displayMode = displayModeA;
+ tileMap->displayModeChanged();
+ tileCompositor->displayModeChanged();
+ }
+}
+
+void DisplayState::setZoom(double zoomA) {
+ if (zoom != zoomA) {
+ zoom = zoomA;
+ tileMap->zoomChanged();
+ tileCompositor->zoomChanged();
+ }
+}
+
+void DisplayState::setRotate(int rotateA) {
+ if (rotate != rotateA) {
+ rotate = rotateA;
+ tileMap->rotateChanged();
+ tileCompositor->rotateChanged();
+ }
+}
+
+void DisplayState::setScrollPosition(int scrollPageA,
+ int scrollXA, int scrollYA) {
+ if (scrollPage != scrollPageA ||
+ scrollX != scrollXA ||
+ scrollY != scrollYA) {
+ scrollPage = scrollPageA;
+ scrollX = scrollXA;
+ scrollY = scrollYA;
+ tileMap->scrollPositionChanged();
+ tileCompositor->scrollPositionChanged();
+ }
+}
+
+void DisplayState::setSelection(int selectPage,
+ double selectX0, double selectY0,
+ double selectX1, double selectY1) {
+ GList *rects;
+ SelectRect *rect;
+
+ rect = new SelectRect(selectPage, selectX0, selectY0, selectX1, selectY1);
+ rects = new GList();
+ rects->append(rect);
+ setSelection(rects);
+}
+
+void DisplayState::setSelection(GList *selectRectsA) {
+ SelectRect *rect, *rectA;
+ int i;
+
+ if (!selectRects && !selectRectsA) {
+ return;
+ }
+ if (selectRects && selectRectsA &&
+ selectRects->getLength() == selectRectsA->getLength()) {
+ for (i = 0; i < selectRects->getLength(); ++i) {
+ rect = (SelectRect *)selectRects->get(i);
+ rectA = (SelectRect *)selectRectsA->get(i);
+ if (*rect != *rectA) {
+ break;
+ }
+ }
+ if (i == selectRects->getLength()) {
+ deleteGList(selectRectsA, SelectRect);
+ return;
+ }
+ }
+ if (selectRects) {
+ deleteGList(selectRects, SelectRect);
+ }
+ selectRects = selectRectsA;
+ tileCompositor->selectionChanged();
+}
+
+void DisplayState::clearSelection() {
+ setSelection(NULL);
+}
+
+int DisplayState::getNumSelectRects() {
+ if (!selectRects) {
+ return 0;
+ }
+ return selectRects->getLength();
+}
+
+SelectRect *DisplayState::getSelectRect(int idx) {
+ return (SelectRect *)selectRects->get(idx);
+}
+
+void DisplayState::optionalContentChanged() {
+ tileCache->optionalContentChanged();
+ tileCompositor->optionalContentChanged();
+}
+
+
+
+void DisplayState::forceRedraw() {
+ tileMap->forceRedraw();
+ tileCache->forceRedraw();
+ tileCompositor->forceRedraw();
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/DisplayState.h b/Build/source/libs/xpdf/xpdf-src/xpdf/DisplayState.h
new file mode 100644
index 00000000000..12c8170f0b3
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/DisplayState.h
@@ -0,0 +1,173 @@
+//========================================================================
+//
+// DisplayState.h
+//
+// Copyright 2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef DISPLAYSTATE_H
+#define DISPLAYSTATE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "SplashTypes.h"
+
+class GString;
+class GList;
+class PDFDoc;
+class TileMap;
+class TileCache;
+class TileCompositor;
+
+//------------------------------------------------------------------------
+// zoom level
+//------------------------------------------------------------------------
+
+// positive zoom levels are percentage of 72 dpi
+// (e.g., 50 means 36dpi, 100 means 72dpi, 150 means 108 dpi)
+
+#define zoomPage -1
+#define zoomWidth -2
+#define zoomHeight -3
+
+//------------------------------------------------------------------------
+// display mode
+//------------------------------------------------------------------------
+
+enum DisplayMode {
+ displaySingle,
+ displayContinuous,
+ displaySideBySideSingle,
+ displaySideBySideContinuous,
+ displayHorizontalContinuous
+};
+
+//------------------------------------------------------------------------
+// SelectRect
+//------------------------------------------------------------------------
+
+class SelectRect {
+public:
+
+ SelectRect(int pageA, double x0A, double y0A, double x1A, double y1A):
+ page(pageA), x0(x0A), y0(y0A), x1(x1A), y1(y1A) {}
+ int operator==(SelectRect r)
+ { return page == r.page && x0 == r.x0 && y0 == r.y0 &&
+ x1 == r.x1 && y1 == r.y1; }
+ int operator!=(SelectRect r)
+ { return page != r.page || x0 != r.x0 || y0 != r.y0 ||
+ x1 != r.x1 || y1 != r.y1; }
+
+ int page;
+ double x0, y0, x1, y1; // user coords
+};
+
+
+//------------------------------------------------------------------------
+// DisplayState
+//------------------------------------------------------------------------
+
+class DisplayState {
+public:
+
+ DisplayState(int maxTileWidthA, int maxTileHeightA,
+ int tileCacheSizeA, int nWorkerThreadsA,
+ SplashColorMode colorModeA, int bitmapRowPadA);
+ ~DisplayState();
+
+ void setTileMap(TileMap *tileMapA)
+ { tileMap = tileMapA; }
+ void setTileCache(TileCache *tileCacheA)
+ { tileCache = tileCacheA; }
+ void setTileCompositor(TileCompositor *tileCompositorA)
+ { tileCompositor = tileCompositorA; }
+
+ void setPaperColor(SplashColorPtr paperColorA);
+ void setMatteColor(SplashColorPtr matteColorA);
+ void setSelectColor(SplashColorPtr selectColorA);
+ void setReverseVideo(GBool reverseVideoA);
+ void setDoc(PDFDoc *docA);
+ void setWindowSize(int winWA, int winHA);
+ void setDisplayMode(DisplayMode displayModeA);
+ void setZoom(double zoomA);
+ void setRotate(int rotateA);
+ void setScrollPosition(int scrollPageA, int scrollXA, int scrollYA);
+ void setSelection(int selectPage, double selectX0, double selectY0,
+ double selectX1, double selectY1);
+ void setSelection(GList *selectRectsA);
+ void clearSelection();
+ void forceRedraw();
+
+ int getMaxTileWidth() { return maxTileWidth; }
+ int getMaxTileHeight() { return maxTileHeight; }
+ int getTileCacheSize() { return tileCacheSize; }
+ int getNWorkerThreads() { return nWorkerThreads; }
+ SplashColorMode getColorMode() { return colorMode; }
+ int getBitmapRowPad() { return bitmapRowPad; }
+ SplashColorPtr getPaperColor() { return paperColor; }
+ SplashColorPtr getMatteColor() { return matteColor; }
+ SplashColorPtr getSelectColor() { return selectColor; }
+ GBool getReverseVideo() { return reverseVideo; }
+ PDFDoc *getDoc() { return doc; }
+ int getWinW() { return winW; }
+ int getWinH() { return winH; }
+ DisplayMode getDisplayMode() { return displayMode; }
+ GBool displayModeIsContinuous()
+ { return displayMode == displayContinuous ||
+ displayMode == displaySideBySideContinuous ||
+ displayMode == displayHorizontalContinuous; }
+ GBool displayModeIsSideBySide()
+ { return displayMode == displaySideBySideSingle ||
+ displayMode == displaySideBySideContinuous; }
+ double getZoom() { return zoom; }
+ int getRotate() { return rotate; }
+ int getScrollPage() { return scrollPage; }
+ int getScrollX() { return scrollX; }
+ int getScrollY() { return scrollY; }
+ GBool hasSelection() { return selectRects != NULL; }
+ GList *getSelectRects() { return selectRects; }
+ int getNumSelectRects();
+ SelectRect *getSelectRect(int idx);
+ void optionalContentChanged();
+
+private:
+
+ int maxTileWidth, maxTileHeight;
+ int tileCacheSize;
+ int nWorkerThreads;
+
+ SplashColorMode colorMode;
+ int bitmapRowPad;
+
+ TileMap *tileMap;
+ TileCache *tileCache;
+ TileCompositor *tileCompositor;
+
+ SplashColor paperColor;
+ SplashColor matteColor;
+ SplashColor selectColor;
+ GBool reverseVideo;
+
+ PDFDoc *doc;
+
+ int winW, winH; // window (draw area) size
+ DisplayMode displayMode;
+ double zoom; // zoom level (see zoom* defines, above)
+ int rotate; // rotation (0, 90, 180, or 270)
+ int scrollPage; // scroll page - only used in
+ // non-continuous modes
+ int scrollX, scrollY;
+
+ GList *selectRects; // selection rectangles [SelectRect]
+ // (NULL if there is no selection)
+
+
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Error.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Error.cc
new file mode 100644
index 00000000000..5a97505ed84
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Error.cc
@@ -0,0 +1,92 @@
+//========================================================================
+//
+// Error.cc
+//
+// Copyright 1996-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include "gmempp.h"
+#include "GString.h"
+#include "GlobalParams.h"
+#include "Error.h"
+
+const char *errorCategoryNames[] = {
+ "Syntax Warning",
+ "Syntax Error",
+ "Config Error",
+ "Command Line Error",
+ "I/O Error",
+ "Permission Error",
+ "Unimplemented Feature",
+ "Internal Error"
+};
+
+static void (*errorCbk)(void *data, ErrorCategory category,
+ int pos, char *msg) = NULL;
+static void *errorCbkData = NULL;
+
+void setErrorCallback(void (*cbk)(void *data, ErrorCategory category,
+ int pos, char *msg),
+ void *data) {
+ errorCbk = cbk;
+ errorCbkData = data;
+}
+
+void *getErrorCallbackData() {
+ return errorCbkData;
+}
+
+void CDECL error(ErrorCategory category, GFileOffset pos,
+ const char *msg, ...) {
+ va_list args;
+ GString *s, *sanitized;
+ char c;
+ int i;
+
+ // NB: this can be called before the globalParams object is created
+ if (!errorCbk && globalParams && globalParams->getErrQuiet()) {
+ return;
+ }
+ va_start(args, msg);
+ s = GString::formatv(msg, args);
+ va_end(args);
+
+ // remove non-printable characters, just in case they might cause
+ // problems for the terminal program
+ sanitized = new GString();
+ for (i = 0; i < s->getLength(); ++i) {
+ c = s->getChar(i);
+ if (c >= 0x20 && c <= 0x7e) {
+ sanitized->append(c);
+ } else {
+ sanitized->appendf("<{0:02x}>", c & 0xff);
+ }
+ }
+
+ if (errorCbk) {
+ (*errorCbk)(errorCbkData, category, (int)pos, sanitized->getCString());
+ } else {
+ fflush(stdout);
+ if (pos >= 0) {
+ fprintf(stderr, "%s (%d): %s\n",
+ errorCategoryNames[category], (int)pos, sanitized->getCString());
+ } else {
+ fprintf(stderr, "%s: %s\n",
+ errorCategoryNames[category], sanitized->getCString());
+ }
+ fflush(stderr);
+ }
+
+ delete s;
+ delete sanitized;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Error.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Error.h
new file mode 100644
index 00000000000..39e7613e4e6
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Error.h
@@ -0,0 +1,48 @@
+//========================================================================
+//
+// Error.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ERROR_H
+#define ERROR_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include "config.h"
+#include "gfile.h"
+
+enum ErrorCategory {
+ errSyntaxWarning, // PDF syntax error which can be worked around;
+ // output will probably be correct
+ errSyntaxError, // PDF syntax error which cannot be worked around;
+ // output will probably be incorrect
+ errConfig, // error in Xpdf config info (xpdfrc file, etc.)
+ errCommandLine, // error in user-supplied parameters, action not
+ // allowed, etc. (only used by command-line tools)
+ errIO, // error in file I/O
+ errNotAllowed, // action not allowed by PDF permission bits
+ errUnimplemented, // unimplemented PDF feature - display will be
+ // incorrect
+ errInternal // internal error - malfunction within the Xpdf code
+};
+
+extern const char *errorCategoryNames[];
+
+extern void setErrorCallback(void (*cbk)(void *data, ErrorCategory category,
+ int pos, char *msg),
+ void *data);
+
+extern void *getErrorCallbackData();
+
+extern void CDECL error(ErrorCategory category, GFileOffset pos,
+ const char *msg, ...);
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ErrorCodes.h b/Build/source/libs/xpdf/xpdf-src/xpdf/ErrorCodes.h
new file mode 100644
index 00000000000..b28528df564
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ErrorCodes.h
@@ -0,0 +1,36 @@
+//========================================================================
+//
+// ErrorCodes.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ERRORCODES_H
+#define ERRORCODES_H
+
+#define errNone 0 // no error
+
+#define errOpenFile 1 // couldn't open the PDF file
+
+#define errBadCatalog 2 // couldn't read the page catalog
+
+#define errDamaged 3 // PDF file was damaged and couldn't be
+ // repaired
+
+#define errEncrypted 4 // file was encrypted and password was
+ // incorrect or not supplied
+
+#define errHighlightFile 5 // nonexistent or invalid highlight file
+
+#define errBadPrinter 6 // invalid printer
+
+#define errPrinting 7 // error during printing
+
+#define errPermission 8 // PDF file doesn't allow that operation
+
+#define errBadPageNum 9 // invalid page number
+
+#define errFileIO 10 // file I/O error
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/FontEncodingTables.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/FontEncodingTables.cc
new file mode 100644
index 00000000000..d4b18ab68c5
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/FontEncodingTables.cc
@@ -0,0 +1,1825 @@
+//========================================================================
+//
+// FontEncodingTables.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdlib.h>
+#include "gmempp.h"
+#include "FontEncodingTables.h"
+
+const char *macRomanEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quotesingle",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "grave",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ NULL,
+ "Adieresis",
+ "Aring",
+ "Ccedilla",
+ "Eacute",
+ "Ntilde",
+ "Odieresis",
+ "Udieresis",
+ "aacute",
+ "agrave",
+ "acircumflex",
+ "adieresis",
+ "atilde",
+ "aring",
+ "ccedilla",
+ "eacute",
+ "egrave",
+ "ecircumflex",
+ "edieresis",
+ "iacute",
+ "igrave",
+ "icircumflex",
+ "idieresis",
+ "ntilde",
+ "oacute",
+ "ograve",
+ "ocircumflex",
+ "odieresis",
+ "otilde",
+ "uacute",
+ "ugrave",
+ "ucircumflex",
+ "udieresis",
+ "dagger",
+ "degree",
+ "cent",
+ "sterling",
+ "section",
+ "bullet",
+ "paragraph",
+ "germandbls",
+ "registered",
+ "copyright",
+ "trademark",
+ "acute",
+ "dieresis",
+ "notequal",
+ "AE",
+ "Oslash",
+ "infinity",
+ "plusminus",
+ "lessequal",
+ "greaterequal",
+ "yen",
+ "mu",
+ "partialdiff",
+ "summation",
+ "product",
+ "pi",
+ "integral",
+ "ordfeminine",
+ "ordmasculine",
+ "Omega",
+ "ae",
+ "oslash",
+ "questiondown",
+ "exclamdown",
+ "logicalnot",
+ "radical",
+ "florin",
+ "approxequal",
+ "Delta",
+ "guillemotleft",
+ "guillemotright",
+ "ellipsis",
+ "space",
+ "Agrave",
+ "Atilde",
+ "Otilde",
+ "OE",
+ "oe",
+ "endash",
+ "emdash",
+ "quotedblleft",
+ "quotedblright",
+ "quoteleft",
+ "quoteright",
+ "divide",
+ "lozenge",
+ "ydieresis",
+ "Ydieresis",
+ "fraction",
+ "currency",
+ "guilsinglleft",
+ "guilsinglright",
+ "fi",
+ "fl",
+ "daggerdbl",
+ "periodcentered",
+ "quotesinglbase",
+ "quotedblbase",
+ "perthousand",
+ "Acircumflex",
+ "Ecircumflex",
+ "Aacute",
+ "Edieresis",
+ "Egrave",
+ "Iacute",
+ "Icircumflex",
+ "Idieresis",
+ "Igrave",
+ "Oacute",
+ "Ocircumflex",
+ "apple",
+ "Ograve",
+ "Uacute",
+ "Ucircumflex",
+ "Ugrave",
+ "dotlessi",
+ "circumflex",
+ "tilde",
+ "macron",
+ "breve",
+ "dotaccent",
+ "ring",
+ "cedilla",
+ "hungarumlaut",
+ "ogonek",
+ "caron"
+};
+
+const char *macExpertEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclamsmall",
+ "Hungarumlautsmall",
+ "centoldstyle",
+ "dollaroldstyle",
+ "dollarsuperior",
+ "ampersandsmall",
+ "Acutesmall",
+ "parenleftsuperior",
+ "parenrightsuperior",
+ "twodotenleader",
+ "onedotenleader",
+ "comma",
+ "hyphen",
+ "period",
+ "fraction",
+ "zerooldstyle",
+ "oneoldstyle",
+ "twooldstyle",
+ "threeoldstyle",
+ "fouroldstyle",
+ "fiveoldstyle",
+ "sixoldstyle",
+ "sevenoldstyle",
+ "eightoldstyle",
+ "nineoldstyle",
+ "colon",
+ "semicolon",
+ NULL,
+ "threequartersemdash",
+ NULL,
+ "questionsmall",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Ethsmall",
+ NULL,
+ NULL,
+ "onequarter",
+ "onehalf",
+ "threequarters",
+ "oneeighth",
+ "threeeighths",
+ "fiveeighths",
+ "seveneighths",
+ "onethird",
+ "twothirds",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "ff",
+ "fi",
+ "fl",
+ "ffi",
+ "ffl",
+ "parenleftinferior",
+ NULL,
+ "parenrightinferior",
+ "Circumflexsmall",
+ "hypheninferior",
+ "Gravesmall",
+ "Asmall",
+ "Bsmall",
+ "Csmall",
+ "Dsmall",
+ "Esmall",
+ "Fsmall",
+ "Gsmall",
+ "Hsmall",
+ "Ismall",
+ "Jsmall",
+ "Ksmall",
+ "Lsmall",
+ "Msmall",
+ "Nsmall",
+ "Osmall",
+ "Psmall",
+ "Qsmall",
+ "Rsmall",
+ "Ssmall",
+ "Tsmall",
+ "Usmall",
+ "Vsmall",
+ "Wsmall",
+ "Xsmall",
+ "Ysmall",
+ "Zsmall",
+ "colonmonetary",
+ "onefitted",
+ "rupiah",
+ "Tildesmall",
+ NULL,
+ NULL,
+ "asuperior",
+ "centsuperior",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Aacutesmall",
+ "Agravesmall",
+ "Acircumflexsmall",
+ "Adieresissmall",
+ "Atildesmall",
+ "Aringsmall",
+ "Ccedillasmall",
+ "Eacutesmall",
+ "Egravesmall",
+ "Ecircumflexsmall",
+ "Edieresissmall",
+ "Iacutesmall",
+ "Igravesmall",
+ "Icircumflexsmall",
+ "Idieresissmall",
+ "Ntildesmall",
+ "Oacutesmall",
+ "Ogravesmall",
+ "Ocircumflexsmall",
+ "Odieresissmall",
+ "Otildesmall",
+ "Uacutesmall",
+ "Ugravesmall",
+ "Ucircumflexsmall",
+ "Udieresissmall",
+ NULL,
+ "eightsuperior",
+ "fourinferior",
+ "threeinferior",
+ "sixinferior",
+ "eightinferior",
+ "seveninferior",
+ "Scaronsmall",
+ NULL,
+ "centinferior",
+ "twoinferior",
+ NULL,
+ "Dieresissmall",
+ NULL,
+ "Caronsmall",
+ "osuperior",
+ "fiveinferior",
+ NULL,
+ "commainferior",
+ "periodinferior",
+ "Yacutesmall",
+ NULL,
+ "dollarinferior",
+ NULL,
+ NULL,
+ "Thornsmall",
+ NULL,
+ "nineinferior",
+ "zeroinferior",
+ "Zcaronsmall",
+ "AEsmall",
+ "Oslashsmall",
+ "questiondownsmall",
+ "oneinferior",
+ "Lslashsmall",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Cedillasmall",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "OEsmall",
+ "figuredash",
+ "hyphensuperior",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "exclamdownsmall",
+ NULL,
+ "Ydieresissmall",
+ NULL,
+ "onesuperior",
+ "twosuperior",
+ "threesuperior",
+ "foursuperior",
+ "fivesuperior",
+ "sixsuperior",
+ "sevensuperior",
+ "ninesuperior",
+ "zerosuperior",
+ NULL,
+ "esuperior",
+ "rsuperior",
+ "tsuperior",
+ NULL,
+ NULL,
+ "isuperior",
+ "ssuperior",
+ "dsuperior",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "lsuperior",
+ "Ogoneksmall",
+ "Brevesmall",
+ "Macronsmall",
+ "bsuperior",
+ "nsuperior",
+ "msuperior",
+ "commasuperior",
+ "periodsuperior",
+ "Dotaccentsmall",
+ "Ringsmall",
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+const char *winAnsiEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quotesingle",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "grave",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ "bullet",
+ "Euro",
+ "bullet",
+ "quotesinglbase",
+ "florin",
+ "quotedblbase",
+ "ellipsis",
+ "dagger",
+ "daggerdbl",
+ "circumflex",
+ "perthousand",
+ "Scaron",
+ "guilsinglleft",
+ "OE",
+ "bullet",
+ "Zcaron",
+ "bullet",
+ "bullet",
+ "quoteleft",
+ "quoteright",
+ "quotedblleft",
+ "quotedblright",
+ "bullet",
+ "endash",
+ "emdash",
+ "tilde",
+ "trademark",
+ "scaron",
+ "guilsinglright",
+ "oe",
+ "bullet",
+ "zcaron",
+ "Ydieresis",
+ "space",
+ "exclamdown",
+ "cent",
+ "sterling",
+ "currency",
+ "yen",
+ "brokenbar",
+ "section",
+ "dieresis",
+ "copyright",
+ "ordfeminine",
+ "guillemotleft",
+ "logicalnot",
+ "hyphen",
+ "registered",
+ "macron",
+ "degree",
+ "plusminus",
+ "twosuperior",
+ "threesuperior",
+ "acute",
+ "mu",
+ "paragraph",
+ "periodcentered",
+ "cedilla",
+ "onesuperior",
+ "ordmasculine",
+ "guillemotright",
+ "onequarter",
+ "onehalf",
+ "threequarters",
+ "questiondown",
+ "Agrave",
+ "Aacute",
+ "Acircumflex",
+ "Atilde",
+ "Adieresis",
+ "Aring",
+ "AE",
+ "Ccedilla",
+ "Egrave",
+ "Eacute",
+ "Ecircumflex",
+ "Edieresis",
+ "Igrave",
+ "Iacute",
+ "Icircumflex",
+ "Idieresis",
+ "Eth",
+ "Ntilde",
+ "Ograve",
+ "Oacute",
+ "Ocircumflex",
+ "Otilde",
+ "Odieresis",
+ "multiply",
+ "Oslash",
+ "Ugrave",
+ "Uacute",
+ "Ucircumflex",
+ "Udieresis",
+ "Yacute",
+ "Thorn",
+ "germandbls",
+ "agrave",
+ "aacute",
+ "acircumflex",
+ "atilde",
+ "adieresis",
+ "aring",
+ "ae",
+ "ccedilla",
+ "egrave",
+ "eacute",
+ "ecircumflex",
+ "edieresis",
+ "igrave",
+ "iacute",
+ "icircumflex",
+ "idieresis",
+ "eth",
+ "ntilde",
+ "ograve",
+ "oacute",
+ "ocircumflex",
+ "otilde",
+ "odieresis",
+ "divide",
+ "oslash",
+ "ugrave",
+ "uacute",
+ "ucircumflex",
+ "udieresis",
+ "yacute",
+ "thorn",
+ "ydieresis"
+};
+
+const char *standardEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quoteright",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "quoteleft",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "exclamdown",
+ "cent",
+ "sterling",
+ "fraction",
+ "yen",
+ "florin",
+ "section",
+ "currency",
+ "quotesingle",
+ "quotedblleft",
+ "guillemotleft",
+ "guilsinglleft",
+ "guilsinglright",
+ "fi",
+ "fl",
+ NULL,
+ "endash",
+ "dagger",
+ "daggerdbl",
+ "periodcentered",
+ NULL,
+ "paragraph",
+ "bullet",
+ "quotesinglbase",
+ "quotedblbase",
+ "quotedblright",
+ "guillemotright",
+ "ellipsis",
+ "perthousand",
+ NULL,
+ "questiondown",
+ NULL,
+ "grave",
+ "acute",
+ "circumflex",
+ "tilde",
+ "macron",
+ "breve",
+ "dotaccent",
+ "dieresis",
+ NULL,
+ "ring",
+ "cedilla",
+ NULL,
+ "hungarumlaut",
+ "ogonek",
+ "caron",
+ "emdash",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "AE",
+ NULL,
+ "ordfeminine",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Lslash",
+ "Oslash",
+ "OE",
+ "ordmasculine",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "ae",
+ NULL,
+ NULL,
+ NULL,
+ "dotlessi",
+ NULL,
+ NULL,
+ "lslash",
+ "oslash",
+ "oe",
+ "germandbls",
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+const char *expertEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclamsmall",
+ "Hungarumlautsmall",
+ NULL,
+ "dollaroldstyle",
+ "dollarsuperior",
+ "ampersandsmall",
+ "Acutesmall",
+ "parenleftsuperior",
+ "parenrightsuperior",
+ "twodotenleader",
+ "onedotenleader",
+ "comma",
+ "hyphen",
+ "period",
+ "fraction",
+ "zerooldstyle",
+ "oneoldstyle",
+ "twooldstyle",
+ "threeoldstyle",
+ "fouroldstyle",
+ "fiveoldstyle",
+ "sixoldstyle",
+ "sevenoldstyle",
+ "eightoldstyle",
+ "nineoldstyle",
+ "colon",
+ "semicolon",
+ "commasuperior",
+ "threequartersemdash",
+ "periodsuperior",
+ "questionsmall",
+ NULL,
+ "asuperior",
+ "bsuperior",
+ "centsuperior",
+ "dsuperior",
+ "esuperior",
+ NULL,
+ NULL,
+ NULL,
+ "isuperior",
+ NULL,
+ NULL,
+ "lsuperior",
+ "msuperior",
+ "nsuperior",
+ "osuperior",
+ NULL,
+ NULL,
+ "rsuperior",
+ "ssuperior",
+ "tsuperior",
+ NULL,
+ "ff",
+ "fi",
+ "fl",
+ "ffi",
+ "ffl",
+ "parenleftinferior",
+ NULL,
+ "parenrightinferior",
+ "Circumflexsmall",
+ "hyphensuperior",
+ "Gravesmall",
+ "Asmall",
+ "Bsmall",
+ "Csmall",
+ "Dsmall",
+ "Esmall",
+ "Fsmall",
+ "Gsmall",
+ "Hsmall",
+ "Ismall",
+ "Jsmall",
+ "Ksmall",
+ "Lsmall",
+ "Msmall",
+ "Nsmall",
+ "Osmall",
+ "Psmall",
+ "Qsmall",
+ "Rsmall",
+ "Ssmall",
+ "Tsmall",
+ "Usmall",
+ "Vsmall",
+ "Wsmall",
+ "Xsmall",
+ "Ysmall",
+ "Zsmall",
+ "colonmonetary",
+ "onefitted",
+ "rupiah",
+ "Tildesmall",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "exclamdownsmall",
+ "centoldstyle",
+ "Lslashsmall",
+ NULL,
+ NULL,
+ "Scaronsmall",
+ "Zcaronsmall",
+ "Dieresissmall",
+ "Brevesmall",
+ "Caronsmall",
+ NULL,
+ "Dotaccentsmall",
+ NULL,
+ NULL,
+ "Macronsmall",
+ NULL,
+ NULL,
+ "figuredash",
+ "hypheninferior",
+ NULL,
+ NULL,
+ "Ogoneksmall",
+ "Ringsmall",
+ "Cedillasmall",
+ NULL,
+ NULL,
+ NULL,
+ "onequarter",
+ "onehalf",
+ "threequarters",
+ "questiondownsmall",
+ "oneeighth",
+ "threeeighths",
+ "fiveeighths",
+ "seveneighths",
+ "onethird",
+ "twothirds",
+ NULL,
+ NULL,
+ "zerosuperior",
+ "onesuperior",
+ "twosuperior",
+ "threesuperior",
+ "foursuperior",
+ "fivesuperior",
+ "sixsuperior",
+ "sevensuperior",
+ "eightsuperior",
+ "ninesuperior",
+ "zeroinferior",
+ "oneinferior",
+ "twoinferior",
+ "threeinferior",
+ "fourinferior",
+ "fiveinferior",
+ "sixinferior",
+ "seveninferior",
+ "eightinferior",
+ "nineinferior",
+ "centinferior",
+ "dollarinferior",
+ "periodinferior",
+ "commainferior",
+ "Agravesmall",
+ "Aacutesmall",
+ "Acircumflexsmall",
+ "Atildesmall",
+ "Adieresissmall",
+ "Aringsmall",
+ "AEsmall",
+ "Ccedillasmall",
+ "Egravesmall",
+ "Eacutesmall",
+ "Ecircumflexsmall",
+ "Edieresissmall",
+ "Igravesmall",
+ "Iacutesmall",
+ "Icircumflexsmall",
+ "Idieresissmall",
+ "Ethsmall",
+ "Ntildesmall",
+ "Ogravesmall",
+ "Oacutesmall",
+ "Ocircumflexsmall",
+ "Otildesmall",
+ "Odieresissmall",
+ "OEsmall",
+ "Oslashsmall",
+ "Ugravesmall",
+ "Uacutesmall",
+ "Ucircumflexsmall",
+ "Udieresissmall",
+ "Yacutesmall",
+ "Thornsmall",
+ "Ydieresissmall"
+};
+
+const char *symbolEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclam",
+ "universal",
+ "numbersign",
+ "existential",
+ "percent",
+ "ampersand",
+ "suchthat",
+ "parenleft",
+ "parenright",
+ "asteriskmath",
+ "plus",
+ "comma",
+ "minus",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "congruent",
+ "Alpha",
+ "Beta",
+ "Chi",
+ "Delta",
+ "Epsilon",
+ "Phi",
+ "Gamma",
+ "Eta",
+ "Iota",
+ "theta1",
+ "Kappa",
+ "Lambda",
+ "Mu",
+ "Nu",
+ "Omicron",
+ "Pi",
+ "Theta",
+ "Rho",
+ "Sigma",
+ "Tau",
+ "Upsilon",
+ "sigma1",
+ "Omega",
+ "Xi",
+ "Psi",
+ "Zeta",
+ "bracketleft",
+ "therefore",
+ "bracketright",
+ "perpendicular",
+ "underscore",
+ "radicalex",
+ "alpha",
+ "beta",
+ "chi",
+ "delta",
+ "epsilon",
+ "phi",
+ "gamma",
+ "eta",
+ "iota",
+ "phi1",
+ "kappa",
+ "lambda",
+ "mu",
+ "nu",
+ "omicron",
+ "pi",
+ "theta",
+ "rho",
+ "sigma",
+ "tau",
+ "upsilon",
+ "omega1",
+ "omega",
+ "xi",
+ "psi",
+ "zeta",
+ "braceleft",
+ "bar",
+ "braceright",
+ "similar",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Upsilon1",
+ "minute",
+ "lessequal",
+ "fraction",
+ "infinity",
+ "florin",
+ "club",
+ "diamond",
+ "heart",
+ "spade",
+ "arrowboth",
+ "arrowleft",
+ "arrowup",
+ "arrowright",
+ "arrowdown",
+ "degree",
+ "plusminus",
+ "second",
+ "greaterequal",
+ "multiply",
+ "proportional",
+ "partialdiff",
+ "bullet",
+ "divide",
+ "notequal",
+ "equivalence",
+ "approxequal",
+ "ellipsis",
+ "arrowvertex",
+ "arrowhorizex",
+ "carriagereturn",
+ "aleph",
+ "Ifraktur",
+ "Rfraktur",
+ "weierstrass",
+ "circlemultiply",
+ "circleplus",
+ "emptyset",
+ "intersection",
+ "union",
+ "propersuperset",
+ "reflexsuperset",
+ "notsubset",
+ "propersubset",
+ "reflexsubset",
+ "element",
+ "notelement",
+ "angle",
+ "gradient",
+ "registerserif",
+ "copyrightserif",
+ "trademarkserif",
+ "product",
+ "radical",
+ "dotmath",
+ "logicalnot",
+ "logicaland",
+ "logicalor",
+ "arrowdblboth",
+ "arrowdblleft",
+ "arrowdblup",
+ "arrowdblright",
+ "arrowdbldown",
+ "lozenge",
+ "angleleft",
+ "registersans",
+ "copyrightsans",
+ "trademarksans",
+ "summation",
+ "parenlefttp",
+ "parenleftex",
+ "parenleftbt",
+ "bracketlefttp",
+ "bracketleftex",
+ "bracketleftbt",
+ "bracelefttp",
+ "braceleftmid",
+ "braceleftbt",
+ "braceex",
+ NULL,
+ "angleright",
+ "integral",
+ "integraltp",
+ "integralex",
+ "integralbt",
+ "parenrighttp",
+ "parenrightex",
+ "parenrightbt",
+ "bracketrighttp",
+ "bracketrightex",
+ "bracketrightbt",
+ "bracerighttp",
+ "bracerightmid",
+ "bracerightbt",
+ NULL
+};
+
+const char *zapfDingbatsEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "a1",
+ "a2",
+ "a202",
+ "a3",
+ "a4",
+ "a5",
+ "a119",
+ "a118",
+ "a117",
+ "a11",
+ "a12",
+ "a13",
+ "a14",
+ "a15",
+ "a16",
+ "a105",
+ "a17",
+ "a18",
+ "a19",
+ "a20",
+ "a21",
+ "a22",
+ "a23",
+ "a24",
+ "a25",
+ "a26",
+ "a27",
+ "a28",
+ "a6",
+ "a7",
+ "a8",
+ "a9",
+ "a10",
+ "a29",
+ "a30",
+ "a31",
+ "a32",
+ "a33",
+ "a34",
+ "a35",
+ "a36",
+ "a37",
+ "a38",
+ "a39",
+ "a40",
+ "a41",
+ "a42",
+ "a43",
+ "a44",
+ "a45",
+ "a46",
+ "a47",
+ "a48",
+ "a49",
+ "a50",
+ "a51",
+ "a52",
+ "a53",
+ "a54",
+ "a55",
+ "a56",
+ "a57",
+ "a58",
+ "a59",
+ "a60",
+ "a61",
+ "a62",
+ "a63",
+ "a64",
+ "a65",
+ "a66",
+ "a67",
+ "a68",
+ "a69",
+ "a70",
+ "a71",
+ "a72",
+ "a73",
+ "a74",
+ "a203",
+ "a75",
+ "a204",
+ "a76",
+ "a77",
+ "a78",
+ "a79",
+ "a81",
+ "a82",
+ "a83",
+ "a84",
+ "a97",
+ "a98",
+ "a99",
+ "a100",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "a101",
+ "a102",
+ "a103",
+ "a104",
+ "a106",
+ "a107",
+ "a108",
+ "a112",
+ "a111",
+ "a110",
+ "a109",
+ "a120",
+ "a121",
+ "a122",
+ "a123",
+ "a124",
+ "a125",
+ "a126",
+ "a127",
+ "a128",
+ "a129",
+ "a130",
+ "a131",
+ "a132",
+ "a133",
+ "a134",
+ "a135",
+ "a136",
+ "a137",
+ "a138",
+ "a139",
+ "a140",
+ "a141",
+ "a142",
+ "a143",
+ "a144",
+ "a145",
+ "a146",
+ "a147",
+ "a148",
+ "a149",
+ "a150",
+ "a151",
+ "a152",
+ "a153",
+ "a154",
+ "a155",
+ "a156",
+ "a157",
+ "a158",
+ "a159",
+ "a160",
+ "a161",
+ "a163",
+ "a164",
+ "a196",
+ "a165",
+ "a192",
+ "a166",
+ "a167",
+ "a168",
+ "a169",
+ "a170",
+ "a171",
+ "a172",
+ "a173",
+ "a162",
+ "a174",
+ "a175",
+ "a176",
+ "a177",
+ "a178",
+ "a179",
+ "a193",
+ "a180",
+ "a199",
+ "a181",
+ "a200",
+ "a182",
+ NULL,
+ "a201",
+ "a183",
+ "a184",
+ "a197",
+ "a185",
+ "a194",
+ "a198",
+ "a186",
+ "a195",
+ "a187",
+ "a188",
+ "a189",
+ "a190",
+ "a191",
+ NULL
+};
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/FontEncodingTables.h b/Build/source/libs/xpdf/xpdf-src/xpdf/FontEncodingTables.h
new file mode 100644
index 00000000000..a417b324e48
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/FontEncodingTables.h
@@ -0,0 +1,20 @@
+//========================================================================
+//
+// FontEncodingTables.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FONTENCODINGTABLES_H
+#define FONTENCODINGTABLES_H
+
+extern const char *macRomanEncoding[];
+extern const char *macExpertEncoding[];
+extern const char *winAnsiEncoding[];
+extern const char *standardEncoding[];
+extern const char *expertEncoding[];
+extern const char *symbolEncoding[];
+extern const char *zapfDingbatsEncoding[];
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Function.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Function.cc
new file mode 100644
index 00000000000..82f32b9dd93
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Function.cc
@@ -0,0 +1,1602 @@
+//========================================================================
+//
+// Function.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GList.h"
+#include "Object.h"
+#include "Dict.h"
+#include "Stream.h"
+#include "Error.h"
+#include "Function.h"
+
+//------------------------------------------------------------------------
+
+// Max depth of nested functions. This is used to catch infinite
+// loops in the function object structure.
+#define recursionLimit 8
+
+//------------------------------------------------------------------------
+// Function
+//------------------------------------------------------------------------
+
+Function::Function() {
+}
+
+Function::~Function() {
+}
+
+Function *Function::parse(Object *funcObj, int expectedInputs,
+ int expectedOutputs, int recursion) {
+ Function *func;
+ Dict *dict;
+ int funcType;
+ Object obj1;
+
+ if (recursion > recursionLimit) {
+ error(errSyntaxError, -1, "Loop detected in function objects");
+ return NULL;
+ }
+
+ if (funcObj->isStream()) {
+ dict = funcObj->streamGetDict();
+ } else if (funcObj->isDict()) {
+ dict = funcObj->getDict();
+ } else if (funcObj->isName("Identity")) {
+ if (expectedInputs != expectedOutputs) {
+ error(errSyntaxError, -1, "Invalid use of identity function");
+ return NULL;
+ }
+ return new IdentityFunction(expectedInputs);
+ } else {
+ error(errSyntaxError, -1, "Expected function dictionary or stream");
+ return NULL;
+ }
+
+ if (!dict->lookup("FunctionType", &obj1)->isInt()) {
+ error(errSyntaxError, -1, "Function type is missing or wrong type");
+ obj1.free();
+ return NULL;
+ }
+ funcType = obj1.getInt();
+ obj1.free();
+
+ if (funcType == 0) {
+ func = new SampledFunction(funcObj, dict);
+ } else if (funcType == 2) {
+ func = new ExponentialFunction(funcObj, dict);
+ } else if (funcType == 3) {
+ func = new StitchingFunction(funcObj, dict, expectedInputs,
+ expectedOutputs, recursion);
+ } else if (funcType == 4) {
+ func = new PostScriptFunction(funcObj, dict);
+ } else {
+ error(errSyntaxError, -1, "Unimplemented function type ({0:d})", funcType);
+ return NULL;
+ }
+ if (!func->isOk()) {
+ delete func;
+ return NULL;
+ }
+
+ if (func->getInputSize() != expectedInputs ||
+ (expectedOutputs >= 0 && func->getOutputSize() != expectedOutputs)) {
+ error(errSyntaxError, -1,
+ "Incorrect number of function inputs or outputs");
+ delete func;
+ return NULL;
+ }
+
+ return func;
+}
+
+GBool Function::init(Dict *dict) {
+ Object obj1, obj2;
+ int i;
+
+ //----- Domain
+ if (!dict->lookup("Domain", &obj1)->isArray()) {
+ error(errSyntaxError, -1, "Function is missing domain");
+ goto err2;
+ }
+ m = obj1.arrayGetLength() / 2;
+ if (m > funcMaxInputs) {
+ error(errSyntaxError, -1,
+ "Functions with more than {0:d} inputs are unsupported",
+ funcMaxInputs);
+ goto err2;
+ }
+ for (i = 0; i < m; ++i) {
+ obj1.arrayGet(2*i, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function domain array");
+ goto err1;
+ }
+ domain[i][0] = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2*i+1, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function domain array");
+ goto err1;
+ }
+ domain[i][1] = obj2.getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ //----- Range
+ hasRange = gFalse;
+ n = 0;
+ if (dict->lookup("Range", &obj1)->isArray()) {
+ hasRange = gTrue;
+ n = obj1.arrayGetLength() / 2;
+ if (n > funcMaxOutputs) {
+ error(errSyntaxError, -1,
+ "Functions with more than {0:d} outputs are unsupported",
+ funcMaxOutputs);
+ goto err2;
+ }
+ for (i = 0; i < n; ++i) {
+ obj1.arrayGet(2*i, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function range array");
+ goto err1;
+ }
+ range[i][0] = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2*i+1, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function range array");
+ goto err1;
+ }
+ range[i][1] = obj2.getNum();
+ obj2.free();
+ }
+ }
+ obj1.free();
+
+ return gTrue;
+
+ err1:
+ obj2.free();
+ err2:
+ obj1.free();
+ return gFalse;
+}
+
+//------------------------------------------------------------------------
+// IdentityFunction
+//------------------------------------------------------------------------
+
+IdentityFunction::IdentityFunction(int nInputs) {
+ int i;
+
+ m = n = nInputs;
+ // domain info shouldn't be used anywhere
+ for (i = 0; i < nInputs; ++i) {
+ domain[i][0] = 0;
+ domain[i][1] = 1;
+ }
+ hasRange = gFalse;
+}
+
+IdentityFunction::~IdentityFunction() {
+}
+
+void IdentityFunction::transform(double *in, double *out) {
+ int i;
+
+ for (i = 0; i < m; ++i) {
+ out[i] = in[i];
+ }
+}
+
+//------------------------------------------------------------------------
+// SampledFunction
+//------------------------------------------------------------------------
+
+SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
+ Stream *str;
+ int sampleBits;
+ double sampleMul;
+ Object obj1, obj2;
+ Guint buf, bitMask;
+ int bits;
+ Guint s;
+ double in[funcMaxInputs];
+ int i, j, t, bit, idx;
+
+ idxOffset = NULL;
+ samples = NULL;
+ sBuf = NULL;
+ ok = gFalse;
+
+ //----- initialize the generic stuff
+ if (!init(dict)) {
+ goto err1;
+ }
+ if (!hasRange) {
+ error(errSyntaxError, -1, "Type 0 function is missing range");
+ goto err1;
+ }
+ if (m > sampledFuncMaxInputs) {
+ error(errSyntaxError, -1,
+ "Sampled functions with more than {0:d} inputs are unsupported",
+ sampledFuncMaxInputs);
+ goto err1;
+ }
+
+ //----- buffer
+ sBuf = (double *)gmallocn(1 << m, sizeof(double));
+
+ //----- get the stream
+ if (!funcObj->isStream()) {
+ error(errSyntaxError, -1, "Type 0 function isn't a stream");
+ goto err1;
+ }
+ str = funcObj->getStream();
+
+ //----- Size
+ if (!dict->lookup("Size", &obj1)->isArray() ||
+ obj1.arrayGetLength() != m) {
+ error(errSyntaxError, -1, "Function has missing or invalid size array");
+ goto err2;
+ }
+ for (i = 0; i < m; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!obj2.isInt()) {
+ error(errSyntaxError, -1, "Illegal value in function size array");
+ goto err3;
+ }
+ sampleSize[i] = obj2.getInt();
+ if (sampleSize[i] <= 0) {
+ error(errSyntaxError, -1, "Illegal non-positive value in function size array");
+ goto err3;
+ }
+ obj2.free();
+ }
+ obj1.free();
+ idxOffset = (int *)gmallocn(1 << m, sizeof(int));
+ for (i = 0; i < (1<<m); ++i) {
+ idx = 0;
+ for (j = m - 1, t = i; j >= 1; --j, t <<= 1) {
+ if (sampleSize[j] == 1) {
+ bit = 0;
+ } else {
+ bit = (t >> (m - 1)) & 1;
+ }
+ idx = (idx + bit) * sampleSize[j-1];
+ }
+ if (sampleSize[0] == 1) {
+ bit = 0;
+ } else {
+ bit = (t >> (m - 1)) & 1;
+ }
+ idxOffset[i] = (idx + bit) * n;
+ }
+
+ //----- BitsPerSample
+ if (!dict->lookup("BitsPerSample", &obj1)->isInt()) {
+ error(errSyntaxError, -1, "Function has missing or invalid BitsPerSample");
+ goto err2;
+ }
+ sampleBits = obj1.getInt();
+ sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1);
+ obj1.free();
+
+ //----- Encode
+ if (dict->lookup("Encode", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2*m) {
+ for (i = 0; i < m; ++i) {
+ obj1.arrayGet(2*i, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function encode array");
+ goto err3;
+ }
+ encode[i][0] = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2*i+1, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function encode array");
+ goto err3;
+ }
+ encode[i][1] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ for (i = 0; i < m; ++i) {
+ encode[i][0] = 0;
+ encode[i][1] = sampleSize[i] - 1;
+ }
+ }
+ obj1.free();
+ for (i = 0; i < m; ++i) {
+ inputMul[i] = (encode[i][1] - encode[i][0]) /
+ (domain[i][1] - domain[i][0]);
+ }
+
+ //----- Decode
+ if (dict->lookup("Decode", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2*n) {
+ for (i = 0; i < n; ++i) {
+ obj1.arrayGet(2*i, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function decode array");
+ goto err3;
+ }
+ decode[i][0] = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2*i+1, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function decode array");
+ goto err3;
+ }
+ decode[i][1] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ for (i = 0; i < n; ++i) {
+ decode[i][0] = range[i][0];
+ decode[i][1] = range[i][1];
+ }
+ }
+ obj1.free();
+
+ //----- samples
+ nSamples = n;
+ for (i = 0; i < m; ++i) {
+ if (nSamples > INT_MAX / sampleSize[i]) {
+ error(errSyntaxError, -1, "Integer overflow in sampled function setup");
+ goto err1;
+ }
+ nSamples *= sampleSize[i];
+ }
+ samples = (double *)gmallocn(nSamples, sizeof(double));
+ buf = 0;
+ bits = 0;
+ bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU;
+ str->reset();
+ for (i = 0; i < nSamples; ++i) {
+ if (sampleBits == 8) {
+ s = str->getChar();
+ } else if (sampleBits == 16) {
+ s = str->getChar();
+ s = (s << 8) + str->getChar();
+ } else if (sampleBits == 32) {
+ s = str->getChar();
+ s = (s << 8) + str->getChar();
+ s = (s << 8) + str->getChar();
+ s = (s << 8) + str->getChar();
+ } else {
+ while (bits < sampleBits) {
+ buf = (buf << 8) | (str->getChar() & 0xff);
+ bits += 8;
+ }
+ s = (buf >> (bits - sampleBits)) & bitMask;
+ bits -= sampleBits;
+ }
+ samples[i] = (double)s * sampleMul;
+ }
+ str->close();
+
+ // set up the cache
+ for (i = 0; i < m; ++i) {
+ in[i] = domain[i][0];
+ cacheIn[i] = in[i] - 1;
+ }
+ transform(in, cacheOut);
+
+ ok = gTrue;
+ return;
+
+ err3:
+ obj2.free();
+ err2:
+ obj1.free();
+ err1:
+ return;
+}
+
+SampledFunction::~SampledFunction() {
+ if (idxOffset) {
+ gfree(idxOffset);
+ }
+ if (samples) {
+ gfree(samples);
+ }
+ if (sBuf) {
+ gfree(sBuf);
+ }
+}
+
+SampledFunction::SampledFunction(SampledFunction *func) {
+ memcpy((void *)this, (void *)func, sizeof(SampledFunction));
+ idxOffset = (int *)gmallocn(1 << m, sizeof(int));
+ memcpy(idxOffset, func->idxOffset, (1 << m) * (int)sizeof(int));
+ samples = (double *)gmallocn(nSamples, sizeof(double));
+ memcpy(samples, func->samples, nSamples * sizeof(double));
+ sBuf = (double *)gmallocn(1 << m, sizeof(double));
+}
+
+void SampledFunction::transform(double *in, double *out) {
+ double x;
+ int e[funcMaxInputs];
+ double efrac0[funcMaxInputs];
+ double efrac1[funcMaxInputs];
+ int i, j, k, idx0, t;
+
+ // check the cache
+ for (i = 0; i < m; ++i) {
+ if (in[i] != cacheIn[i]) {
+ break;
+ }
+ }
+ if (i == m) {
+ for (i = 0; i < n; ++i) {
+ out[i] = cacheOut[i];
+ }
+ return;
+ }
+
+ // map input values into sample array
+ for (i = 0; i < m; ++i) {
+ x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0];
+ if (x < 0 || x != x) { // x!=x is a more portable version of isnan(x)
+ x = 0;
+ } else if (x > sampleSize[i] - 1) {
+ x = sampleSize[i] - 1;
+ }
+ e[i] = (int)x;
+ if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) {
+ // this happens if in[i] = domain[i][1]
+ e[i] = sampleSize[i] - 2;
+ }
+ efrac1[i] = x - e[i];
+ efrac0[i] = 1 - efrac1[i];
+ }
+
+ // compute index for the first sample to be used
+ idx0 = 0;
+ for (k = m - 1; k >= 1; --k) {
+ idx0 = (idx0 + e[k]) * sampleSize[k-1];
+ }
+ idx0 = (idx0 + e[0]) * n;
+
+ // for each output, do m-linear interpolation
+ for (i = 0; i < n; ++i) {
+
+ // pull 2^m values out of the sample array
+ for (j = 0; j < (1<<m); ++j) {
+ sBuf[j] = samples[idx0 + idxOffset[j] + i];
+ }
+
+ // do m sets of interpolations
+ for (j = 0, t = (1<<m); j < m; ++j, t >>= 1) {
+ for (k = 0; k < t; k += 2) {
+ sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k+1];
+ }
+ }
+
+ // map output value to range
+ out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0];
+ if (out[i] < range[i][0]) {
+ out[i] = range[i][0];
+ } else if (out[i] > range[i][1]) {
+ out[i] = range[i][1];
+ }
+ }
+
+ // save current result in the cache
+ for (i = 0; i < m; ++i) {
+ cacheIn[i] = in[i];
+ }
+ for (i = 0; i < n; ++i) {
+ cacheOut[i] = out[i];
+ }
+}
+
+//------------------------------------------------------------------------
+// ExponentialFunction
+//------------------------------------------------------------------------
+
+ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
+ Object obj1, obj2;
+ int i;
+
+ ok = gFalse;
+
+ //----- initialize the generic stuff
+ if (!init(dict)) {
+ goto err1;
+ }
+ if (m != 1) {
+ error(errSyntaxError, -1, "Exponential function with more than one input");
+ goto err1;
+ }
+
+ //----- C0
+ if (dict->lookup("C0", &obj1)->isArray()) {
+ if (hasRange && obj1.arrayGetLength() != n) {
+ error(errSyntaxError, -1, "Function's C0 array is wrong length");
+ goto err2;
+ }
+ n = obj1.arrayGetLength();
+ if (n > funcMaxOutputs) {
+ error(errSyntaxError, -1,
+ "Functions with more than {0:d} outputs are unsupported",
+ funcMaxOutputs);
+ goto err2;
+ }
+ for (i = 0; i < n; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function C0 array");
+ goto err3;
+ }
+ c0[i] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ if (hasRange && n != 1) {
+ error(errSyntaxError, -1, "Function's C0 array is wrong length");
+ goto err2;
+ }
+ n = 1;
+ c0[0] = 0;
+ }
+ obj1.free();
+
+ //----- C1
+ if (dict->lookup("C1", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() != n) {
+ error(errSyntaxError, -1, "Function's C1 array is wrong length");
+ goto err2;
+ }
+ for (i = 0; i < n; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function C1 array");
+ goto err3;
+ }
+ c1[i] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ if (n != 1) {
+ error(errSyntaxError, -1, "Function's C1 array is wrong length");
+ goto err2;
+ }
+ c1[0] = 1;
+ }
+ obj1.free();
+
+ //----- N (exponent)
+ if (!dict->lookup("N", &obj1)->isNum()) {
+ error(errSyntaxError, -1, "Function has missing or invalid N");
+ goto err2;
+ }
+ e = obj1.getNum();
+ obj1.free();
+
+ ok = gTrue;
+ return;
+
+ err3:
+ obj2.free();
+ err2:
+ obj1.free();
+ err1:
+ return;
+}
+
+ExponentialFunction::~ExponentialFunction() {
+}
+
+ExponentialFunction::ExponentialFunction(ExponentialFunction *func) {
+ memcpy((void *)this, (void *)func, sizeof(ExponentialFunction));
+}
+
+void ExponentialFunction::transform(double *in, double *out) {
+ double x;
+ int i;
+
+ if (in[0] < domain[0][0]) {
+ x = domain[0][0];
+ } else if (in[0] > domain[0][1]) {
+ x = domain[0][1];
+ } else {
+ x = in[0];
+ }
+ for (i = 0; i < n; ++i) {
+ out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]);
+ if (hasRange) {
+ if (out[i] < range[i][0]) {
+ out[i] = range[i][0];
+ } else if (out[i] > range[i][1]) {
+ out[i] = range[i][1];
+ }
+ }
+ }
+ return;
+}
+
+//------------------------------------------------------------------------
+// StitchingFunction
+//------------------------------------------------------------------------
+
+StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict,
+ int expectedInputs, int expectedOutputs,
+ int recursion) {
+ Object obj1, obj2;
+ int i;
+
+ ok = gFalse;
+ funcs = NULL;
+ bounds = NULL;
+ encode = NULL;
+ scale = NULL;
+
+ //----- initialize the generic stuff
+ if (!init(dict)) {
+ goto err1;
+ }
+ if (m != 1) {
+ error(errSyntaxError, -1, "Stitching function with more than one input");
+ goto err1;
+ }
+
+ //----- Functions
+ if (!dict->lookup("Functions", &obj1)->isArray() ||
+ obj1.arrayGetLength() < 1) {
+ error(errSyntaxError, -1,
+ "Missing 'Functions' entry in stitching function");
+ goto err1;
+ }
+ k = obj1.arrayGetLength();
+ funcs = (Function **)gmallocn(k, sizeof(Function *));
+ bounds = (double *)gmallocn(k + 1, sizeof(double));
+ encode = (double *)gmallocn(2 * k, sizeof(double));
+ scale = (double *)gmallocn(k, sizeof(double));
+ for (i = 0; i < k; ++i) {
+ funcs[i] = NULL;
+ }
+ for (i = 0; i < k; ++i) {
+ if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2),
+ expectedInputs, expectedOutputs,
+ recursion + 1))) {
+ goto err2;
+ }
+ if (i == 0) {
+ n = funcs[0]->getOutputSize();
+ }
+ if (funcs[i]->getInputSize() != 1 || funcs[i]->getOutputSize() != n) {
+ error(errSyntaxError, -1,
+ "Incompatible subfunctions in stitching function");
+ goto err2;
+ }
+ obj2.free();
+ }
+ obj1.free();
+
+ //----- Bounds
+ if (!dict->lookup("Bounds", &obj1)->isArray() ||
+ obj1.arrayGetLength() != k - 1) {
+ error(errSyntaxError, -1,
+ "Missing or invalid 'Bounds' entry in stitching function");
+ goto err1;
+ }
+ bounds[0] = domain[0][0];
+ for (i = 1; i < k; ++i) {
+ if (!obj1.arrayGet(i - 1, &obj2)->isNum()) {
+ error(errSyntaxError, -1,
+ "Invalid type in 'Bounds' array in stitching function");
+ goto err2;
+ }
+ bounds[i] = obj2.getNum();
+ obj2.free();
+ }
+ bounds[k] = domain[0][1];
+ obj1.free();
+
+ //----- Encode
+ if (!dict->lookup("Encode", &obj1)->isArray() ||
+ obj1.arrayGetLength() != 2 * k) {
+ error(errSyntaxError, -1,
+ "Missing or invalid 'Encode' entry in stitching function");
+ goto err1;
+ }
+ for (i = 0; i < 2 * k; ++i) {
+ if (!obj1.arrayGet(i, &obj2)->isNum()) {
+ error(errSyntaxError, -1,
+ "Invalid type in 'Encode' array in stitching function");
+ goto err2;
+ }
+ encode[i] = obj2.getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ //----- pre-compute the scale factors
+ for (i = 0; i < k; ++i) {
+ if (bounds[i] == bounds[i+1]) {
+ // avoid a divide-by-zero -- in this situation, function i will
+ // never be used anyway
+ scale[i] = 0;
+ } else {
+ scale[i] = (encode[2*i+1] - encode[2*i]) / (bounds[i+1] - bounds[i]);
+ }
+ }
+
+ ok = gTrue;
+ return;
+
+ err2:
+ obj2.free();
+ err1:
+ obj1.free();
+}
+
+StitchingFunction::StitchingFunction(StitchingFunction *func) {
+ int i;
+
+ memcpy((void *)this, (void *)func, sizeof(StitchingFunction));
+ funcs = (Function **)gmallocn(k, sizeof(Function *));
+ for (i = 0; i < k; ++i) {
+ funcs[i] = func->funcs[i]->copy();
+ }
+ bounds = (double *)gmallocn(k + 1, sizeof(double));
+ memcpy(bounds, func->bounds, (k + 1) * sizeof(double));
+ encode = (double *)gmallocn(2 * k, sizeof(double));
+ memcpy(encode, func->encode, 2 * k * sizeof(double));
+ scale = (double *)gmallocn(k, sizeof(double));
+ memcpy(scale, func->scale, k * sizeof(double));
+ ok = gTrue;
+}
+
+StitchingFunction::~StitchingFunction() {
+ int i;
+
+ if (funcs) {
+ for (i = 0; i < k; ++i) {
+ if (funcs[i]) {
+ delete funcs[i];
+ }
+ }
+ }
+ gfree(funcs);
+ gfree(bounds);
+ gfree(encode);
+ gfree(scale);
+}
+
+void StitchingFunction::transform(double *in, double *out) {
+ double x;
+ int i;
+
+ if (in[0] < domain[0][0]) {
+ x = domain[0][0];
+ } else if (in[0] > domain[0][1]) {
+ x = domain[0][1];
+ } else {
+ x = in[0];
+ }
+ for (i = 0; i < k - 1; ++i) {
+ if (x < bounds[i+1]) {
+ break;
+ }
+ }
+ x = encode[2*i] + (x - bounds[i]) * scale[i];
+ funcs[i]->transform(&x, out);
+}
+
+//------------------------------------------------------------------------
+// PostScriptFunction
+//------------------------------------------------------------------------
+
+// This is not an enum, because we can't foreward-declare the enum
+// type in Function.h
+//
+// NB: This must be kept in sync with psOpNames[] below.
+#define psOpAbs 0
+#define psOpAdd 1
+#define psOpAnd 2
+#define psOpAtan 3
+#define psOpBitshift 4
+#define psOpCeiling 5
+#define psOpCopy 6
+#define psOpCos 7
+#define psOpCvi 8
+#define psOpCvr 9
+#define psOpDiv 10
+#define psOpDup 11
+#define psOpEq 12
+#define psOpExch 13
+#define psOpExp 14
+#define psOpFalse 15
+#define psOpFloor 16
+#define psOpGe 17
+#define psOpGt 18
+#define psOpIdiv 19
+#define psOpIndex 20
+#define psOpLe 21
+#define psOpLn 22
+#define psOpLog 23
+#define psOpLt 24
+#define psOpMod 25
+#define psOpMul 26
+#define psOpNe 27
+#define psOpNeg 28
+#define psOpNot 29
+#define psOpOr 30
+#define psOpPop 31
+#define psOpRoll 32
+#define psOpRound 33
+#define psOpSin 34
+#define psOpSqrt 35
+#define psOpSub 36
+#define psOpTrue 37
+#define psOpTruncate 38
+#define psOpXor 39
+// the push/j/jz ops are used internally (and are not listed in psOpNames[])
+#define psOpPush 40
+#define psOpJ 41
+#define psOpJz 42
+
+#define nPSOps (sizeof(psOpNames) / sizeof(const char *))
+
+// Note: 'if' and 'ifelse' are parsed separately.
+// The rest are listed here in alphabetical order.
+//
+// NB: This must be kept in sync with the psOpXXX defines above.
+static const char *psOpNames[] = {
+ "abs",
+ "add",
+ "and",
+ "atan",
+ "bitshift",
+ "ceiling",
+ "copy",
+ "cos",
+ "cvi",
+ "cvr",
+ "div",
+ "dup",
+ "eq",
+ "exch",
+ "exp",
+ "false",
+ "floor",
+ "ge",
+ "gt",
+ "idiv",
+ "index",
+ "le",
+ "ln",
+ "log",
+ "lt",
+ "mod",
+ "mul",
+ "ne",
+ "neg",
+ "not",
+ "or",
+ "pop",
+ "roll",
+ "round",
+ "sin",
+ "sqrt",
+ "sub",
+ "true",
+ "truncate",
+ "xor"
+};
+
+struct PSCode {
+ int op;
+ union {
+ double d;
+ int i;
+ } val;
+};
+
+#define psStackSize 100
+
+PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
+ Stream *str;
+ GList *tokens;
+ GString *tok;
+ double in[funcMaxInputs];
+ int tokPtr, codePtr, i;
+
+ codeString = NULL;
+ code = NULL;
+ codeSize = 0;
+ ok = gFalse;
+
+ //----- initialize the generic stuff
+ if (!init(dict)) {
+ goto err1;
+ }
+ if (!hasRange) {
+ error(errSyntaxError, -1, "Type 4 function is missing range");
+ goto err1;
+ }
+
+ //----- get the stream
+ if (!funcObj->isStream()) {
+ error(errSyntaxError, -1, "Type 4 function isn't a stream");
+ goto err1;
+ }
+ str = funcObj->getStream();
+
+ //----- tokenize the function
+ codeString = new GString();
+ tokens = new GList();
+ str->reset();
+ while ((tok = getToken(str))) {
+ tokens->append(tok);
+ }
+ str->close();
+
+ //----- parse the function
+ if (tokens->getLength() < 1 ||
+ ((GString *)tokens->get(0))->cmp("{")) {
+ error(errSyntaxError, -1, "Expected '{{' at start of PostScript function");
+ goto err2;
+ }
+ tokPtr = 1;
+ codePtr = 0;
+ if (!parseCode(tokens, &tokPtr, &codePtr)) {
+ goto err2;
+ }
+ codeLen = codePtr;
+
+ //----- set up the cache
+ for (i = 0; i < m; ++i) {
+ in[i] = domain[i][0];
+ cacheIn[i] = in[i] - 1;
+ }
+ transform(in, cacheOut);
+
+ ok = gTrue;
+
+ err2:
+ deleteGList(tokens, GString);
+ err1:
+ return;
+}
+
+PostScriptFunction::PostScriptFunction(PostScriptFunction *func) {
+ memcpy((void *)this, (void *)func, sizeof(PostScriptFunction));
+ codeString = func->codeString->copy();
+ code = (PSCode *)gmallocn(codeSize, sizeof(PSCode));
+ memcpy(code, func->code, codeSize * sizeof(PSCode));
+}
+
+PostScriptFunction::~PostScriptFunction() {
+ gfree(code);
+ if (codeString) {
+ delete codeString;
+ }
+}
+
+void PostScriptFunction::transform(double *in, double *out) {
+ double stack[psStackSize];
+ double x;
+ int sp, i;
+
+ // check the cache
+ for (i = 0; i < m; ++i) {
+ if (in[i] != cacheIn[i]) {
+ break;
+ }
+ }
+ if (i == m) {
+ for (i = 0; i < n; ++i) {
+ out[i] = cacheOut[i];
+ }
+ return;
+ }
+
+ for (i = 0; i < m; ++i) {
+ stack[psStackSize - 1 - i] = in[i];
+ }
+ sp = exec(stack, psStackSize - m);
+ // if (sp < psStackSize - n) {
+ // error(errSyntaxWarning, -1,
+ // "Extra values on stack at end of PostScript function");
+ // }
+ if (sp > psStackSize - n) {
+ error(errSyntaxError, -1, "Stack underflow in PostScript function");
+ sp = psStackSize - n;
+ }
+ for (i = 0; i < n; ++i) {
+ x = stack[sp + n - 1 - i];
+ if (x < range[i][0]) {
+ out[i] = range[i][0];
+ } else if (x > range[i][1]) {
+ out[i] = range[i][1];
+ } else {
+ out[i] = x;
+ }
+ }
+
+ // save current result in the cache
+ for (i = 0; i < m; ++i) {
+ cacheIn[i] = in[i];
+ }
+ for (i = 0; i < n; ++i) {
+ cacheOut[i] = out[i];
+ }
+}
+
+GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) {
+ GString *tok;
+ char *p;
+ int a, b, mid, cmp;
+ int codePtr0, codePtr1;
+
+ while (1) {
+ if (*tokPtr >= tokens->getLength()) {
+ error(errSyntaxError, -1,
+ "Unexpected end of PostScript function stream");
+ return gFalse;
+ }
+ tok = (GString *)tokens->get((*tokPtr)++);
+ p = tok->getCString();
+ if (isdigit(*p) || *p == '.' || *p == '-') {
+ addCodeD(codePtr, psOpPush, atof(tok->getCString()));
+ } else if (!tok->cmp("{")) {
+ codePtr0 = *codePtr;
+ addCodeI(codePtr, psOpJz, 0);
+ if (!parseCode(tokens, tokPtr, codePtr)) {
+ return gFalse;
+ }
+ if (*tokPtr >= tokens->getLength()) {
+ error(errSyntaxError, -1,
+ "Unexpected end of PostScript function stream");
+ return gFalse;
+ }
+ tok = (GString *)tokens->get((*tokPtr)++);
+ if (!tok->cmp("if")) {
+ code[codePtr0].val.i = *codePtr;
+ } else if (!tok->cmp("{")) {
+ codePtr1 = *codePtr;
+ addCodeI(codePtr, psOpJ, 0);
+ code[codePtr0].val.i = *codePtr;
+ if (!parseCode(tokens, tokPtr, codePtr)) {
+ return gFalse;
+ }
+ if (*tokPtr >= tokens->getLength()) {
+ error(errSyntaxError, -1,
+ "Unexpected end of PostScript function stream");
+ return gFalse;
+ }
+ tok = (GString *)tokens->get((*tokPtr)++);
+ if (!tok->cmp("ifelse")) {
+ code[codePtr1].val.i = *codePtr;
+ } else {
+ error(errSyntaxError, -1,
+ "Expected 'ifelse' in PostScript function stream");
+ return gFalse;
+ }
+ } else {
+ error(errSyntaxError, -1,
+ "Expected 'if' in PostScript function stream");
+ return gFalse;
+ }
+ } else if (!tok->cmp("}")) {
+ break;
+ } else if (!tok->cmp("if")) {
+ error(errSyntaxError, -1,
+ "Unexpected 'if' in PostScript function stream");
+ return gFalse;
+ } else if (!tok->cmp("ifelse")) {
+ error(errSyntaxError, -1,
+ "Unexpected 'ifelse' in PostScript function stream");
+ return gFalse;
+ } else {
+ a = -1;
+ b = nPSOps;
+ cmp = 0; // make gcc happy
+ // invariant: psOpNames[a] < tok < psOpNames[b]
+ while (b - a > 1) {
+ mid = (a + b) / 2;
+ cmp = tok->cmp(psOpNames[mid]);
+ if (cmp > 0) {
+ a = mid;
+ } else if (cmp < 0) {
+ b = mid;
+ } else {
+ a = b = mid;
+ }
+ }
+ if (cmp != 0) {
+ error(errSyntaxError, -1,
+ "Unknown operator '{0:t}' in PostScript function",
+ tok);
+ return gFalse;
+ }
+ addCode(codePtr, a);
+ }
+ }
+ return gTrue;
+}
+
+void PostScriptFunction::addCode(int *codePtr, int op) {
+ if (*codePtr >= codeSize) {
+ if (codeSize) {
+ codeSize *= 2;
+ } else {
+ codeSize = 16;
+ }
+ code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
+ }
+ code[*codePtr].op = op;
+ ++(*codePtr);
+}
+
+void PostScriptFunction::addCodeI(int *codePtr, int op, int x) {
+ if (*codePtr >= codeSize) {
+ if (codeSize) {
+ codeSize *= 2;
+ } else {
+ codeSize = 16;
+ }
+ code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
+ }
+ code[*codePtr].op = op;
+ code[*codePtr].val.i = x;
+ ++(*codePtr);
+}
+
+void PostScriptFunction::addCodeD(int *codePtr, int op, double x) {
+ if (*codePtr >= codeSize) {
+ if (codeSize) {
+ codeSize *= 2;
+ } else {
+ codeSize = 16;
+ }
+ code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
+ }
+ code[*codePtr].op = op;
+ code[*codePtr].val.d = x;
+ ++(*codePtr);
+}
+
+GString *PostScriptFunction::getToken(Stream *str) {
+ GString *s;
+ int c;
+ GBool comment;
+
+ s = new GString();
+ comment = gFalse;
+ while (1) {
+ if ((c = str->getChar()) == EOF) {
+ delete s;
+ return NULL;
+ }
+ codeString->append((char)c);
+ if (comment) {
+ if (c == '\x0a' || c == '\x0d') {
+ comment = gFalse;
+ }
+ } else if (c == '%') {
+ comment = gTrue;
+ } else if (!isspace(c)) {
+ break;
+ }
+ }
+ if (c == '{' || c == '}') {
+ s->append((char)c);
+ } else if (isdigit(c) || c == '.' || c == '-') {
+ while (1) {
+ s->append((char)c);
+ c = str->lookChar();
+ if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) {
+ break;
+ }
+ str->getChar();
+ codeString->append((char)c);
+ }
+ } else {
+ while (1) {
+ s->append((char)c);
+ c = str->lookChar();
+ if (c == EOF || !isalnum(c)) {
+ break;
+ }
+ str->getChar();
+ codeString->append((char)c);
+ }
+ }
+ return s;
+}
+
+int PostScriptFunction::exec(double *stack, int sp0) {
+ PSCode *c;
+ double tmp[psStackSize];
+ double t;
+ int sp, ip, nn, k, i;
+
+ sp = sp0;
+ ip = 0;
+ while (ip < codeLen) {
+ c = &code[ip++];
+ switch(c->op) {
+ case psOpAbs:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = fabs(stack[sp]);
+ break;
+ case psOpAdd:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] + stack[sp];
+ ++sp;
+ break;
+ case psOpAnd:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp];
+ ++sp;
+ break;
+ case psOpAtan:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = atan2(stack[sp + 1], stack[sp]);
+ ++sp;
+ break;
+ case psOpBitshift:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp + 1];
+ nn = (int)stack[sp];
+ if (nn > 0) {
+ stack[sp + 1] = k << nn;
+ } else if (nn < 0) {
+ stack[sp + 1] = k >> -nn;
+ } else {
+ stack[sp + 1] = k;
+ }
+ ++sp;
+ break;
+ case psOpCeiling:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = ceil(stack[sp]);
+ break;
+ case psOpCopy:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ nn = (int)stack[sp++];
+ if (nn < 0) {
+ goto invalidArg;
+ }
+ if (sp + nn > psStackSize) {
+ goto underflow;
+ }
+ if (sp - nn < 0) {
+ goto overflow;
+ }
+ for (i = 0; i < nn; ++i) {
+ stack[sp - nn + i] = stack[sp + i];
+ }
+ sp -= nn;
+ break;
+ case psOpCos:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = cos(stack[sp]);
+ break;
+ case psOpCvi:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = (int)stack[sp];
+ break;
+ case psOpCvr:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ break;
+ case psOpDiv:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ if (stack[sp] == 0) {
+ goto invalidArg;
+ }
+ stack[sp + 1] = stack[sp + 1] / stack[sp];
+ ++sp;
+ break;
+ case psOpDup:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[sp - 1] = stack[sp];
+ --sp;
+ break;
+ case psOpEq:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpExch:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ t = stack[sp];
+ stack[sp] = stack[sp + 1];
+ stack[sp + 1] = t;
+ break;
+ case psOpExp:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = pow(stack[sp + 1], stack[sp]);
+ ++sp;
+ break;
+ case psOpFalse:
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[sp - 1] = 0;
+ --sp;
+ break;
+ case psOpFloor:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = floor(stack[sp]);
+ break;
+ case psOpGe:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpGt:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpIdiv:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp];
+ if (k == 0) {
+ goto invalidArg;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] / k;
+ ++sp;
+ break;
+ case psOpIndex:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp];
+ if (k < 0) {
+ goto invalidArg;
+ }
+ if (sp + 1 + k >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = stack[sp + 1 + k];
+ break;
+ case psOpLe:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpLn:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = log(stack[sp]);
+ break;
+ case psOpLog:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = log10(stack[sp]);
+ break;
+ case psOpLt:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpMod:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp];
+ if (k == 0) {
+ goto invalidArg;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] % k;
+ ++sp;
+ break;
+ case psOpMul:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] * stack[sp];
+ ++sp;
+ break;
+ case psOpNe:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpNeg:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = -stack[sp];
+ break;
+ case psOpNot:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = stack[sp] == 0 ? 1 : 0;
+ break;
+ case psOpOr:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp];
+ ++sp;
+ break;
+ case psOpPop:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ ++sp;
+ break;
+ case psOpRoll:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp++];
+ nn = (int)stack[sp++];
+ if (nn < 0) {
+ goto invalidArg;
+ }
+ if (nn > 0) {
+ if (sp + nn > psStackSize) {
+ goto underflow;
+ }
+ if (k >= 0) {
+ k %= nn;
+ } else {
+ k = -k % nn;
+ if (k) {
+ k = nn - k;
+ }
+ }
+ for (i = 0; i < nn; ++i) {
+ tmp[i] = stack[sp + i];
+ }
+ for (i = 0; i < nn; ++i) {
+ stack[sp + i] = tmp[(i + k) % nn];
+ }
+ }
+ break;
+ case psOpRound:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ t = stack[sp];
+ stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5);
+ break;
+ case psOpSin:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = sin(stack[sp]);
+ break;
+ case psOpSqrt:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = sqrt(stack[sp]);
+ break;
+ case psOpSub:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] - stack[sp];
+ ++sp;
+ break;
+ case psOpTrue:
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[sp - 1] = 1;
+ --sp;
+ break;
+ case psOpTruncate:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ t = stack[sp];
+ stack[sp] = (t >= 0) ? floor(t) : ceil(t);
+ break;
+ case psOpXor:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp];
+ ++sp;
+ break;
+ case psOpPush:
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[--sp] = c->val.d;
+ break;
+ case psOpJ:
+ ip = c->val.i;
+ break;
+ case psOpJz:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp++];
+ if (k == 0) {
+ ip = c->val.i;
+ }
+ break;
+ }
+ }
+ return sp;
+
+ underflow:
+ error(errSyntaxError, -1, "Stack underflow in PostScript function");
+ return sp;
+ overflow:
+ error(errSyntaxError, -1, "Stack overflow in PostScript function");
+ return sp;
+ invalidArg:
+ error(errSyntaxError, -1, "Invalid arg in PostScript function");
+ return sp;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Function.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Function.h
new file mode 100644
index 00000000000..bf1f25e51cc
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Function.h
@@ -0,0 +1,240 @@
+//========================================================================
+//
+// Function.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FUNCTION_H
+#define FUNCTION_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+
+class GList;
+class Dict;
+class Stream;
+struct PSCode;
+
+//------------------------------------------------------------------------
+// Function
+//------------------------------------------------------------------------
+
+#define funcMaxInputs 32
+#define funcMaxOutputs 32
+#define sampledFuncMaxInputs 16
+
+class Function {
+public:
+
+ Function();
+
+ virtual ~Function();
+
+ // Construct a function, with [expectedInputs] inputs and
+ // [expectedOutputs] outputs. [expectedOutputs] can be -1 to
+ // indicate unknown. Returns NULL if unsuccessful.
+ static Function *parse(Object *funcObj, int expectedInputs,
+ int expectedOutputs, int recursion = 0);
+
+ // Initialize the entries common to all function types.
+ GBool init(Dict *dict);
+
+ virtual Function *copy() = 0;
+
+ // Return the function type:
+ // -1 : identity
+ // 0 : sampled
+ // 2 : exponential
+ // 3 : stitching
+ // 4 : PostScript
+ virtual int getType() = 0;
+
+ // Return size of input and output tuples.
+ int getInputSize() { return m; }
+ int getOutputSize() { return n; }
+
+ double getDomainMin(int i) { return domain[i][0]; }
+ double getDomainMax(int i) { return domain[i][1]; }
+ double getRangeMin(int i) { return range[i][0]; }
+ double getRangeMax(int i) { return range[i][1]; }
+ GBool getHasRange() { return hasRange; }
+
+ // Transform an input tuple into an output tuple.
+ virtual void transform(double *in, double *out) = 0;
+
+ virtual GBool isOk() = 0;
+
+protected:
+
+ int m, n; // size of input and output tuples
+ double // min and max values for function domain
+ domain[funcMaxInputs][2];
+ double // min and max values for function range
+ range[funcMaxOutputs][2];
+ GBool hasRange; // set if range is defined
+};
+
+//------------------------------------------------------------------------
+// IdentityFunction
+//------------------------------------------------------------------------
+
+class IdentityFunction: public Function {
+public:
+
+ IdentityFunction(int nInputs);
+ virtual ~IdentityFunction();
+ virtual Function *copy() { return new IdentityFunction(m); }
+ virtual int getType() { return -1; }
+ virtual void transform(double *in, double *out);
+ virtual GBool isOk() { return gTrue; }
+
+private:
+};
+
+//------------------------------------------------------------------------
+// SampledFunction
+//------------------------------------------------------------------------
+
+class SampledFunction: public Function {
+public:
+
+ SampledFunction(Object *funcObj, Dict *dict);
+ virtual ~SampledFunction();
+ virtual Function *copy() { return new SampledFunction(this); }
+ virtual int getType() { return 0; }
+ virtual void transform(double *in, double *out);
+ virtual GBool isOk() { return ok; }
+
+ int getSampleSize(int i) { return sampleSize[i]; }
+ double getEncodeMin(int i) { return encode[i][0]; }
+ double getEncodeMax(int i) { return encode[i][1]; }
+ double getDecodeMin(int i) { return decode[i][0]; }
+ double getDecodeMax(int i) { return decode[i][1]; }
+ double *getSamples() { return samples; }
+
+private:
+
+ SampledFunction(SampledFunction *func);
+
+ int // number of samples for each domain element
+ sampleSize[funcMaxInputs];
+ double // min and max values for domain encoder
+ encode[funcMaxInputs][2];
+ double // min and max values for range decoder
+ decode[funcMaxOutputs][2];
+ double // input multipliers
+ inputMul[funcMaxInputs];
+ int *idxOffset;
+ double *samples; // the samples
+ int nSamples; // size of the samples array
+ double *sBuf; // buffer for the transform function
+ double cacheIn[funcMaxInputs];
+ double cacheOut[funcMaxOutputs];
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// ExponentialFunction
+//------------------------------------------------------------------------
+
+class ExponentialFunction: public Function {
+public:
+
+ ExponentialFunction(Object *funcObj, Dict *dict);
+ virtual ~ExponentialFunction();
+ virtual Function *copy() { return new ExponentialFunction(this); }
+ virtual int getType() { return 2; }
+ virtual void transform(double *in, double *out);
+ virtual GBool isOk() { return ok; }
+
+ double *getC0() { return c0; }
+ double *getC1() { return c1; }
+ double getE() { return e; }
+
+private:
+
+ ExponentialFunction(ExponentialFunction *func);
+
+ double c0[funcMaxOutputs];
+ double c1[funcMaxOutputs];
+ double e;
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// StitchingFunction
+//------------------------------------------------------------------------
+
+class StitchingFunction: public Function {
+public:
+
+ StitchingFunction(Object *funcObj, Dict *dict, int expectedInputs,
+ int expectedOutputs, int recursion);
+ virtual ~StitchingFunction();
+ virtual Function *copy() { return new StitchingFunction(this); }
+ virtual int getType() { return 3; }
+ virtual void transform(double *in, double *out);
+ virtual GBool isOk() { return ok; }
+
+ int getNumFuncs() { return k; }
+ Function *getFunc(int i) { return funcs[i]; }
+ double *getBounds() { return bounds; }
+ double *getEncode() { return encode; }
+ double *getScale() { return scale; }
+
+private:
+
+ StitchingFunction(StitchingFunction *func);
+
+ int k;
+ Function **funcs;
+ double *bounds;
+ double *encode;
+ double *scale;
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// PostScriptFunction
+//------------------------------------------------------------------------
+
+class PostScriptFunction: public Function {
+public:
+
+ PostScriptFunction(Object *funcObj, Dict *dict);
+ virtual ~PostScriptFunction();
+ virtual Function *copy() { return new PostScriptFunction(this); }
+ virtual int getType() { return 4; }
+ virtual void transform(double *in, double *out);
+ virtual GBool isOk() { return ok; }
+
+ GString *getCodeString() { return codeString; }
+
+private:
+
+ PostScriptFunction(PostScriptFunction *func);
+ GBool parseCode(GList *tokens, int *tokPtr, int *codePtr);
+ void addCode(int *codePtr, int op);
+ void addCodeI(int *codePtr, int op, int x);
+ void addCodeD(int *codePtr, int op, double x);
+ GString *getToken(Stream *str);
+ int exec(double *stack, int sp0);
+
+ GString *codeString;
+ PSCode *code;
+ int codeLen;
+ int codeSize;
+ double cacheIn[funcMaxInputs];
+ double cacheOut[funcMaxOutputs];
+ GBool ok;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc
new file mode 100644
index 00000000000..ed98d9d165f
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.cc
@@ -0,0 +1,5338 @@
+//========================================================================
+//
+// Gfx.cc
+//
+// Copyright 1996-2016 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "Trace.h"
+#include "GlobalParams.h"
+#include "CharTypes.h"
+#include "Object.h"
+#include "PDFDoc.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Stream.h"
+#include "Lexer.h"
+#include "Parser.h"
+#include "GfxFont.h"
+#include "GfxState.h"
+#include "OutputDev.h"
+#include "Page.h"
+#include "Annot.h"
+#include "OptionalContent.h"
+#include "Error.h"
+#include "TextString.h"
+#include "Gfx.h"
+
+// the MSVC math.h doesn't define this
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+//------------------------------------------------------------------------
+// constants
+//------------------------------------------------------------------------
+
+// Max recursive depth for a function shading fill.
+#define functionMaxDepth 6
+
+// Max delta allowed in any color component for a function shading fill.
+#define functionColorDelta (dblToCol(1 / 256.0))
+
+// Number of splits along the t axis for an axial shading fill.
+#define axialSplits 256
+
+// Max delta allowed in any color component for an axial shading fill.
+#define axialColorDelta (dblToCol(1 / 256.0))
+
+// Max number of splits along the t axis for a radial shading fill.
+#define radialMaxSplits 256
+
+// Max delta allowed in any color component for a radial shading fill.
+#define radialColorDelta (dblToCol(1 / 256.0))
+
+// Max recursive depth for a Gouraud triangle shading fill.
+#define gouraudMaxDepth 6
+
+// Max delta allowed in any color component for a Gouraud triangle
+// shading fill.
+#define gouraudColorDelta (dblToCol(1 / 256.0))
+
+// Max recursive depth for a patch mesh shading fill.
+#define patchMaxDepth 6
+
+// Max delta allowed in any color component for a patch mesh shading
+// fill.
+#define patchColorDelta (dblToCol(1 / 256.0))
+
+// Max errors (undefined operator, wrong number of args) allowed before
+// giving up on a content stream.
+#define contentStreamErrorLimit 500
+
+//------------------------------------------------------------------------
+// Operator table
+//------------------------------------------------------------------------
+
+#ifdef _WIN32 // this works around a bug in the VC7 compiler
+# pragma optimize("",off)
+#endif
+
+Operator Gfx::opTab[] = {
+ {"\"", 3, {tchkNum, tchkNum, tchkString},
+ &Gfx::opMoveSetShowText},
+ {"'", 1, {tchkString},
+ &Gfx::opMoveShowText},
+ {"B", 0, {tchkNone},
+ &Gfx::opFillStroke},
+ {"B*", 0, {tchkNone},
+ &Gfx::opEOFillStroke},
+ {"BDC", 2, {tchkName, tchkProps},
+ &Gfx::opBeginMarkedContent},
+ {"BI", 0, {tchkNone},
+ &Gfx::opBeginImage},
+ {"BMC", 1, {tchkName},
+ &Gfx::opBeginMarkedContent},
+ {"BT", 0, {tchkNone},
+ &Gfx::opBeginText},
+ {"BX", 0, {tchkNone},
+ &Gfx::opBeginIgnoreUndef},
+ {"CS", 1, {tchkName},
+ &Gfx::opSetStrokeColorSpace},
+ {"DP", 2, {tchkName, tchkProps},
+ &Gfx::opMarkPoint},
+ {"Do", 1, {tchkName},
+ &Gfx::opXObject},
+ {"EI", 0, {tchkNone},
+ &Gfx::opEndImage},
+ {"EMC", 0, {tchkNone},
+ &Gfx::opEndMarkedContent},
+ {"ET", 0, {tchkNone},
+ &Gfx::opEndText},
+ {"EX", 0, {tchkNone},
+ &Gfx::opEndIgnoreUndef},
+ {"F", 0, {tchkNone},
+ &Gfx::opFill},
+ {"G", 1, {tchkNum},
+ &Gfx::opSetStrokeGray},
+ {"ID", 0, {tchkNone},
+ &Gfx::opImageData},
+ {"J", 1, {tchkInt},
+ &Gfx::opSetLineCap},
+ {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ &Gfx::opSetStrokeCMYKColor},
+ {"M", 1, {tchkNum},
+ &Gfx::opSetMiterLimit},
+ {"MP", 1, {tchkName},
+ &Gfx::opMarkPoint},
+ {"Q", 0, {tchkNone},
+ &Gfx::opRestore},
+ {"RG", 3, {tchkNum, tchkNum, tchkNum},
+ &Gfx::opSetStrokeRGBColor},
+ {"S", 0, {tchkNone},
+ &Gfx::opStroke},
+ {"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,
+ tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN},
+ &Gfx::opSetStrokeColorN},
+ {"T*", 0, {tchkNone},
+ &Gfx::opTextNextLine},
+ {"TD", 2, {tchkNum, tchkNum},
+ &Gfx::opTextMoveSet},
+ {"TJ", 1, {tchkArray},
+ &Gfx::opShowSpaceText},
+ {"TL", 1, {tchkNum},
+ &Gfx::opSetTextLeading},
+ {"Tc", 1, {tchkNum},
+ &Gfx::opSetCharSpacing},
+ {"Td", 2, {tchkNum, tchkNum},
+ &Gfx::opTextMove},
+ {"Tf", 2, {tchkName, tchkNum},
+ &Gfx::opSetFont},
+ {"Tj", 1, {tchkString},
+ &Gfx::opShowText},
+ {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum},
+ &Gfx::opSetTextMatrix},
+ {"Tr", 1, {tchkInt},
+ &Gfx::opSetTextRender},
+ {"Ts", 1, {tchkNum},
+ &Gfx::opSetTextRise},
+ {"Tw", 1, {tchkNum},
+ &Gfx::opSetWordSpacing},
+ {"Tz", 1, {tchkNum},
+ &Gfx::opSetHorizScaling},
+ {"W", 0, {tchkNone},
+ &Gfx::opClip},
+ {"W*", 0, {tchkNone},
+ &Gfx::opEOClip},
+ {"b", 0, {tchkNone},
+ &Gfx::opCloseFillStroke},
+ {"b*", 0, {tchkNone},
+ &Gfx::opCloseEOFillStroke},
+ {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum},
+ &Gfx::opCurveTo},
+ {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum},
+ &Gfx::opConcat},
+ {"cs", 1, {tchkName},
+ &Gfx::opSetFillColorSpace},
+ {"d", 2, {tchkArray, tchkNum},
+ &Gfx::opSetDash},
+ {"d0", 2, {tchkNum, tchkNum},
+ &Gfx::opSetCharWidth},
+ {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum},
+ &Gfx::opSetCacheDevice},
+ {"f", 0, {tchkNone},
+ &Gfx::opFill},
+ {"f*", 0, {tchkNone},
+ &Gfx::opEOFill},
+ {"g", 1, {tchkNum},
+ &Gfx::opSetFillGray},
+ {"gs", 1, {tchkName},
+ &Gfx::opSetExtGState},
+ {"h", 0, {tchkNone},
+ &Gfx::opClosePath},
+ {"i", 1, {tchkNum},
+ &Gfx::opSetFlat},
+ {"j", 1, {tchkInt},
+ &Gfx::opSetLineJoin},
+ {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ &Gfx::opSetFillCMYKColor},
+ {"l", 2, {tchkNum, tchkNum},
+ &Gfx::opLineTo},
+ {"m", 2, {tchkNum, tchkNum},
+ &Gfx::opMoveTo},
+ {"n", 0, {tchkNone},
+ &Gfx::opEndPath},
+ {"q", 0, {tchkNone},
+ &Gfx::opSave},
+ {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ &Gfx::opRectangle},
+ {"rg", 3, {tchkNum, tchkNum, tchkNum},
+ &Gfx::opSetFillRGBColor},
+ {"ri", 1, {tchkName},
+ &Gfx::opSetRenderingIntent},
+ {"s", 0, {tchkNone},
+ &Gfx::opCloseStroke},
+ {"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,
+ tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN},
+ &Gfx::opSetFillColorN},
+ {"sh", 1, {tchkName},
+ &Gfx::opShFill},
+ {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ &Gfx::opCurveTo1},
+ {"w", 1, {tchkNum},
+ &Gfx::opSetLineWidth},
+ {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ &Gfx::opCurveTo2},
+};
+
+#ifdef _WIN32 // this works around a bug in the VC7 compiler
+# pragma optimize("",on)
+#endif
+
+#define numOps (sizeof(opTab) / sizeof(Operator))
+
+//------------------------------------------------------------------------
+// GfxResources
+//------------------------------------------------------------------------
+
+GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
+ Object obj1, obj2;
+ Ref r;
+
+ if (resDict) {
+ valid = gTrue;
+
+ // build font dictionary
+ fonts = NULL;
+ resDict->lookupNF("Font", &obj1);
+ if (obj1.isRef()) {
+ obj1.fetch(xref, &obj2);
+ if (obj2.isDict()) {
+ r = obj1.getRef();
+ fonts = new GfxFontDict(xref, &r, obj2.getDict());
+ }
+ obj2.free();
+ } else if (obj1.isDict()) {
+ fonts = new GfxFontDict(xref, NULL, obj1.getDict());
+ }
+ obj1.free();
+
+ // get XObject dictionary
+ resDict->lookup("XObject", &xObjDict);
+
+ // get color space dictionary
+ resDict->lookup("ColorSpace", &colorSpaceDict);
+
+ // get pattern dictionary
+ resDict->lookup("Pattern", &patternDict);
+
+ // get shading dictionary
+ resDict->lookup("Shading", &shadingDict);
+
+ // get graphics state parameter dictionary
+ resDict->lookup("ExtGState", &gStateDict);
+
+ // get properties dictionary
+ resDict->lookup("Properties", &propsDict);
+
+ } else {
+ valid = gFalse;
+ fonts = NULL;
+ xObjDict.initNull();
+ colorSpaceDict.initNull();
+ patternDict.initNull();
+ shadingDict.initNull();
+ gStateDict.initNull();
+ propsDict.initNull();
+ }
+
+ next = nextA;
+}
+
+GfxResources::~GfxResources() {
+ if (fonts) {
+ delete fonts;
+ }
+ xObjDict.free();
+ colorSpaceDict.free();
+ patternDict.free();
+ shadingDict.free();
+ gStateDict.free();
+ propsDict.free();
+}
+
+GfxFont *GfxResources::lookupFont(char *name) {
+ GfxFont *font;
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->fonts) {
+ if ((font = resPtr->fonts->lookup(name))) {
+ return font;
+ }
+ }
+ }
+ error(errSyntaxError, -1, "Unknown font tag '{0:s}'", name);
+ return NULL;
+}
+
+GfxFont *GfxResources::lookupFontByRef(Ref ref) {
+ GfxFont *font;
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->fonts) {
+ if ((font = resPtr->fonts->lookupByRef(ref))) {
+ return font;
+ }
+ }
+ }
+ error(errSyntaxError, -1, "Unknown font ref {0:d}.{1:d}", ref.num, ref.gen);
+ return NULL;
+}
+
+GBool GfxResources::lookupXObject(const char *name, Object *obj) {
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->xObjDict.isDict()) {
+ if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
+ return gTrue;
+ obj->free();
+ }
+ }
+ error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name);
+ return gFalse;
+}
+
+GBool GfxResources::lookupXObjectNF(const char *name, Object *obj) {
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->xObjDict.isDict()) {
+ if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
+ return gTrue;
+ obj->free();
+ }
+ }
+ error(errSyntaxError, -1, "XObject '{0:s}' is unknown", name);
+ return gFalse;
+}
+
+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 (?)
+ if (!strcmp(name, "DeviceGray") ||
+ !strcmp(name, "DeviceRGB") ||
+ !strcmp(name, "DeviceCMYK")) {
+ obj->initNull();
+ return;
+ }
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->colorSpaceDict.isDict()) {
+ if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
+ return;
+ }
+ obj->free();
+ }
+ if (!inherit && valid) {
+ break;
+ }
+ }
+ obj->initNull();
+}
+
+GfxPattern *GfxResources::lookupPattern(const char *name
+ ) {
+ GfxResources *resPtr;
+ GfxPattern *pattern;
+ Object objRef, obj;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->patternDict.isDict()) {
+ if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
+ resPtr->patternDict.dictLookupNF(name, &objRef);
+ pattern = GfxPattern::parse(&objRef, &obj
+ );
+ objRef.free();
+ obj.free();
+ return pattern;
+ }
+ obj.free();
+ }
+ }
+ error(errSyntaxError, -1, "Unknown pattern '{0:s}'", name);
+ return NULL;
+}
+
+GfxShading *GfxResources::lookupShading(const char *name
+ ) {
+ GfxResources *resPtr;
+ GfxShading *shading;
+ Object obj;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->shadingDict.isDict()) {
+ if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
+ shading = GfxShading::parse(&obj
+ );
+ obj.free();
+ return shading;
+ }
+ obj.free();
+ }
+ }
+ error(errSyntaxError, -1, "Unknown shading '{0:s}'", name);
+ return NULL;
+}
+
+GBool GfxResources::lookupGState(const char *name, Object *obj) {
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->gStateDict.isDict()) {
+ if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
+ return gTrue;
+ }
+ obj->free();
+ }
+ }
+ error(errSyntaxError, -1, "ExtGState '{0:s}' is unknown", name);
+ return gFalse;
+}
+
+GBool GfxResources::lookupPropertiesNF(const char *name, Object *obj) {
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->propsDict.isDict()) {
+ if (!resPtr->propsDict.dictLookupNF(name, obj)->isNull()) {
+ return gTrue;
+ }
+ obj->free();
+ }
+ }
+ error(errSyntaxError, -1, "Properties '{0:s}' is unknown", name);
+ return gFalse;
+}
+
+//------------------------------------------------------------------------
+// Gfx
+//------------------------------------------------------------------------
+
+Gfx::Gfx(PDFDoc *docA, OutputDev *outA, int pageNum, Dict *resDict,
+ double hDPI, double vDPI, PDFRectangle *box,
+ PDFRectangle *cropBox, int rotate,
+ GBool (*abortCheckCbkA)(void *data),
+ void *abortCheckCbkDataA) {
+ int i;
+
+ doc = docA;
+ xref = doc->getXRef();
+ subPage = gFalse;
+ printCommands = globalParams->getPrintCommands();
+
+ // start the resource stack
+ res = new GfxResources(xref, resDict, NULL);
+
+ // initialize
+ out = outA;
+ state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
+ fontChanged = gFalse;
+ clip = clipNone;
+ ignoreUndef = 0;
+ out->startPage(pageNum, state);
+ out->setDefaultCTM(state->getCTM());
+ out->updateAll(state);
+ for (i = 0; i < 6; ++i) {
+ baseMatrix[i] = state->getCTM()[i];
+ }
+ formDepth = 0;
+ markedContentStack = new GList();
+ ocState = gTrue;
+ parser = NULL;
+ contentStreamStack = new GList();
+ abortCheckCbk = abortCheckCbkA;
+ abortCheckCbkData = abortCheckCbkDataA;
+
+ // set crop box
+ if (cropBox) {
+ state->moveTo(cropBox->x1, cropBox->y1);
+ state->lineTo(cropBox->x2, cropBox->y1);
+ state->lineTo(cropBox->x2, cropBox->y2);
+ state->lineTo(cropBox->x1, cropBox->y2);
+ state->closePath();
+ state->clip();
+ out->clip(state);
+ state->clearPath();
+ }
+}
+
+Gfx::Gfx(PDFDoc *docA, OutputDev *outA, Dict *resDict,
+ PDFRectangle *box, PDFRectangle *cropBox,
+ GBool (*abortCheckCbkA)(void *data),
+ void *abortCheckCbkDataA) {
+ int i;
+
+ doc = docA;
+ xref = doc->getXRef();
+ subPage = gTrue;
+ printCommands = globalParams->getPrintCommands();
+
+ // start the resource stack
+ res = new GfxResources(xref, resDict, NULL);
+
+ // initialize
+ out = outA;
+ state = new GfxState(72, 72, box, 0, gFalse);
+ fontChanged = gFalse;
+ clip = clipNone;
+ ignoreUndef = 0;
+ for (i = 0; i < 6; ++i) {
+ baseMatrix[i] = state->getCTM()[i];
+ }
+ formDepth = 0;
+ markedContentStack = new GList();
+ ocState = gTrue;
+ parser = NULL;
+ contentStreamStack = new GList();
+ abortCheckCbk = abortCheckCbkA;
+ abortCheckCbkData = abortCheckCbkDataA;
+
+ // set crop box
+ if (cropBox) {
+ state->moveTo(cropBox->x1, cropBox->y1);
+ state->lineTo(cropBox->x2, cropBox->y1);
+ state->lineTo(cropBox->x2, cropBox->y2);
+ state->lineTo(cropBox->x1, cropBox->y2);
+ state->closePath();
+ state->clip();
+ out->clip(state);
+ state->clearPath();
+ }
+}
+
+Gfx::~Gfx() {
+ if (!subPage) {
+ out->endPage();
+ }
+ while (state->hasSaves()) {
+ restoreState();
+ }
+ delete state;
+ while (res) {
+ popResources();
+ }
+ deleteGList(markedContentStack, GfxMarkedContent);
+ delete contentStreamStack;
+}
+
+void Gfx::display(Object *objRef, GBool topLevel) {
+ Object obj1, obj2;
+ int i;
+
+ objRef->fetch(xref, &obj1);
+ if (obj1.isArray()) {
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ obj1.arrayGetNF(i, &obj2);
+ if (checkForContentStreamLoop(&obj2)) {
+ obj2.free();
+ obj1.free();
+ return;
+ }
+ obj2.free();
+ }
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!obj2.isStream()) {
+ error(errSyntaxError, -1, "Invalid object type for content stream");
+ obj2.free();
+ obj1.free();
+ return;
+ }
+ obj2.free();
+ }
+ contentStreamStack->append(&obj1);
+ } else if (obj1.isStream()) {
+ if (checkForContentStreamLoop(objRef)) {
+ obj1.free();
+ return;
+ }
+ contentStreamStack->append(objRef);
+ } else {
+ error(errSyntaxError, -1, "Invalid object type for content stream");
+ obj1.free();
+ return;
+ }
+ parser = new Parser(xref, new Lexer(xref, &obj1), gFalse);
+ go(topLevel);
+ delete parser;
+ parser = NULL;
+ contentStreamStack->del(contentStreamStack->getLength() - 1);
+ obj1.free();
+}
+
+// If <ref> is already on contentStreamStack, i.e., if there is a loop
+// in the content streams, report an error, and return true.
+GBool Gfx::checkForContentStreamLoop(Object *ref) {
+ Object *objPtr;
+ Object obj1;
+ int i, j;
+
+ if (ref->isRef()) {
+ for (i = 0; i < contentStreamStack->getLength(); ++i) {
+ objPtr = (Object *)contentStreamStack->get(i);
+ if (objPtr->isRef()) {
+ if (ref->getRefNum() == objPtr->getRefNum() &&
+ ref->getRefGen() == objPtr->getRefGen()) {
+ error(errSyntaxError, -1, "Loop in content streams");
+ return gTrue;
+ }
+ } else if (objPtr->isArray()) {
+ for (j = 0; j < objPtr->arrayGetLength(); ++j) {
+ objPtr->arrayGetNF(j, &obj1);
+ if (obj1.isRef()) {
+ if (ref->getRefNum() == obj1.getRefNum() &&
+ ref->getRefGen() == obj1.getRefGen()) {
+ error(errSyntaxError, -1, "Loop in content streams");
+ obj1.free();
+ return gTrue;
+ }
+ }
+ obj1.free();
+ }
+ }
+ }
+ }
+ return gFalse;
+}
+
+void Gfx::go(GBool topLevel) {
+ Object obj;
+ Object args[maxArgs];
+ GBool aborted;
+ int numArgs, i;
+ int errCount;
+
+ // scan a sequence of objects
+ opCounter = 0;
+ aborted = gFalse;
+ errCount = 0;
+ numArgs = 0;
+ getContentObj(&obj);
+ while (!obj.isEOF()) {
+
+ // check for an abort
+ ++opCounter;
+ if (abortCheckCbk && opCounter > 100) {
+ if ((*abortCheckCbk)(abortCheckCbkData)) {
+ aborted = gTrue;
+ break;
+ }
+ opCounter = 0;
+ }
+
+ // got a command - execute it
+ if (obj.isCmd()) {
+ if (printCommands) {
+ obj.print(stdout);
+ for (i = 0; i < numArgs; ++i) {
+ printf(" ");
+ args[i].print(stdout);
+ }
+ printf("\n");
+ fflush(stdout);
+ }
+ if (!execOp(&obj, args, numArgs)) {
+ ++errCount;
+ }
+ obj.free();
+ for (i = 0; i < numArgs; ++i)
+ args[i].free();
+ numArgs = 0;
+
+ // check for too many errors
+ if (errCount > contentStreamErrorLimit) {
+ error(errSyntaxError, -1,
+ "Too many errors - giving up on this content stream");
+ break;
+ }
+
+ // got an argument - save it
+ } else if (numArgs < maxArgs) {
+ args[numArgs++] = obj;
+
+ // too many arguments - something is wrong
+ } else {
+ error(errSyntaxError, getPos(), "Too many args in content stream");
+ if (printCommands) {
+ printf("throwing away arg: ");
+ obj.print(stdout);
+ printf("\n");
+ fflush(stdout);
+ }
+ obj.free();
+ }
+
+ // grab the next object
+ getContentObj(&obj);
+ }
+ obj.free();
+
+ // args at end with no command
+ if (numArgs > 0) {
+ if (!aborted) {
+ error(errSyntaxError, getPos(), "Leftover args in content stream");
+ if (printCommands) {
+ printf("%d leftovers:", numArgs);
+ for (i = 0; i < numArgs; ++i) {
+ printf(" ");
+ args[i].print(stdout);
+ }
+ printf("\n");
+ fflush(stdout);
+ }
+ }
+ for (i = 0; i < numArgs; ++i) {
+ args[i].free();
+ }
+ }
+}
+
+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;
+ char *name;
+ Object *argPtr;
+ int i;
+
+ // find operator
+ name = cmd->getCmd();
+ if (!(op = findOp(name))) {
+ if (ignoreUndef > 0) {
+ return gTrue;
+ }
+ error(errSyntaxError, getPos(), "Unknown operator '{0:s}'", name);
+ return gFalse;
+ }
+
+ // type check args
+ argPtr = args;
+ if (op->numArgs >= 0) {
+ if (numArgs < op->numArgs) {
+ error(errSyntaxError, getPos(),
+ "Too few ({0:d}) args to '{1:s}' operator", numArgs, name);
+ return gFalse;
+ }
+ if (numArgs > op->numArgs) {
+#if 0
+ error(errSyntaxWarning, getPos(),
+ "Too many ({0:d}) args to '{1:s}' operator", numArgs, name);
+#endif
+ argPtr += numArgs - op->numArgs;
+ numArgs = op->numArgs;
+ }
+ } else {
+ if (numArgs > -op->numArgs) {
+ error(errSyntaxWarning, getPos(),
+ "Too many ({0:d}) args to '{1:s}' operator",
+ numArgs, name);
+ }
+ }
+ for (i = 0; i < numArgs; ++i) {
+ if (!checkArg(&argPtr[i], op->tchk[i])) {
+ error(errSyntaxError, getPos(),
+ "Arg #{0:d} to '{1:s}' operator is wrong type ({2:s})",
+ i, name, argPtr[i].getTypeName());
+ return gFalse;
+ }
+ }
+
+ // do it
+ (this->*op->func)(argPtr, numArgs);
+
+ return gTrue;
+}
+
+Operator *Gfx::findOp(char *name) {
+ int a, b, m, cmp;
+
+ a = -1;
+ b = numOps;
+ cmp = 0; // make gcc happy
+ // invariant: opTab[a] < name < opTab[b]
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ cmp = strcmp(opTab[m].name, name);
+ if (cmp < 0)
+ a = m;
+ else if (cmp > 0)
+ b = m;
+ else
+ a = b = m;
+ }
+ if (cmp != 0)
+ return NULL;
+ return &opTab[a];
+}
+
+GBool Gfx::checkArg(Object *arg, TchkType type) {
+ switch (type) {
+ case tchkBool: return arg->isBool();
+ case tchkInt: return arg->isInt();
+ case tchkNum: return arg->isNum();
+ case tchkString: return arg->isString();
+ case tchkName: return arg->isName();
+ case tchkArray: return arg->isArray();
+ case tchkProps: return arg->isDict() || arg->isName();
+ case tchkSCN: return arg->isNum() || arg->isName();
+ case tchkNone: return gFalse;
+ }
+ return gFalse;
+}
+
+GFileOffset Gfx::getPos() {
+ return parser ? parser->getPos() : -1;
+}
+
+//------------------------------------------------------------------------
+// graphics state operators
+//------------------------------------------------------------------------
+
+void Gfx::opSave(Object args[], int numArgs) {
+ saveState();
+}
+
+void Gfx::opRestore(Object args[], int numArgs) {
+ restoreState();
+}
+
+void Gfx::opConcat(Object args[], int numArgs) {
+ state->concatCTM(args[0].getNum(), args[1].getNum(),
+ args[2].getNum(), args[3].getNum(),
+ args[4].getNum(), args[5].getNum());
+ out->updateCTM(state, args[0].getNum(), args[1].getNum(),
+ args[2].getNum(), args[3].getNum(),
+ args[4].getNum(), args[5].getNum());
+ fontChanged = gTrue;
+}
+
+void Gfx::opSetDash(Object args[], int numArgs) {
+ Array *a;
+ int length;
+ Object obj;
+ double *dash;
+ int i;
+
+ a = args[0].getArray();
+ length = a->getLength();
+ if (length == 0) {
+ dash = NULL;
+ } else {
+ dash = (double *)gmallocn(length, sizeof(double));
+ for (i = 0; i < length; ++i) {
+ dash[i] = a->get(i, &obj)->getNum();
+ obj.free();
+ }
+ }
+ state->setLineDash(dash, length, args[1].getNum());
+ out->updateLineDash(state);
+}
+
+void Gfx::opSetFlat(Object args[], int numArgs) {
+ state->setFlatness((int)args[0].getNum());
+ out->updateFlatness(state);
+}
+
+void Gfx::opSetLineJoin(Object args[], int numArgs) {
+ int lineJoin;
+
+ lineJoin = args[0].getInt();
+ if (lineJoin < 0 || lineJoin > 2) {
+ lineJoin = 0;
+ }
+ state->setLineJoin(lineJoin);
+ out->updateLineJoin(state);
+}
+
+void Gfx::opSetLineCap(Object args[], int numArgs) {
+ int lineCap;
+
+ lineCap = args[0].getInt();
+ if (lineCap < 0 || lineCap > 2) {
+ lineCap = 0;
+ }
+ state->setLineCap(lineCap);
+ out->updateLineCap(state);
+}
+
+void Gfx::opSetMiterLimit(Object args[], int numArgs) {
+ state->setMiterLimit(args[0].getNum());
+ out->updateMiterLimit(state);
+}
+
+void Gfx::opSetLineWidth(Object args[], int numArgs) {
+ state->setLineWidth(args[0].getNum());
+ out->updateLineWidth(state);
+}
+
+void Gfx::opSetExtGState(Object args[], int numArgs) {
+ Object obj1, obj2, obj3, objRef3, obj4, obj5, backdropColorObj;
+ Object args2[2];
+ GfxBlendMode mode;
+ GBool haveFillOP;
+ Function *funcs[4];
+ GBool alpha, knockout;
+ double opac;
+ int i;
+
+ if (!res->lookupGState(args[0].getName(), &obj1)) {
+ return;
+ }
+ if (!obj1.isDict()) {
+ error(errSyntaxError, getPos(),
+ "ExtGState '{0:s}' is wrong type", args[0].getName());
+ obj1.free();
+ return;
+ }
+ if (printCommands) {
+ printf(" gfx state dict: ");
+ obj1.print();
+ printf("\n");
+ }
+
+ // parameters that are also set by individual PDF operators
+ if (obj1.dictLookup("LW", &obj2)->isNum()) {
+ opSetLineWidth(&obj2, 1);
+ }
+ obj2.free();
+ if (obj1.dictLookup("LC", &obj2)->isInt()) {
+ opSetLineCap(&obj2, 1);
+ }
+ obj2.free();
+ if (obj1.dictLookup("LJ", &obj2)->isInt()) {
+ opSetLineJoin(&obj2, 1);
+ }
+ obj2.free();
+ if (obj1.dictLookup("ML", &obj2)->isNum()) {
+ opSetMiterLimit(&obj2, 1);
+ }
+ obj2.free();
+ if (obj1.dictLookup("D", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 2) {
+ obj2.arrayGet(0, &args2[0]);
+ obj2.arrayGet(1, &args2[1]);
+ if (args2[0].isArray() && args2[1].isNum()) {
+ opSetDash(args2, 2);
+ }
+ args2[0].free();
+ args2[1].free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("FL", &obj2)->isNum()) {
+ opSetFlat(&obj2, 1);
+ }
+ obj2.free();
+ if (obj1.dictLookup("RI", &obj2)->isName()) {
+ opSetRenderingIntent(&obj2, 1);
+ }
+ obj2.free();
+
+ // font
+ if (obj1.dictLookup("Font", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 2) {
+ obj2.arrayGetNF(0, &obj3);
+ obj2.arrayGetNF(1, &obj4);
+ if (obj3.isRef() && obj4.isNum()) {
+ doSetFont(res->lookupFontByRef(obj3.getRef()), obj4.getNum());
+ }
+ obj3.free();
+ obj4.free();
+ }
+ obj2.free();
+
+ // transparency support: blend mode, fill/stroke opacity
+ if (!obj1.dictLookup("BM", &obj2)->isNull()) {
+ if (state->parseBlendMode(&obj2, &mode)) {
+ state->setBlendMode(mode);
+ out->updateBlendMode(state);
+ } else {
+ error(errSyntaxError, getPos(), "Invalid blend mode in ExtGState");
+ }
+ }
+ obj2.free();
+ if (obj1.dictLookup("ca", &obj2)->isNum()) {
+ opac = obj2.getNum();
+ state->setFillOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac);
+ out->updateFillOpacity(state);
+ }
+ obj2.free();
+ if (obj1.dictLookup("CA", &obj2)->isNum()) {
+ opac = obj2.getNum();
+ state->setStrokeOpacity(opac < 0 ? 0 : opac > 1 ? 1 : opac);
+ out->updateStrokeOpacity(state);
+ }
+ obj2.free();
+
+ // fill/stroke overprint, overprint mode
+ if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
+ if (!state->getIgnoreColorOps()) {
+ state->setFillOverprint(obj2.getBool());
+ out->updateFillOverprint(state);
+ } else {
+ error(errSyntaxWarning, getPos(), "Ignoring overprint setting"
+ " in uncolored Type 3 char or tiling pattern");
+ }
+ }
+ obj2.free();
+ if (obj1.dictLookup("OP", &obj2)->isBool()) {
+ if (!state->getIgnoreColorOps()) {
+ state->setStrokeOverprint(obj2.getBool());
+ out->updateStrokeOverprint(state);
+ if (!haveFillOP) {
+ state->setFillOverprint(obj2.getBool());
+ out->updateFillOverprint(state);
+ }
+ } else {
+ error(errSyntaxWarning, getPos(), "Ignoring overprint setting"
+ " in uncolored Type 3 char or tiling pattern");
+ }
+ }
+ obj2.free();
+ if (obj1.dictLookup("OPM", &obj2)->isInt()) {
+ if (!state->getIgnoreColorOps()) {
+ state->setOverprintMode(obj2.getInt());
+ out->updateOverprintMode(state);
+ } else {
+ error(errSyntaxWarning, getPos(), "Ignoring overprint setting"
+ " in uncolored Type 3 char or tiling pattern");
+ }
+ }
+ obj2.free();
+
+ // stroke adjust
+ if (obj1.dictLookup("SA", &obj2)->isBool()) {
+ state->setStrokeAdjust(obj2.getBool());
+ out->updateStrokeAdjust(state);
+ }
+ obj2.free();
+
+ // transfer function
+ if (obj1.dictLookup("TR2", &obj2)->isNull()) {
+ obj2.free();
+ obj1.dictLookup("TR", &obj2);
+ }
+ if (!obj2.isNull()) {
+ if (!state->getIgnoreColorOps()) {
+ if (obj2.isName("Default") ||
+ obj2.isName("Identity")) {
+ funcs[0] = funcs[1] = funcs[2] = funcs[3] = NULL;
+ state->setTransfer(funcs);
+ out->updateTransfer(state);
+ } else if (obj2.isArray() && obj2.arrayGetLength() == 4) {
+ for (i = 0; i < 4; ++i) {
+ obj2.arrayGet(i, &obj3);
+ funcs[i] = Function::parse(&obj3, 1, 1);
+ obj3.free();
+ if (!funcs[i]) {
+ break;
+ }
+ }
+ if (i == 4) {
+ state->setTransfer(funcs);
+ out->updateTransfer(state);
+ }
+ } else if (obj2.isName() || obj2.isDict() || obj2.isStream()) {
+ if ((funcs[0] = Function::parse(&obj2, 1, 1))) {
+ funcs[1] = funcs[2] = funcs[3] = NULL;
+ state->setTransfer(funcs);
+ out->updateTransfer(state);
+ }
+ } else {
+ error(errSyntaxError, getPos(),
+ "Invalid transfer function in ExtGState");
+ }
+ } else {
+ error(errSyntaxWarning, getPos(), "Ignoring transfer function setting"
+ " in uncolored Type 3 char or tiling pattern");
+ }
+ }
+ obj2.free();
+
+ // soft mask
+ if (!obj1.dictLookup("SMask", &obj2)->isNull()) {
+ if (obj2.isName("None")) {
+ out->clearSoftMask(state);
+ } else if (obj2.isDict()) {
+ obj2.dictLookup("S", &obj3);
+ if (obj3.isName("Alpha")) {
+ alpha = gTrue;
+ } else if (obj3.isName("Luminosity")) {
+ alpha = gFalse;
+ } else {
+ error(errSyntaxError, getPos(),
+ "Missing S (subtype) entry in soft mask");
+ alpha = gFalse;
+ }
+ obj3.free();
+ funcs[0] = NULL;
+ if (!obj2.dictLookup("TR", &obj3)->isNull()) {
+ if (obj3.isName("Default") ||
+ obj3.isName("Identity")) {
+ funcs[0] = NULL;
+ } else {
+ if (!(funcs[0] = Function::parse(&obj3, 1, 1))) {
+ error(errSyntaxError, getPos(),
+ "Invalid transfer function in soft mask in ExtGState");
+ delete funcs[0];
+ funcs[0] = NULL;
+ }
+ }
+ }
+ obj3.free();
+ obj2.dictLookup("BC", &backdropColorObj);
+ if (obj2.dictLookup("G", &obj3)->isStream()) {
+ if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {
+ knockout = gFalse;
+ if (obj4.dictLookup("K", &obj5)->isBool()) {
+ knockout = obj5.getBool();
+ }
+ obj5.free();
+ 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, gTrue, knockout, funcs[0],
+ &backdropColorObj);
+ objRef3.free();
+ if (funcs[0]) {
+ delete funcs[0];
+ }
+ } else {
+ error(errSyntaxError, getPos(),
+ "Invalid soft mask in ExtGState - missing group");
+ }
+ obj4.free();
+ } else {
+ error(errSyntaxError, getPos(),
+ "Invalid soft mask in ExtGState - missing group");
+ }
+ obj3.free();
+ backdropColorObj.free();
+ } else if (!obj2.isNull()) {
+ error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState");
+ }
+ }
+ obj2.free();
+
+ obj1.free();
+}
+
+void Gfx::doSoftMask(Object *str, Object *strRef, GBool alpha,
+ GBool isolated, GBool knockout,
+ Function *transferFunc, Object *backdropColorObj) {
+ Dict *dict, *resDict;
+ double m[6], bbox[4];
+ Object obj1, obj2;
+ int i;
+
+ // check for excessive recursion
+ if (formDepth > 20) {
+ return;
+ }
+
+ // get stream dict
+ dict = str->streamGetDict();
+
+ // check form type
+ dict->lookup("FormType", &obj1);
+ if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
+ error(errSyntaxError, getPos(), "Unknown form type");
+ }
+ obj1.free();
+
+ // get bounding box
+ dict->lookup("BBox", &obj1);
+ if (!obj1.isArray()) {
+ obj1.free();
+ error(errSyntaxError, getPos(), "Bad form bounding box");
+ return;
+ }
+ for (i = 0; i < 4; ++i) {
+ obj1.arrayGet(i, &obj2);
+ bbox[i] = obj2.getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ // get matrix
+ dict->lookup("Matrix", &obj1);
+ if (obj1.isArray()) {
+ for (i = 0; i < 6; ++i) {
+ obj1.arrayGet(i, &obj2);
+ m[i] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ m[0] = 1; m[1] = 0;
+ m[2] = 0; m[3] = 1;
+ m[4] = 0; m[5] = 0;
+ }
+ obj1.free();
+
+ // get resources
+ dict->lookup("Resources", &obj1);
+ resDict = obj1.isDict() ? obj1.getDict() : (Dict *)NULL;
+
+ // draw it
+ ++formDepth;
+ drawForm(strRef, resDict, m, bbox, gTrue, gTrue, isolated, knockout,
+ alpha, transferFunc, backdropColorObj);
+ --formDepth;
+
+ obj1.free();
+}
+
+void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
+ GfxRenderingIntent ri;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring rendering intent setting"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+ ri = parseRenderingIntent(args[0].getName());
+ state->setRenderingIntent(ri);
+ out->updateRenderingIntent(state);
+}
+
+GfxRenderingIntent Gfx::parseRenderingIntent(const char *name) {
+ if (!strcmp(name, "AbsoluteColorimetric")) {
+ return gfxRenderingIntentAbsoluteColorimetric;
+ }
+ if (!strcmp(name, "Saturation")) {
+ return gfxRenderingIntentSaturation;
+ }
+ if (!strcmp(name, "Perceptual")) {
+ return gfxRenderingIntentPerceptual;
+ }
+ return gfxRenderingIntentRelativeColorimetric;
+}
+
+//------------------------------------------------------------------------
+// color operators
+//------------------------------------------------------------------------
+
+void Gfx::opSetFillGray(Object args[], int numArgs) {
+ GfxColor color;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring color setting"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+ state->setFillPattern(NULL);
+ state->setFillColorSpace(GfxColorSpace::create(csDeviceGray));
+ out->updateFillColorSpace(state);
+ color.c[0] = dblToCol(args[0].getNum());
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeGray(Object args[], int numArgs) {
+ GfxColor color;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring color setting"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
+ out->updateStrokeColorSpace(state);
+ color.c[0] = dblToCol(args[0].getNum());
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
+ GfxColor color;
+ int i;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring color setting"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+ state->setFillPattern(NULL);
+ state->setFillColorSpace(GfxColorSpace::create(csDeviceCMYK));
+ out->updateFillColorSpace(state);
+ for (i = 0; i < 4; ++i) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
+ GfxColor color;
+ int i;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring color setting"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK));
+ out->updateStrokeColorSpace(state);
+ for (i = 0; i < 4; ++i) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
+ GfxColor color;
+ int i;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring color setting"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+ state->setFillPattern(NULL);
+ state->setFillColorSpace(GfxColorSpace::create(csDeviceRGB));
+ out->updateFillColorSpace(state);
+ for (i = 0; i < 3; ++i) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
+ GfxColor color;
+ int i;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring color setting"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB));
+ out->updateStrokeColorSpace(state);
+ for (i = 0; i < 3; ++i) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
+ Object obj;
+ GfxColorSpace *colorSpace;
+ GfxColor color;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring color space setting"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+ state->setFillPattern(NULL);
+ res->lookupColorSpace(args[0].getName(), &obj);
+ if (obj.isNull()) {
+ colorSpace = GfxColorSpace::parse(&args[0]
+ );
+ } else {
+ colorSpace = GfxColorSpace::parse(&obj
+ );
+ }
+ obj.free();
+ if (colorSpace) {
+ state->setFillColorSpace(colorSpace);
+ out->updateFillColorSpace(state);
+ colorSpace->getDefaultColor(&color);
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+ } else {
+ error(errSyntaxError, getPos(), "Bad color space (fill)");
+ }
+}
+
+void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
+ Object obj;
+ GfxColorSpace *colorSpace;
+ GfxColor color;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring color space setting"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+ state->setStrokePattern(NULL);
+ res->lookupColorSpace(args[0].getName(), &obj);
+ if (obj.isNull()) {
+ colorSpace = GfxColorSpace::parse(&args[0]
+ );
+ } else {
+ colorSpace = GfxColorSpace::parse(&obj
+ );
+ }
+ obj.free();
+ if (colorSpace) {
+ state->setStrokeColorSpace(colorSpace);
+ out->updateStrokeColorSpace(state);
+ colorSpace->getDefaultColor(&color);
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+ } else {
+ error(errSyntaxError, getPos(), "Bad color space (stroke)");
+ }
+}
+
+// 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;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring color setting"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+ if (numArgs != state->getFillColorSpace()->getNComps()) {
+ error(errSyntaxError, getPos(),
+ "Incorrect number of arguments in 'sc' command");
+ return;
+ }
+ state->setFillPattern(NULL);
+ for (i = 0; i < numArgs; ++i) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ state->setFillColor(&color);
+ 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;
+
+ if (numArgs != state->getStrokeColorSpace()->getNComps()) {
+ error(errSyntaxError, getPos(),
+ "Incorrect number of arguments in 'SC' command");
+ return;
+ }
+ state->setStrokePattern(NULL);
+ for (i = 0; i < numArgs; ++i) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillColorN(Object args[], int numArgs) {
+ GfxColor color;
+ GfxPattern *pattern;
+ int i;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring color setting"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ if (numArgs == 0 || !args[numArgs-1].isName()) {
+ error(errSyntaxError, getPos(),
+ "Invalid arguments in 'scn' command");
+ return;
+ }
+ if (numArgs > 1) {
+ if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||
+ numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())
+ ->getUnder()->getNComps()) {
+ error(errSyntaxError, getPos(),
+ "Incorrect number of arguments in 'scn' command");
+ return;
+ }
+ for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
+ if (args[i].isNum()) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ }
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+ }
+ if ((pattern = res->lookupPattern(args[numArgs-1].getName()
+ ))) {
+ state->setFillPattern(pattern);
+ }
+
+ } else {
+ if (numArgs != state->getFillColorSpace()->getNComps()) {
+ error(errSyntaxError, getPos(),
+ "Incorrect number of arguments in 'scn' command");
+ return;
+ }
+ state->setFillPattern(NULL);
+ for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
+ if (args[i].isNum()) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ }
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+ }
+}
+
+void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
+ GfxColor color;
+ GfxPattern *pattern;
+ int i;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring color setting"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+ if (state->getStrokeColorSpace()->getMode() == csPattern) {
+ if (numArgs == 0 || !args[numArgs-1].isName()) {
+ error(errSyntaxError, getPos(),
+ "Invalid arguments in 'SCN' command");
+ return;
+ }
+ if (numArgs > 1) {
+ if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())
+ ->getUnder() ||
+ numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())
+ ->getUnder()->getNComps()) {
+ error(errSyntaxError, getPos(),
+ "Incorrect number of arguments in 'SCN' command");
+ return;
+ }
+ for (i = 0; i < numArgs - 1 && i < gfxColorMaxComps; ++i) {
+ if (args[i].isNum()) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ }
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+ }
+ if ((pattern = res->lookupPattern(args[numArgs-1].getName()
+ ))) {
+ state->setStrokePattern(pattern);
+ }
+
+ } else {
+ if (numArgs != state->getStrokeColorSpace()->getNComps()) {
+ error(errSyntaxError, getPos(),
+ "Incorrect number of arguments in 'SCN' command");
+ return;
+ }
+ state->setStrokePattern(NULL);
+ for (i = 0; i < numArgs && i < gfxColorMaxComps; ++i) {
+ if (args[i].isNum()) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ }
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+ }
+}
+
+//------------------------------------------------------------------------
+// path segment operators
+//------------------------------------------------------------------------
+
+void Gfx::opMoveTo(Object args[], int numArgs) {
+ state->moveTo(args[0].getNum(), args[1].getNum());
+}
+
+void Gfx::opLineTo(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ error(errSyntaxError, getPos(), "No current point in lineto");
+ return;
+ }
+ state->lineTo(args[0].getNum(), args[1].getNum());
+}
+
+void Gfx::opCurveTo(Object args[], int numArgs) {
+ double x1, y1, x2, y2, x3, y3;
+
+ if (!state->isCurPt()) {
+ error(errSyntaxError, getPos(), "No current point in curveto");
+ return;
+ }
+ x1 = args[0].getNum();
+ y1 = args[1].getNum();
+ x2 = args[2].getNum();
+ y2 = args[3].getNum();
+ x3 = args[4].getNum();
+ y3 = args[5].getNum();
+ state->curveTo(x1, y1, x2, y2, x3, y3);
+}
+
+void Gfx::opCurveTo1(Object args[], int numArgs) {
+ double x1, y1, x2, y2, x3, y3;
+
+ if (!state->isCurPt()) {
+ error(errSyntaxError, getPos(), "No current point in curveto1");
+ return;
+ }
+ x1 = state->getCurX();
+ y1 = state->getCurY();
+ x2 = args[0].getNum();
+ y2 = args[1].getNum();
+ x3 = args[2].getNum();
+ y3 = args[3].getNum();
+ state->curveTo(x1, y1, x2, y2, x3, y3);
+}
+
+void Gfx::opCurveTo2(Object args[], int numArgs) {
+ double x1, y1, x2, y2, x3, y3;
+
+ if (!state->isCurPt()) {
+ error(errSyntaxError, getPos(), "No current point in curveto2");
+ return;
+ }
+ x1 = args[0].getNum();
+ y1 = args[1].getNum();
+ x2 = args[2].getNum();
+ y2 = args[3].getNum();
+ x3 = x2;
+ y3 = y2;
+ state->curveTo(x1, y1, x2, y2, x3, y3);
+}
+
+void Gfx::opRectangle(Object args[], int numArgs) {
+ double x, y, w, h;
+
+ x = args[0].getNum();
+ y = args[1].getNum();
+ w = args[2].getNum();
+ h = args[3].getNum();
+ state->moveTo(x, y);
+ state->lineTo(x + w, y);
+ state->lineTo(x + w, y + h);
+ state->lineTo(x, y + h);
+ state->closePath();
+}
+
+void Gfx::opClosePath(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ error(errSyntaxError, getPos(), "No current point in closepath");
+ return;
+ }
+ state->closePath();
+}
+
+//------------------------------------------------------------------------
+// path painting operators
+//------------------------------------------------------------------------
+
+void Gfx::opEndPath(Object args[], int numArgs) {
+ doEndPath();
+}
+
+void Gfx::opStroke(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(errSyntaxError, getPos(), "No path in stroke");
+ return;
+ }
+ if (state->isPath()) {
+ if (ocState) {
+ if (state->getStrokeColorSpace()->getMode() == csPattern) {
+ doPatternStroke();
+ } else {
+ out->stroke(state);
+ }
+ }
+ }
+ doEndPath();
+}
+
+void Gfx::opCloseStroke(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(errSyntaxError, getPos(), "No path in closepath/stroke");
+ return;
+ }
+ if (state->isPath()) {
+ state->closePath();
+ if (ocState) {
+ if (state->getStrokeColorSpace()->getMode() == csPattern) {
+ doPatternStroke();
+ } else {
+ out->stroke(state);
+ }
+ }
+ }
+ doEndPath();
+}
+
+void Gfx::opFill(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(errSyntaxError, getPos(), "No path in fill");
+ return;
+ }
+ if (state->isPath()) {
+ if (ocState) {
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternFill(gFalse);
+ } else {
+ out->fill(state);
+ }
+ }
+ }
+ doEndPath();
+}
+
+void Gfx::opEOFill(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(errSyntaxError, getPos(), "No path in eofill");
+ return;
+ }
+ if (state->isPath()) {
+ if (ocState) {
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternFill(gTrue);
+ } else {
+ out->eoFill(state);
+ }
+ }
+ }
+ doEndPath();
+}
+
+void Gfx::opFillStroke(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(errSyntaxError, getPos(), "No path in fill/stroke");
+ return;
+ }
+ if (state->isPath()) {
+ if (ocState) {
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternFill(gFalse);
+ } else {
+ out->fill(state);
+ }
+ if (state->getStrokeColorSpace()->getMode() == csPattern) {
+ doPatternStroke();
+ } else {
+ out->stroke(state);
+ }
+ }
+ }
+ doEndPath();
+}
+
+void Gfx::opCloseFillStroke(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(errSyntaxError, getPos(), "No path in closepath/fill/stroke");
+ return;
+ }
+ if (state->isPath()) {
+ state->closePath();
+ if (ocState) {
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternFill(gFalse);
+ } else {
+ out->fill(state);
+ }
+ if (state->getStrokeColorSpace()->getMode() == csPattern) {
+ doPatternStroke();
+ } else {
+ out->stroke(state);
+ }
+ }
+ }
+ doEndPath();
+}
+
+void Gfx::opEOFillStroke(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(errSyntaxError, getPos(), "No path in eofill/stroke");
+ return;
+ }
+ if (state->isPath()) {
+ if (ocState) {
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternFill(gTrue);
+ } else {
+ out->eoFill(state);
+ }
+ if (state->getStrokeColorSpace()->getMode() == csPattern) {
+ doPatternStroke();
+ } else {
+ out->stroke(state);
+ }
+ }
+ }
+ doEndPath();
+}
+
+void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(errSyntaxError, getPos(), "No path in closepath/eofill/stroke");
+ return;
+ }
+ if (state->isPath()) {
+ state->closePath();
+ if (ocState) {
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternFill(gTrue);
+ } else {
+ out->eoFill(state);
+ }
+ if (state->getStrokeColorSpace()->getMode() == csPattern) {
+ doPatternStroke();
+ } else {
+ out->stroke(state);
+ }
+ }
+ }
+ doEndPath();
+}
+
+void Gfx::doPatternFill(GBool eoFill) {
+ GfxPattern *pattern;
+
+ // this is a bit of a kludge -- patterns can be really slow, so we
+ // skip them if we're only doing text extraction, since they almost
+ // certainly don't contain any text
+ if (!out->needNonText()) {
+ return;
+ }
+
+ if (!(pattern = state->getFillPattern())) {
+ return;
+ }
+ switch (pattern->getType()) {
+ case 1:
+ doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, eoFill, gFalse);
+ break;
+ case 2:
+ doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill, gFalse);
+ break;
+ default:
+ error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill",
+ pattern->getType());
+ break;
+ }
+}
+
+void Gfx::doPatternStroke() {
+ GfxPattern *pattern;
+
+ // this is a bit of a kludge -- patterns can be really slow, so we
+ // skip them if we're only doing text extraction, since they almost
+ // certainly don't contain any text
+ if (!out->needNonText()) {
+ return;
+ }
+
+ if (!(pattern = state->getStrokePattern())) {
+ return;
+ }
+ switch (pattern->getType()) {
+ case 1:
+ doTilingPatternFill((GfxTilingPattern *)pattern, gTrue, gFalse, gFalse);
+ break;
+ case 2:
+ doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse, gFalse);
+ break;
+ default:
+ error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in stroke",
+ pattern->getType());
+ break;
+ }
+}
+
+void Gfx::doPatternText() {
+ GfxPattern *pattern;
+
+ // this is a bit of a kludge -- patterns can be really slow, so we
+ // skip them if we're only doing text extraction, since they almost
+ // certainly don't contain any text
+ if (!out->needNonText()) {
+ return;
+ }
+
+ if (!(pattern = state->getFillPattern())) {
+ return;
+ }
+ switch (pattern->getType()) {
+ case 1:
+ doTilingPatternFill((GfxTilingPattern *)pattern, gFalse, gFalse, gTrue);
+ break;
+ case 2:
+ doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, gFalse, gTrue);
+ break;
+ default:
+ error(errSyntaxError, getPos(), "Unknown pattern type ({0:d}) in fill",
+ pattern->getType());
+ break;
+ }
+}
+
+void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height,
+ GBool invert, GBool inlineImg, GBool interpolate) {
+ saveState();
+
+ out->setSoftMaskFromImageMask(state, ref, str,
+ width, height, invert, inlineImg, interpolate);
+
+ state->clearPath();
+ state->moveTo(0, 0);
+ state->lineTo(1, 0);
+ state->lineTo(1, 1);
+ state->lineTo(0, 1);
+ state->closePath();
+ doPatternFill(gTrue);
+ state->clearPath();
+
+ restoreState();
+}
+
+void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,
+ GBool stroke, GBool eoFill, GBool text) {
+ GfxPatternColorSpace *patCS;
+ GfxColorSpace *cs;
+ GfxColor color;
+ GfxState *savedState;
+ double xMin, yMin, xMax, yMax, x, y, x1, y1, t;
+ double cxMin, cyMin, cxMax, cyMax;
+ int xi0, yi0, xi1, yi1, xi, yi;
+ double *ctm, *btm, *ptm;
+ double bbox[4], m[6], ictm[6], m1[6], imb[6];
+ double det;
+ double xstep, ystep;
+ int abortCheckCounter, i;
+
+ // get color space
+ patCS = (GfxPatternColorSpace *)(stroke ? state->getStrokeColorSpace()
+ : state->getFillColorSpace());
+
+ // construct a (pattern space) -> (current space) transform matrix
+ ctm = state->getCTM();
+ btm = baseMatrix;
+ ptm = tPat->getMatrix();
+ // iCTM = invert CTM
+ det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
+ if (fabs(det) <= 1e-10) {
+ error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
+ return;
+ }
+ det = 1 / det;
+ 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;
+ // m1 = PTM * BTM = PTM * base transform matrix
+ m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
+ m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
+ m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
+ m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
+ m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
+ m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
+ // m = m1 * iCTM = (PTM * BTM) * (iCTM)
+ m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
+ m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
+ m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
+ m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
+ m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
+ m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
+
+ // construct a (device space) -> (pattern space) transform matrix
+ det = m1[0] * m1[3] - m1[1] * m1[2];
+ if (fabs(det) <= 1e-10) {
+ error(errSyntaxError, getPos(), "Singular matrix in tiling pattern fill");
+ return;
+ }
+ det = 1 / det;
+ imb[0] = m1[3] * det;
+ imb[1] = -m1[1] * det;
+ imb[2] = -m1[2] * det;
+ imb[3] = m1[0] * det;
+ imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
+ imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
+
+ // save current graphics state
+ savedState = saveStateStack();
+
+ // set underlying color space (for uncolored tiling patterns); set
+ // various other parameters (stroke color, line width) to match
+ // Adobe's behavior
+ state->setFillPattern(NULL);
+ state->setStrokePattern(NULL);
+ if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
+ state->setFillColorSpace(cs->copy());
+ out->updateFillColorSpace(state);
+ state->setStrokeColorSpace(cs->copy());
+ out->updateStrokeColorSpace(state);
+ state->setStrokeColor(state->getFillColor());
+ out->updateFillColor(state);
+ out->updateStrokeColor(state);
+ state->setIgnoreColorOps(gTrue);
+ } else {
+ state->setFillColorSpace(GfxColorSpace::create(csDeviceGray));
+ out->updateFillColorSpace(state);
+ state->getFillColorSpace()->getDefaultColor(&color);
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
+ out->updateStrokeColorSpace(state);
+ state->getStrokeColorSpace()->getDefaultColor(&color);
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+ }
+ if (!stroke) {
+ state->setLineWidth(0);
+ out->updateLineWidth(state);
+ state->setLineDash(NULL, 0, 0);
+ out->updateLineDash(state);
+ }
+
+ // clip to current path
+ if (stroke) {
+ state->clipToStrokePath();
+ out->clipToStrokePath(state);
+ } else if (!text) {
+ state->clip();
+ if (eoFill) {
+ out->eoClip(state);
+ } else {
+ out->clip(state);
+ }
+ }
+ state->clearPath();
+
+ // get the clip region, check for empty
+ state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
+ if (cxMin > cxMax || cyMin > cyMax) {
+ goto err;
+ }
+
+ // transform clip region bbox to pattern space
+ xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
+ yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
+ x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
+ y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+ x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
+ y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+ x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
+ y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+
+ // draw the pattern
+ //~ this should treat negative steps differently -- start at right/top
+ //~ edge instead of left/bottom (?)
+ bbox[0] = tPat->getBBox()[0];
+ bbox[1] = tPat->getBBox()[1];
+ bbox[2] = tPat->getBBox()[2];
+ bbox[3] = tPat->getBBox()[3];
+ if (bbox[0] > bbox[2]) {
+ t = bbox[0]; bbox[0] = bbox[2]; bbox[2] = t;
+ }
+ if (bbox[1] > bbox[3]) {
+ t = bbox[1]; bbox[1] = bbox[3]; bbox[3] = t;
+ }
+ xstep = fabs(tPat->getXStep());
+ ystep = fabs(tPat->getYStep());
+ xi0 = (int)ceil((xMin - bbox[2]) / xstep);
+ xi1 = (int)floor((xMax - bbox[0]) / xstep) + 1;
+ yi0 = (int)ceil((yMin - bbox[3]) / ystep);
+ yi1 = (int)floor((yMax - bbox[1]) / ystep) + 1;
+ for (i = 0; i < 4; ++i) {
+ m1[i] = m[i];
+ }
+ if (out->useTilingPatternFill()) {
+ m1[4] = m[4];
+ m1[5] = m[5];
+ out->tilingPatternFill(state, this, tPat->getContentStreamRef(),
+ tPat->getPaintType(), tPat->getTilingType(),
+ tPat->getResDict(),
+ m1, bbox,
+ xi0, yi0, xi1, yi1, xstep, ystep);
+ } else {
+ abortCheckCounter = 0;
+ for (yi = yi0; yi < yi1; ++yi) {
+ for (xi = xi0; xi < xi1; ++xi) {
+ if (abortCheckCbk) {
+ ++abortCheckCounter;
+ if (abortCheckCounter > 100) {
+ if ((*abortCheckCbk)(abortCheckCbkData)) {
+ goto err;
+ }
+ abortCheckCounter = 0;
+ }
+ }
+ x = xi * xstep;
+ y = yi * ystep;
+ m1[4] = x * m[0] + y * m[2] + m[4];
+ m1[5] = x * m[1] + y * m[3] + m[5];
+ drawForm(tPat->getContentStreamRef(), tPat->getResDict(),
+ m1, bbox);
+ }
+ }
+ }
+
+ // restore graphics state
+ err:
+ restoreStateStack(savedState);
+}
+
+void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,
+ GBool stroke, GBool eoFill, GBool text) {
+ GfxShading *shading;
+ GfxState *savedState;
+ double *ctm, *btm, *ptm;
+ double m[6], ictm[6], m1[6];
+ double xMin, yMin, xMax, yMax;
+ double det;
+
+ shading = sPat->getShading();
+
+ // save current graphics state
+ savedState = saveStateStack();
+
+ // clip to current path
+ if (stroke) {
+ state->clipToStrokePath();
+ out->clipToStrokePath(state);
+ state->setFillOverprint(state->getStrokeOverprint());
+ } else if (!text) {
+ state->clip();
+ if (eoFill) {
+ out->eoClip(state);
+ } else {
+ out->clip(state);
+ }
+ }
+ state->clearPath();
+
+ // construct a (pattern space) -> (current space) transform matrix
+ ctm = state->getCTM();
+ btm = baseMatrix;
+ ptm = sPat->getMatrix();
+ // iCTM = invert CTM
+ det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
+ if (fabs(det) <= 1e-10) {
+ error(errSyntaxError, getPos(), "Singular matrix in shading pattern fill");
+ restoreStateStack(savedState);
+ return;
+ }
+ det = 1 / det;
+ 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;
+ // m1 = PTM * BTM = PTM * base transform matrix
+ m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
+ m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
+ m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
+ m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
+ m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
+ m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
+ // m = m1 * iCTM = (PTM * BTM) * (iCTM)
+ m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
+ m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
+ m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
+ m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
+ m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
+ m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
+
+ // set the new matrix
+ state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
+ out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
+
+ // clip to bbox
+ if (shading->getHasBBox()) {
+ shading->getBBox(&xMin, &yMin, &xMax, &yMax);
+ state->moveTo(xMin, yMin);
+ state->lineTo(xMax, yMin);
+ state->lineTo(xMax, yMax);
+ state->lineTo(xMin, yMax);
+ state->closePath();
+ state->clip();
+ out->clip(state);
+ state->clearPath();
+ }
+
+ // set the color space
+ state->setFillColorSpace(shading->getColorSpace()->copy());
+ out->updateFillColorSpace(state);
+
+ // background color fill
+ if (shading->getHasBackground()) {
+ state->setFillColor(shading->getBackground());
+ out->updateFillColor(state);
+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+ state->moveTo(xMin, yMin);
+ state->lineTo(xMax, yMin);
+ state->lineTo(xMax, yMax);
+ state->lineTo(xMin, yMax);
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+ }
+
+ // perform the fill
+ doShFill(shading);
+
+ // restore graphics state
+ restoreStateStack(savedState);
+}
+
+void Gfx::opShFill(Object args[], int numArgs) {
+ GfxShading *shading;
+ GfxState *savedState;
+ double xMin, yMin, xMax, yMax;
+
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring shaded fill"
+ " in uncolored Type 3 char or tiling pattern");
+ return;
+ }
+
+ if (!out->needNonText()) {
+ return;
+ }
+
+ if (!ocState) {
+ return;
+ }
+
+ if (!(shading = res->lookupShading(args[0].getName()
+ ))) {
+ return;
+ }
+
+ // save current graphics state
+ savedState = saveStateStack();
+
+ // clip to bbox
+ if (shading->getHasBBox()) {
+ shading->getBBox(&xMin, &yMin, &xMax, &yMax);
+ state->moveTo(xMin, yMin);
+ state->lineTo(xMax, yMin);
+ state->lineTo(xMax, yMax);
+ state->lineTo(xMin, yMax);
+ state->closePath();
+ state->clip();
+ out->clip(state);
+ state->clearPath();
+ }
+
+ // set the color space
+ state->setFillColorSpace(shading->getColorSpace()->copy());
+ out->updateFillColorSpace(state);
+
+ // 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:
+ 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;
+ }
+}
+
+void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
+ double x0, y0, x1, y1;
+ GfxColor colors[4];
+
+ shading->getDomain(&x0, &y0, &x1, &y1);
+ shading->getColor(x0, y0, &colors[0]);
+ shading->getColor(x0, y1, &colors[1]);
+ shading->getColor(x1, y0, &colors[2]);
+ shading->getColor(x1, y1, &colors[3]);
+ doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
+}
+
+void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
+ double x0, double y0,
+ double x1, double y1,
+ GfxColor *colors, int depth) {
+ GfxColor fillColor;
+ GfxColor color0M, color1M, colorM0, colorM1, colorMM;
+ GfxColor colors2[4];
+ double *matrix;
+ double xM, yM;
+ int nComps, i, j;
+
+ nComps = shading->getColorSpace()->getNComps();
+ matrix = shading->getMatrix();
+
+ // compare the four corner colors
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < nComps; ++j) {
+ if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
+ break;
+ }
+ }
+ if (j < nComps) {
+ break;
+ }
+ }
+
+ // center of the rectangle
+ xM = 0.5 * (x0 + x1);
+ yM = 0.5 * (y0 + y1);
+
+ // the four corner colors are close (or we hit the recursive limit)
+ // -- fill the rectangle; but require at least one subdivision
+ // (depth==0) to avoid problems when the four outer corners of the
+ // shaded region are the same color
+ if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
+
+ // use the center color
+ shading->getColor(xM, yM, &fillColor);
+ state->setFillColor(&fillColor);
+ out->updateFillColor(state);
+
+ // fill the rectangle
+ state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
+ x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
+ state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
+ x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
+ state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
+ x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
+ state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
+ x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+
+ // the four corner colors are not close enough -- subdivide the
+ // rectangle
+ } else {
+
+ // colors[0] colorM0 colors[2]
+ // (x0,y0) (xM,y0) (x1,y0)
+ // +----------+----------+
+ // | | |
+ // | UL | UR |
+ // color0M | colorMM | color1M
+ // (x0,yM) +----------+----------+ (x1,yM)
+ // | (xM,yM) |
+ // | LL | LR |
+ // | | |
+ // +----------+----------+
+ // colors[1] colorM1 colors[3]
+ // (x0,y1) (xM,y1) (x1,y1)
+
+ shading->getColor(x0, yM, &color0M);
+ shading->getColor(x1, yM, &color1M);
+ shading->getColor(xM, y0, &colorM0);
+ shading->getColor(xM, y1, &colorM1);
+ shading->getColor(xM, yM, &colorMM);
+
+ // upper-left sub-rectangle
+ colors2[0] = colors[0];
+ colors2[1] = color0M;
+ colors2[2] = colorM0;
+ colors2[3] = colorMM;
+ doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
+
+ // lower-left sub-rectangle
+ colors2[0] = color0M;
+ colors2[1] = colors[1];
+ colors2[2] = colorMM;
+ colors2[3] = colorM1;
+ doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
+
+ // upper-right sub-rectangle
+ colors2[0] = colorM0;
+ colors2[1] = colorMM;
+ colors2[2] = colors[2];
+ colors2[3] = color1M;
+ doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
+
+ // lower-right sub-rectangle
+ colors2[0] = colorMM;
+ colors2[1] = colorM1;
+ colors2[2] = color1M;
+ colors2[3] = colors[3];
+ doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
+ }
+}
+
+void Gfx::doAxialShFill(GfxAxialShading *shading) {
+ double xMin, yMin, xMax, yMax;
+ double x0, y0, x1, y1;
+ double dx, dy, mul;
+ GBool dxdyZero, horiz;
+ double tMin, tMax, tMinExt, tMaxExt, t, tx, ty;
+ double sMin, sMax, tmp;
+ double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
+ double t0, t1, tt;
+ GfxColor colors[axialSplits];
+ int abortCheckCounter, nComps, i, j, k;
+
+ // get the clip region bbox
+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+
+ // compute min and max t values, based on the four corners of the
+ // clip region bbox
+ shading->getCoords(&x0, &y0, &x1, &y1);
+ dx = x1 - x0;
+ dy = y1 - y0;
+ dxdyZero = fabs(dx) < 0.0001 && fabs(dy) < 0.0001;
+ horiz = fabs(dy) < fabs(dx);
+ if (dxdyZero) {
+ tMinExt = tMaxExt = 0;
+ tMin = tMax = 0;
+ } else {
+ mul = 1 / (dx * dx + dy * dy);
+ tMinExt = tMaxExt = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
+ t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
+ if (t < tMinExt) {
+ tMinExt = t;
+ } else if (t > tMaxExt) {
+ tMaxExt = t;
+ }
+ t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
+ if (t < tMinExt) {
+ tMinExt = t;
+ } else if (t > tMaxExt) {
+ tMaxExt = t;
+ }
+ t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
+ if (t < tMinExt) {
+ tMinExt = t;
+ } else if (t > tMaxExt) {
+ tMaxExt = t;
+ }
+ if ((tMin = tMinExt) < 0) {
+ tMin = 0;
+ }
+ if (!shading->getExtend0()) {
+ tMinExt = tMin;
+ }
+ if ((tMax = tMaxExt) > 1) {
+ tMax = 1;
+ }
+ if (!shading->getExtend1()) {
+ tMaxExt = tMax;
+ }
+ }
+
+ // get the function domain
+ t0 = shading->getDomain0();
+ t1 = shading->getDomain1();
+
+ // Traverse the t axis and do the shading.
+ //
+ // For each point (tx, ty) on the t axis, consider a line through
+ // that point perpendicular to the t axis:
+ //
+ // x(s) = tx + s * -dy --> s = (x - tx) / -dy
+ // y(s) = ty + s * dx --> s = (y - ty) / dx
+ //
+ // Then look at the intersection of this line with the bounding box
+ // (xMin, yMin, xMax, yMax). For -1 < |dy/dx| < 1, look at the
+ // intersection with yMin, yMax:
+ //
+ // s0 = (yMin - ty) / dx
+ // s1 = (yMax - ty) / dx
+ //
+ // else look at the intersection with xMin, xMax:
+ //
+ // s0 = (xMin - tx) / -dy
+ // s1 = (xMax - tx) / -dy
+ //
+ // Each filled polygon is bounded by two of these line segments
+ // perpdendicular to the t axis.
+ //
+ // The t axis is bisected into smaller regions until the color
+ // difference across a region is small enough, and then the region
+ // is painted with a single color.
+
+ // compute the coordinates of the point on the t axis at t = tMin;
+ // then compute the intersection of the perpendicular line with the
+ // bounding box
+ tx = x0 + tMin * dx;
+ ty = y0 + tMin * dy;
+ if (dxdyZero) {
+ sMin = sMax = 0;
+ } else {
+ if (horiz) {
+ sMin = (yMin - ty) / dx;
+ sMax = (yMax - ty) / dx;
+ } else {
+ sMin = (xMin - tx) / -dy;
+ sMax = (xMax - tx) / -dy;
+ }
+ if (sMin > sMax) {
+ tmp = sMin; sMin = sMax; sMax = tmp;
+ }
+ }
+ ux0 = tx - sMin * dy;
+ uy0 = ty + sMin * dx;
+ vx0 = tx - sMax * dy;
+ vy0 = ty + sMax * dx;
+
+ // fill the extension at t0
+ if (shading->getExtend0() && tMinExt < tMin) {
+
+ // compute the color at t0
+ shading->getColor(t0, &colors[0]);
+
+ // compute the coordinates of the point on the t axis at t =
+ // tMinExt; then compute the intersection of the perpendicular
+ // line with the bounding box
+ tx = x0 + tMinExt * dx;
+ ty = y0 + tMinExt * dy;
+ if (dxdyZero) {
+ sMin = sMax = 0;
+ } else {
+ if (horiz) {
+ sMin = (yMin - ty) / dx;
+ sMax = (yMax - ty) / dx;
+ } else {
+ sMin = (xMin - tx) / -dy;
+ sMax = (xMax - tx) / -dy;
+ }
+ if (sMin > sMax) {
+ tmp = sMin; sMin = sMax; sMax = tmp;
+ }
+ }
+ ux1 = tx - sMin * dy;
+ uy1 = ty + sMin * dx;
+ vx1 = tx - sMax * dy;
+ vy1 = ty + sMax * dx;
+
+ // set the color
+ state->setFillColor(&colors[0]);
+ out->updateFillColor(state);
+
+ // fill the region
+ state->moveTo(ux1, uy1);
+ state->lineTo(vx1, vy1);
+ state->lineTo(vx0, vy0);
+ state->lineTo(ux0, uy0);
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+ }
+
+ // traverse the t axis, splitting [tMin, tMax] into axialSplits regions
+
+ // compute the color in the center of each region
+ for (i = 0; i < axialSplits; ++i) {
+ t = tMin + (tMax - tMin) * (i + 0.5) / axialSplits;
+ tt = t0 + (t1 - t0) * t;
+ shading->getColor(tt, &colors[i]);
+ }
+
+ // each iteration draws one or more regions, starting at i --
+ // if the colors are similar, it will combine regions i, i+1, ...
+ nComps = shading->getColorSpace()->getNComps();
+ abortCheckCounter = 0;
+ i = 0;
+ while (i < axialSplits) {
+
+ if (abortCheckCbk) {
+ ++abortCheckCounter;
+ if (abortCheckCounter > 100) {
+ if ((*abortCheckCbk)(abortCheckCbkData)) {
+ break;
+ }
+ abortCheckCounter = 0;
+ }
+ }
+
+ // check for similar colors
+ for (j = i + 1; j < axialSplits; ++j) {
+ for (k = 0; k < nComps; ++k) {
+ if (abs(colors[j].c[k] - colors[i].c[k]) > axialColorDelta) {
+ break;
+ }
+ }
+ if (k < nComps) {
+ break;
+ }
+ }
+
+ // compute the coordinates of the point on the t axis; then
+ // compute the intersection of the perpendicular line with the
+ // bounding box
+ t = tMin + (tMax - tMin) * (double)j / axialSplits;
+ tx = x0 + t * dx;
+ ty = y0 + t * dy;
+ if (dxdyZero) {
+ sMin = sMax = 0;
+ } else {
+ if (horiz) {
+ sMin = (yMin - ty) / dx;
+ sMax = (yMax - ty) / dx;
+ } else {
+ sMin = (xMin - tx) / -dy;
+ sMax = (xMax - tx) / -dy;
+ }
+ if (sMin > sMax) {
+ tmp = sMin; sMin = sMax; sMax = tmp;
+ }
+ }
+ ux1 = tx - sMin * dy;
+ uy1 = ty + sMin * dx;
+ vx1 = tx - sMax * dy;
+ vy1 = ty + sMax * dx;
+
+ // set the color
+ state->setFillColor(&colors[i]);
+ out->updateFillColor(state);
+
+ // fill the region
+ state->moveTo(ux0, uy0);
+ state->lineTo(vx0, vy0);
+ state->lineTo(vx1, vy1);
+ state->lineTo(ux1, uy1);
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+
+ // set up for next region
+ ux0 = ux1;
+ uy0 = uy1;
+ vx0 = vx1;
+ vy0 = vy1;
+
+ i = j;
+ }
+
+ // fill the extension at t1
+ if (shading->getExtend1() && tMaxExt > tMax) {
+
+ // compute the color at t1
+ shading->getColor(t1, &colors[0]);
+
+ // compute the coordinates of the point on the t axis at t =
+ // tMaxExt; then compute the intersection of the perpendicular
+ // line with the bounding box
+ tx = x0 + tMaxExt * dx;
+ ty = y0 + tMaxExt * dy;
+ if (dxdyZero) {
+ sMin = sMax = 0;
+ } else {
+ if (horiz) {
+ sMin = (yMin - ty) / dx;
+ sMax = (yMax - ty) / dx;
+ } else {
+ sMin = (xMin - tx) / -dy;
+ sMax = (xMax - tx) / -dy;
+ }
+ if (sMin > sMax) {
+ tmp = sMin; sMin = sMax; sMax = tmp;
+ }
+ }
+ ux1 = tx - sMin * dy;
+ uy1 = ty + sMin * dx;
+ vx1 = tx - sMax * dy;
+ vy1 = ty + sMax * dx;
+
+ // set the color
+ state->setFillColor(&colors[0]);
+ out->updateFillColor(state);
+
+ // fill the region
+ state->moveTo(ux0, uy0);
+ state->lineTo(vx0, vy0);
+ state->lineTo(vx1, vy1);
+ state->lineTo(ux1, uy1);
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+ }
+}
+
+#if defined(__GNUC__) && !defined(__clang__)
+// this function makes a lot of sin()/cos() calls, which are slow
+// with glibc 2.16 and newer on x86; accuracy isn't terribly
+// important here, so tell gcc to use the fast version
+#pragma GCC optimize ("fast-math")
+#endif
+
+void Gfx::doRadialShFill(GfxRadialShading *shading) {
+ double xMin, yMin, xMax, yMax;
+ double x0, y0, r0, x1, y1, r1, t0, t1;
+ int nComps;
+ GfxColor colorA, colorB;
+ double xa, ya, xb, yb, ra, rb;
+ double ta, tb, sa, sb;
+ double sMin, sMax, h;
+ double sLeft, sRight, sTop, sBottom, sZero, sDiag;
+ GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero;
+ GBool haveSMin, haveSMax;
+ GBool enclosed;
+ double *ctm;
+ double theta, alpha, angle, t;
+ int abortCheckCounter, ia, ib, k, n;
+
+ // get the shading info
+ shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
+ t0 = shading->getDomain0();
+ t1 = shading->getDomain1();
+ nComps = shading->getColorSpace()->getNComps();
+
+ // Compute the point at which r(s) = 0; check for the enclosed
+ // circles case; and compute the angles for the tangent lines.
+ h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
+ if (h == 0) {
+ enclosed = gTrue;
+ theta = 0; // make gcc happy
+ } else if (r1 - r0 == 0) {
+ enclosed = gFalse;
+ theta = 0;
+ } else if (fabs(r1 - r0) >= h - 0.0001) {
+ enclosed = gTrue;
+ theta = 0; // make gcc happy
+ } else {
+ enclosed = gFalse;
+ theta = asin((r1 - r0) / h);
+ }
+ if (enclosed) {
+ alpha = 0;
+ } else {
+ alpha = atan2(y1 - y0, x1 - x0);
+ }
+
+ // compute the (possibly extended) s range
+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+ if (enclosed) {
+ sMin = 0;
+ sMax = 1;
+ } else {
+ // solve x(sLeft) + r(sLeft) = xMin
+ if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) {
+ sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
+ } else {
+ sLeft = 0; // make gcc happy
+ }
+ // solve x(sRight) - r(sRight) = xMax
+ if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) {
+ sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
+ } else {
+ sRight = 0; // make gcc happy
+ }
+ // solve y(sBottom) + r(sBottom) = yMin
+ if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) {
+ sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
+ } else {
+ sBottom = 0; // make gcc happy
+ }
+ // solve y(sTop) - r(sTop) = yMax
+ if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) {
+ sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
+ } else {
+ sTop = 0; // make gcc happy
+ }
+ // solve r(sZero) = 0
+ if ((haveSZero = fabs(r1 - r0) > 0.000001)) {
+ sZero = -r0 / (r1 - r0);
+ } else {
+ sZero = 0; // make gcc happy
+ }
+ // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2)
+ if (haveSZero) {
+ sDiag = (sqrt((xMax - xMin) * (xMax - xMin) +
+ (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
+ } else {
+ sDiag = 0; // make gcc happy
+ }
+ // compute sMin
+ if (shading->getExtend0()) {
+ sMin = 0;
+ haveSMin = gFalse;
+ if (x0 < x1 && haveSLeft && sLeft < 0) {
+ sMin = sLeft;
+ haveSMin = gTrue;
+ } else if (x0 > x1 && haveSRight && sRight < 0) {
+ sMin = sRight;
+ haveSMin = gTrue;
+ }
+ if (y0 < y1 && haveSBottom && sBottom < 0) {
+ if (!haveSMin || sBottom > sMin) {
+ sMin = sBottom;
+ haveSMin = gTrue;
+ }
+ } else if (y0 > y1 && haveSTop && sTop < 0) {
+ if (!haveSMin || sTop > sMin) {
+ sMin = sTop;
+ haveSMin = gTrue;
+ }
+ }
+ if (haveSZero && sZero <= 0) {
+ if (!haveSMin || sZero > sMin) {
+ sMin = sZero;
+ }
+ }
+ } else {
+ sMin = 0;
+ }
+ // compute sMax
+ if (shading->getExtend1()) {
+ sMax = 1;
+ haveSMax = gFalse;
+ if (x1 < x0 && haveSLeft && sLeft > 1) {
+ sMax = sLeft;
+ haveSMax = gTrue;
+ } else if (x1 > x0 && haveSRight && sRight > 1) {
+ sMax = sRight;
+ haveSMax = gTrue;
+ }
+ if (y1 < y0 && haveSBottom && sBottom > 1) {
+ if (!haveSMax || sBottom < sMax) {
+ sMax = sBottom;
+ haveSMax = gTrue;
+ }
+ } else if (y1 > y0 && haveSTop && sTop > 1) {
+ if (!haveSMax || sTop < sMax) {
+ sMax = sTop;
+ haveSMax = gTrue;
+ }
+ }
+ if (haveSZero && sDiag > 1) {
+ if (!haveSMax || sDiag < sMax) {
+ sMax = sDiag;
+ }
+ }
+ } else {
+ sMax = 1;
+ }
+ }
+
+ // compute the number of steps into which circles must be divided to
+ // achieve a curve flatness of 0.1 pixel in device space for the
+ // largest circle (note that "device space" is 72 dpi when generating
+ // PostScript, hence the relatively small 0.1 pixel accuracy)
+ ctm = state->getCTM();
+ t = fabs(ctm[0]);
+ if (fabs(ctm[1]) > t) {
+ t = fabs(ctm[1]);
+ }
+ if (fabs(ctm[2]) > t) {
+ t = fabs(ctm[2]);
+ }
+ if (fabs(ctm[3]) > t) {
+ t = fabs(ctm[3]);
+ }
+ if (r0 > r1) {
+ t *= r0;
+ } else {
+ t *= r1;
+ }
+ if (t < 1) {
+ n = 3;
+ } else {
+ n = (int)(M_PI / acos(1 - 0.1 / t));
+ if (n < 3) {
+ n = 3;
+ } else if (n > 200) {
+ n = 200;
+ }
+ }
+
+ // setup for the start circle
+ ia = 0;
+ sa = sMin;
+ ta = t0 + sa * (t1 - t0);
+ xa = x0 + sa * (x1 - x0);
+ ya = y0 + sa * (y1 - y0);
+ ra = r0 + sa * (r1 - r0);
+ if (ta < t0) {
+ shading->getColor(t0, &colorA);
+ } else if (ta > t1) {
+ shading->getColor(t1, &colorA);
+ } else {
+ shading->getColor(ta, &colorA);
+ }
+
+ // fill the circles
+ abortCheckCounter = 0;
+ while (ia < radialMaxSplits) {
+
+ if (abortCheckCbk) {
+ ++abortCheckCounter;
+ if (abortCheckCounter > 100) {
+ if ((*abortCheckCbk)(abortCheckCbkData)) {
+ break;
+ }
+ abortCheckCounter = 0;
+ }
+ }
+
+ // go as far along the t axis (toward t1) as we can, such that the
+ // color difference is within the tolerance (radialColorDelta) --
+ // this uses bisection (between the current value, t, and t1),
+ // limited to radialMaxSplits points along the t axis; require at
+ // least one split to avoid problems when the innermost and
+ // outermost colors are the same
+ ib = radialMaxSplits;
+ sb = sMax;
+ tb = t0 + sb * (t1 - t0);
+ if (tb < t0) {
+ shading->getColor(t0, &colorB);
+ } else if (tb > t1) {
+ shading->getColor(t1, &colorB);
+ } else {
+ shading->getColor(tb, &colorB);
+ }
+ while (ib - ia > 1) {
+ for (k = 0; k < nComps; ++k) {
+ if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
+ break;
+ }
+ }
+ if (k == nComps && ib < radialMaxSplits) {
+ break;
+ }
+ ib = (ia + ib) / 2;
+ sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
+ tb = t0 + sb * (t1 - t0);
+ if (tb < t0) {
+ shading->getColor(t0, &colorB);
+ } else if (tb > t1) {
+ shading->getColor(t1, &colorB);
+ } else {
+ shading->getColor(tb, &colorB);
+ }
+ }
+
+ // compute center and radius of the circle
+ xb = x0 + sb * (x1 - x0);
+ yb = y0 + sb * (y1 - y0);
+ rb = r0 + sb * (r1 - r0);
+
+ // use the average of the colors at the two circles
+ for (k = 0; k < nComps; ++k) {
+ colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
+ }
+ state->setFillColor(&colorA);
+ out->updateFillColor(state);
+
+ if (enclosed) {
+
+ // construct path for first circle (counterclockwise)
+ state->moveTo(xa + ra, ya);
+ for (k = 1; k < n; ++k) {
+ angle = ((double)k / (double)n) * 2 * M_PI;
+ state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
+ }
+ state->closePath();
+
+ // construct and append path for second circle (clockwise)
+ state->moveTo(xb + rb, yb);
+ for (k = 1; k < n; ++k) {
+ angle = -((double)k / (double)n) * 2 * M_PI;
+ state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
+ }
+ state->closePath();
+
+ } else {
+
+ // construct the first subpath (clockwise)
+ state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
+ ya + ra * sin(alpha + theta + 0.5 * M_PI));
+ for (k = 0; k < n; ++k) {
+ angle = alpha + theta + 0.5 * M_PI
+ - ((double)k / (double)n) * (2 * theta + M_PI);
+ state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
+ }
+ for (k = 0; k < n; ++k) {
+ angle = alpha - theta - 0.5 * M_PI
+ + ((double)k / (double)n) * (2 * theta - M_PI);
+ state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
+ }
+ state->closePath();
+
+ // construct the second subpath (counterclockwise)
+ state->moveTo(xa + ra * cos(alpha + theta + 0.5 * M_PI),
+ ya + ra * sin(alpha + theta + 0.5 * M_PI));
+ for (k = 0; k < n; ++k) {
+ angle = alpha + theta + 0.5 * M_PI
+ + ((double)k / (double)n) * (-2 * theta + M_PI);
+ state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
+ }
+ for (k = 0; k < n; ++k) {
+ angle = alpha - theta - 0.5 * M_PI
+ + ((double)k / (double)n) * (2 * theta + M_PI);
+ state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
+ }
+ state->closePath();
+ }
+
+ // fill the path
+ out->fill(state);
+ state->clearPath();
+
+ // step to the next value of t
+ ia = ib;
+ sa = sb;
+ ta = tb;
+ xa = xb;
+ ya = yb;
+ ra = rb;
+ colorA = colorB;
+ }
+
+ if (enclosed) {
+ // extend the smaller circle
+ if ((shading->getExtend0() && r0 <= r1) ||
+ (shading->getExtend1() && r1 < r0)) {
+ if (r0 <= r1) {
+ ta = t0;
+ ra = r0;
+ xa = x0;
+ ya = y0;
+ } else {
+ ta = t1;
+ ra = r1;
+ xa = x1;
+ ya = y1;
+ }
+ shading->getColor(ta, &colorA);
+ state->setFillColor(&colorA);
+ out->updateFillColor(state);
+ state->moveTo(xa + ra, ya);
+ for (k = 1; k < n; ++k) {
+ angle = ((double)k / (double)n) * 2 * M_PI;
+ state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
+ }
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+ }
+
+ // extend the larger circle
+ if ((shading->getExtend0() && r0 > r1) ||
+ (shading->getExtend1() && r1 >= r0)) {
+ if (r0 > r1) {
+ ta = t0;
+ ra = r0;
+ xa = x0;
+ ya = y0;
+ } else {
+ ta = t1;
+ ra = r1;
+ xa = x1;
+ ya = y1;
+ }
+ shading->getColor(ta, &colorA);
+ state->setFillColor(&colorA);
+ out->updateFillColor(state);
+ state->moveTo(xMin, yMin);
+ state->lineTo(xMin, yMax);
+ state->lineTo(xMax, yMax);
+ state->lineTo(xMax, yMin);
+ state->closePath();
+ state->moveTo(xa + ra, ya);
+ for (k = 1; k < n; ++k) {
+ angle = ((double)k / (double)n) * 2 * M_PI;
+ state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
+ }
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+ }
+ }
+}
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC reset_options
+#endif
+
+void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
+ double x0, y0, x1, y1, x2, y2;
+ double color0[gfxColorMaxComps];
+ double color1[gfxColorMaxComps];
+ double color2[gfxColorMaxComps];
+ int abortCheckCounter, i;
+
+ abortCheckCounter = 0;
+ for (i = 0; i < shading->getNTriangles(); ++i) {
+ if (abortCheckCbk) {
+ ++abortCheckCounter;
+ if (abortCheckCounter > 25) {
+ if ((*abortCheckCbk)(abortCheckCbkData)) {
+ break;
+ }
+ abortCheckCounter = 0;
+ }
+ }
+ shading->getTriangle(i, &x0, &y0, color0,
+ &x1, &y1, color1,
+ &x2, &y2, color2);
+ gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2,
+ shading, 0);
+ }
+}
+
+void Gfx::gouraudFillTriangle(double x0, double y0, double *color0,
+ double x1, double y1, double *color1,
+ double x2, double y2, double *color2,
+ GfxGouraudTriangleShading *shading, int depth) {
+ double dx0, dy0, dx1, dy1, dx2, dy2;
+ double x01, y01, x12, y12, x20, y20;
+ double color01[gfxColorMaxComps];
+ double color12[gfxColorMaxComps];
+ double color20[gfxColorMaxComps];
+ GfxColor c0, c1, c2;
+ int nComps, i;
+
+ // recursion ends when:
+ // (1) color difference is smaller than gouraudColorDelta; or
+ // (2) triangles are smaller than 0.5 pixel (note that "device
+ // space" is 72dpi when generating PostScript); or
+ // (3) max recursion depth (gouraudMaxDepth) is hit.
+ nComps = shading->getColorSpace()->getNComps();
+ shading->getColor(color0, &c0);
+ shading->getColor(color1, &c1);
+ shading->getColor(color2, &c2);
+ for (i = 0; i < nComps; ++i) {
+ if (abs(c0.c[i] - c1.c[i]) > gouraudColorDelta ||
+ abs(c1.c[i] - c2.c[i]) > gouraudColorDelta) {
+ break;
+ }
+ }
+ state->transformDelta(x1 - x0, y1 - y0, &dx0, &dy0);
+ state->transformDelta(x2 - x1, y2 - y1, &dx1, &dy1);
+ state->transformDelta(x0 - x2, y0 - y2, &dx2, &dy2);
+ if (i == nComps ||
+ depth == gouraudMaxDepth ||
+ (fabs(dx0) < 0.5 && fabs(dy0) < 0.5 &&
+ fabs(dx1) < 0.5 && fabs(dy1) < 0.5 &&
+ fabs(dx2) < 0.5 && fabs(dy2) < 0.5)) {
+ state->setFillColor(&c0);
+ out->updateFillColor(state);
+ state->moveTo(x0, y0);
+ state->lineTo(x1, y1);
+ state->lineTo(x2, y2);
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+ } else {
+ x01 = 0.5 * (x0 + x1);
+ y01 = 0.5 * (y0 + y1);
+ x12 = 0.5 * (x1 + x2);
+ y12 = 0.5 * (y1 + y2);
+ x20 = 0.5 * (x2 + x0);
+ y20 = 0.5 * (y2 + y0);
+ for (i = 0; i < shading->getNComps(); ++i) {
+ color01[i] = 0.5 * (color0[i] + color1[i]);
+ color12[i] = 0.5 * (color1[i] + color2[i]);
+ color20[i] = 0.5 * (color2[i] + color0[i]);
+ }
+ gouraudFillTriangle(x0, y0, color0, x01, y01, color01,
+ x20, y20, color20, shading, depth + 1);
+ gouraudFillTriangle(x01, y01, color01, x1, y1, color1,
+ x12, y12, color12, shading, depth + 1);
+ gouraudFillTriangle(x01, y01, color01, x12, y12, color12,
+ x20, y20, color20, shading, depth + 1);
+ gouraudFillTriangle(x20, y20, color20, x12, y12, color12,
+ x2, y2, color2, shading, depth + 1);
+ }
+}
+
+void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
+ int start, abortCheckCounter, i;
+
+ if (shading->getNPatches() > 128) {
+ start = 3;
+ } else if (shading->getNPatches() > 64) {
+ start = 2;
+ } else if (shading->getNPatches() > 16) {
+ start = 1;
+ } else {
+ start = 0;
+ }
+ abortCheckCounter = 0;
+ for (i = 0; i < shading->getNPatches(); ++i) {
+ if (abortCheckCbk) {
+ ++abortCheckCounter;
+ if (abortCheckCounter > 25) {
+ if ((*abortCheckCbk)(abortCheckCbkData)) {
+ break;
+ }
+ abortCheckCounter = 0;
+ }
+ }
+ fillPatch(shading->getPatch(i), shading, start);
+ }
+}
+
+void Gfx::fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth) {
+ GfxPatch patch00, patch01, patch10, patch11;
+ GfxColor c00, c01, c10, c11;
+ double xx[4][8], yy[4][8];
+ double x, y, xMin, yMin, xMax, yMax, xxm, yym;
+ int nComps, i, j;
+ GBool stop;
+
+ shading->getColor(patch->color[0][0], &c00);
+ stop = gFalse;
+
+ // stop subdivision at max depth
+ if (depth == patchMaxDepth) {
+ stop = gTrue;
+ }
+
+ // stop subdivision if colors are close enough
+ if (!stop) {
+ nComps = shading->getColorSpace()->getNComps();
+ shading->getColor(patch->color[0][1], &c01);
+ shading->getColor(patch->color[1][0], &c10);
+ shading->getColor(patch->color[1][1], &c11);
+ 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) {
+ xMin = yMin = xMax = yMax = 0;
+ for (j = 0; j < 4; ++j) {
+ for (i = 0; i < 4; ++i) {
+ state->transformDelta(patch->x[i][j], patch->y[i][j], &x, &y);
+ if (i == 0 && j == 0) {
+ xMin = xMax = x;
+ yMin = yMax = y;
+ } else {
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ }
+ }
+ }
+ if (xMax - xMin < 1 && yMax - yMin < 1) {
+ stop = gTrue;
+ }
+ }
+
+ // draw the patch
+ if (stop) {
+ state->setFillColor(&c00);
+ out->updateFillColor(state);
+ state->moveTo(patch->x[0][0], patch->y[0][0]);
+ state->curveTo(patch->x[0][1], patch->y[0][1],
+ patch->x[0][2], patch->y[0][2],
+ patch->x[0][3], patch->y[0][3]);
+ state->curveTo(patch->x[1][3], patch->y[1][3],
+ patch->x[2][3], patch->y[2][3],
+ patch->x[3][3], patch->y[3][3]);
+ state->curveTo(patch->x[3][2], patch->y[3][2],
+ patch->x[3][1], patch->y[3][1],
+ patch->x[3][0], patch->y[3][0]);
+ state->curveTo(patch->x[2][0], patch->y[2][0],
+ patch->x[1][0], patch->y[1][0],
+ patch->x[0][0], patch->y[0][0]);
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+
+ // subdivide the patch
+ } else {
+ for (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]);
+ xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
+ 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];
+ }
+ for (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]);
+ xxm = 0.5 * (xx[1][i] + xx[2][i]);
+ 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 (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]);
+ xxm = 0.5 * (xx[1][i] + xx[2][i]);
+ 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 (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(&patch00, shading, depth + 1);
+ fillPatch(&patch10, shading, depth + 1);
+ fillPatch(&patch01, shading, depth + 1);
+ fillPatch(&patch11, shading, depth + 1);
+ }
+}
+
+void Gfx::doEndPath() {
+ if (state->isCurPt() && clip != clipNone) {
+ state->clip();
+ if (clip == clipNormal) {
+ out->clip(state);
+ } else {
+ out->eoClip(state);
+ }
+ }
+ clip = clipNone;
+ state->clearPath();
+}
+
+//------------------------------------------------------------------------
+// path clipping operators
+//------------------------------------------------------------------------
+
+void Gfx::opClip(Object args[], int numArgs) {
+ clip = clipNormal;
+}
+
+void Gfx::opEOClip(Object args[], int numArgs) {
+ clip = clipEO;
+}
+
+//------------------------------------------------------------------------
+// text object operators
+//------------------------------------------------------------------------
+
+void Gfx::opBeginText(Object args[], int numArgs) {
+ state->setTextMat(1, 0, 0, 1, 0, 0);
+ state->textMoveTo(0, 0);
+ out->updateTextMat(state);
+ out->updateTextPos(state);
+ fontChanged = gTrue;
+}
+
+void Gfx::opEndText(Object args[], int numArgs) {
+ out->endTextObject(state);
+}
+
+//------------------------------------------------------------------------
+// text state operators
+//------------------------------------------------------------------------
+
+void Gfx::opSetCharSpacing(Object args[], int numArgs) {
+ state->setCharSpace(args[0].getNum());
+ out->updateCharSpace(state);
+}
+
+void Gfx::opSetFont(Object args[], int numArgs) {
+ doSetFont(res->lookupFont(args[0].getName()), args[1].getNum());
+}
+
+void Gfx::doSetFont(GfxFont *font, double size) {
+ if (!font) {
+ state->setFont(NULL, 0);
+ return;
+ }
+ if (printCommands) {
+ printf(" font: tag=%s name='%s' %g\n",
+ font->getTag()->getCString(),
+ font->getName() ? font->getName()->getCString() : "???",
+ size);
+ fflush(stdout);
+ }
+ state->setFont(font, size);
+ fontChanged = gTrue;
+}
+
+void Gfx::opSetTextLeading(Object args[], int numArgs) {
+ state->setLeading(args[0].getNum());
+}
+
+void Gfx::opSetTextRender(Object args[], int numArgs) {
+ state->setRender(args[0].getInt());
+ out->updateRender(state);
+}
+
+void Gfx::opSetTextRise(Object args[], int numArgs) {
+ state->setRise(args[0].getNum());
+ out->updateRise(state);
+}
+
+void Gfx::opSetWordSpacing(Object args[], int numArgs) {
+ state->setWordSpace(args[0].getNum());
+ out->updateWordSpace(state);
+}
+
+void Gfx::opSetHorizScaling(Object args[], int numArgs) {
+ state->setHorizScaling(args[0].getNum());
+ out->updateHorizScaling(state);
+ fontChanged = gTrue;
+}
+
+//------------------------------------------------------------------------
+// text positioning operators
+//------------------------------------------------------------------------
+
+void Gfx::opTextMove(Object args[], int numArgs) {
+ double tx, ty;
+
+ tx = state->getLineX() + args[0].getNum();
+ ty = state->getLineY() + args[1].getNum();
+ state->textMoveTo(tx, ty);
+ out->updateTextPos(state);
+}
+
+void Gfx::opTextMoveSet(Object args[], int numArgs) {
+ double tx, ty;
+
+ tx = state->getLineX() + args[0].getNum();
+ ty = args[1].getNum();
+ state->setLeading(-ty);
+ ty += state->getLineY();
+ state->textMoveTo(tx, ty);
+ out->updateTextPos(state);
+}
+
+void Gfx::opSetTextMatrix(Object args[], int numArgs) {
+ state->setTextMat(args[0].getNum(), args[1].getNum(),
+ args[2].getNum(), args[3].getNum(),
+ args[4].getNum(), args[5].getNum());
+ state->textMoveTo(0, 0);
+ out->updateTextMat(state);
+ out->updateTextPos(state);
+ fontChanged = gTrue;
+}
+
+void Gfx::opTextNextLine(Object args[], int numArgs) {
+ double tx, ty;
+
+ tx = state->getLineX();
+ ty = state->getLineY() - state->getLeading();
+ state->textMoveTo(tx, ty);
+ out->updateTextPos(state);
+}
+
+//------------------------------------------------------------------------
+// text string operators
+//------------------------------------------------------------------------
+
+void Gfx::opShowText(Object args[], int numArgs) {
+ if (!state->getFont()) {
+ error(errSyntaxError, getPos(), "No font in show");
+ return;
+ }
+ if (fontChanged) {
+ out->updateFont(state);
+ fontChanged = gFalse;
+ }
+ if (ocState) {
+ out->beginStringOp(state);
+ doShowText(args[0].getString());
+ out->endStringOp(state);
+ } else {
+ doIncCharCount(args[0].getString());
+ }
+}
+
+void Gfx::opMoveShowText(Object args[], int numArgs) {
+ double tx, ty;
+
+ if (!state->getFont()) {
+ error(errSyntaxError, getPos(), "No font in move/show");
+ return;
+ }
+ if (fontChanged) {
+ out->updateFont(state);
+ fontChanged = gFalse;
+ }
+ tx = state->getLineX();
+ ty = state->getLineY() - state->getLeading();
+ state->textMoveTo(tx, ty);
+ out->updateTextPos(state);
+ if (ocState) {
+ out->beginStringOp(state);
+ doShowText(args[0].getString());
+ out->endStringOp(state);
+ } else {
+ doIncCharCount(args[0].getString());
+ }
+}
+
+void Gfx::opMoveSetShowText(Object args[], int numArgs) {
+ double tx, ty;
+
+ if (!state->getFont()) {
+ error(errSyntaxError, getPos(), "No font in move/set/show");
+ return;
+ }
+ if (fontChanged) {
+ out->updateFont(state);
+ fontChanged = gFalse;
+ }
+ state->setWordSpace(args[0].getNum());
+ state->setCharSpace(args[1].getNum());
+ tx = state->getLineX();
+ ty = state->getLineY() - state->getLeading();
+ state->textMoveTo(tx, ty);
+ out->updateWordSpace(state);
+ out->updateCharSpace(state);
+ out->updateTextPos(state);
+ if (ocState) {
+ out->beginStringOp(state);
+ doShowText(args[2].getString());
+ out->endStringOp(state);
+ } else {
+ doIncCharCount(args[2].getString());
+ }
+}
+
+void Gfx::opShowSpaceText(Object args[], int numArgs) {
+ Array *a;
+ Object obj;
+ int wMode;
+ int i;
+
+ if (!state->getFont()) {
+ error(errSyntaxError, getPos(), "No font in show/space");
+ return;
+ }
+ if (fontChanged) {
+ out->updateFont(state);
+ fontChanged = gFalse;
+ }
+ if (ocState) {
+ out->beginStringOp(state);
+ wMode = state->getFont()->getWMode();
+ a = args[0].getArray();
+ for (i = 0; i < a->getLength(); ++i) {
+ a->get(i, &obj);
+ if (obj.isNum()) {
+ if (wMode) {
+ state->textShift(0, -obj.getNum() * 0.001 *
+ state->getFontSize());
+ } else {
+ state->textShift(-obj.getNum() * 0.001 *
+ state->getFontSize() *
+ state->getHorizScaling(), 0);
+ }
+ out->updateTextShift(state, obj.getNum());
+ } else if (obj.isString()) {
+ doShowText(obj.getString());
+ } else {
+ error(errSyntaxError, getPos(),
+ "Element of show/space array must be number or string");
+ }
+ obj.free();
+ }
+ out->endStringOp(state);
+ } else {
+ a = args[0].getArray();
+ for (i = 0; i < a->getLength(); ++i) {
+ a->get(i, &obj);
+ if (obj.isString()) {
+ doIncCharCount(obj.getString());
+ }
+ obj.free();
+ }
+ }
+}
+
+void Gfx::doShowText(GString *s) {
+ GfxFont *font;
+ int wMode;
+ double riseX, riseY;
+ CharCode code;
+ Unicode u[8];
+ double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, ddx, ddy;
+ double originX, originY, tOriginX, tOriginY;
+ double x0, y0, x1, y1;
+ double oldCTM[6], newCTM[6];
+ double *mat;
+ Object charProcRef, charProc;
+ Dict *resDict;
+ Parser *oldParser;
+ GfxState *savedState;
+ char *p;
+ int render;
+ GBool patternFill;
+ int len, n, uLen, nChars, nSpaces, i;
+
+ font = state->getFont();
+ wMode = font->getWMode();
+
+ if (globalParams->isDroppedFont(font->getName()
+ ? font->getName()->getCString() : "")) {
+ doIncCharCount(s);
+ return;
+ }
+
+ if (out->useDrawChar()) {
+ out->beginString(state, s);
+ }
+
+ // if we're doing a pattern fill, set up clipping
+ render = state->getRender();
+ if (!(render & 1) &&
+ state->getFillColorSpace()->getMode() == csPattern) {
+ patternFill = gTrue;
+ saveState();
+ // disable fill, enable clipping, leave stroke unchanged
+ if ((render ^ (render >> 1)) & 1) {
+ render = 5;
+ } else {
+ render = 7;
+ }
+ state->setRender(render);
+ out->updateRender(state);
+ } else {
+ patternFill = gFalse;
+ }
+
+ state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
+ x0 = state->getCurX() + riseX;
+ y0 = state->getCurY() + riseY;
+
+ // handle a Type 3 char
+ if (font->getType() == fontType3 && out->interpretType3Chars()) {
+ mat = state->getCTM();
+ for (i = 0; i < 6; ++i) {
+ oldCTM[i] = mat[i];
+ }
+ mat = state->getTextMat();
+ newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
+ newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
+ newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
+ newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
+ mat = font->getFontMatrix();
+ newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
+ newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
+ newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
+ newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
+ newCTM[0] *= state->getFontSize();
+ newCTM[1] *= state->getFontSize();
+ newCTM[2] *= state->getFontSize();
+ newCTM[3] *= state->getFontSize();
+ newCTM[0] *= state->getHorizScaling();
+ newCTM[2] *= state->getHorizScaling();
+ curX = state->getCurX();
+ curY = state->getCurY();
+ oldParser = parser;
+ p = s->getCString();
+ len = s->getLength();
+ while (len > 0) {
+ n = font->getNextChar(p, len, &code,
+ u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+ &dx, &dy, &originX, &originY);
+ dx = dx * state->getFontSize() + state->getCharSpace();
+ if (n == 1 && *p == ' ') {
+ dx += state->getWordSpace();
+ }
+ dx *= state->getHorizScaling();
+ dy *= state->getFontSize();
+ state->textTransformDelta(dx, dy, &tdx, &tdy);
+ state->transform(curX + riseX, curY + riseY, &x, &y);
+ savedState = saveStateStack();
+ state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
+ //~ 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);
+ charProcRef.fetch(xref, &charProc);
+ if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
+ pushResources(resDict);
+ }
+ if (charProc.isStream()) {
+ display(&charProcRef, gFalse);
+ } else {
+ error(errSyntaxError, getPos(),
+ "Missing or bad Type3 CharProc entry");
+ }
+ out->endType3Char(state);
+ if (resDict) {
+ popResources();
+ }
+ charProc.free();
+ charProcRef.free();
+ }
+ restoreStateStack(savedState);
+ curX += tdx;
+ curY += tdy;
+ state->moveTo(curX, curY);
+ p += n;
+ len -= n;
+ }
+ parser = oldParser;
+
+ } else if (out->useDrawChar()) {
+ p = s->getCString();
+ len = s->getLength();
+ while (len > 0) {
+ n = font->getNextChar(p, len, &code,
+ u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+ &dx, &dy, &originX, &originY);
+ if (wMode) {
+ dx *= state->getFontSize();
+ dy = dy * state->getFontSize() + state->getCharSpace();
+ if (n == 1 && *p == ' ') {
+ dy += state->getWordSpace();
+ }
+ } else {
+ dx = dx * state->getFontSize() + state->getCharSpace();
+ if (n == 1 && *p == ' ') {
+ dx += state->getWordSpace();
+ }
+ dx *= state->getHorizScaling();
+ dy *= state->getFontSize();
+ }
+ state->textTransformDelta(dx, dy, &tdx, &tdy);
+ originX *= state->getFontSize();
+ originY *= state->getFontSize();
+ state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
+ out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
+ tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
+ state->shift(tdx, tdy);
+ p += n;
+ len -= n;
+ }
+
+ } else {
+ dx = dy = 0;
+ p = s->getCString();
+ len = s->getLength();
+ nChars = nSpaces = 0;
+ while (len > 0) {
+ n = font->getNextChar(p, len, &code,
+ u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+ &dx2, &dy2, &originX, &originY);
+ dx += dx2;
+ dy += dy2;
+ if (n == 1 && *p == ' ') {
+ ++nSpaces;
+ }
+ ++nChars;
+ p += n;
+ len -= n;
+ }
+ if (wMode) {
+ dx *= state->getFontSize();
+ dy = dy * state->getFontSize()
+ + nChars * state->getCharSpace()
+ + nSpaces * state->getWordSpace();
+ } else {
+ dx = dx * state->getFontSize()
+ + nChars * state->getCharSpace()
+ + nSpaces * state->getWordSpace();
+ dx *= state->getHorizScaling();
+ dy *= state->getFontSize();
+ }
+ state->textTransformDelta(dx, dy, &tdx, &tdy);
+ out->drawString(state, s);
+ state->shift(tdx, tdy);
+ }
+
+ if (out->useDrawChar()) {
+ out->endString(state);
+ }
+
+ if (patternFill) {
+ out->saveTextPos(state);
+ // tell the OutputDev to do the clipping
+ out->endTextObject(state);
+ // set up a clipping bbox so doPatternText will work -- assume
+ // that the text bounding box does not extend past the baseline in
+ // any direction by more than twice the font size
+ x1 = state->getCurX() + riseX;
+ y1 = state->getCurY() + riseY;
+ if (x0 > x1) {
+ x = x0; x0 = x1; x1 = x;
+ }
+ if (y0 > y1) {
+ y = y0; y0 = y1; y1 = y;
+ }
+ state->textTransformDelta(0, state->getFontSize(), &dx, &dy);
+ state->textTransformDelta(state->getFontSize(), 0, &dx2, &dy2);
+ dx = fabs(dx);
+ dx2 = fabs(dx2);
+ if (dx2 > dx) {
+ dx = dx2;
+ }
+ dy = fabs(dy);
+ dy2 = fabs(dy2);
+ if (dy2 > dy) {
+ dy = dy2;
+ }
+ state->clipToRect(x0 - 2 * dx, y0 - 2 * dy, x1 + 2 * dx, y1 + 2 * dy);
+ // set render mode to fill-only
+ state->setRender(0);
+ out->updateRender(state);
+ doPatternText();
+ restoreState();
+ out->restoreTextPos(state);
+ }
+
+ opCounter += 10 * s->getLength();
+}
+
+// NB: this is only called when ocState is false.
+void Gfx::doIncCharCount(GString *s) {
+ if (out->needCharCount()) {
+ out->incCharCount(s->getLength());
+ }
+}
+
+//------------------------------------------------------------------------
+// XObject operators
+//------------------------------------------------------------------------
+
+void Gfx::opXObject(Object args[], int numArgs) {
+ char *name;
+ Object obj1, obj2, obj3, refObj;
+ GBool ocSaved, oc;
+#if OPI_SUPPORT
+ Object opiDict;
+#endif
+
+ if (!ocState && !out->needCharCount()) {
+ return;
+ }
+ name = args[0].getName();
+ if (!res->lookupXObject(name, &obj1)) {
+ return;
+ }
+ if (!obj1.isStream()) {
+ error(errSyntaxError, getPos(), "XObject '{0:s}' is wrong type", name);
+ obj1.free();
+ return;
+ }
+
+ // check for optional content key
+ ocSaved = ocState;
+ obj1.streamGetDict()->lookupNF("OC", &obj2);
+ if (doc->getOptionalContent()->evalOCObject(&obj2, &oc)) {
+ ocState &= oc;
+ }
+ obj2.free();
+
+#if USE_EXCEPTIONS
+ try {
+#endif
+#if OPI_SUPPORT
+ obj1.streamGetDict()->lookup("OPI", &opiDict);
+ if (opiDict.isDict()) {
+ out->opiBegin(state, opiDict.getDict());
+ }
+#endif
+ obj1.streamGetDict()->lookup("Subtype", &obj2);
+ if (obj2.isName("Image")) {
+ if (out->needNonText()) {
+ res->lookupXObjectNF(name, &refObj);
+ doImage(&refObj, obj1.getStream(), gFalse);
+ refObj.free();
+ }
+ } else if (obj2.isName("Form")) {
+ res->lookupXObjectNF(name, &refObj);
+ if (out->useDrawForm() && refObj.isRef()) {
+ if (ocState) {
+ out->drawForm(refObj.getRef());
+ }
+ } else {
+ doForm(&refObj, &obj1);
+ }
+ refObj.free();
+ } else if (obj2.isName("PS")) {
+ if (ocState) {
+ obj1.streamGetDict()->lookup("Level1", &obj3);
+ out->psXObject(obj1.getStream(),
+ obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
+ }
+ } else if (obj2.isName()) {
+ error(errSyntaxError, getPos(),
+ "Unknown XObject subtype '{0:s}'", obj2.getName());
+ } else {
+ error(errSyntaxError, getPos(),
+ "XObject subtype is missing or wrong type");
+ }
+ obj2.free();
+#if OPI_SUPPORT
+ if (opiDict.isDict()) {
+ out->opiEnd(state, opiDict.getDict());
+ }
+ opiDict.free();
+#endif
+#if USE_EXCEPTIONS
+ } catch (GMemException e) {
+ obj1.free();
+ throw;
+ }
+#endif
+ obj1.free();
+
+ ocState = ocSaved;
+}
+
+GBool Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
+ Dict *dict, *maskDict;
+ int width, height;
+ int bits, maskBits;
+ StreamColorSpaceMode csMode;
+ GBool mask, invert;
+ GfxColorSpace *colorSpace, *maskColorSpace;
+ GfxImageColorMap *colorMap, *maskColorMap;
+ Object maskObj, smaskObj, maskRef;
+ GBool haveColorKeyMask, haveExplicitMask, haveSoftMask, haveMatte;
+ int maskColors[2*gfxColorMaxComps];
+ int maskWidth, maskHeight;
+ GBool maskInvert;
+ Stream *maskStr;
+ double matte[gfxColorMaxComps];
+ GBool interpolate;
+ GfxRenderingIntent riSaved;
+ Object obj1, obj2;
+ int i, n;
+
+ // check for optional content
+ if (!ocState && !inlineImg) {
+ return gTrue;
+ }
+
+ // get info from the stream
+ bits = 0;
+ csMode = streamCSNone;
+ str->getImageParams(&bits, &csMode);
+
+ // get stream dict
+ dict = str->getDict();
+
+ // save the current rendering intent
+ riSaved = state->getRenderingIntent();
+
+ // get size
+ dict->lookup("Width", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("W", &obj1);
+ }
+ if (!obj1.isInt()) {
+ goto err2;
+ }
+ width = obj1.getInt();
+ obj1.free();
+ if (width <= 0) {
+ goto err1;
+ }
+ dict->lookup("Height", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("H", &obj1);
+ }
+ if (!obj1.isInt()) {
+ goto err2;
+ }
+ height = obj1.getInt();
+ obj1.free();
+ if (height <= 0) {
+ goto err1;
+ }
+
+ // image or mask?
+ dict->lookup("ImageMask", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("IM", &obj1);
+ }
+ mask = gFalse;
+ if (obj1.isBool())
+ mask = obj1.getBool();
+ else if (!obj1.isNull())
+ goto err2;
+ obj1.free();
+
+ // bit depth
+ if (bits == 0) {
+ dict->lookup("BitsPerComponent", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("BPC", &obj1);
+ }
+ if (obj1.isInt()) {
+ bits = obj1.getInt();
+ if (bits < 1 || bits > 16) {
+ goto err2;
+ }
+ } else if (mask) {
+ bits = 1;
+ } else {
+ goto err2;
+ }
+ obj1.free();
+ }
+
+ // interpolate flag
+ dict->lookup("Interpolate", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("I", &obj1);
+ }
+ interpolate = obj1.isBool() && obj1.getBool();
+ obj1.free();
+
+ // display a mask
+ if (mask) {
+
+ // check for inverted mask
+ if (bits != 1)
+ goto err1;
+ invert = gFalse;
+ dict->lookup("Decode", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("D", &obj1);
+ }
+ if (obj1.isArray()) {
+ obj1.arrayGet(0, &obj2);
+ invert = obj2.isNum() && obj2.getNum() == 1;
+ obj2.free();
+ } else if (!obj1.isNull()) {
+ goto err2;
+ }
+ obj1.free();
+
+ // if drawing is disabled, skip over inline image data
+ if (!ocState) {
+ str->reset();
+ n = height * ((width + 7) / 8);
+ for (i = 0; i < n; ++i) {
+ str->getChar();
+ }
+ str->close();
+
+ // draw it
+ } else {
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternImageMask(ref, str, width, height, invert, inlineImg,
+ interpolate);
+ } else {
+ out->drawImageMask(state, ref, str, width, height, invert, inlineImg,
+ interpolate);
+ }
+ }
+
+ } else {
+
+ // rendering intent
+ if (dict->lookup("Intent", &obj1)->isName()) {
+ opSetRenderingIntent(&obj1, 1);
+ }
+ obj1.free();
+
+ // get color space and color map
+ dict->lookup("ColorSpace", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("CS", &obj1);
+ }
+ if (obj1.isName()) {
+ res->lookupColorSpace(obj1.getName(), &obj2);
+ if (!obj2.isNull()) {
+ obj1.free();
+ obj1 = obj2;
+ } else {
+ obj2.free();
+ }
+ }
+ if (!obj1.isNull()) {
+ colorSpace = GfxColorSpace::parse(&obj1
+ );
+ } else if (csMode == streamCSDeviceGray) {
+ colorSpace = GfxColorSpace::create(csDeviceGray);
+ } else if (csMode == streamCSDeviceRGB) {
+ colorSpace = GfxColorSpace::create(csDeviceRGB);
+ } else if (csMode == streamCSDeviceCMYK) {
+ colorSpace = GfxColorSpace::create(csDeviceCMYK);
+ } else {
+ colorSpace = NULL;
+ }
+ obj1.free();
+ if (!colorSpace) {
+ goto err1;
+ }
+ if (colorSpace->getMode() == csPattern) {
+ error(errSyntaxError, getPos(), "Image with a Pattern color space");
+ delete colorSpace;
+ goto err1;
+ }
+ dict->lookup("Decode", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("D", &obj1);
+ }
+ colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
+ obj1.free();
+ if (!colorMap->isOk()) {
+ delete colorMap;
+ goto err1;
+ }
+
+ // get the mask
+ haveColorKeyMask = haveExplicitMask = haveSoftMask = haveMatte = gFalse;
+ maskStr = NULL; // make gcc happy
+ maskWidth = maskHeight = 0; // make gcc happy
+ maskInvert = gFalse; // make gcc happy
+ maskColorMap = NULL; // make gcc happy
+ dict->lookup("Mask", &maskObj);
+ dict->lookup("SMask", &smaskObj);
+ if (smaskObj.isStream()) {
+ // soft mask
+ if (inlineImg) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err1;
+ }
+ maskStr = smaskObj.getStream();
+ maskDict = smaskObj.streamGetDict();
+ maskDict->lookup("Width", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("W", &obj1);
+ }
+ if (!obj1.isInt()) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err2;
+ }
+ maskWidth = obj1.getInt();
+ obj1.free();
+ maskDict->lookup("Height", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("H", &obj1);
+ }
+ if (!obj1.isInt()) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err2;
+ }
+ maskHeight = obj1.getInt();
+ obj1.free();
+ if (maskWidth <= 0 || maskHeight <= 0) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err1;
+ }
+ maskDict->lookup("BitsPerComponent", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("BPC", &obj1);
+ }
+ if (!obj1.isInt()) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err2;
+ }
+ maskBits = obj1.getInt();
+ obj1.free();
+ if (maskBits < 1 || maskBits > 16) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err1;
+ }
+ maskDict->lookup("ColorSpace", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("CS", &obj1);
+ }
+ if (obj1.isName()) {
+ res->lookupColorSpace(obj1.getName(), &obj2);
+ if (!obj2.isNull()) {
+ obj1.free();
+ obj1 = obj2;
+ } else {
+ obj2.free();
+ }
+ }
+ if (!obj1.isName("DeviceGray")) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err2;
+ }
+ maskColorSpace = new GfxDeviceGrayColorSpace();
+ obj1.free();
+ maskDict->lookup("Decode", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("D", &obj1);
+ }
+ maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
+ obj1.free();
+ if (!maskColorMap->isOk()) {
+ delete maskColorMap;
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err1;
+ }
+ if (maskDict->lookup("Matte", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() == colorSpace->getNComps()) {
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ matte[i] = obj2.getNum();
+ } else {
+ error(errSyntaxError, getPos(),
+ "Invalid Matte entry in soft mask");
+ matte[i] = 0;
+ }
+ obj2.free();
+ }
+ haveMatte = gTrue;
+ } else {
+ error(errSyntaxError, getPos(), "Invalid Matte entry in soft mask");
+ }
+ }
+ obj1.free();
+ haveSoftMask = gTrue;
+ } else if (maskObj.isArray()) {
+ // color key mask
+ haveColorKeyMask = gTrue;
+ for (i = 0;
+ i+1 < maskObj.arrayGetLength() && i+1 < 2*gfxColorMaxComps;
+ i += 2) {
+ maskObj.arrayGet(i, &obj1);
+ if (!obj1.isInt()) {
+ obj1.free();
+ haveColorKeyMask = gFalse;
+ break;
+ }
+ maskColors[i] = obj1.getInt();
+ obj1.free();
+ if (maskColors[i] < 0 || maskColors[i] >= (1 << bits)) {
+ haveColorKeyMask = gFalse;
+ break;
+ }
+ maskObj.arrayGet(i+1, &obj1);
+ if (!obj1.isInt()) {
+ obj1.free();
+ haveColorKeyMask = gFalse;
+ break;
+ }
+ maskColors[i+1] = obj1.getInt();
+ obj1.free();
+ if (maskColors[i+1] < 0 || maskColors[i+1] >= (1 << bits) ||
+ maskColors[i] > maskColors[i+1]) {
+ haveColorKeyMask = gFalse;
+ break;
+ }
+ }
+ } else if (maskObj.isStream()) {
+ // explicit mask
+ if (inlineImg) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err1;
+ }
+ maskStr = maskObj.getStream();
+ maskDict = maskObj.streamGetDict();
+ maskDict->lookup("Width", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("W", &obj1);
+ }
+ if (!obj1.isInt()) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err2;
+ }
+ maskWidth = obj1.getInt();
+ obj1.free();
+ maskDict->lookup("Height", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("H", &obj1);
+ }
+ if (!obj1.isInt()) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err2;
+ }
+ maskHeight = obj1.getInt();
+ obj1.free();
+ if (maskWidth <= 0 || maskHeight <= 0) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err2;
+ }
+ maskDict->lookup("ImageMask", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("IM", &obj1);
+ }
+ if (!obj1.isBool() || !obj1.getBool()) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err2;
+ }
+ obj1.free();
+ maskInvert = gFalse;
+ maskDict->lookup("Decode", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("D", &obj1);
+ }
+ if (obj1.isArray()) {
+ obj1.arrayGet(0, &obj2);
+ maskInvert = obj2.isNum() && obj2.getNum() == 1;
+ obj2.free();
+ } else if (!obj1.isNull()) {
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ goto err2;
+ }
+ obj1.free();
+ haveExplicitMask = gTrue;
+ }
+
+ // if drawing is disabled, skip over inline image data
+ if (state->getIgnoreColorOps() || !ocState) {
+ if (state->getIgnoreColorOps()) {
+ error(errSyntaxWarning, getPos(), "Ignoring image"
+ " in uncolored Type 3 char or tiling pattern");
+ }
+ if (inlineImg) {
+ str->reset();
+ n = height * ((width * colorMap->getNumPixelComps() *
+ colorMap->getBits() + 7) / 8);
+ str->discardChars(n);
+ str->close();
+ }
+
+ // draw it
+ } else {
+ if (haveSoftMask) {
+ dict->lookupNF("Mask", &maskRef);
+ out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
+ &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,
+ &maskRef, maskStr, maskWidth, maskHeight,
+ maskInvert, interpolate);
+ maskRef.free();
+ } else {
+ out->drawImage(state, ref, str, width, height, colorMap,
+ haveColorKeyMask ? maskColors : (int *)NULL, inlineImg,
+ interpolate);
+ }
+ }
+
+ delete colorMap;
+ maskObj.free();
+ smaskObj.free();
+ }
+
+ // restore rendering intent
+ if (state->getRenderingIntent() != riSaved) {
+ state->setRenderingIntent(riSaved);
+ out->updateRenderingIntent(state);
+ }
+
+ if ((i = width * height) > 1000) {
+ i = 1000;
+ }
+ opCounter += i;
+
+ return gTrue;
+
+ err2:
+ obj1.free();
+ err1:
+ error(errSyntaxError, getPos(), "Bad image parameters");
+
+ // restore rendering intent
+ if (state->getRenderingIntent() != riSaved) {
+ state->setRenderingIntent(riSaved);
+ out->updateRenderingIntent(state);
+ }
+
+ return gFalse;
+}
+
+void Gfx::doForm(Object *strRef, Object *str) {
+ Dict *dict;
+ GBool transpGroup, isolated, knockout;
+ Object matrixObj, bboxObj;
+ double m[6], bbox[4];
+ Object resObj;
+ Dict *resDict;
+ Object obj1, obj2, obj3;
+ int i;
+
+ // check for excessive recursion
+ if (formDepth > 100) {
+ return;
+ }
+
+ // check for optional content
+ if (!ocState && !out->needCharCount()) {
+ return;
+ }
+
+ // get stream dict
+ dict = str->streamGetDict();
+
+ // check form type
+ dict->lookup("FormType", &obj1);
+ if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
+ error(errSyntaxError, getPos(), "Unknown form type");
+ }
+ obj1.free();
+
+ // get bounding box
+ dict->lookup("BBox", &bboxObj);
+ if (!bboxObj.isArray()) {
+ bboxObj.free();
+ error(errSyntaxError, getPos(), "Bad form bounding box");
+ return;
+ }
+ for (i = 0; i < 4; ++i) {
+ bboxObj.arrayGet(i, &obj1);
+ bbox[i] = obj1.getNum();
+ obj1.free();
+ }
+ bboxObj.free();
+
+ // get matrix
+ dict->lookup("Matrix", &matrixObj);
+ if (matrixObj.isArray()) {
+ for (i = 0; i < 6; ++i) {
+ matrixObj.arrayGet(i, &obj1);
+ m[i] = obj1.getNum();
+ obj1.free();
+ }
+ } else {
+ m[0] = 1; m[1] = 0;
+ m[2] = 0; m[3] = 1;
+ m[4] = 0; m[5] = 0;
+ }
+ matrixObj.free();
+
+ // get resources
+ dict->lookup("Resources", &resObj);
+ resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
+
+ // check for a transparency group
+ transpGroup = isolated = knockout = gFalse;
+ if (dict->lookup("Group", &obj1)->isDict()) {
+ if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {
+ transpGroup = gTrue;
+ if (obj1.dictLookup("I", &obj3)->isBool()) {
+ isolated = obj3.getBool();
+ }
+ obj3.free();
+ if (obj1.dictLookup("K", &obj3)->isBool()) {
+ knockout = obj3.getBool();
+ }
+ obj3.free();
+ }
+ obj2.free();
+ }
+ obj1.free();
+
+ // draw it
+ ++formDepth;
+ drawForm(strRef, resDict, m, bbox, transpGroup, gFalse, isolated, knockout);
+ --formDepth;
+
+ resObj.free();
+}
+
+void Gfx::drawForm(Object *strRef, Dict *resDict,
+ double *matrix, double *bbox,
+ GBool transpGroup, GBool softMask,
+ GBool isolated, GBool knockout,
+ GBool alpha, Function *transferFunc,
+ Object *backdropColorObj) {
+ Parser *oldParser;
+ GfxState *savedState;
+ GfxColorSpace *blendingColorSpace;
+ GfxColor backdropColor;
+ Object strObj, groupAttrsObj, csObj, obj1;
+ double oldBaseMatrix[6];
+ int i;
+
+ // push new resources on stack
+ pushResources(resDict);
+
+ // save current graphics state
+ saveState();
+
+ // kill any pre-existing path
+ state->clearPath();
+
+ // save current parser
+ oldParser = parser;
+
+ // set form transformation matrix
+ state->concatCTM(matrix[0], matrix[1], matrix[2],
+ matrix[3], matrix[4], matrix[5]);
+ out->updateCTM(state, matrix[0], matrix[1], matrix[2],
+ matrix[3], matrix[4], matrix[5]);
+
+ // set form bounding box
+ state->moveTo(bbox[0], bbox[1]);
+ state->lineTo(bbox[2], bbox[1]);
+ state->lineTo(bbox[2], bbox[3]);
+ state->lineTo(bbox[0], bbox[3]);
+ state->closePath();
+ state->clip();
+ 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);
+ }
+ if (state->getFillOpacity() != 1) {
+ state->setFillOpacity(1);
+ out->updateFillOpacity(state);
+ }
+ if (state->getStrokeOpacity() != 1) {
+ state->setStrokeOpacity(1);
+ out->updateStrokeOpacity(state);
+ }
+ out->clearSoftMask(state);
+ out->beginTransparencyGroup(state, bbox, blendingColorSpace,
+ isolated, knockout, softMask);
+ }
+
+ // set new base matrix
+ for (i = 0; i < 6; ++i) {
+ oldBaseMatrix[i] = baseMatrix[i];
+ baseMatrix[i] = state->getCTM()[i];
+ }
+
+ // save the state stack -- this handles the case where the form
+ // contents have unbalanced q/Q operators
+ savedState = saveStateStack();
+
+ // draw the form
+ display(strRef, gFalse);
+
+ restoreStateStack(savedState);
+
+ if (softMask || transpGroup) {
+ out->endTransparencyGroup(state);
+ }
+
+ // restore base matrix
+ for (i = 0; i < 6; ++i) {
+ baseMatrix[i] = oldBaseMatrix[i];
+ }
+
+ // restore parser
+ parser = oldParser;
+
+ // restore graphics state
+ restoreState();
+
+ // pop resource stack
+ popResources();
+
+ if (softMask) {
+ 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;
+}
+
+void Gfx::takeContentStreamStack(Gfx *oldGfx) {
+ contentStreamStack->append(oldGfx->contentStreamStack);
+}
+
+void Gfx::endOfPage() {
+ while (state->hasSaves()) {
+ restoreState();
+ }
+ while (markedContentStack->getLength() > 0) {
+ opEndMarkedContent(NULL, 0);
+ }
+}
+
+//------------------------------------------------------------------------
+// in-line image operators
+//------------------------------------------------------------------------
+
+void Gfx::opBeginImage(Object args[], int numArgs) {
+ Stream *str;
+ GBool haveLength;
+ int c1, c2, c3;
+
+ // NB: this function is run even if ocState is false -- doImage() is
+ // responsible for skipping over the inline image data
+
+ // build dict/stream
+ str = buildImageStream(&haveLength);
+
+ // display the image
+ if (str) {
+ if (!doImage(NULL, str, gTrue)) {
+ delete str;
+
+ // if we have the stream length, skip to end-of-stream and then
+ // skip 'EI' in the original stream
+ } else if (haveLength) {
+ while ((c1 = str->getChar()) != EOF) ;
+ delete str;
+ str = parser->getStream();
+ c1 = str->getChar();
+ c2 = str->getChar();
+ c3 = str->lookChar();
+ while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) {
+ c1 = c2;
+ c2 = str->getChar();
+ c3 = str->lookChar();
+ }
+
+ // else, look for the 'EI' tag and skip it
+ } else {
+ c1 = str->getUndecodedStream()->getChar();
+ c2 = str->getUndecodedStream()->getChar();
+ c3 = str->getUndecodedStream()->lookChar();
+ while (!(c1 == 'E' && c2 == 'I' && Lexer::isSpace(c3)) && c3 != EOF) {
+ c1 = c2;
+ c2 = str->getUndecodedStream()->getChar();
+ c3 = str->getUndecodedStream()->lookChar();
+ }
+ delete str;
+ }
+ }
+}
+
+Stream *Gfx::buildImageStream(GBool *haveLength) {
+ Object dict;
+ Object obj, lengthObj;
+ char *key;
+ int length;
+ Stream *str;
+
+ // build dictionary
+ dict.initDict(xref);
+ getContentObj(&obj);
+ while (!obj.isCmd("ID") && !obj.isEOF()) {
+ if (!obj.isName()) {
+ error(errSyntaxError, getPos(),
+ "Inline image dictionary key must be a name object");
+ obj.free();
+ } else {
+ key = copyString(obj.getName());
+ obj.free();
+ getContentObj(&obj);
+ if (obj.isEOF()) {
+ gfree(key);
+ break;
+ }
+ if (obj.isError()) {
+ gfree(key);
+ obj.free();
+ } else {
+ dict.dictAdd(key, &obj);
+ }
+ }
+ getContentObj(&obj);
+ }
+ if (obj.isEOF()) {
+ error(errSyntaxError, getPos(), "End of file in inline image");
+ obj.free();
+ dict.free();
+ return NULL;
+ }
+ obj.free();
+
+ // check for length field
+ length = 0;
+ *haveLength = gFalse;
+ if (!dict.dictLookup("Length", &lengthObj)->isInt()) {
+ lengthObj.free();
+ dict.dictLookup("L", &lengthObj);
+ }
+ if (lengthObj.isInt()) {
+ length = lengthObj.getInt();
+ *haveLength = gTrue;
+ }
+ lengthObj.free();
+
+ // make stream
+ if (!(str = parser->getStream())) {
+ error(errSyntaxError, getPos(), "Invalid inline image data");
+ dict.free();
+ return NULL;
+ }
+ str = new EmbedStream(str, &dict, *haveLength, (GFileOffset)length);
+ str = str->addFilters(&dict);
+
+ return str;
+}
+
+void Gfx::opImageData(Object args[], int numArgs) {
+ error(errInternal, getPos(), "Got 'ID' operator");
+}
+
+void Gfx::opEndImage(Object args[], int numArgs) {
+ error(errInternal, getPos(), "Got 'EI' operator");
+}
+
+//------------------------------------------------------------------------
+// type 3 font operators
+//------------------------------------------------------------------------
+
+void Gfx::opSetCharWidth(Object args[], int numArgs) {
+ out->type3D0(state, args[0].getNum(), args[1].getNum());
+}
+
+void Gfx::opSetCacheDevice(Object args[], int numArgs) {
+ state->setIgnoreColorOps(gTrue);
+ out->type3D1(state, args[0].getNum(), args[1].getNum(),
+ args[2].getNum(), args[3].getNum(),
+ args[4].getNum(), args[5].getNum());
+}
+
+//------------------------------------------------------------------------
+// compatibility operators
+//------------------------------------------------------------------------
+
+void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
+ ++ignoreUndef;
+}
+
+void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
+ if (ignoreUndef > 0)
+ --ignoreUndef;
+}
+
+//------------------------------------------------------------------------
+// marked content operators
+//------------------------------------------------------------------------
+
+void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
+ GfxMarkedContent *mc;
+ Object obj;
+ GBool ocStateNew;
+ TextString *s;
+ GfxMarkedContentKind mcKind;
+
+ if (printCommands) {
+ printf(" marked content: %s ", args[0].getName());
+ if (numArgs == 2) {
+ args[1].print(stdout);
+ }
+ printf("\n");
+ fflush(stdout);
+ }
+ mcKind = gfxMCOther;
+ if (args[0].isName("OC") && numArgs == 2 && args[1].isName() &&
+ res->lookupPropertiesNF(args[1].getName(), &obj)) {
+ if (doc->getOptionalContent()->evalOCObject(&obj, &ocStateNew)) {
+ ocState &= ocStateNew;
+ }
+ obj.free();
+ mcKind = gfxMCOptionalContent;
+ } else if (args[0].isName("Span") && numArgs == 2 && args[1].isDict()) {
+ if (args[1].dictLookup("ActualText", &obj)->isString()) {
+ s = new TextString(obj.getString());
+ out->beginActualText(state, s->getUnicode(), s->getLength());
+ delete s;
+ mcKind = gfxMCActualText;
+ }
+ obj.free();
+ }
+ mc = new GfxMarkedContent(mcKind, ocState);
+ markedContentStack->append(mc);
+}
+
+void Gfx::opEndMarkedContent(Object args[], int numArgs) {
+ GfxMarkedContent *mc;
+ GfxMarkedContentKind mcKind;
+
+ if (markedContentStack->getLength() > 0) {
+ mc = (GfxMarkedContent *)
+ markedContentStack->del(markedContentStack->getLength() - 1);
+ mcKind = mc->kind;
+ delete mc;
+ if (mcKind == gfxMCOptionalContent) {
+ if (markedContentStack->getLength() > 0) {
+ mc = (GfxMarkedContent *)
+ markedContentStack->get(markedContentStack->getLength() - 1);
+ ocState = mc->ocState;
+ } else {
+ ocState = gTrue;
+ }
+ } else if (mcKind == gfxMCActualText) {
+ out->endActualText(state);
+ }
+ } else {
+ error(errSyntaxWarning, getPos(), "Mismatched EMC operator");
+ }
+}
+
+void Gfx::opMarkPoint(Object args[], int numArgs) {
+ if (printCommands) {
+ printf(" mark point: %s ", args[0].getName());
+ if (numArgs == 2)
+ args[1].print(stdout);
+ printf("\n");
+ fflush(stdout);
+ }
+}
+
+//------------------------------------------------------------------------
+// misc
+//------------------------------------------------------------------------
+
+void Gfx::drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle,
+ double xMin, double yMin, double xMax, double yMax) {
+ Dict *dict, *resDict;
+ Object str, matrixObj, bboxObj, resObj, obj1;
+ double formXMin, formYMin, formXMax, formYMax;
+ double x, y, sx, sy, tx, ty;
+ double m[6], bbox[4];
+ double *borderColor;
+ GfxColor color;
+ double *dash, *dash2;
+ int dashLength;
+ int i;
+
+ // this function assumes that we are in the default user space,
+ // i.e., baseMatrix = ctm
+
+ // if the bounding box has zero width or height, don't draw anything
+ // at all
+ if (xMin == xMax || yMin == yMax) {
+ return;
+ }
+
+ // draw the appearance stream (if there is one)
+ strRef->fetch(xref, &str);
+ if (str.isStream()) {
+
+ // get stream dict
+ dict = str.streamGetDict();
+
+ // get the form bounding box
+ dict->lookup("BBox", &bboxObj);
+ if (!bboxObj.isArray() || bboxObj.arrayGetLength() != 4) {
+ error(errSyntaxError, getPos(), "Bad form bounding box");
+ bboxObj.free();
+ str.free();
+ return;
+ }
+ for (i = 0; i < 4; ++i) {
+ bboxObj.arrayGet(i, &obj1);
+ if (obj1.isNum()) {
+ bbox[i] = obj1.getNum();
+ } else {
+ bbox[i] = 0;
+ }
+ obj1.free();
+ }
+ bboxObj.free();
+
+ // get the form matrix
+ dict->lookup("Matrix", &matrixObj);
+ if (matrixObj.isArray()) {
+ for (i = 0; i < 6; ++i) {
+ matrixObj.arrayGet(i, &obj1);
+ m[i] = obj1.getNum();
+ obj1.free();
+ }
+ } else {
+ m[0] = 1; m[1] = 0;
+ m[2] = 0; m[3] = 1;
+ m[4] = 0; m[5] = 0;
+ }
+ matrixObj.free();
+
+ // transform the four corners of the form bbox to default user
+ // space, and construct the transformed bbox
+ x = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
+ y = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
+ formXMin = formXMax = x;
+ formYMin = formYMax = y;
+ x = bbox[0] * m[0] + bbox[3] * m[2] + m[4];
+ y = bbox[0] * m[1] + bbox[3] * m[3] + m[5];
+ if (x < formXMin) {
+ formXMin = x;
+ } else if (x > formXMax) {
+ formXMax = x;
+ }
+ if (y < formYMin) {
+ formYMin = y;
+ } else if (y > formYMax) {
+ formYMax = y;
+ }
+ x = bbox[2] * m[0] + bbox[1] * m[2] + m[4];
+ y = bbox[2] * m[1] + bbox[1] * m[3] + m[5];
+ if (x < formXMin) {
+ formXMin = x;
+ } else if (x > formXMax) {
+ formXMax = x;
+ }
+ if (y < formYMin) {
+ formYMin = y;
+ } else if (y > formYMax) {
+ formYMax = y;
+ }
+ x = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
+ y = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
+ if (x < formXMin) {
+ formXMin = x;
+ } else if (x > formXMax) {
+ formXMax = x;
+ }
+ if (y < formYMin) {
+ formYMin = y;
+ } else if (y > formYMax) {
+ formYMax = y;
+ }
+
+ // construct a mapping matrix, [sx 0 0], which maps the transformed
+ // [0 sy 0]
+ // [tx ty 1]
+ // bbox to the annotation rectangle
+ if (formXMin == formXMax) {
+ // this shouldn't happen
+ sx = 1;
+ } else {
+ sx = (xMax - xMin) / (formXMax - formXMin);
+ }
+ if (formYMin == formYMax) {
+ // this shouldn't happen
+ sy = 1;
+ } else {
+ sy = (yMax - yMin) / (formYMax - formYMin);
+ }
+ tx = -formXMin * sx + xMin;
+ ty = -formYMin * sy + yMin;
+
+ // the final transform matrix is (form matrix) * (mapping matrix)
+ m[0] *= sx;
+ m[1] *= sy;
+ m[2] *= sx;
+ m[3] *= sy;
+ m[4] = m[4] * sx + tx;
+ m[5] = m[5] * sy + ty;
+
+ // get the resources
+ dict->lookup("Resources", &resObj);
+ resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
+
+ // draw it
+ drawForm(strRef, resDict, m, bbox);
+
+ resObj.free();
+ }
+ str.free();
+
+ // draw the border
+ if (borderStyle && borderStyle->getWidth() > 0 &&
+ borderStyle->getNumColorComps() > 0) {
+ borderColor = borderStyle->getColor();
+ switch (borderStyle->getNumColorComps()) {
+ case 1:
+ if (state->getStrokeColorSpace()->getMode() != csDeviceGray) {
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceGray));
+ out->updateStrokeColorSpace(state);
+ }
+ break;
+ case 3:
+ if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceRGB));
+ out->updateStrokeColorSpace(state);
+ }
+ break;
+ case 4:
+ if (state->getStrokeColorSpace()->getMode() != csDeviceCMYK) {
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(GfxColorSpace::create(csDeviceCMYK));
+ out->updateStrokeColorSpace(state);
+ }
+ break;
+ }
+ color.c[0] = dblToCol(borderColor[0]);
+ color.c[1] = dblToCol(borderColor[1]);
+ color.c[2] = dblToCol(borderColor[2]);
+ color.c[3] = dblToCol(borderColor[3]);
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+ state->setLineWidth(borderStyle->getWidth());
+ out->updateLineWidth(state);
+ borderStyle->getDash(&dash, &dashLength);
+ if (borderStyle->getType() == annotBorderDashed && dashLength > 0) {
+ dash2 = (double *)gmallocn(dashLength, sizeof(double));
+ memcpy(dash2, dash, dashLength * sizeof(double));
+ state->setLineDash(dash2, dashLength, 0);
+ out->updateLineDash(state);
+ }
+ //~ this doesn't currently handle the beveled and engraved styles
+ state->clearPath();
+ state->moveTo(xMin, yMin);
+ state->lineTo(xMax, yMin);
+ if (borderStyle->getType() != annotBorderUnderlined) {
+ state->lineTo(xMax, yMax);
+ state->lineTo(xMin, yMax);
+ state->closePath();
+ }
+ out->stroke(state);
+ }
+}
+
+void Gfx::saveState() {
+ out->saveState(state);
+ state = state->save();
+}
+
+void Gfx::restoreState() {
+ state = state->restore();
+ out->restoreState(state);
+}
+
+// Create a new state stack, and initialize it with a copy of the
+// current state.
+GfxState *Gfx::saveStateStack() {
+ GfxState *oldState;
+
+ out->saveState(state);
+ oldState = state;
+ state = state->copy(gTrue);
+ return oldState;
+}
+
+// Switch back to the previous state stack.
+void Gfx::restoreStateStack(GfxState *oldState) {
+ while (state->hasSaves()) {
+ restoreState();
+ }
+ delete state;
+ state = oldState;
+ out->restoreState(state);
+}
+
+void Gfx::pushResources(Dict *resDict) {
+ res = new GfxResources(xref, resDict, res);
+}
+
+void Gfx::popResources() {
+ GfxResources *resPtr;
+
+ resPtr = res->getNext();
+ delete res;
+ res = resPtr;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h
new file mode 100644
index 00000000000..4625ac289d0
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Gfx.h
@@ -0,0 +1,362 @@
+//========================================================================
+//
+// Gfx.h
+//
+// Copyright 1996-2016 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GFX_H
+#define GFX_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "gfile.h"
+#include "GfxState.h"
+
+class GString;
+class GList;
+class PDFDoc;
+class XRef;
+class Array;
+class Stream;
+class Parser;
+class Dict;
+class Function;
+class OutputDev;
+class GfxFontDict;
+class GfxFont;
+class Gfx;
+class PDFRectangle;
+class AnnotBorderStyle;
+
+//------------------------------------------------------------------------
+
+enum GfxClipType {
+ clipNone,
+ clipNormal,
+ clipEO
+};
+
+enum TchkType {
+ tchkBool, // boolean
+ tchkInt, // integer
+ tchkNum, // number (integer or real)
+ tchkString, // string
+ tchkName, // name
+ tchkArray, // array
+ tchkProps, // properties (dictionary or name)
+ tchkSCN, // scn/SCN args (number of name)
+ tchkNone // used to avoid empty initializer lists
+};
+
+#define maxArgs 33
+
+struct Operator {
+ char name[4];
+ int numArgs;
+ TchkType tchk[maxArgs];
+ void (Gfx::*func)(Object args[], int numArgs);
+};
+
+//------------------------------------------------------------------------
+
+class GfxResources {
+public:
+
+ GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA);
+ ~GfxResources();
+
+ GfxFont *lookupFont(char *name);
+ 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, GBool inherit = gTrue);
+ GfxPattern *lookupPattern(const char *name
+ );
+ GfxShading *lookupShading(const char *name
+ );
+ GBool lookupGState(const char *name, Object *obj);
+ GBool lookupPropertiesNF(const char *name, Object *obj);
+
+ GfxResources *getNext() { return next; }
+
+private:
+
+ GBool valid;
+ GfxFontDict *fonts;
+ Object xObjDict;
+ Object colorSpaceDict;
+ Object patternDict;
+ Object shadingDict;
+ Object gStateDict;
+ Object propsDict;
+ GfxResources *next;
+};
+
+//------------------------------------------------------------------------
+// GfxMarkedContent
+//------------------------------------------------------------------------
+
+enum GfxMarkedContentKind {
+ gfxMCOptionalContent,
+ gfxMCActualText,
+ gfxMCOther
+};
+
+class GfxMarkedContent {
+public:
+
+ GfxMarkedContent(GfxMarkedContentKind kindA, GBool ocStateA) {
+ kind = kindA;
+ ocState = ocStateA;
+ }
+ ~GfxMarkedContent() {}
+
+ GfxMarkedContentKind kind;
+ GBool ocState; // true if drawing is enabled, false if
+ // disabled
+};
+
+//------------------------------------------------------------------------
+// Gfx
+//------------------------------------------------------------------------
+
+class Gfx {
+public:
+
+ // Constructor for regular output.
+ Gfx(PDFDoc *docA, OutputDev *outA, int pageNum, Dict *resDict,
+ double hDPI, double vDPI, PDFRectangle *box,
+ PDFRectangle *cropBox, int rotate,
+ GBool (*abortCheckCbkA)(void *data) = NULL,
+ void *abortCheckCbkDataA = NULL);
+
+ // Constructor for a sub-page object.
+ Gfx(PDFDoc *docA, OutputDev *outA, Dict *resDict,
+ PDFRectangle *box, PDFRectangle *cropBox,
+ GBool (*abortCheckCbkA)(void *data) = NULL,
+ void *abortCheckCbkDataA = NULL);
+
+ ~Gfx();
+
+ // Interpret a stream or array of streams. <objRef> should be a
+ // reference wherever possible (for loop-checking).
+ void display(Object *objRef, GBool topLevel = gTrue);
+
+ // Display an annotation, given its appearance (a Form XObject),
+ // border style, and bounding box (in default user space).
+ void drawAnnot(Object *strRef, AnnotBorderStyle *borderStyle,
+ double xMin, double yMin, double xMax, double yMax);
+
+ // Save graphics state.
+ void saveState();
+
+ // Restore graphics state.
+ void restoreState();
+
+ // Get the current graphics state object.
+ GfxState *getState() { return state; }
+
+ void drawForm(Object *strRef, Dict *resDict, double *matrix, double *bbox,
+ GBool transpGroup = gFalse, GBool softMask = gFalse,
+ GBool isolated = gFalse, GBool knockout = gFalse,
+ GBool alpha = gFalse, Function *transferFunc = 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,
+ // etc., where it's useful to check for loops that span both Gfx
+ // objects. This function should be called immediately after the
+ // Gfx constructor, i.e., before processing any content streams with
+ // the new Gfx object.
+ void takeContentStreamStack(Gfx *oldGfx);
+
+ // Clear the state stack and the marked content stack.
+ void endOfPage();
+
+private:
+
+ PDFDoc *doc;
+ XRef *xref; // the xref table for this PDF file
+ OutputDev *out; // output device
+ GBool subPage; // is this a sub-page object?
+ GBool printCommands; // print the drawing commands (for debugging)
+ GfxResources *res; // resource stack
+ int opCounter; // operation counter (used to decide when
+ // to check for an abort)
+
+ GfxState *state; // current graphics state
+ GBool fontChanged; // set if font or text matrix has changed
+ GfxClipType clip; // do a clip?
+ int ignoreUndef; // current BX/EX nesting level
+ double baseMatrix[6]; // default matrix for most recent
+ // page/form/pattern
+ int formDepth;
+ GBool ocState; // true if drawing is enabled, false if
+ // disabled
+ GList *markedContentStack; // BMC/BDC/EMC stack [GfxMarkedContent]
+
+ Parser *parser; // parser for page content stream(s)
+ GList *contentStreamStack; // stack of open content streams, used
+ // for loop-checking
+
+ GBool // callback to check for an abort
+ (*abortCheckCbk)(void *data);
+ void *abortCheckCbkData;
+
+ static Operator opTab[]; // table of operators
+
+ 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);
+ GFileOffset getPos();
+
+ // graphics state operators
+ void opSave(Object args[], int numArgs);
+ void opRestore(Object args[], int numArgs);
+ void opConcat(Object args[], int numArgs);
+ void opSetDash(Object args[], int numArgs);
+ void opSetFlat(Object args[], int numArgs);
+ void opSetLineJoin(Object args[], int numArgs);
+ void opSetLineCap(Object args[], int numArgs);
+ void opSetMiterLimit(Object args[], int numArgs);
+ void opSetLineWidth(Object args[], int numArgs);
+ void opSetExtGState(Object args[], int numArgs);
+ void doSoftMask(Object *str, Object *strRef, GBool alpha,
+ GBool isolated, GBool knockout,
+ Function *transferFunc, Object *backdropColorObj);
+ void opSetRenderingIntent(Object args[], int numArgs);
+ GfxRenderingIntent parseRenderingIntent(const char *name);
+
+ // color operators
+ void opSetFillGray(Object args[], int numArgs);
+ void opSetStrokeGray(Object args[], int numArgs);
+ void opSetFillCMYKColor(Object args[], int numArgs);
+ void opSetStrokeCMYKColor(Object args[], int numArgs);
+ void opSetFillRGBColor(Object args[], int numArgs);
+ void opSetStrokeRGBColor(Object args[], int numArgs);
+ void opSetFillColorSpace(Object args[], int numArgs);
+ void opSetStrokeColorSpace(Object args[], int numArgs);
+ void opSetFillColor(Object args[], int numArgs);
+ void opSetStrokeColor(Object args[], int numArgs);
+ void opSetFillColorN(Object args[], int numArgs);
+ void opSetStrokeColorN(Object args[], int numArgs);
+
+ // path segment operators
+ void opMoveTo(Object args[], int numArgs);
+ void opLineTo(Object args[], int numArgs);
+ void opCurveTo(Object args[], int numArgs);
+ void opCurveTo1(Object args[], int numArgs);
+ void opCurveTo2(Object args[], int numArgs);
+ void opRectangle(Object args[], int numArgs);
+ void opClosePath(Object args[], int numArgs);
+
+ // path painting operators
+ void opEndPath(Object args[], int numArgs);
+ void opStroke(Object args[], int numArgs);
+ void opCloseStroke(Object args[], int numArgs);
+ void opFill(Object args[], int numArgs);
+ void opEOFill(Object args[], int numArgs);
+ void opFillStroke(Object args[], int numArgs);
+ void opCloseFillStroke(Object args[], int numArgs);
+ void opEOFillStroke(Object args[], int numArgs);
+ void opCloseEOFillStroke(Object args[], int numArgs);
+ void doPatternFill(GBool eoFill);
+ void doPatternStroke();
+ void doPatternText();
+ void doPatternImageMask(Object *ref, Stream *str, int width, int height,
+ GBool invert, GBool inlineImg, GBool interpolate);
+ void doTilingPatternFill(GfxTilingPattern *tPat,
+ GBool stroke, GBool eoFill, GBool text);
+ 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,
+ double x1, double y1,
+ GfxColor *colors, int depth);
+ void doAxialShFill(GfxAxialShading *shading);
+ void doRadialShFill(GfxRadialShading *shading);
+ void doGouraudTriangleShFill(GfxGouraudTriangleShading *shading);
+ void gouraudFillTriangle(double x0, double y0, double *color0,
+ double x1, double y1, double *color1,
+ double x2, double y2, double *color2,
+ GfxGouraudTriangleShading *shading, int depth);
+ void doPatchMeshShFill(GfxPatchMeshShading *shading);
+ void fillPatch(GfxPatch *patch, GfxPatchMeshShading *shading, int depth);
+ void doEndPath();
+
+ // path clipping operators
+ void opClip(Object args[], int numArgs);
+ void opEOClip(Object args[], int numArgs);
+
+ // text object operators
+ void opBeginText(Object args[], int numArgs);
+ void opEndText(Object args[], int numArgs);
+
+ // text state operators
+ void opSetCharSpacing(Object args[], int numArgs);
+ void opSetFont(Object args[], int numArgs);
+ void doSetFont(GfxFont *font, double size);
+ void opSetTextLeading(Object args[], int numArgs);
+ void opSetTextRender(Object args[], int numArgs);
+ void opSetTextRise(Object args[], int numArgs);
+ void opSetWordSpacing(Object args[], int numArgs);
+ void opSetHorizScaling(Object args[], int numArgs);
+
+ // text positioning operators
+ void opTextMove(Object args[], int numArgs);
+ void opTextMoveSet(Object args[], int numArgs);
+ void opSetTextMatrix(Object args[], int numArgs);
+ void opTextNextLine(Object args[], int numArgs);
+
+ // text string operators
+ void opShowText(Object args[], int numArgs);
+ void opMoveShowText(Object args[], int numArgs);
+ void opMoveSetShowText(Object args[], int numArgs);
+ void opShowSpaceText(Object args[], int numArgs);
+ void doShowText(GString *s);
+ void doIncCharCount(GString *s);
+
+ // XObject operators
+ void opXObject(Object args[], int numArgs);
+ GBool doImage(Object *ref, Stream *str, GBool inlineImg);
+ void doForm(Object *strRef, Object *str);
+
+ // in-line image operators
+ void opBeginImage(Object args[], int numArgs);
+ Stream *buildImageStream(GBool *haveLength);
+ void opImageData(Object args[], int numArgs);
+ void opEndImage(Object args[], int numArgs);
+
+ // type 3 font operators
+ void opSetCharWidth(Object args[], int numArgs);
+ void opSetCacheDevice(Object args[], int numArgs);
+
+ // compatibility operators
+ void opBeginIgnoreUndef(Object args[], int numArgs);
+ void opEndIgnoreUndef(Object args[], int numArgs);
+
+ // marked content operators
+ void opBeginMarkedContent(Object args[], int numArgs);
+ void opEndMarkedContent(Object args[], int numArgs);
+ void opMarkPoint(Object args[], int numArgs);
+
+ GfxState *saveStateStack();
+ void restoreStateStack(GfxState *oldState);
+ void pushResources(Dict *resDict);
+ void popResources();
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc
new file mode 100644
index 00000000000..f224ff75b5f
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.cc
@@ -0,0 +1,2303 @@
+//========================================================================
+//
+// GfxFont.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <limits.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GList.h"
+#include "GHash.h"
+#include "Error.h"
+#include "Object.h"
+#include "Dict.h"
+#include "GlobalParams.h"
+#include "CMap.h"
+#include "CharCodeToUnicode.h"
+#include "FontEncodingTables.h"
+#include "BuiltinFontTables.h"
+#include "FoFiIdentifier.h"
+#include "FoFiType1.h"
+#include "FoFiType1C.h"
+#include "FoFiTrueType.h"
+#include "GfxFont.h"
+
+//------------------------------------------------------------------------
+
+struct Base14FontMapEntry {
+ const char *altName;
+ const char *base14Name;
+};
+
+static Base14FontMapEntry base14FontMap[] = {
+ { "Arial", "Helvetica" },
+ { "Arial,Bold", "Helvetica-Bold" },
+ { "Arial,BoldItalic", "Helvetica-BoldOblique" },
+ { "Arial,Italic", "Helvetica-Oblique" },
+ { "Arial-Bold", "Helvetica-Bold" },
+ { "Arial-BoldItalic", "Helvetica-BoldOblique" },
+ { "Arial-BoldItalicMT", "Helvetica-BoldOblique" },
+ { "Arial-BoldMT", "Helvetica-Bold" },
+ { "Arial-Italic", "Helvetica-Oblique" },
+ { "Arial-ItalicMT", "Helvetica-Oblique" },
+ { "ArialMT", "Helvetica" },
+ { "Courier", "Courier" },
+ { "Courier,Bold", "Courier-Bold" },
+ { "Courier,BoldItalic", "Courier-BoldOblique" },
+ { "Courier,Italic", "Courier-Oblique" },
+ { "Courier-Bold", "Courier-Bold" },
+ { "Courier-BoldOblique", "Courier-BoldOblique" },
+ { "Courier-Oblique", "Courier-Oblique" },
+ { "CourierNew", "Courier" },
+ { "CourierNew,Bold", "Courier-Bold" },
+ { "CourierNew,BoldItalic", "Courier-BoldOblique" },
+ { "CourierNew,Italic", "Courier-Oblique" },
+ { "CourierNew-Bold", "Courier-Bold" },
+ { "CourierNew-BoldItalic", "Courier-BoldOblique" },
+ { "CourierNew-Italic", "Courier-Oblique" },
+ { "CourierNewPS-BoldItalicMT", "Courier-BoldOblique" },
+ { "CourierNewPS-BoldMT", "Courier-Bold" },
+ { "CourierNewPS-ItalicMT", "Courier-Oblique" },
+ { "CourierNewPSMT", "Courier" },
+ { "Helvetica", "Helvetica" },
+ { "Helvetica,Bold", "Helvetica-Bold" },
+ { "Helvetica,BoldItalic", "Helvetica-BoldOblique" },
+ { "Helvetica,Italic", "Helvetica-Oblique" },
+ { "Helvetica-Bold", "Helvetica-Bold" },
+ { "Helvetica-BoldItalic", "Helvetica-BoldOblique" },
+ { "Helvetica-BoldOblique", "Helvetica-BoldOblique" },
+ { "Helvetica-Italic", "Helvetica-Oblique" },
+ { "Helvetica-Oblique", "Helvetica-Oblique" },
+ { "Symbol", "Symbol" },
+ { "Symbol,Bold", "Symbol" },
+ { "Symbol,BoldItalic", "Symbol" },
+ { "Symbol,Italic", "Symbol" },
+ { "Times-Bold", "Times-Bold" },
+ { "Times-BoldItalic", "Times-BoldItalic" },
+ { "Times-Italic", "Times-Italic" },
+ { "Times-Roman", "Times-Roman" },
+ { "TimesNewRoman", "Times-Roman" },
+ { "TimesNewRoman,Bold", "Times-Bold" },
+ { "TimesNewRoman,BoldItalic", "Times-BoldItalic" },
+ { "TimesNewRoman,Italic", "Times-Italic" },
+ { "TimesNewRoman-Bold", "Times-Bold" },
+ { "TimesNewRoman-BoldItalic", "Times-BoldItalic" },
+ { "TimesNewRoman-Italic", "Times-Italic" },
+ { "TimesNewRomanPS", "Times-Roman" },
+ { "TimesNewRomanPS-Bold", "Times-Bold" },
+ { "TimesNewRomanPS-BoldItalic", "Times-BoldItalic" },
+ { "TimesNewRomanPS-BoldItalicMT", "Times-BoldItalic" },
+ { "TimesNewRomanPS-BoldMT", "Times-Bold" },
+ { "TimesNewRomanPS-Italic", "Times-Italic" },
+ { "TimesNewRomanPS-ItalicMT", "Times-Italic" },
+ { "TimesNewRomanPSMT", "Times-Roman" },
+ { "TimesNewRomanPSMT,Bold", "Times-Bold" },
+ { "TimesNewRomanPSMT,BoldItalic", "Times-BoldItalic" },
+ { "TimesNewRomanPSMT,Italic", "Times-Italic" },
+ { "ZapfDingbats", "ZapfDingbats" }
+};
+
+//------------------------------------------------------------------------
+
+// index: {fixed:0, sans-serif:4, serif:8} + bold*2 + italic
+// NB: must be in same order as psSubstFonts in PSOutputDev.cc
+static const char *base14SubstFonts[14] = {
+ "Courier",
+ "Courier-Oblique",
+ "Courier-Bold",
+ "Courier-BoldOblique",
+ "Helvetica",
+ "Helvetica-Oblique",
+ "Helvetica-Bold",
+ "Helvetica-BoldOblique",
+ "Times-Roman",
+ "Times-Italic",
+ "Times-Bold",
+ "Times-BoldItalic",
+ // the last two are never used for substitution
+ "Symbol",
+ "ZapfDingbats"
+};
+
+//------------------------------------------------------------------------
+
+static int readFromStream(void *data) {
+ return ((Stream *)data)->getChar();
+}
+
+//------------------------------------------------------------------------
+// GfxFontLoc
+//------------------------------------------------------------------------
+
+GfxFontLoc::GfxFontLoc() {
+ path = NULL;
+ fontNum = 0;
+ oblique = 0;
+ encoding = NULL;
+ substIdx = -1;
+}
+
+GfxFontLoc::~GfxFontLoc() {
+ if (path) {
+ delete path;
+ }
+ if (encoding) {
+ delete encoding;
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxFont
+//------------------------------------------------------------------------
+
+GfxFont *GfxFont::makeFont(XRef *xref, const char *tagA,
+ Ref idA, Dict *fontDict) {
+ GString *nameA;
+ Ref embFontIDA;
+ GfxFontType typeA;
+ GfxFont *font;
+ Object obj1;
+
+ // get base font name
+ nameA = NULL;
+ fontDict->lookup("BaseFont", &obj1);
+ if (obj1.isName()) {
+ nameA = new GString(obj1.getName());
+ } else if (obj1.isString()) {
+ nameA = obj1.getString()->copy();
+ }
+ obj1.free();
+
+ // get embedded font ID and font type
+ typeA = getFontType(xref, fontDict, &embFontIDA);
+
+ // create the font object
+ font = NULL;
+ if (typeA < fontCIDType0) {
+ font = new Gfx8BitFont(xref, tagA, idA, nameA, typeA, embFontIDA,
+ fontDict);
+ } else {
+ font = new GfxCIDFont(xref, tagA, idA, nameA, typeA, embFontIDA,
+ fontDict);
+ }
+
+ return font;
+}
+
+GfxFont::GfxFont(const char *tagA, Ref idA, GString *nameA,
+ GfxFontType typeA, Ref embFontIDA) {
+ ok = gFalse;
+ tag = new GString(tagA);
+ id = idA;
+ name = nameA;
+ type = typeA;
+ embFontID = embFontIDA;
+ embFontName = NULL;
+ hasToUnicode = gFalse;
+}
+
+GfxFont::~GfxFont() {
+ delete tag;
+ if (name) {
+ delete name;
+ }
+ if (embFontName) {
+ delete embFontName;
+ }
+}
+
+// This function extracts three pieces of information:
+// 1. the "expected" font type, i.e., the font type implied by
+// Font.Subtype, DescendantFont.Subtype, and
+// FontDescriptor.FontFile3.Subtype
+// 2. the embedded font object ID
+// 3. the actual font type - determined by examining the embedded font
+// if there is one, otherwise equal to the expected font type
+// If the expected and actual font types don't match, a warning
+// message is printed. The expected font type is not used for
+// anything else.
+GfxFontType GfxFont::getFontType(XRef *xref, Dict *fontDict, Ref *embID) {
+ GfxFontType t, expectedType;
+ FoFiIdentifierType fft;
+ Dict *fontDict2;
+ Object subtype, fontDesc, obj1, obj2, obj3, obj4;
+ GBool isType0, err;
+
+ t = fontUnknownType;
+ embID->num = embID->gen = -1;
+ err = gFalse;
+
+ fontDict->lookup("Subtype", &subtype);
+ expectedType = fontUnknownType;
+ isType0 = gFalse;
+ if (subtype.isName("Type1") || subtype.isName("MMType1")) {
+ expectedType = fontType1;
+ } else if (subtype.isName("Type1C")) {
+ expectedType = fontType1C;
+ } else if (subtype.isName("Type3")) {
+ expectedType = fontType3;
+ } else if (subtype.isName("TrueType")) {
+ expectedType = fontTrueType;
+ } else if (subtype.isName("Type0")) {
+ isType0 = gTrue;
+ } else {
+ error(errSyntaxWarning, -1, "Unknown font type: '{0:s}'",
+ subtype.isName() ? subtype.getName() : "???");
+ }
+ subtype.free();
+
+ fontDict2 = fontDict;
+ if (fontDict->lookup("DescendantFonts", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() == 0) {
+ error(errSyntaxWarning, -1, "Empty DescendantFonts array in font");
+ obj2.initNull();
+ } else if (obj1.arrayGet(0, &obj2)->isDict()) {
+ if (!isType0) {
+ error(errSyntaxWarning, -1, "Non-CID font with DescendantFonts array");
+ }
+ fontDict2 = obj2.getDict();
+ fontDict2->lookup("Subtype", &subtype);
+ if (subtype.isName("CIDFontType0")) {
+ if (isType0) {
+ expectedType = fontCIDType0;
+ }
+ } else if (subtype.isName("CIDFontType2")) {
+ if (isType0) {
+ expectedType = fontCIDType2;
+ }
+ }
+ subtype.free();
+ }
+ } else {
+ obj2.initNull();
+ }
+
+ if (fontDict2->lookup("FontDescriptor", &fontDesc)->isDict()) {
+ if (fontDesc.dictLookupNF("FontFile", &obj3)->isRef()) {
+ *embID = obj3.getRef();
+ if (expectedType != fontType1) {
+ err = gTrue;
+ }
+ }
+ obj3.free();
+ if (embID->num == -1 &&
+ fontDesc.dictLookupNF("FontFile2", &obj3)->isRef()) {
+ *embID = obj3.getRef();
+ if (isType0) {
+ expectedType = fontCIDType2;
+ } else if (expectedType != fontTrueType) {
+ err = gTrue;
+ }
+ }
+ obj3.free();
+ if (embID->num == -1 &&
+ fontDesc.dictLookupNF("FontFile3", &obj3)->isRef()) {
+ *embID = obj3.getRef();
+ if (obj3.fetch(xref, &obj4)->isStream()) {
+ obj4.streamGetDict()->lookup("Subtype", &subtype);
+ if (subtype.isName("Type1")) {
+ if (expectedType != fontType1) {
+ err = gTrue;
+ expectedType = isType0 ? fontCIDType0 : fontType1;
+ }
+ } else if (subtype.isName("Type1C")) {
+ if (expectedType == fontType1) {
+ expectedType = fontType1C;
+ } else if (expectedType != fontType1C) {
+ err = gTrue;
+ expectedType = isType0 ? fontCIDType0C : fontType1C;
+ }
+ } else if (subtype.isName("TrueType")) {
+ if (expectedType != fontTrueType) {
+ err = gTrue;
+ expectedType = isType0 ? fontCIDType2 : fontTrueType;
+ }
+ } else if (subtype.isName("CIDFontType0C")) {
+ if (expectedType == fontCIDType0) {
+ expectedType = fontCIDType0C;
+ } else {
+ err = gTrue;
+ expectedType = isType0 ? fontCIDType0C : fontType1C;
+ }
+ } else if (subtype.isName("OpenType")) {
+ if (expectedType == fontTrueType) {
+ expectedType = fontTrueTypeOT;
+ } else if (expectedType == fontType1) {
+ expectedType = fontType1COT;
+ } else if (expectedType == fontCIDType0) {
+ expectedType = fontCIDType0COT;
+ } else if (expectedType == fontCIDType2) {
+ expectedType = fontCIDType2OT;
+ } else {
+ err = gTrue;
+ }
+ } else {
+ error(errSyntaxError, -1, "Unknown font type '{0:s}'",
+ subtype.isName() ? subtype.getName() : "???");
+ }
+ subtype.free();
+ }
+ obj4.free();
+ }
+ obj3.free();
+ }
+ fontDesc.free();
+
+ t = fontUnknownType;
+ if (embID->num >= 0) {
+ obj3.initRef(embID->num, embID->gen);
+ obj3.fetch(xref, &obj4);
+ if (obj4.isStream()) {
+ obj4.streamReset();
+ fft = FoFiIdentifier::identifyStream(&readFromStream, obj4.getStream());
+ obj4.streamClose();
+ switch (fft) {
+ case fofiIdType1PFA:
+ case fofiIdType1PFB:
+ t = fontType1;
+ break;
+ case fofiIdCFF8Bit:
+ t = isType0 ? fontCIDType0C : fontType1C;
+ break;
+ case fofiIdCFFCID:
+ t = fontCIDType0C;
+ break;
+ case fofiIdTrueType:
+ case fofiIdTrueTypeCollection:
+ t = isType0 ? fontCIDType2 : fontTrueType;
+ break;
+ case fofiIdOpenTypeCFF8Bit:
+ t = isType0 ? fontCIDType0COT : fontType1COT;
+ break;
+ case fofiIdOpenTypeCFFCID:
+ t = fontCIDType0COT;
+ break;
+ default:
+ error(errSyntaxError, -1, "Embedded font file may be invalid");
+ break;
+ }
+ }
+ obj4.free();
+ obj3.free();
+ }
+
+ if (t == fontUnknownType) {
+ t = expectedType;
+ }
+
+ if (t != expectedType) {
+ err = gTrue;
+ }
+
+ if (err) {
+ error(errSyntaxWarning, -1,
+ "Mismatch between font type and embedded font file");
+ }
+
+ obj2.free();
+ obj1.free();
+
+ return t;
+}
+
+void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
+ Object obj1, obj2, obj3, obj4;
+ double t, t2;
+ int i;
+
+ // assume Times-Roman by default (for substitution purposes)
+ flags = fontSerif;
+
+ if (fontDict->lookup("FontDescriptor", &obj1)->isDict()) {
+
+ // get flags
+ if (obj1.dictLookup("Flags", &obj2)->isInt()) {
+ flags = obj2.getInt();
+ }
+ obj2.free();
+
+ // get name
+ obj1.dictLookup("FontName", &obj2);
+ if (obj2.isName()) {
+ embFontName = new GString(obj2.getName());
+ }
+ obj2.free();
+
+ // look for MissingWidth
+ obj1.dictLookup("MissingWidth", &obj2);
+ if (obj2.isNum()) {
+ missingWidth = obj2.getNum();
+ }
+ obj2.free();
+
+ // get Ascent
+ // (CapHeight is a little more reliable - so use it if present)
+ obj1.dictLookup("Ascent", &obj2);
+ obj1.dictLookup("CapHeight", &obj3);
+ if (obj2.isNum() || obj3.isNum()) {
+ if (obj2.isNum()) {
+ t = 0.001 * obj2.getNum();
+ // some broken font descriptors specify a negative ascent
+ if (t < 0) {
+ t = -t;
+ }
+ } else {
+ t = 0;
+ }
+ if (obj3.isNum()) {
+ t2 = 0.001 * obj3.getNum();
+ // some broken font descriptors specify a negative ascent
+ if (t2 < 0) {
+ t2 = -t2;
+ }
+ } 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)) {
+ t = t2;
+ }
+ // some broken font descriptors set ascent and descent to 0;
+ // others set it to ridiculous values (e.g., 32768)
+ if (t != 0 && t < 1.9) {
+ ascent = t;
+ }
+ }
+ obj2.free();
+ obj3.free();
+
+ // get Descent
+ obj1.dictLookup("Descent", &obj2);
+ if (obj2.isNum()) {
+ t = 0.001 * obj2.getNum();
+ // some broken font descriptors specify a positive descent
+ if (t > 0) {
+ t = -t;
+ }
+ // some broken font descriptors set ascent and descent to 0
+ if (t != 0 && t > -1.9) {
+ descent = t;
+ }
+ }
+ obj2.free();
+
+ // font FontBBox
+ if (obj1.dictLookup("FontBBox", &obj2)->isArray()) {
+ for (i = 0; i < 4 && i < obj2.arrayGetLength(); ++i) {
+ if (obj2.arrayGet(i, &obj3)->isNum()) {
+ fontBBox[i] = 0.001 * obj3.getNum();
+ }
+ obj3.free();
+ }
+ }
+ obj2.free();
+
+ }
+ obj1.free();
+}
+
+CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits,
+ CharCodeToUnicode *ctu) {
+ GString *buf;
+ Object obj1;
+ char buf2[4096];
+ int n;
+
+ if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) {
+ obj1.free();
+ return NULL;
+ }
+ buf = new GString();
+ obj1.streamReset();
+ while ((n = obj1.streamGetBlock(buf2, sizeof(buf2))) > 0) {
+ buf->append(buf2, n);
+ }
+ obj1.streamClose();
+ obj1.free();
+ if (ctu) {
+ ctu->mergeCMap(buf, nBits);
+ } else {
+ ctu = CharCodeToUnicode::parseCMap(buf, nBits);
+ }
+ delete buf;
+ hasToUnicode = gTrue;
+ return ctu;
+}
+
+GfxFontLoc *GfxFont::locateFont(XRef *xref, GBool ps) {
+ GfxFontLoc *fontLoc;
+ SysFontType sysFontType;
+ FoFiIdentifierType fft;
+ GString *path, *base14Name, *substName;
+ PSFontParam16 *psFont16;
+ Object refObj, embFontObj;
+ int substIdx, fontNum;
+ double oblique;
+ GBool embed;
+
+ if (type == fontType3) {
+ return NULL;
+ }
+
+ //----- embedded font
+ if (embFontID.num >= 0) {
+ embed = gTrue;
+ refObj.initRef(embFontID.num, embFontID.gen);
+ refObj.fetch(xref, &embFontObj);
+ if (!embFontObj.isStream()) {
+ error(errSyntaxError, -1, "Embedded font object is wrong type");
+ embed = gFalse;
+ }
+ embFontObj.free();
+ refObj.free();
+ if (embed) {
+ if (ps) {
+ switch (type) {
+ case fontType1:
+ case fontType1C:
+ case fontType1COT:
+ embed = globalParams->getPSEmbedType1();
+ break;
+ case fontTrueType:
+ case fontTrueTypeOT:
+ embed = globalParams->getPSEmbedTrueType();
+ break;
+ case fontCIDType0C:
+ case fontCIDType0COT:
+ embed = globalParams->getPSEmbedCIDPostScript();
+ break;
+ case fontCIDType2:
+ case fontCIDType2OT:
+ embed = globalParams->getPSEmbedCIDTrueType();
+ break;
+ default:
+ break;
+ }
+ }
+ if (embed) {
+ fontLoc = new GfxFontLoc();
+ fontLoc->locType = gfxFontLocEmbedded;
+ fontLoc->fontType = type;
+ fontLoc->embFontID = embFontID;
+ return fontLoc;
+ }
+ }
+ }
+
+ //----- PS passthrough
+ if (ps && name && !isCIDFont() && globalParams->getPSFontPassthrough()) {
+ fontLoc = new GfxFontLoc();
+ fontLoc->locType = gfxFontLocResident;
+ fontLoc->fontType = fontType1;
+ fontLoc->path = name->copy();
+ return fontLoc;
+ }
+
+ //----- external font file (fontFile, fontDir)
+ if (name && (path = globalParams->findFontFile(name))) {
+ if ((fontLoc = getExternalFont(path, 0, 0, isCIDFont()))) {
+ return fontLoc;
+ }
+ }
+
+ //----- PS resident Base-14 font
+ if (ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) {
+ fontLoc = new GfxFontLoc();
+ fontLoc->locType = gfxFontLocResident;
+ fontLoc->fontType = fontType1;
+ fontLoc->path = new GString(((Gfx8BitFont *)this)->base14->base14Name);
+ return fontLoc;
+ }
+
+ //----- external font file for Base-14 font
+ if (!ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) {
+ base14Name = new GString(((Gfx8BitFont *)this)->base14->base14Name);
+ path = globalParams->findBase14FontFile(base14Name, &fontNum, &oblique);
+ delete base14Name;
+ if (path && (fontLoc = getExternalFont(path, fontNum, oblique, gFalse))) {
+ return fontLoc;
+ }
+ }
+
+ //----- system font
+ if (name && (path = globalParams->findSystemFontFile(name, &sysFontType,
+ &fontNum))) {
+ fontLoc = new GfxFontLoc();
+ fontLoc->locType = gfxFontLocExternal;
+ fontLoc->path = path;
+ fontLoc->fontNum = fontNum;
+ if (isCIDFont()) {
+ if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) {
+ fontLoc->fontType = fontCIDType2;
+ return fontLoc;
+ } else if (sysFontType == sysFontOTF) {
+ fft = FoFiIdentifier::identifyFile(fontLoc->path->getCString());
+ if (fft == fofiIdOpenTypeCFFCID) {
+ fontLoc->fontType = fontCIDType0COT;
+ return fontLoc;
+ } else if (fft == fofiIdTrueType) {
+ fontLoc->fontType = fontCIDType2;
+ return fontLoc;
+ }
+ }
+ } else {
+ if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) {
+ fontLoc->fontType = fontTrueType;
+ return fontLoc;
+ } else if (sysFontType == sysFontPFA || sysFontType == sysFontPFB) {
+ fontLoc->fontType = fontType1;
+ return fontLoc;
+ } else if (sysFontType == sysFontOTF) {
+ fft = FoFiIdentifier::identifyFile(fontLoc->path->getCString());
+ if (fft == fofiIdOpenTypeCFF8Bit) {
+ fontLoc->fontType = fontType1COT;
+ return fontLoc;
+ } else if (fft == fofiIdTrueType) {
+ fontLoc->fontType = fontTrueTypeOT;
+ return fontLoc;
+ }
+ }
+ }
+ delete fontLoc;
+ }
+
+ if (!isCIDFont()) {
+
+ //----- 8-bit PS resident font
+ if (ps) {
+ if (name && (path = globalParams->getPSResidentFont(name))) {
+ fontLoc = new GfxFontLoc();
+ fontLoc->locType = gfxFontLocResident;
+ fontLoc->fontType = fontType1;
+ fontLoc->path = path;
+ return fontLoc;
+ }
+ }
+
+ //----- 8-bit font substitution
+ if (flags & fontFixedWidth) {
+ substIdx = 0;
+ } else if (flags & fontSerif) {
+ substIdx = 8;
+ } else {
+ substIdx = 4;
+ }
+ if (isBold()) {
+ substIdx += 2;
+ }
+ if (isItalic()) {
+ substIdx += 1;
+ }
+ substName = new GString(base14SubstFonts[substIdx]);
+ if (ps) {
+ error(errSyntaxWarning, -1, "Substituting font '{0:s}' for '{1:t}'",
+ base14SubstFonts[substIdx], name);
+ fontLoc = new GfxFontLoc();
+ fontLoc->locType = gfxFontLocResident;
+ fontLoc->fontType = fontType1;
+ fontLoc->path = substName;
+ fontLoc->substIdx = substIdx;
+ return fontLoc;
+ } else {
+ path = globalParams->findBase14FontFile(substName, &fontNum, &oblique);
+ delete substName;
+ if (path) {
+ if ((fontLoc = getExternalFont(path, fontNum, oblique, gFalse))) {
+ error(errSyntaxWarning, -1, "Substituting font '{0:s}' for '{1:t}'",
+ base14SubstFonts[substIdx], name);
+ fontLoc->substIdx = substIdx;
+ return fontLoc;
+ }
+ }
+ }
+
+ // failed to find a substitute font
+ return NULL;
+ }
+
+ //----- 16-bit PS resident font
+ if (ps && name && ((psFont16 = globalParams->getPSResidentFont16(
+ name,
+ ((GfxCIDFont *)this)->getWMode())))) {
+ fontLoc = new GfxFontLoc();
+ fontLoc->locType = gfxFontLocResident;
+ fontLoc->fontType = fontCIDType0; // this is not used
+ fontLoc->path = psFont16->psFontName->copy();
+ fontLoc->encoding = psFont16->encoding->copy();
+ fontLoc->wMode = psFont16->wMode;
+ return fontLoc;
+ }
+ if (ps && ((psFont16 = globalParams->getPSResidentFontCC(
+ ((GfxCIDFont *)this)->getCollection(),
+ ((GfxCIDFont *)this)->getWMode())))) {
+ error(errSyntaxWarning, -1, "Substituting font '{0:t}' for '{1:t}'",
+ psFont16->psFontName, name);
+ fontLoc = new GfxFontLoc();
+ fontLoc->locType = gfxFontLocResident;
+ fontLoc->fontType = fontCIDType0; // this is not used
+ fontLoc->path = psFont16->psFontName->copy();
+ fontLoc->encoding = psFont16->encoding->copy();
+ fontLoc->wMode = psFont16->wMode;
+ return fontLoc;
+ }
+
+ //----- CID font substitution
+ if ((path = globalParams->findCCFontFile(
+ ((GfxCIDFont *)this)->getCollection()))) {
+ if ((fontLoc = getExternalFont(path, 0, 0, gTrue))) {
+ error(errSyntaxWarning, -1, "Substituting font '{0:t}' for '{1:t}'",
+ fontLoc->path, name);
+ return fontLoc;
+ }
+ }
+
+ // failed to find a substitute font
+ return NULL;
+}
+
+GfxFontLoc *GfxFont::locateBase14Font(GString *base14Name) {
+ GString *path;
+ int fontNum;
+ double oblique;
+
+ path = globalParams->findBase14FontFile(base14Name, &fontNum, &oblique);
+ if (!path) {
+ return NULL;
+ }
+ return getExternalFont(path, fontNum, oblique, gFalse);
+}
+
+GfxFontLoc *GfxFont::getExternalFont(GString *path, int fontNum,
+ double oblique, GBool cid) {
+ FoFiIdentifierType fft;
+ GfxFontType fontType;
+ GfxFontLoc *fontLoc;
+
+ fft = FoFiIdentifier::identifyFile(path->getCString());
+ switch (fft) {
+ case fofiIdType1PFA:
+ case fofiIdType1PFB:
+ fontType = fontType1;
+ break;
+ case fofiIdCFF8Bit:
+ fontType = fontType1C;
+ break;
+ case fofiIdCFFCID:
+ fontType = fontCIDType0C;
+ break;
+ case fofiIdTrueType:
+ case fofiIdTrueTypeCollection:
+ fontType = cid ? fontCIDType2 : fontTrueType;
+ break;
+ case fofiIdOpenTypeCFF8Bit:
+ fontType = fontType1COT;
+ break;
+ case fofiIdOpenTypeCFFCID:
+ fontType = fontCIDType0COT;
+ break;
+ case fofiIdDfont:
+ fontType = cid ? fontCIDType2 : fontTrueType;
+ break;
+ case fofiIdUnknown:
+ case fofiIdError:
+ default:
+ fontType = fontUnknownType;
+ break;
+ }
+ if (fontType == fontUnknownType ||
+ (cid ? (fontType < fontCIDType0)
+ : (fontType >= fontCIDType0))) {
+ delete path;
+ return NULL;
+ }
+ fontLoc = new GfxFontLoc();
+ fontLoc->locType = gfxFontLocExternal;
+ fontLoc->fontType = fontType;
+ fontLoc->path = path;
+ fontLoc->fontNum = fontNum;
+ fontLoc->oblique = oblique;
+ return fontLoc;
+}
+
+char *GfxFont::readEmbFontFile(XRef *xref, int *len) {
+ char *buf;
+ Object obj1, obj2;
+ Stream *str;
+ int size, n;
+
+ obj1.initRef(embFontID.num, embFontID.gen);
+ obj1.fetch(xref, &obj2);
+ if (!obj2.isStream()) {
+ error(errSyntaxError, -1, "Embedded font file is not a stream");
+ obj2.free();
+ obj1.free();
+ embFontID.num = -1;
+ return NULL;
+ }
+ str = obj2.getStream();
+
+ size = 4096;
+ buf = (char *)gmalloc(size);
+ *len = 0;
+ str->reset();
+ do {
+ 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);
+ }
+ n = str->getBlock(buf + *len, 4096);
+ *len += n;
+ } while (n == 4096);
+ str->close();
+
+ obj2.free();
+ obj1.free();
+
+ return buf;
+}
+
+//------------------------------------------------------------------------
+// Gfx8BitFont
+//------------------------------------------------------------------------
+
+Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, GString *nameA,
+ GfxFontType typeA, Ref embFontIDA, Dict *fontDict):
+ GfxFont(tagA, idA, nameA, typeA, embFontIDA)
+{
+ GString *name2;
+ BuiltinFont *builtinFont;
+ const char **baseEnc;
+ char *buf;
+ int len;
+ FoFiType1 *ffT1;
+ FoFiType1C *ffT1C;
+ int code, code2;
+ char *charName;
+ GBool missing, hex;
+ Unicode toUnicode[256];
+ CharCodeToUnicode *utu, *ctu2;
+ Unicode uBuf[8];
+ double mul;
+ int firstChar, lastChar;
+ Gushort w;
+ Object obj1, obj2, obj3;
+ int n, i, a, b, m;
+
+ ctu = NULL;
+
+ // do font name substitution for various aliases of the Base 14 font
+ // names
+ base14 = NULL;
+ if (name) {
+ name2 = name->copy();
+ i = 0;
+ while (i < name2->getLength()) {
+ if (name2->getChar(i) == ' ') {
+ name2->del(i);
+ } else {
+ ++i;
+ }
+ }
+ a = 0;
+ b = sizeof(base14FontMap) / sizeof(Base14FontMapEntry);
+ // invariant: base14FontMap[a].altName <= name2 < base14FontMap[b].altName
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (name2->cmp(base14FontMap[m].altName) >= 0) {
+ a = m;
+ } else {
+ b = m;
+ }
+ }
+ if (!name2->cmp(base14FontMap[a].altName)) {
+ base14 = &base14FontMap[a];
+ }
+ delete name2;
+ }
+
+ // is it a built-in font?
+ builtinFont = NULL;
+ if (base14) {
+ for (i = 0; i < nBuiltinFonts; ++i) {
+ if (!strcmp(base14->base14Name, builtinFonts[i].name)) {
+ builtinFont = &builtinFonts[i];
+ break;
+ }
+ }
+ }
+
+ // default ascent/descent values
+ if (builtinFont) {
+ 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];
+ fontBBox[3] = 0.001 * builtinFont->bbox[3];
+ } else {
+ missingWidth = 0;
+ ascent = 0.75;
+ descent = -0.25;
+ declaredAscent = ascent;
+ fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
+ }
+
+ // get info from font descriptor
+ readFontDescriptor(xref, fontDict);
+
+ // for Base-14 fonts (even if embedded), don't trust the
+ // ascent/descent/bbox values from the font descriptor
+ 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];
+ fontBBox[3] = 0.001 * builtinFont->bbox[3];
+ }
+
+ // get font matrix
+ fontMat[0] = fontMat[3] = 1;
+ fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0;
+ if (fontDict->lookup("FontMatrix", &obj1)->isArray()) {
+ for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ fontMat[i] = obj2.getNum();
+ }
+ obj2.free();
+ }
+ }
+ obj1.free();
+
+ // get Type 3 bounding box, font definition, and resources
+ if (type == fontType3) {
+ if (fontDict->lookup("FontBBox", &obj1)->isArray()) {
+ for (i = 0; i < 4 && i < obj1.arrayGetLength(); ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ fontBBox[i] = obj2.getNum();
+ }
+ obj2.free();
+ }
+ }
+ obj1.free();
+ if (!fontDict->lookup("CharProcs", &charProcs)->isDict()) {
+ error(errSyntaxError, -1,
+ "Missing or invalid CharProcs dictionary in Type 3 font");
+ charProcs.free();
+ }
+ if (!fontDict->lookup("Resources", &resources)->isDict()) {
+ resources.free();
+ }
+ }
+
+ //----- build the font encoding -----
+
+ // Encodings start with a base encoding, which can come from
+ // (in order of priority):
+ // 1. FontDict.Encoding or FontDict.Encoding.BaseEncoding
+ // - MacRoman / MacExpert / WinAnsi / Standard
+ // 2. embedded or external font file
+ // 3. default:
+ // - builtin --> builtin encoding
+ // - TrueType --> WinAnsiEncoding
+ // - others --> StandardEncoding
+ // and then add a list of differences (if any) from
+ // FontDict.Encoding.Differences.
+
+ // check FontDict for base encoding
+ hasEncoding = gFalse;
+ usesMacRomanEnc = gFalse;
+ baseEnc = NULL;
+ baseEncFromFontFile = gFalse;
+ fontDict->lookup("Encoding", &obj1);
+ if (obj1.isDict()) {
+ obj1.dictLookup("BaseEncoding", &obj2);
+ if (obj2.isName("MacRomanEncoding")) {
+ hasEncoding = gTrue;
+ usesMacRomanEnc = gTrue;
+ baseEnc = macRomanEncoding;
+ } else if (obj2.isName("MacExpertEncoding")) {
+ hasEncoding = gTrue;
+ baseEnc = macExpertEncoding;
+ } else if (obj2.isName("WinAnsiEncoding")) {
+ hasEncoding = gTrue;
+ baseEnc = winAnsiEncoding;
+ }
+ obj2.free();
+ } else if (obj1.isName("MacRomanEncoding")) {
+ hasEncoding = gTrue;
+ usesMacRomanEnc = gTrue;
+ baseEnc = macRomanEncoding;
+ } else if (obj1.isName("MacExpertEncoding")) {
+ hasEncoding = gTrue;
+ baseEnc = macExpertEncoding;
+ } else if (obj1.isName("WinAnsiEncoding")) {
+ hasEncoding = gTrue;
+ baseEnc = winAnsiEncoding;
+ }
+
+ // check embedded font file for base encoding
+ // (only for Type 1 fonts - trying to get an encoding out of a
+ // TrueType font is a losing proposition)
+ ffT1 = NULL;
+ ffT1C = NULL;
+ buf = NULL;
+ if (type == fontType1 && embFontID.num >= 0) {
+ if ((buf = readEmbFontFile(xref, &len))) {
+ if ((ffT1 = FoFiType1::make(buf, len))) {
+ if (ffT1->getName()) {
+ if (embFontName) {
+ delete embFontName;
+ }
+ embFontName = new GString(ffT1->getName());
+ }
+ if (!baseEnc) {
+ baseEnc = (const char **)ffT1->getEncoding();
+ baseEncFromFontFile = gTrue;
+ }
+ }
+ gfree(buf);
+ }
+ } else if (type == fontType1C && embFontID.num >= 0) {
+ if ((buf = readEmbFontFile(xref, &len))) {
+ if ((ffT1C = FoFiType1C::make(buf, len))) {
+ if (ffT1C->getName()) {
+ if (embFontName) {
+ delete embFontName;
+ }
+ embFontName = new GString(ffT1C->getName());
+ }
+ if (!baseEnc) {
+ baseEnc = (const char **)ffT1C->getEncoding();
+ baseEncFromFontFile = gTrue;
+ }
+ }
+ gfree(buf);
+ }
+ }
+
+ // get default base encoding
+ if (!baseEnc) {
+ if (builtinFont && embFontID.num < 0) {
+ baseEnc = builtinFont->defaultBaseEnc;
+ hasEncoding = gTrue;
+ } else if (type == fontTrueType) {
+ baseEnc = winAnsiEncoding;
+ } else {
+ baseEnc = standardEncoding;
+ }
+ }
+
+ // copy the base encoding
+ for (i = 0; i < 256; ++i) {
+ enc[i] = (char *)baseEnc[i];
+ if ((encFree[i] = (char)baseEncFromFontFile) && enc[i]) {
+ enc[i] = copyString(baseEnc[i]);
+ }
+ }
+
+ // some Type 1C font files have empty encodings, which can break the
+ // T1C->T1 conversion (since the 'seac' operator depends on having
+ // the accents in the encoding), so we fill in any gaps from
+ // StandardEncoding
+ if (type == fontType1C && embFontID.num >= 0 && baseEncFromFontFile) {
+ for (i = 0; i < 256; ++i) {
+ if (!enc[i] && standardEncoding[i]) {
+ enc[i] = (char *)standardEncoding[i];
+ encFree[i] = gFalse;
+ }
+ }
+ }
+
+ // merge differences into encoding
+ if (obj1.isDict()) {
+ obj1.dictLookup("Differences", &obj2);
+ if (obj2.isArray()) {
+ hasEncoding = gTrue;
+ code = 0;
+ for (i = 0; i < obj2.arrayGetLength(); ++i) {
+ obj2.arrayGet(i, &obj3);
+ if (obj3.isInt()) {
+ code = obj3.getInt();
+ } else if (obj3.isName()) {
+ if (code >= 0 && code < 256) {
+ if (encFree[code]) {
+ gfree(enc[code]);
+ }
+ enc[code] = copyString(obj3.getName());
+ encFree[code] = gTrue;
+ }
+ ++code;
+ } else {
+ error(errSyntaxError, -1,
+ "Wrong type in font encoding resource differences ({0:s})",
+ obj3.getTypeName());
+ }
+ obj3.free();
+ }
+ }
+ obj2.free();
+ }
+ obj1.free();
+ if (ffT1) {
+ delete ffT1;
+ }
+ if (ffT1C) {
+ delete ffT1C;
+ }
+
+ //----- build the mapping to Unicode -----
+
+ // pass 1: use the name-to-Unicode mapping table
+ missing = hex = gFalse;
+ for (code = 0; code < 256; ++code) {
+ if ((charName = enc[code])) {
+ if (!(toUnicode[code] = globalParams->mapNameToUnicode(charName)) &&
+ strcmp(charName, ".notdef")) {
+ // if it wasn't in the name-to-Unicode table, check for a
+ // name that looks like 'Axx' or 'xx', where 'A' is any letter
+ // and 'xx' is two hex digits
+ if ((strlen(charName) == 3 &&
+ isalpha(charName[0] & 0xff) &&
+ isxdigit(charName[1] & 0xff) && isxdigit(charName[2] & 0xff) &&
+ ((charName[1] >= 'a' && charName[1] <= 'f') ||
+ (charName[1] >= 'A' && charName[1] <= 'F') ||
+ (charName[2] >= 'a' && charName[2] <= 'f') ||
+ (charName[2] >= 'A' && charName[2] <= 'F'))) ||
+ (strlen(charName) == 2 &&
+ isxdigit(charName[0] & 0xff) && isxdigit(charName[1] & 0xff) &&
+ ((charName[0] >= 'a' && charName[0] <= 'f') ||
+ (charName[0] >= 'A' && charName[0] <= 'F') ||
+ (charName[1] >= 'a' && charName[1] <= 'f') ||
+ (charName[1] >= 'A' && charName[1] <= 'F')))) {
+ hex = gTrue;
+ }
+ missing = gTrue;
+ }
+ } else {
+ toUnicode[code] = 0;
+ }
+ }
+
+ // pass 2: try to fill in the missing chars, looking for names of
+ // any of the following forms:
+ // - 'xx'
+ // - 'Axx'
+ // - 'nn'
+ // - 'Ann'
+ // - 'ABnn'
+ // - 'unixxxx' (possibly followed by garbage - some Arabic files
+ // use 'uni0628.medi', etc.)
+ // where 'A' and 'B' are any letters, 'xx' is two hex digits, 'xxxx'
+ // is four hex digits, and 'nn' is 2-4 decimal digits
+ usedNumericHeuristic = gFalse;
+ if (missing && globalParams->getMapNumericCharNames()) {
+ for (code = 0; code < 256; ++code) {
+ if ((charName = enc[code]) && !toUnicode[code] &&
+ strcmp(charName, ".notdef")) {
+ n = (int)strlen(charName);
+ code2 = -1;
+ if (hex && n == 3 && isalpha(charName[0] & 0xff) &&
+ isxdigit(charName[1] & 0xff) && isxdigit(charName[2] & 0xff)) {
+ sscanf(charName+1, "%x", &code2);
+ } else if (hex && n == 2 &&
+ isxdigit(charName[0] & 0xff) &&
+ isxdigit(charName[1] & 0xff)) {
+ sscanf(charName, "%x", &code2);
+ } else if (!hex && n >= 2 && n <= 4 &&
+ isdigit(charName[0] & 0xff) && isdigit(charName[1] & 0xff)) {
+ code2 = atoi(charName);
+ } else if (n >= 3 && n <= 5 &&
+ isdigit(charName[1] & 0xff) && isdigit(charName[2] & 0xff)) {
+ code2 = atoi(charName+1);
+ } else if (n >= 4 && n <= 6 &&
+ isdigit(charName[2] & 0xff) && isdigit(charName[3] & 0xff)) {
+ code2 = atoi(charName+2);
+ } else if (n >= 7 && charName[0] == 'u' && charName[1] == 'n' &&
+ charName[2] == 'i' &&
+ isxdigit(charName[3] & 0xff) &&
+ isxdigit(charName[4] & 0xff) &&
+ isxdigit(charName[5] & 0xff) &&
+ isxdigit(charName[6] & 0xff)) {
+ sscanf(charName + 3, "%x", &code2);
+ }
+ if (code2 >= 0 && code2 <= 0xffff) {
+ toUnicode[code] = (Unicode)code2;
+ usedNumericHeuristic = gTrue;
+ }
+ }
+ }
+
+ // if the 'mapUnknownCharNames' flag is set, do a simple pass-through
+ // mapping for unknown character names
+ } else if (missing && globalParams->getMapUnknownCharNames()) {
+ for (code = 0; code < 256; ++code) {
+ if (!toUnicode[code]) {
+ toUnicode[code] = code;
+ }
+ }
+ }
+
+ // construct the char code -> Unicode mapping object
+ ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode);
+
+ // merge in a ToUnicode CMap, if there is one -- this overwrites
+ // existing entries in ctu, i.e., the ToUnicode CMap takes
+ // precedence, but the other encoding info is allowed to fill in any
+ // holes
+ readToUnicodeCMap(fontDict, 8, ctu);
+
+ // look for a Unicode-to-Unicode mapping
+ if (name && (utu = globalParams->getUnicodeToUnicode(name))) {
+ for (i = 0; i < 256; ++i) {
+ toUnicode[i] = 0;
+ }
+ ctu2 = CharCodeToUnicode::make8BitToUnicode(toUnicode);
+ for (i = 0; i < 256; ++i) {
+ n = ctu->mapToUnicode((CharCode)i, uBuf, 8);
+ if (n >= 1) {
+ n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8);
+ if (n >= 1) {
+ ctu2->setMapping((CharCode)i, uBuf, n);
+ }
+ }
+ }
+ utu->decRefCnt();
+ delete ctu;
+ ctu = ctu2;
+ }
+
+ //----- get the character widths -----
+
+ // initialize all widths
+ for (code = 0; code < 256; ++code) {
+ widths[code] = missingWidth * 0.001;
+ }
+
+ // use widths from font dict, if present
+ fontDict->lookup("FirstChar", &obj1);
+ firstChar = obj1.isInt() ? obj1.getInt() : 0;
+ obj1.free();
+ if (firstChar < 0 || firstChar > 255) {
+ firstChar = 0;
+ }
+ fontDict->lookup("LastChar", &obj1);
+ lastChar = obj1.isInt() ? obj1.getInt() : 255;
+ obj1.free();
+ if (lastChar < 0 || lastChar > 255) {
+ lastChar = 255;
+ }
+ mul = (type == fontType3) ? fontMat[0] : 0.001;
+ fontDict->lookup("Widths", &obj1);
+ if (obj1.isArray()) {
+ flags |= fontFixedWidth;
+ if (obj1.arrayGetLength() < lastChar - firstChar + 1) {
+ lastChar = firstChar + obj1.arrayGetLength() - 1;
+ }
+ for (code = firstChar; code <= lastChar; ++code) {
+ obj1.arrayGet(code - firstChar, &obj2);
+ if (obj2.isNum()) {
+ widths[code] = obj2.getNum() * mul;
+ if (fabs(widths[code] - widths[firstChar]) > 0.00001) {
+ flags &= ~fontFixedWidth;
+ }
+ }
+ obj2.free();
+ }
+
+ // use widths from built-in font
+ } else if (builtinFont) {
+ // this is a kludge for broken PDF files that encode char 32
+ // as .notdef
+ if (builtinFont->widths->getWidth("space", &w)) {
+ widths[32] = 0.001 * w;
+ }
+ for (code = 0; code < 256; ++code) {
+ if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) {
+ widths[code] = 0.001 * w;
+ }
+ }
+
+ // couldn't find widths -- use defaults
+ } else {
+ // this is technically an error -- the Widths entry is required
+ // for all but the Base-14 fonts -- but certain PDF generators
+ // apparently don't include widths for Arial and TimesNewRoman
+ if (isFixedWidth()) {
+ i = 0;
+ } else if (isSerif()) {
+ i = 8;
+ } else {
+ i = 4;
+ }
+ if (isBold()) {
+ i += 2;
+ }
+ if (isItalic()) {
+ i += 1;
+ }
+ builtinFont = builtinFontSubst[i];
+ // this is a kludge for broken PDF files that encode char 32
+ // as .notdef
+ if (builtinFont->widths->getWidth("space", &w)) {
+ widths[32] = 0.001 * w;
+ }
+ for (code = 0; code < 256; ++code) {
+ if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) {
+ widths[code] = 0.001 * w;
+ }
+ }
+ }
+ obj1.free();
+
+ ok = gTrue;
+}
+
+Gfx8BitFont::~Gfx8BitFont() {
+ int i;
+
+ for (i = 0; i < 256; ++i) {
+ if (encFree[i] && enc[i]) {
+ gfree(enc[i]);
+ }
+ }
+ ctu->decRefCnt();
+ if (charProcs.isDict()) {
+ charProcs.free();
+ }
+ if (resources.isDict()) {
+ resources.free();
+ }
+}
+
+int Gfx8BitFont::getNextChar(char *s, int len, CharCode *code,
+ Unicode *u, int uSize, int *uLen,
+ double *dx, double *dy, double *ox, double *oy) {
+ CharCode c;
+
+ *code = c = (CharCode)(*s & 0xff);
+ *uLen = ctu->mapToUnicode(c, u, uSize);
+ *dx = widths[c];
+ *dy = *ox = *oy = 0;
+ return 1;
+}
+
+CharCodeToUnicode *Gfx8BitFont::getToUnicode() {
+ ctu->incRefCnt();
+ return ctu;
+}
+
+int *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) {
+ int *map;
+ int cmapPlatform, cmapEncoding;
+ int unicodeCmap, macRomanCmap, msSymbolCmap, cmap;
+ GBool nonsymbolic, useMacRoman, useUnicode;
+ char *charName;
+ Unicode u;
+ int code, i, n;
+
+ map = (int *)gmallocn(256, sizeof(int));
+ for (i = 0; i < 256; ++i) {
+ map[i] = 0;
+ }
+
+ // This is based on the cmap/encoding selection algorithm in the PDF
+ // 2.0 spec, but with some differences to match up with Adobe's
+ // behavior.
+ unicodeCmap = macRomanCmap = msSymbolCmap = -1;
+ for (i = 0; i < ff->getNumCmaps(); ++i) {
+ cmapPlatform = ff->getCmapPlatform(i);
+ cmapEncoding = ff->getCmapEncoding(i);
+ if ((cmapPlatform == 3 && cmapEncoding == 1) ||
+ (cmapPlatform == 0 && cmapEncoding <= 4)) {
+ unicodeCmap = i;
+ } else if (cmapPlatform == 1 && cmapEncoding == 0) {
+ macRomanCmap = i;
+ } else if (cmapPlatform == 3 && cmapEncoding == 0) {
+ msSymbolCmap = i;
+ }
+ }
+ useMacRoman = gFalse;
+ useUnicode = gFalse;
+ nonsymbolic = !(flags & fontSymbolic);
+ if (usesMacRomanEnc && macRomanCmap >= 0) {
+ cmap = macRomanCmap;
+ useMacRoman = gTrue;
+ } else if (embFontID.num < 0 && hasEncoding && unicodeCmap >= 0) {
+ cmap = unicodeCmap;
+ useUnicode = gTrue;
+ } else if (nonsymbolic && unicodeCmap >= 0) {
+ cmap = unicodeCmap;
+ useUnicode = gTrue;
+ } else if (nonsymbolic && macRomanCmap >= 0) {
+ cmap = macRomanCmap;
+ useMacRoman = gTrue;
+ } else if (msSymbolCmap >= 0) {
+ cmap = msSymbolCmap;
+ } else if (unicodeCmap >= 0) {
+ cmap = unicodeCmap;
+ } else if (macRomanCmap >= 0) {
+ cmap = macRomanCmap;
+ } else {
+ cmap = 0;
+ }
+
+ // reverse map the char names through MacRomanEncoding, then map the
+ // 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;
+ }
+ }
+
+ // map Unicode through the cmap
+ } else if (useUnicode) {
+ for (i = 0; i < 256; ++i) {
+ if (((charName = enc[i]) &&
+ (u = globalParams->mapNameToUnicode(charName))) ||
+ (n = ctu->mapToUnicode((CharCode)i, &u, 1))) {
+ map[i] = ff->mapCodeToGID(cmap, u);
+ } else {
+ map[i] = -1;
+ }
+ }
+
+ // map the char codes through the cmap, possibly with an offset of
+ // 0xf000
+ } else {
+ for (i = 0; i < 256; ++i) {
+ if (!(map[i] = ff->mapCodeToGID(cmap, i))) {
+ map[i] = ff->mapCodeToGID(cmap, 0xf000 + i);
+ }
+ }
+ }
+
+ // try the TrueType 'post' table to handle any unmapped characters
+ for (i = 0; i < 256; ++i) {
+ if (map[i] <= 0 && (charName = enc[i])) {
+ map[i] = ff->mapNameToGID(charName);
+ }
+ }
+
+ return map;
+}
+
+int *Gfx8BitFont::getCodeToGIDMap(FoFiType1C *ff) {
+ int *map;
+ GHash *nameToGID;
+ int i, gid;
+
+ map = (int *)gmallocn(256, sizeof(int));
+ for (i = 0; i < 256; ++i) {
+ map[i] = 0;
+ }
+
+ nameToGID = ff->getNameToGIDMap();
+ for (i = 0; i < 256; ++i) {
+ if (!enc[i]) {
+ continue;
+ }
+ gid = nameToGID->lookupInt(enc[i]);
+ if (gid < 0 || gid >= 65536) {
+ continue;
+ }
+ map[i] = gid;
+ }
+
+ delete nameToGID;
+
+ return map;
+}
+
+Dict *Gfx8BitFont::getCharProcs() {
+ return charProcs.isDict() ? charProcs.getDict() : (Dict *)NULL;
+}
+
+Object *Gfx8BitFont::getCharProc(int code, Object *proc) {
+ if (enc[code] && charProcs.isDict()) {
+ charProcs.dictLookup(enc[code], proc);
+ } else {
+ proc->initNull();
+ }
+ return proc;
+}
+
+Object *Gfx8BitFont::getCharProcNF(int code, Object *proc) {
+ if (enc[code] && charProcs.isDict()) {
+ charProcs.dictLookupNF(enc[code], proc);
+ } else {
+ proc->initNull();
+ }
+ return proc;
+}
+
+Dict *Gfx8BitFont::getResources() {
+ return resources.isDict() ? resources.getDict() : (Dict *)NULL;
+}
+
+GBool Gfx8BitFont::problematicForUnicode() {
+ GString *nameLC;
+ GBool symbolic;
+
+ // potential inputs:
+ // - font is embedded (GfxFont.embFontID.num >= 0)
+ // - font name (GfxFont.name)
+ // - font type (GfxFont.type)
+ // - Base-14 font (Gfx8BitFont.base14 != NULL)
+ // - symbolic (GfxFont.flags & fontSymbolic)
+ // - has Encoding array (Gfx8BitFont.hasEncoding)
+ // - extracted base encoding from embedded font file
+ // (Gfx8BitFont.baseEncFromFontFile)
+ // - has a ToUnicode map (GfxFont.hasToUnicode)
+ // - used the numeric glyph name heuristic
+ // (Gfx8BitFont.usedNumericHeuristic)
+
+ if (name) {
+ nameLC = name->copy();
+ nameLC->lowerCase();
+ symbolic = strstr(nameLC->getCString(), "dingbat") ||
+ strstr(nameLC->getCString(), "wingding") ||
+ strstr(nameLC->getCString(), "commpi");
+ delete nameLC;
+ if (symbolic) {
+ return gFalse;
+ }
+ }
+
+ if (embFontID.num >= 0) {
+ switch (type) {
+ case fontType1:
+ case fontType1C:
+ case fontType1COT:
+ return !hasToUnicode && (!hasEncoding || usedNumericHeuristic);
+
+ case fontType3:
+ return !hasToUnicode && !hasEncoding;
+
+ case fontTrueType:
+ case fontTrueTypeOT:
+ return !hasToUnicode && !hasEncoding;
+
+ default:
+ return !hasToUnicode;
+ }
+
+ } else {
+ // NB: type will be fontTypeUnknown if the PDF specifies an
+ // invalid font type -- which is ok, if we have a ToUnicode map or
+ // an encoding
+ return !hasToUnicode && !hasEncoding;
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxCIDFont
+//------------------------------------------------------------------------
+
+GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, GString *nameA,
+ GfxFontType typeA, Ref embFontIDA, Dict *fontDict):
+ GfxFont(tagA, idA, nameA, typeA, embFontIDA)
+{
+ Dict *desFontDict;
+ Object desFontDictObj;
+ Object obj1, obj2, obj3, obj4, obj5, obj6;
+ CharCodeToUnicode *utu;
+ CharCode c;
+ Unicode uBuf[8];
+ int c1, c2;
+ int excepsSize, i, j, k, n;
+
+ missingWidth = 0;
+ ascent = 0.95;
+ descent = -0.35;
+ declaredAscent = ascent;
+ fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
+ collection = NULL;
+ cMap = NULL;
+ ctu = NULL;
+ ctuUsesCharCode = gTrue;
+ widths.defWidth = 1.0;
+ widths.defHeight = -1.0;
+ widths.defVY = 0.880;
+ widths.exceps = NULL;
+ widths.nExceps = 0;
+ widths.excepsV = NULL;
+ widths.nExcepsV = 0;
+ cidToGID = NULL;
+ cidToGIDLen = 0;
+
+ // get the descendant font
+ if (!fontDict->lookup("DescendantFonts", &obj1)->isArray() ||
+ obj1.arrayGetLength() == 0) {
+ error(errSyntaxError, -1,
+ "Missing or empty DescendantFonts entry in Type 0 font");
+ obj1.free();
+ goto err1;
+ }
+ if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) {
+ error(errSyntaxError, -1, "Bad descendant font in Type 0 font");
+ goto err2;
+ }
+ obj1.free();
+ desFontDict = desFontDictObj.getDict();
+
+ // get info from font descriptor
+ readFontDescriptor(xref, desFontDict);
+
+ //----- encoding info -----
+
+ // char collection
+ if (!desFontDict->lookup("CIDSystemInfo", &obj1)->isDict()) {
+ error(errSyntaxError, -1,
+ "Missing CIDSystemInfo dictionary in Type 0 descendant font");
+ goto err2;
+ }
+ obj1.dictLookup("Registry", &obj2);
+ obj1.dictLookup("Ordering", &obj3);
+ if (!obj2.isString() || !obj3.isString()) {
+ error(errSyntaxError, -1,
+ "Invalid CIDSystemInfo dictionary in Type 0 descendant font");
+ goto err3;
+ }
+ collection = obj2.getString()->copy()->append('-')->append(obj3.getString());
+ obj3.free();
+ obj2.free();
+ obj1.free();
+
+ // look for a ToUnicode CMap
+ hasKnownCollection = gFalse;
+ if (!(ctu = readToUnicodeCMap(fontDict, 16, NULL))) {
+ ctuUsesCharCode = gFalse;
+
+ // use an identity mapping for the "Adobe-Identity" and
+ // "Adobe-UCS" collections
+ if (!collection->cmp("Adobe-Identity") ||
+ !collection->cmp("Adobe-UCS")) {
+ ctu = CharCodeToUnicode::makeIdentityMapping();
+
+ // look for a user-supplied .cidToUnicode file
+ } else if ((ctu = globalParams->getCIDToUnicode(collection))) {
+ hasKnownCollection = gTrue;
+
+ } else {
+ error(errSyntaxError, -1,
+ "Unknown character collection '{0:t}'", collection);
+
+ // fall back to an identity mapping
+ ctu = CharCodeToUnicode::makeIdentityMapping();
+ }
+ }
+
+ // look for a Unicode-to-Unicode mapping
+ if (name && (utu = globalParams->getUnicodeToUnicode(name))) {
+ if (ctu) {
+ if (ctu->isIdentity()) {
+ ctu->decRefCnt();
+ ctu = utu;
+ } else {
+ for (c = 0; c < ctu->getLength(); ++c) {
+ n = ctu->mapToUnicode(c, uBuf, 8);
+ if (n >= 1) {
+ n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8);
+ if (n >= 1) {
+ ctu->setMapping(c, uBuf, n);
+ }
+ }
+ }
+ utu->decRefCnt();
+ }
+ } else {
+ ctu = utu;
+ }
+ }
+
+ // encoding (i.e., CMap)
+ if (fontDict->lookup("Encoding", &obj1)->isNull()) {
+ error(errSyntaxError, -1, "Missing Encoding entry in Type 0 font");
+ goto err2;
+ }
+ if (!(cMap = CMap::parse(NULL, collection, &obj1))) {
+ goto err2;
+ }
+
+ // check for fonts that use the Identity-H encoding (cmap), and the
+ // Adobe-Identity character collection
+ identityEnc = obj1.isName("Identity-H") &&
+ !collection->cmp("Adobe-Identity");
+
+ obj1.free();
+
+ // CIDToGIDMap
+ // (the PDF 1.7 spec only allows these for TrueType fonts, but
+ // Acrobat apparently also allows them for OpenType CFF fonts -- and
+ // the PDF 2.0 spec has removed the prohibition)
+ hasIdentityCIDToGID = gFalse;
+ desFontDict->lookup("CIDToGIDMap", &obj1);
+ if (obj1.isStream()) {
+ cidToGIDLen = 0;
+ i = 64;
+ cidToGID = (int *)gmallocn(i, sizeof(int));
+ obj1.streamReset();
+ while ((c1 = obj1.streamGetChar()) != EOF &&
+ (c2 = obj1.streamGetChar()) != EOF) {
+ if (cidToGIDLen == i) {
+ i *= 2;
+ cidToGID = (int *)greallocn(cidToGID, i, sizeof(int));
+ }
+ cidToGID[cidToGIDLen++] = (c1 << 8) + c2;
+ }
+ obj1.streamClose();
+ identityEnc = gFalse;
+ } else if (obj1.isName("Identity")) {
+ hasIdentityCIDToGID = gTrue;
+ } else if (!obj1.isNull()) {
+ error(errSyntaxError, -1, "Invalid CIDToGIDMap entry in CID font");
+ }
+ obj1.free();
+
+ //----- character metrics -----
+
+ // default char width
+ if (desFontDict->lookup("DW", &obj1)->isInt()) {
+ widths.defWidth = obj1.getInt() * 0.001;
+ }
+ obj1.free();
+
+ // char width exceptions
+ if (desFontDict->lookup("W", &obj1)->isArray()) {
+ excepsSize = 0;
+ i = 0;
+ while (i + 1 < obj1.arrayGetLength()) {
+ obj1.arrayGet(i, &obj2);
+ obj1.arrayGet(i + 1, &obj3);
+ if (obj2.isInt() && obj3.isInt() && i + 2 < obj1.arrayGetLength()) {
+ if (obj1.arrayGet(i + 2, &obj4)->isNum()) {
+ if (widths.nExceps == excepsSize) {
+ excepsSize += 16;
+ widths.exceps = (GfxFontCIDWidthExcep *)
+ greallocn(widths.exceps,
+ excepsSize, sizeof(GfxFontCIDWidthExcep));
+ }
+ widths.exceps[widths.nExceps].first = obj2.getInt();
+ widths.exceps[widths.nExceps].last = obj3.getInt();
+ widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001;
+ ++widths.nExceps;
+ } else {
+ error(errSyntaxError, -1, "Bad widths array in Type 0 font");
+ }
+ obj4.free();
+ i += 3;
+ } else if (obj2.isInt() && obj3.isArray()) {
+ if (widths.nExceps + obj3.arrayGetLength() > excepsSize) {
+ excepsSize = (widths.nExceps + obj3.arrayGetLength() + 15) & ~15;
+ widths.exceps = (GfxFontCIDWidthExcep *)
+ greallocn(widths.exceps,
+ excepsSize, sizeof(GfxFontCIDWidthExcep));
+ }
+ j = obj2.getInt();
+ for (k = 0; k < obj3.arrayGetLength(); ++k) {
+ if (obj3.arrayGet(k, &obj4)->isNum()) {
+ widths.exceps[widths.nExceps].first = j;
+ widths.exceps[widths.nExceps].last = j;
+ widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001;
+ ++j;
+ ++widths.nExceps;
+ } else {
+ error(errSyntaxError, -1, "Bad widths array in Type 0 font");
+ }
+ obj4.free();
+ }
+ i += 2;
+ } else {
+ error(errSyntaxError, -1, "Bad widths array in Type 0 font");
+ ++i;
+ }
+ obj3.free();
+ obj2.free();
+ }
+ }
+ obj1.free();
+
+ // default metrics for vertical font
+ if (desFontDict->lookup("DW2", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ if (obj1.arrayGet(0, &obj2)->isNum()) {
+ widths.defVY = obj2.getNum() * 0.001;
+ }
+ obj2.free();
+ if (obj1.arrayGet(1, &obj2)->isNum()) {
+ widths.defHeight = obj2.getNum() * 0.001;
+ }
+ obj2.free();
+ }
+ obj1.free();
+
+ // char metric exceptions for vertical font
+ if (desFontDict->lookup("W2", &obj1)->isArray()) {
+ excepsSize = 0;
+ i = 0;
+ while (i + 1 < obj1.arrayGetLength()) {
+ obj1.arrayGet(i, &obj2);
+ obj1.arrayGet(i+ 1, &obj3);
+ if (obj2.isInt() && obj3.isInt() && i + 4 < obj1.arrayGetLength()) {
+ if (obj1.arrayGet(i + 2, &obj4)->isNum() &&
+ obj1.arrayGet(i + 3, &obj5)->isNum() &&
+ obj1.arrayGet(i + 4, &obj6)->isNum()) {
+ if (widths.nExcepsV == excepsSize) {
+ excepsSize += 16;
+ widths.excepsV = (GfxFontCIDWidthExcepV *)
+ greallocn(widths.excepsV,
+ excepsSize, sizeof(GfxFontCIDWidthExcepV));
+ }
+ widths.excepsV[widths.nExcepsV].first = obj2.getInt();
+ widths.excepsV[widths.nExcepsV].last = obj3.getInt();
+ widths.excepsV[widths.nExcepsV].height = obj4.getNum() * 0.001;
+ widths.excepsV[widths.nExcepsV].vx = obj5.getNum() * 0.001;
+ widths.excepsV[widths.nExcepsV].vy = obj6.getNum() * 0.001;
+ ++widths.nExcepsV;
+ } else {
+ error(errSyntaxError, -1, "Bad widths (W2) array in Type 0 font");
+ }
+ obj6.free();
+ obj5.free();
+ obj4.free();
+ i += 5;
+ } else if (obj2.isInt() && obj3.isArray()) {
+ if (widths.nExcepsV + obj3.arrayGetLength() / 3 > excepsSize) {
+ excepsSize =
+ (widths.nExcepsV + obj3.arrayGetLength() / 3 + 15) & ~15;
+ widths.excepsV = (GfxFontCIDWidthExcepV *)
+ greallocn(widths.excepsV,
+ excepsSize, sizeof(GfxFontCIDWidthExcepV));
+ }
+ j = obj2.getInt();
+ for (k = 0; k + 2 < obj3.arrayGetLength(); k += 3) {
+ if (obj3.arrayGet(k, &obj4)->isNum() &&
+ obj3.arrayGet(k+1, &obj5)->isNum() &&
+ obj3.arrayGet(k+2, &obj6)->isNum()) {
+ widths.excepsV[widths.nExcepsV].first = j;
+ widths.excepsV[widths.nExcepsV].last = j;
+ widths.excepsV[widths.nExcepsV].height = obj4.getNum() * 0.001;
+ widths.excepsV[widths.nExcepsV].vx = obj5.getNum() * 0.001;
+ widths.excepsV[widths.nExcepsV].vy = obj6.getNum() * 0.001;
+ ++j;
+ ++widths.nExcepsV;
+ } else {
+ error(errSyntaxError, -1, "Bad widths (W2) array in Type 0 font");
+ }
+ obj6.free();
+ obj5.free();
+ obj4.free();
+ }
+ i += 2;
+ } else {
+ error(errSyntaxError, -1, "Bad widths (W2) array in Type 0 font");
+ ++i;
+ }
+ obj3.free();
+ obj2.free();
+ }
+ }
+ obj1.free();
+
+ desFontDictObj.free();
+ ok = gTrue;
+ return;
+
+ err3:
+ obj3.free();
+ obj2.free();
+ err2:
+ obj1.free();
+ desFontDictObj.free();
+ err1:
+ error(errSyntaxError, -1, "Failed to parse font object for '{0:t}'", name);
+}
+
+GfxCIDFont::~GfxCIDFont() {
+ if (collection) {
+ delete collection;
+ }
+ if (cMap) {
+ cMap->decRefCnt();
+ }
+ if (ctu) {
+ ctu->decRefCnt();
+ }
+ gfree(widths.exceps);
+ gfree(widths.excepsV);
+ if (cidToGID) {
+ gfree(cidToGID);
+ }
+}
+
+int GfxCIDFont::getNextChar(char *s, int len, CharCode *code,
+ Unicode *u, int uSize, int *uLen,
+ double *dx, double *dy, double *ox, double *oy) {
+ CID cid;
+ CharCode c;
+ int n;
+
+ if (!cMap) {
+ *code = 0;
+ *uLen = 0;
+ *dx = *dy = 0;
+ return 1;
+ }
+
+ *code = (CharCode)(cid = cMap->getCID(s, len, &c, &n));
+ if (ctu) {
+ *uLen = ctu->mapToUnicode(ctuUsesCharCode ? c : cid, u, uSize);
+ } else {
+ *uLen = 0;
+ }
+ if (!*uLen && uSize >= 1 && globalParams->getMapUnknownCharNames()) {
+ u[0] = *code;
+ *uLen = 1;
+ }
+
+ // horizontal
+ if (cMap->getWMode() == 0) {
+ getHorizontalMetrics(cid, dx);
+ *dy = *ox = *oy = 0;
+
+ // vertical
+ } else {
+ getVerticalMetrics(cid, dy, ox, oy);
+ *dx = 0;
+ }
+
+ return n;
+}
+
+// NB: Section 9.7.4.3 in the PDF 2.0 spec says that, in the case of
+// duplicate entries in the metrics, the first entry should be used.
+// This means we need to leave the metrics in the original order and
+// perform a linear search. (Or use a more complex data structure.)
+void GfxCIDFont::getHorizontalMetrics(CID cid, double *w) {
+ int i;
+ for (i = 0; i < widths.nExceps; ++i) {
+ if (widths.exceps[i].first <= cid && cid <= widths.exceps[i].last) {
+ *w = widths.exceps[i].width;
+ return;
+ }
+ }
+ *w = widths.defWidth;
+}
+
+// NB: Section 9.7.4.3 in the PDF 2.0 spec says that, in the case of
+// duplicate entries in the metrics, the first entry should be used.
+// This means we need to leave the metrics in the original order and
+// perform a linear search. (Or use a more complex data structure.)
+void GfxCIDFont::getVerticalMetrics(CID cid, double *h,
+ double *vx, double *vy) {
+ int i;
+ for (i = 0; i < widths.nExcepsV; ++i) {
+ if (widths.excepsV[i].first <= cid && cid <= widths.excepsV[i].last) {
+ *h = widths.excepsV[i].height;
+ *vx = widths.excepsV[i].vx;
+ *vy = widths.excepsV[i].vy;
+ return;
+ }
+ }
+ *h = widths.defHeight;
+ getHorizontalMetrics(cid, vx);
+ *vx /= 2;
+ *vy = widths.defVY;
+}
+
+int GfxCIDFont::getWMode() {
+ return cMap ? cMap->getWMode() : 0;
+}
+
+CharCodeToUnicode *GfxCIDFont::getToUnicode() {
+ if (ctu) {
+ ctu->incRefCnt();
+ }
+ return ctu;
+}
+
+GString *GfxCIDFont::getCollection() {
+ return cMap ? cMap->getCollection() : (GString *)NULL;
+}
+
+double GfxCIDFont::getWidth(CID cid) {
+ double w;
+
+ getHorizontalMetrics(cid, &w);
+ return w;
+}
+
+GBool GfxCIDFont::problematicForUnicode() {
+ GString *nameLC;
+ GBool symbolic;
+
+ // potential inputs:
+ // - font is embedded (GfxFont.embFontID.num >= 0)
+ // - font name (GfxFont.name)
+ // - font type (GfxFont.type)
+ // - symbolic (GfxFont.flags & fontSymbolic)
+ // - has a ToUnicode map (GfxFont.hasToUnicode)
+ // - collection is Adobe-Identity or Adobe-UCS
+ // (GfxCIDFont.collection - compare string)
+ // - collection is known AdobeCJK (GfxCIDFont.hasKnownCollection)
+ // - has non-Identity CIDToGIDMap (GfxCIDFont.cidToGID != NULL)
+ // - has Identity CIDToGIDMap (GfxCIDFont.hasIdentityCIDToGID)
+
+ if (name) {
+ nameLC = name->copy();
+ nameLC->lowerCase();
+ symbolic = strstr(nameLC->getCString(), "dingbat") ||
+ strstr(nameLC->getCString(), "wingding") ||
+ strstr(nameLC->getCString(), "commpi");
+ delete nameLC;
+ if (symbolic) {
+ return gFalse;
+ }
+ }
+
+ if (embFontID.num >= 0) {
+ switch (type) {
+ case fontCIDType0:
+ case fontCIDType0C:
+ case fontCIDType0COT:
+ return !hasToUnicode && !hasKnownCollection;
+
+ case fontCIDType2:
+ case fontCIDType2OT:
+ return !hasToUnicode && !hasKnownCollection;
+
+ default:
+ return !hasToUnicode;
+ }
+
+ } else {
+ return !hasToUnicode;
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxFontDict
+//------------------------------------------------------------------------
+
+GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) {
+ GfxFont *font;
+ char *tag;
+ Object obj1, obj2;
+ Ref r;
+ int i;
+
+ fonts = new GHash(gTrue);
+ uniqueFonts = new GList();
+ for (i = 0; i < fontDict->getLength(); ++i) {
+ tag = fontDict->getKey(i);
+ fontDict->getValNF(i, &obj1);
+ obj1.fetch(xref, &obj2);
+ if (!obj2.isDict()) {
+ error(errSyntaxError, -1, "font resource is not a dictionary");
+ } else if (obj1.isRef() && (font = lookupByRef(obj1.getRef()))) {
+ fonts->add(new GString(tag), font);
+ } else {
+ if (obj1.isRef()) {
+ r = obj1.getRef();
+ } else if (fontDictRef) {
+ // legal generation numbers are five digits, so we use a
+ // 6-digit number here
+ r.gen = 100000 + fontDictRef->num;
+ r.num = i;
+ } else {
+ // no indirect reference for this font, or for the containing
+ // font dict, so hash the font and use that
+ r.gen = 100000;
+ r.num = hashFontObject(&obj2);
+ }
+ if ((font = GfxFont::makeFont(xref, tag, r, obj2.getDict()))) {
+ if (!font->isOk()) {
+ delete font;
+ } else {
+ uniqueFonts->append(font);
+ fonts->add(new GString(tag), font);
+ }
+ }
+ }
+ obj1.free();
+ obj2.free();
+ }
+}
+
+GfxFontDict::~GfxFontDict() {
+ deleteGList(uniqueFonts, GfxFont);
+ delete fonts;
+}
+
+GfxFont *GfxFontDict::lookup(char *tag) {
+ return (GfxFont *)fonts->lookup(tag);
+}
+
+GfxFont *GfxFontDict::lookupByRef(Ref ref) {
+ GfxFont *font;
+ int i;
+
+ for (i = 0; i < uniqueFonts->getLength(); ++i) {
+ font = (GfxFont *)uniqueFonts->get(i);
+ if (font->getID()->num == ref.num &&
+ font->getID()->gen == ref.gen) {
+ return font;
+ }
+ }
+ return NULL;
+}
+
+int GfxFontDict::getNumFonts() {
+ return uniqueFonts->getLength();
+}
+
+GfxFont *GfxFontDict::getFont(int i) {
+ return (GfxFont *)uniqueFonts->get(i);
+}
+
+// FNV-1a hash
+class FNVHash {
+public:
+
+ FNVHash() {
+ h = 2166136261U;
+ }
+
+ void hash(char c) {
+ h ^= c & 0xff;
+ h *= 16777619;
+ }
+
+ void hash(char *p, int n) {
+ int i;
+ for (i = 0; i < n; ++i) {
+ hash(p[i]);
+ }
+ }
+
+ int get31() {
+ return (h ^ (h >> 31)) & 0x7fffffff;
+ }
+
+private:
+
+ Guint h;
+};
+
+int GfxFontDict::hashFontObject(Object *obj) {
+ FNVHash h;
+
+ hashFontObject1(obj, &h);
+ return h.get31();
+}
+
+void GfxFontDict::hashFontObject1(Object *obj, FNVHash *h) {
+ Object obj2;
+ GString *s;
+ char *p;
+ double r;
+ int n, i;
+
+ switch (obj->getType()) {
+ case objBool:
+ h->hash('b');
+ h->hash(obj->getBool() ? 1 : 0);
+ break;
+ case objInt:
+ h->hash('i');
+ n = obj->getInt();
+ h->hash((char *)&n, sizeof(int));
+ break;
+ case objReal:
+ h->hash('r');
+ r = obj->getReal();
+ h->hash((char *)&r, sizeof(double));
+ break;
+ case objString:
+ h->hash('s');
+ s = obj->getString();
+ h->hash(s->getCString(), s->getLength());
+ break;
+ case objName:
+ h->hash('n');
+ p = obj->getName();
+ h->hash(p, (int)strlen(p));
+ break;
+ case objNull:
+ h->hash('z');
+ break;
+ case objArray:
+ h->hash('a');
+ n = obj->arrayGetLength();
+ h->hash((char *)&n, sizeof(int));
+ for (i = 0; i < n; ++i) {
+ obj->arrayGetNF(i, &obj2);
+ hashFontObject1(&obj2, h);
+ obj2.free();
+ }
+ break;
+ case objDict:
+ h->hash('d');
+ n = obj->dictGetLength();
+ h->hash((char *)&n, sizeof(int));
+ for (i = 0; i < n; ++i) {
+ p = obj->dictGetKey(i);
+ h->hash(p, (int)strlen(p));
+ obj->dictGetValNF(i, &obj2);
+ hashFontObject1(&obj2, h);
+ obj2.free();
+ }
+ break;
+ case objStream:
+ // this should never happen - streams must be indirect refs
+ break;
+ case objRef:
+ h->hash('f');
+ n = obj->getRefNum();
+ h->hash((char *)&n, sizeof(int));
+ n = obj->getRefGen();
+ h->hash((char *)&n, sizeof(int));
+ break;
+ default:
+ h->hash('u');
+ break;
+ }
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h
new file mode 100644
index 00000000000..ded34bc1ee7
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxFont.h
@@ -0,0 +1,410 @@
+//========================================================================
+//
+// GfxFont.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GFXFONT_H
+#define GFXFONT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "GString.h"
+#include "Object.h"
+#include "CharTypes.h"
+
+class GList;
+class GHash;
+class Dict;
+class CMap;
+class CharCodeToUnicode;
+class FoFiTrueType;
+class FoFiType1C;
+struct GfxFontCIDWidths;
+struct Base14FontMapEntry;
+class FNVHash;
+
+//------------------------------------------------------------------------
+// GfxFontType
+//------------------------------------------------------------------------
+
+enum GfxFontType {
+ //----- Gfx8BitFont
+ fontUnknownType,
+ fontType1,
+ fontType1C,
+ fontType1COT,
+ fontType3,
+ fontTrueType,
+ fontTrueTypeOT,
+ //----- GfxCIDFont
+ fontCIDType0,
+ fontCIDType0C,
+ fontCIDType0COT,
+ fontCIDType2,
+ fontCIDType2OT
+};
+
+//------------------------------------------------------------------------
+// GfxFontCIDWidths
+//------------------------------------------------------------------------
+
+struct GfxFontCIDWidthExcep {
+ CID first; // this record applies to
+ CID last; // CIDs <first>..<last>
+ double width; // char width
+};
+
+struct GfxFontCIDWidthExcepV {
+ CID first; // this record applies to
+ CID last; // CIDs <first>..<last>
+ double height; // char height
+ double vx, vy; // origin position
+};
+
+struct GfxFontCIDWidths {
+ double defWidth; // default char width
+ double defHeight; // default char height
+ double defVY; // default origin position
+ GfxFontCIDWidthExcep *exceps; // exceptions
+ int nExceps; // number of valid entries in exceps
+ GfxFontCIDWidthExcepV * // exceptions for vertical font
+ excepsV;
+ int nExcepsV; // number of valid entries in excepsV
+};
+
+//------------------------------------------------------------------------
+// GfxFontLoc
+//------------------------------------------------------------------------
+
+enum GfxFontLocType {
+ gfxFontLocEmbedded, // font embedded in PDF file
+ gfxFontLocExternal, // external font file
+ gfxFontLocResident // font resident in PS printer
+};
+
+class GfxFontLoc {
+public:
+
+ GfxFontLoc();
+ ~GfxFontLoc();
+
+ GfxFontLocType locType;
+ GfxFontType fontType;
+ Ref embFontID; // embedded stream obj ID
+ // (if locType == gfxFontLocEmbedded)
+ GString *path; // font file path
+ // (if locType == gfxFontLocExternal)
+ // PS font name
+ // (if locType == gfxFontLocResident)
+ int fontNum; // for TrueType collections and Mac dfonts
+ // (if locType == gfxFontLocExternal)
+ double oblique; // sheer factor to oblique this font
+ // (used when substituting a plain
+ // font for an oblique font)
+ GString *encoding; // PS font encoding, only for 16-bit fonts
+ // (if locType == gfxFontLocResident)
+ int wMode; // writing mode, only for 16-bit fonts
+ // (if locType == gfxFontLocResident)
+ int substIdx; // substitute font index
+ // (if locType == gfxFontLocExternal,
+ // and a Base-14 substitution was made)
+};
+
+//------------------------------------------------------------------------
+// GfxFont
+//------------------------------------------------------------------------
+
+#define fontFixedWidth (1 << 0)
+#define fontSerif (1 << 1)
+#define fontSymbolic (1 << 2)
+#define fontItalic (1 << 6)
+#define fontBold (1 << 18)
+
+class GfxFont {
+public:
+
+ // Build a GfxFont object.
+ static GfxFont *makeFont(XRef *xref, const char *tagA,
+ Ref idA, Dict *fontDict);
+
+ GfxFont(const char *tagA, Ref idA, GString *nameA,
+ GfxFontType typeA, Ref embFontIDA);
+
+ virtual ~GfxFont();
+
+ GBool isOk() { return ok; }
+
+ // Get font tag.
+ GString *getTag() { return tag; }
+
+ // Get font dictionary ID.
+ Ref *getID() { return &id; }
+
+ // Does this font match the tag?
+ GBool matches(char *tagA) { return !tag->cmp(tagA); }
+
+ // Get the original font name (ignornig any munging that might have
+ // been done to map to a canonical Base-14 font name).
+ GString *getName() { return name; }
+
+ // Get font type.
+ GfxFontType getType() { return type; }
+ virtual GBool isCIDFont() { return gFalse; }
+
+ // Get embedded font ID, i.e., a ref for the font file stream.
+ // Returns false if there is no embedded font.
+ GBool getEmbeddedFontID(Ref *embID)
+ { *embID = embFontID; return embFontID.num >= 0; }
+
+ // Get the PostScript font name for the embedded font. Returns
+ // NULL if there is no embedded font.
+ GString *getEmbeddedFontName() { return embFontName; }
+
+ // Get font descriptor flags.
+ int getFlags() { return flags; }
+ GBool isFixedWidth() { return flags & fontFixedWidth; }
+ GBool isSerif() { return flags & fontSerif; }
+ GBool isSymbolic() { return flags & fontSymbolic; }
+ GBool isItalic() { return flags & fontItalic; }
+ GBool isBold() { return flags & fontBold; }
+
+ // Return the font matrix.
+ double *getFontMatrix() { return fontMat; }
+
+ // Return the font bounding box.
+ double *getFontBBox() { return fontBBox; }
+
+ // 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; }
+
+ // Locate the font file for this font. If <ps> is true, includes PS
+ // printer-resident fonts. Returns NULL on failure.
+ GfxFontLoc *locateFont(XRef *xref, GBool ps);
+
+ // Locate a Base-14 font file for a specified font name.
+ static GfxFontLoc *locateBase14Font(GString *base14Name);
+
+ // Read an embedded font file into a buffer.
+ char *readEmbFontFile(XRef *xref, int *len);
+
+ // Get the next char from a string <s> of <len> bytes, returning the
+ // char <code>, its Unicode mapping <u>, its displacement vector
+ // (<dx>, <dy>), and its origin offset vector (<ox>, <oy>). <uSize>
+ // is the number of entries available in <u>, and <uLen> is set to
+ // the number actually used. Returns the number of bytes used by
+ // the char code.
+ virtual int getNextChar(char *s, int len, CharCode *code,
+ Unicode *u, int uSize, int *uLen,
+ double *dx, double *dy, double *ox, double *oy) = 0;
+
+ // Returns true if this font is likely to be problematic when
+ // converting text to Unicode.
+ virtual GBool problematicForUnicode() = 0;
+
+protected:
+
+ static GfxFontType getFontType(XRef *xref, Dict *fontDict, Ref *embID);
+ void readFontDescriptor(XRef *xref, Dict *fontDict);
+ CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits,
+ CharCodeToUnicode *ctu);
+ static GfxFontLoc *getExternalFont(GString *path, int fontNum,
+ double oblique, GBool cid);
+
+ GString *tag; // PDF font tag
+ Ref id; // reference (used as unique ID)
+ GString *name; // font name
+ GfxFontType type; // type of font
+ int flags; // font descriptor flags
+ GString *embFontName; // name of embedded font
+ Ref embFontID; // ref to embedded font file stream
+ double fontMat[6]; // font matrix
+ double fontBBox[4]; // font bounding box
+ 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;
+};
+
+//------------------------------------------------------------------------
+// Gfx8BitFont
+//------------------------------------------------------------------------
+
+class Gfx8BitFont: public GfxFont {
+public:
+
+ Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, GString *nameA,
+ GfxFontType typeA, Ref embFontIDA, Dict *fontDict);
+
+ virtual ~Gfx8BitFont();
+
+ virtual int getNextChar(char *s, int len, CharCode *code,
+ Unicode *u, int uSize, int *uLen,
+ double *dx, double *dy, double *ox, double *oy);
+
+ // Return the encoding.
+ char **getEncoding() { return enc; }
+
+ // Return the Unicode map.
+ CharCodeToUnicode *getToUnicode();
+
+ // Return the character name associated with <code>.
+ char *getCharName(int code) { return enc[code]; }
+
+ // Returns true if the PDF font specified an encoding.
+ GBool getHasEncoding() { return hasEncoding; }
+
+ // Returns true if the PDF font specified MacRomanEncoding.
+ GBool getUsesMacRomanEnc() { return usesMacRomanEnc; }
+
+ // Get width of a character.
+ double getWidth(Guchar c) { return widths[c]; }
+
+ // Return a char code-to-GID mapping for the provided font file.
+ // (This is only useful for TrueType fonts.)
+ int *getCodeToGIDMap(FoFiTrueType *ff);
+
+ // Return a char code-to-GID mapping for the provided font file.
+ // (This is only useful for Type1C fonts.)
+ int *getCodeToGIDMap(FoFiType1C *ff);
+
+ // Return the Type 3 CharProc dictionary, or NULL if none.
+ Dict *getCharProcs();
+
+ // Return the Type 3 CharProc for the character associated with <code>.
+ Object *getCharProc(int code, Object *proc);
+ Object *getCharProcNF(int code, Object *proc);
+
+ // Return the Type 3 Resources dictionary, or NULL if none.
+ Dict *getResources();
+
+ virtual GBool problematicForUnicode();
+
+private:
+
+ Base14FontMapEntry *base14; // for Base-14 fonts only; NULL otherwise
+ char *enc[256]; // char code --> char name
+ char encFree[256]; // boolean for each char name: if set,
+ // the string is malloc'ed
+ CharCodeToUnicode *ctu; // char code --> Unicode
+ GBool hasEncoding;
+ GBool usesMacRomanEnc;
+ GBool baseEncFromFontFile;
+ GBool usedNumericHeuristic;
+ double widths[256]; // character widths
+ Object charProcs; // Type 3 CharProcs dictionary
+ Object resources; // Type 3 Resources dictionary
+
+ friend class GfxFont;
+};
+
+//------------------------------------------------------------------------
+// GfxCIDFont
+//------------------------------------------------------------------------
+
+class GfxCIDFont: public GfxFont {
+public:
+
+ GfxCIDFont(XRef *xref, const char *tagA, Ref idA, GString *nameA,
+ GfxFontType typeA, Ref embFontIDA, Dict *fontDict);
+
+ virtual ~GfxCIDFont();
+
+ virtual GBool isCIDFont() { return gTrue; }
+
+ virtual int getNextChar(char *s, int len, CharCode *code,
+ Unicode *u, int uSize, int *uLen,
+ double *dx, double *dy, double *ox, double *oy);
+
+ // Return the writing mode (0=horizontal, 1=vertical).
+ virtual int getWMode();
+
+ // Return the Unicode map.
+ CharCodeToUnicode *getToUnicode();
+
+ // Get the collection name (<registry>-<ordering>).
+ GString *getCollection();
+
+ // Return the horizontal width for <cid>.
+ double getWidth(CID cid);
+
+ // Return the CID-to-GID mapping table. These should only be called
+ // if type is fontCIDType2.
+ int *getCIDToGID() { return cidToGID; }
+ int getCIDToGIDLen() { return cidToGIDLen; }
+
+ // Returns true if this font uses the Identity-H encoding (cmap),
+ // and the Adobe-Identity character collection, and does not have a
+ // CIDToGIDMap. When this is true for a CID TrueType font, Adobe
+ // appears to treat char codes as raw GIDs.
+ GBool usesIdentityEncoding() { return identityEnc; }
+
+ virtual GBool problematicForUnicode();
+
+private:
+
+ void getHorizontalMetrics(CID cid, double *w);
+ void getVerticalMetrics(CID cid, double *h,
+ double *vx, double *vy);
+
+ GString *collection; // collection name
+ CMap *cMap; // char code --> CID
+ CharCodeToUnicode *ctu; // CID/char code --> Unicode
+ GBool ctuUsesCharCode; // true: ctu maps char code to Unicode;
+ // false: ctu maps CID to Unicode
+ GfxFontCIDWidths widths; // character widths
+ int *cidToGID; // CID --> GID mapping (for embedded
+ // TrueType fonts)
+ int cidToGIDLen;
+ GBool hasKnownCollection;
+ GBool hasIdentityCIDToGID;
+ GBool identityEnc;
+};
+
+//------------------------------------------------------------------------
+// GfxFontDict
+//------------------------------------------------------------------------
+
+class GfxFontDict {
+public:
+
+ // Build the font dictionary, given the PDF font dictionary.
+ GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict);
+
+ // Destructor.
+ ~GfxFontDict();
+
+ // Get the specified font.
+ GfxFont *lookup(char *tag);
+ GfxFont *lookupByRef(Ref ref);
+
+ // Iterative access.
+ int getNumFonts();
+ GfxFont *getFont(int i);
+
+private:
+
+ int hashFontObject(Object *obj);
+ void hashFontObject1(Object *obj, FNVHash *h);
+
+ GHash *fonts; // hash table of fonts -- this may
+ // include duplicates, i.e., when
+ // two tags map to the same font
+ GList *uniqueFonts; // list of all unique font objects (no dups)
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc
new file mode 100644
index 00000000000..5e9991e1b97
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.cc
@@ -0,0 +1,4778 @@
+//========================================================================
+//
+// GfxState.cc
+//
+// Copyright 1996-2016 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include <math.h>
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "Object.h"
+#include "Array.h"
+#include "Page.h"
+#include "XRef.h"
+#include "GfxState.h"
+
+//------------------------------------------------------------------------
+
+// Max depth of nested color spaces. This is used to catch infinite
+// loops in the color space object structure.
+#define colorSpaceRecursionLimit 8
+
+
+//------------------------------------------------------------------------
+
+static inline GfxColorComp clip01(GfxColorComp x) {
+ return (x < 0) ? 0 : (x > gfxColorComp1) ? gfxColorComp1 : x;
+}
+
+static inline double clip01(double x) {
+ return (x < 0) ? 0 : (x > 1) ? 1 : x;
+}
+
+//------------------------------------------------------------------------
+
+struct GfxBlendModeInfo {
+ const char *name;
+ GfxBlendMode mode;
+};
+
+static GfxBlendModeInfo gfxBlendModeNames[] = {
+ { "Normal", gfxBlendNormal },
+ { "Compatible", gfxBlendNormal },
+ { "Multiply", gfxBlendMultiply },
+ { "Screen", gfxBlendScreen },
+ { "Overlay", gfxBlendOverlay },
+ { "Darken", gfxBlendDarken },
+ { "Lighten", gfxBlendLighten },
+ { "ColorDodge", gfxBlendColorDodge },
+ { "ColorBurn", gfxBlendColorBurn },
+ { "HardLight", gfxBlendHardLight },
+ { "SoftLight", gfxBlendSoftLight },
+ { "Difference", gfxBlendDifference },
+ { "Exclusion", gfxBlendExclusion },
+ { "Hue", gfxBlendHue },
+ { "Saturation", gfxBlendSaturation },
+ { "Color", gfxBlendColor },
+ { "Luminosity", gfxBlendLuminosity }
+};
+
+#define nGfxBlendModeNames \
+ ((int)((sizeof(gfxBlendModeNames) / sizeof(GfxBlendModeInfo))))
+
+//------------------------------------------------------------------------
+
+// NB: This must match the GfxColorSpaceMode enum defined in
+// GfxState.h
+static const char *gfxColorSpaceModeNames[] = {
+ "DeviceGray",
+ "CalGray",
+ "DeviceRGB",
+ "CalRGB",
+ "DeviceCMYK",
+ "Lab",
+ "ICCBased",
+ "Indexed",
+ "Separation",
+ "DeviceN",
+ "Pattern"
+};
+
+#define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))
+
+
+
+
+//------------------------------------------------------------------------
+// GfxColorSpace
+//------------------------------------------------------------------------
+
+GfxColorSpace::GfxColorSpace() {
+ overprintMask = 0x0f;
+ defaultColorSpace = gFalse;
+}
+
+GfxColorSpace::~GfxColorSpace() {
+}
+
+GfxColorSpace *GfxColorSpace::parse(Object *csObj,
+ int recursion) {
+ GfxColorSpace *cs;
+ Object obj1;
+
+ if (recursion > colorSpaceRecursionLimit) {
+ error(errSyntaxError, -1, "Loop detected in color space objects");
+ return NULL;
+ }
+ cs = NULL;
+ if (csObj->isName()) {
+ if (csObj->isName("DeviceGray") || csObj->isName("G")) {
+ cs = GfxColorSpace::create(csDeviceGray);
+ } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
+ cs = GfxColorSpace::create(csDeviceRGB);
+ } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
+ cs = GfxColorSpace::create(csDeviceCMYK);
+ } else if (csObj->isName("Pattern")) {
+ cs = new GfxPatternColorSpace(NULL);
+ } else {
+ error(errSyntaxError, -1, "Bad color space '{0:s}'", csObj->getName());
+ }
+ } else if (csObj->isArray() && csObj->arrayGetLength() > 0) {
+ csObj->arrayGet(0, &obj1);
+ if (obj1.isName("DeviceGray") || obj1.isName("G")) {
+ cs = GfxColorSpace::create(csDeviceGray);
+ } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
+ cs = GfxColorSpace::create(csDeviceRGB);
+ } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
+ cs = GfxColorSpace::create(csDeviceCMYK);
+ } else if (obj1.isName("CalGray")) {
+ cs = GfxCalGrayColorSpace::parse(csObj->getArray(), recursion);
+ } else if (obj1.isName("CalRGB")) {
+ cs = GfxCalRGBColorSpace::parse(csObj->getArray(), recursion);
+ } else if (obj1.isName("Lab")) {
+ cs = GfxLabColorSpace::parse(csObj->getArray(), recursion);
+ } else if (obj1.isName("ICCBased")) {
+ cs = GfxICCBasedColorSpace::parse(csObj->getArray(),
+ recursion);
+ } else if (obj1.isName("Indexed") || obj1.isName("I")) {
+ cs = GfxIndexedColorSpace::parse(csObj->getArray(),
+ recursion);
+ } else if (obj1.isName("Separation")) {
+ cs = GfxSeparationColorSpace::parse(csObj->getArray(),
+ recursion);
+ } else if (obj1.isName("DeviceN")) {
+ cs = GfxDeviceNColorSpace::parse(csObj->getArray(),
+ recursion);
+ } else if (obj1.isName("Pattern")) {
+ cs = GfxPatternColorSpace::parse(csObj->getArray(),
+ recursion);
+ } else {
+ error(errSyntaxError, -1, "Bad color space");
+ }
+ obj1.free();
+ } else {
+ error(errSyntaxError, -1, "Bad color space - expected name or array");
+ }
+ return cs;
+}
+
+GfxColorSpace *GfxColorSpace::create(GfxColorSpaceMode mode) {
+ GfxColorSpace *cs;
+
+ cs = NULL;
+ if (mode == csDeviceGray) {
+ cs = new GfxDeviceGrayColorSpace();
+ } else if (mode == csDeviceRGB) {
+ cs = new GfxDeviceRGBColorSpace();
+ } else if (mode == csDeviceCMYK) {
+ cs = new GfxDeviceCMYKColorSpace();
+ }
+ return cs;
+}
+
+void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
+ int maxImgPixel) {
+ int i;
+
+ for (i = 0; i < getNComps(); ++i) {
+ decodeLow[i] = 0;
+ decodeRange[i] = 1;
+ }
+}
+
+int GfxColorSpace::getNumColorSpaceModes() {
+ return nGfxColorSpaceModes;
+}
+
+const char *GfxColorSpace::getColorSpaceModeName(int idx) {
+ return gfxColorSpaceModeNames[idx];
+}
+
+//------------------------------------------------------------------------
+// GfxDeviceGrayColorSpace
+//------------------------------------------------------------------------
+
+GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() {
+}
+
+GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
+}
+
+GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
+ GfxDeviceGrayColorSpace *cs;
+
+ cs = new GfxDeviceGrayColorSpace();
+ return cs;
+}
+
+
+void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray,
+ GfxRenderingIntent ri) {
+ *gray = clip01(color->c[0]);
+}
+
+void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb,
+ GfxRenderingIntent ri) {
+ rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
+}
+
+void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) {
+ cmyk->c = cmyk->m = cmyk->y = 0;
+ cmyk->k = clip01(gfxColorComp1 - color->c[0]);
+}
+
+
+
+void GfxDeviceGrayColorSpace::getDefaultColor(GfxColor *color) {
+ color->c[0] = 0;
+}
+
+//------------------------------------------------------------------------
+// GfxCalGrayColorSpace
+//------------------------------------------------------------------------
+
+GfxCalGrayColorSpace::GfxCalGrayColorSpace() {
+ whiteX = whiteY = whiteZ = 1;
+ blackX = blackY = blackZ = 0;
+ gamma = 1;
+}
+
+GfxCalGrayColorSpace::~GfxCalGrayColorSpace() {
+}
+
+GfxColorSpace *GfxCalGrayColorSpace::copy() {
+ GfxCalGrayColorSpace *cs;
+
+ cs = new GfxCalGrayColorSpace();
+ cs->whiteX = whiteX;
+ cs->whiteY = whiteY;
+ cs->whiteZ = whiteZ;
+ cs->blackX = blackX;
+ cs->blackY = blackY;
+ cs->blackZ = blackZ;
+ cs->gamma = gamma;
+ return cs;
+}
+
+
+GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr, int recursion) {
+ GfxCalGrayColorSpace *cs;
+ Object obj1, obj2, obj3;
+
+ if (arr->getLength() < 2) {
+ error(errSyntaxError, -1, "Bad CalGray color space");
+ return NULL;
+ }
+ arr->get(1, &obj1);
+ if (!obj1.isDict()) {
+ error(errSyntaxError, -1, "Bad CalGray color space");
+ obj1.free();
+ return NULL;
+ }
+ cs = new GfxCalGrayColorSpace();
+ if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->whiteX = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->whiteY = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->whiteZ = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->blackX = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->blackY = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->blackZ = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("Gamma", &obj2)->isNum()) {
+ cs->gamma = obj2.getNum();
+ }
+ obj2.free();
+ obj1.free();
+ return cs;
+}
+
+void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray,
+ GfxRenderingIntent ri) {
+ *gray = clip01(color->c[0]);
+}
+
+void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb,
+ GfxRenderingIntent ri) {
+ rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
+}
+
+void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) {
+ cmyk->c = cmyk->m = cmyk->y = 0;
+ cmyk->k = clip01(gfxColorComp1 - color->c[0]);
+}
+
+
+
+void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) {
+ color->c[0] = 0;
+}
+
+//------------------------------------------------------------------------
+// GfxDeviceRGBColorSpace
+//------------------------------------------------------------------------
+
+GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() {
+}
+
+GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
+}
+
+GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
+ GfxDeviceRGBColorSpace *cs;
+
+ cs = new GfxDeviceRGBColorSpace();
+ return cs;
+}
+
+
+void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray,
+ GfxRenderingIntent ri) {
+ *gray = clip01((GfxColorComp)(0.3 * color->c[0] +
+ 0.59 * color->c[1] +
+ 0.11 * color->c[2] + 0.5));
+}
+
+void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb,
+ GfxRenderingIntent ri) {
+ rgb->r = clip01(color->c[0]);
+ rgb->g = clip01(color->c[1]);
+ rgb->b = clip01(color->c[2]);
+}
+
+void GfxDeviceRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) {
+ GfxColorComp c, m, y, k;
+
+ c = clip01(gfxColorComp1 - color->c[0]);
+ m = clip01(gfxColorComp1 - color->c[1]);
+ y = clip01(gfxColorComp1 - color->c[2]);
+ k = c;
+ if (m < k) {
+ k = m;
+ }
+ if (y < k) {
+ k = y;
+ }
+ cmyk->c = c - k;
+ cmyk->m = m - k;
+ cmyk->y = y - k;
+ cmyk->k = k;
+}
+
+
+
+void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor *color) {
+ color->c[0] = 0;
+ color->c[1] = 0;
+ color->c[2] = 0;
+}
+
+//------------------------------------------------------------------------
+// GfxCalRGBColorSpace
+//------------------------------------------------------------------------
+
+GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
+ whiteX = whiteY = whiteZ = 1;
+ blackX = blackY = blackZ = 0;
+ gammaR = gammaG = gammaB = 1;
+ mat[0] = 1; mat[1] = 0; mat[2] = 0;
+ mat[3] = 0; mat[4] = 1; mat[5] = 0;
+ mat[6] = 0; mat[7] = 0; mat[8] = 1;
+}
+
+GfxCalRGBColorSpace::~GfxCalRGBColorSpace() {
+}
+
+GfxColorSpace *GfxCalRGBColorSpace::copy() {
+ GfxCalRGBColorSpace *cs;
+ int i;
+
+ cs = new GfxCalRGBColorSpace();
+ cs->whiteX = whiteX;
+ cs->whiteY = whiteY;
+ cs->whiteZ = whiteZ;
+ cs->blackX = blackX;
+ cs->blackY = blackY;
+ cs->blackZ = blackZ;
+ cs->gammaR = gammaR;
+ cs->gammaG = gammaG;
+ cs->gammaB = gammaB;
+ for (i = 0; i < 9; ++i) {
+ cs->mat[i] = mat[i];
+ }
+ return cs;
+}
+
+GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr, int recursion) {
+ GfxCalRGBColorSpace *cs;
+ Object obj1, obj2, obj3;
+ int i;
+
+ if (arr->getLength() < 2) {
+ error(errSyntaxError, -1, "Bad CalRGB color space");
+ return NULL;
+ }
+ arr->get(1, &obj1);
+ if (!obj1.isDict()) {
+ error(errSyntaxError, -1, "Bad CalRGB color space");
+ obj1.free();
+ return NULL;
+ }
+ cs = new GfxCalRGBColorSpace();
+ if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->whiteX = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->whiteY = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->whiteZ = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->blackX = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->blackY = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->blackZ = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("Gamma", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->gammaR = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->gammaG = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->gammaB = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("Matrix", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 9) {
+ for (i = 0; i < 9; ++i) {
+ obj2.arrayGet(i, &obj3);
+ cs->mat[i] = obj3.getNum();
+ obj3.free();
+ }
+ }
+ obj2.free();
+ obj1.free();
+ return cs;
+}
+
+
+void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray,
+ GfxRenderingIntent ri) {
+ *gray = clip01((GfxColorComp)(0.299 * color->c[0] +
+ 0.587 * color->c[1] +
+ 0.114 * color->c[2] + 0.5));
+}
+
+void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb,
+ GfxRenderingIntent ri) {
+ rgb->r = clip01(color->c[0]);
+ rgb->g = clip01(color->c[1]);
+ rgb->b = clip01(color->c[2]);
+}
+
+void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) {
+ GfxColorComp c, m, y, k;
+
+ c = clip01(gfxColorComp1 - color->c[0]);
+ m = clip01(gfxColorComp1 - color->c[1]);
+ y = clip01(gfxColorComp1 - color->c[2]);
+ k = c;
+ if (m < k) {
+ k = m;
+ }
+ if (y < k) {
+ k = y;
+ }
+ cmyk->c = c - k;
+ cmyk->m = m - k;
+ cmyk->y = y - k;
+ cmyk->k = k;
+}
+
+
+
+void GfxCalRGBColorSpace::getDefaultColor(GfxColor *color) {
+ color->c[0] = 0;
+ color->c[1] = 0;
+ color->c[2] = 0;
+}
+
+//------------------------------------------------------------------------
+// GfxDeviceCMYKColorSpace
+//------------------------------------------------------------------------
+
+GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() {
+}
+
+GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
+}
+
+GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
+ GfxDeviceCMYKColorSpace *cs;
+
+ cs = new GfxDeviceCMYKColorSpace();
+ return cs;
+}
+
+
+void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray,
+ GfxRenderingIntent ri) {
+ *gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3]
+ - 0.3 * color->c[0]
+ - 0.59 * color->c[1]
+ - 0.11 * color->c[2] + 0.5));
+}
+
+void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb,
+ GfxRenderingIntent ri) {
+ double c, m, y, k, c1, m1, y1, k1, r, g, b, x;
+
+ c = colToDbl(color->c[0]);
+ m = colToDbl(color->c[1]);
+ y = colToDbl(color->c[2]);
+ k = colToDbl(color->c[3]);
+ c1 = 1 - c;
+ m1 = 1 - m;
+ y1 = 1 - y;
+ k1 = 1 - k;
+ // this is a matrix multiplication, unrolled for performance
+ // C M Y K
+ x = c1 * m1 * y1 * k1; // 0 0 0 0
+ r = g = b = x;
+ x = c1 * m1 * y1 * k; // 0 0 0 1
+ r += 0.1373 * x;
+ g += 0.1216 * x;
+ b += 0.1255 * x;
+ x = c1 * m1 * y * k1; // 0 0 1 0
+ r += x;
+ g += 0.9490 * x;
+ x = c1 * m1 * y * k; // 0 0 1 1
+ r += 0.1098 * x;
+ g += 0.1020 * x;
+ x = c1 * m * y1 * k1; // 0 1 0 0
+ r += 0.9255 * x;
+ b += 0.5490 * x;
+ x = c1 * m * y1 * k; // 0 1 0 1
+ r += 0.1412 * x;
+ x = c1 * m * y * k1; // 0 1 1 0
+ r += 0.9294 * x;
+ g += 0.1098 * x;
+ b += 0.1412 * x;
+ x = c1 * m * y * k; // 0 1 1 1
+ r += 0.1333 * x;
+ x = c * m1 * y1 * k1; // 1 0 0 0
+ g += 0.6784 * x;
+ b += 0.9373 * x;
+ x = c * m1 * y1 * k; // 1 0 0 1
+ g += 0.0588 * x;
+ b += 0.1412 * x;
+ x = c * m1 * y * k1; // 1 0 1 0
+ g += 0.6510 * x;
+ b += 0.3137 * x;
+ x = c * m1 * y * k; // 1 0 1 1
+ g += 0.0745 * x;
+ x = c * m * y1 * k1; // 1 1 0 0
+ r += 0.1804 * x;
+ g += 0.1922 * x;
+ b += 0.5725 * x;
+ x = c * m * y1 * k; // 1 1 0 1
+ b += 0.0078 * x;
+ x = c * m * y * k1; // 1 1 1 0
+ r += 0.2118 * x;
+ g += 0.2119 * x;
+ b += 0.2235 * x;
+ rgb->r = clip01(dblToCol(r));
+ rgb->g = clip01(dblToCol(g));
+ rgb->b = clip01(dblToCol(b));
+}
+
+void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) {
+ cmyk->c = clip01(color->c[0]);
+ cmyk->m = clip01(color->c[1]);
+ cmyk->y = clip01(color->c[2]);
+ cmyk->k = clip01(color->c[3]);
+}
+
+
+
+void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor *color) {
+ color->c[0] = 0;
+ color->c[1] = 0;
+ color->c[2] = 0;
+ color->c[3] = gfxColorComp1;
+}
+
+
+//------------------------------------------------------------------------
+// GfxLabColorSpace
+//------------------------------------------------------------------------
+
+// This is the inverse of MatrixLMN in Example 4.10 from the PostScript
+// Language Reference, Third Edition.
+static double xyzrgb[3][3] = {
+ { 3.240449, -1.537136, -0.498531 },
+ { -0.969265, 1.876011, 0.041556 },
+ { 0.055643, -0.204026, 1.057229 }
+};
+
+GfxLabColorSpace::GfxLabColorSpace() {
+ whiteX = whiteY = whiteZ = 1;
+ blackX = blackY = blackZ = 0;
+ aMin = bMin = -100;
+ aMax = bMax = 100;
+}
+
+GfxLabColorSpace::~GfxLabColorSpace() {
+}
+
+GfxColorSpace *GfxLabColorSpace::copy() {
+ GfxLabColorSpace *cs;
+
+ cs = new GfxLabColorSpace();
+ cs->whiteX = whiteX;
+ cs->whiteY = whiteY;
+ cs->whiteZ = whiteZ;
+ cs->blackX = blackX;
+ cs->blackY = blackY;
+ cs->blackZ = blackZ;
+ cs->aMin = aMin;
+ cs->aMax = aMax;
+ cs->bMin = bMin;
+ cs->bMax = bMax;
+ cs->kr = kr;
+ cs->kg = kg;
+ cs->kb = kb;
+ return cs;
+}
+
+GfxColorSpace *GfxLabColorSpace::parse(Array *arr, int recursion) {
+ GfxLabColorSpace *cs;
+ Object obj1, obj2, obj3;
+
+ if (arr->getLength() < 2) {
+ error(errSyntaxError, -1, "Bad Lab color space");
+ return NULL;
+ }
+ arr->get(1, &obj1);
+ if (!obj1.isDict()) {
+ error(errSyntaxError, -1, "Bad Lab color space");
+ obj1.free();
+ return NULL;
+ }
+ cs = new GfxLabColorSpace();
+ if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->whiteX = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->whiteY = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->whiteZ = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->blackX = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->blackY = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->blackZ = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("Range", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 4) {
+ obj2.arrayGet(0, &obj3);
+ cs->aMin = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->aMax = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->bMin = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(3, &obj3);
+ cs->bMax = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ obj1.free();
+
+ cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
+ xyzrgb[0][1] * cs->whiteY +
+ xyzrgb[0][2] * cs->whiteZ);
+ cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
+ xyzrgb[1][1] * cs->whiteY +
+ xyzrgb[1][2] * cs->whiteZ);
+ cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
+ xyzrgb[2][1] * cs->whiteY +
+ xyzrgb[2][2] * cs->whiteZ);
+
+ return cs;
+}
+
+
+void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray,
+ GfxRenderingIntent ri) {
+ GfxRGB rgb;
+
+ getRGB(color, &rgb, ri);
+ *gray = clip01((GfxColorComp)(0.299 * rgb.r +
+ 0.587 * rgb.g +
+ 0.114 * rgb.b + 0.5));
+}
+
+void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb,
+ GfxRenderingIntent ri) {
+ double X, Y, Z;
+ double t1, t2;
+ double r, g, b;
+
+
+ // convert L*a*b* to CIE 1931 XYZ color space
+ t1 = (colToDbl(color->c[0]) + 16) / 116;
+ t2 = t1 + colToDbl(color->c[1]) / 500;
+ if (t2 >= (6.0 / 29.0)) {
+ X = t2 * t2 * t2;
+ } else {
+ X = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
+ }
+ X *= whiteX;
+ if (t1 >= (6.0 / 29.0)) {
+ Y = t1 * t1 * t1;
+ } else {
+ Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
+ }
+ Y *= whiteY;
+ t2 = t1 - colToDbl(color->c[2]) / 200;
+ if (t2 >= (6.0 / 29.0)) {
+ Z = t2 * t2 * t2;
+ } else {
+ Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
+ }
+ Z *= whiteZ;
+
+ // convert XYZ to RGB, including gamut mapping and gamma correction
+ r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
+ g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
+ b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
+ rgb->r = dblToCol(pow(clip01(r * kr), 0.5));
+ rgb->g = dblToCol(pow(clip01(g * kg), 0.5));
+ rgb->b = dblToCol(pow(clip01(b * kb), 0.5));
+}
+
+void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) {
+ GfxRGB rgb;
+ GfxColorComp c, m, y, k;
+
+
+ getRGB(color, &rgb, ri);
+ c = clip01(gfxColorComp1 - rgb.r);
+ m = clip01(gfxColorComp1 - rgb.g);
+ y = clip01(gfxColorComp1 - rgb.b);
+ k = c;
+ if (m < k) {
+ k = m;
+ }
+ if (y < k) {
+ k = y;
+ }
+ cmyk->c = c - k;
+ cmyk->m = m - k;
+ cmyk->y = y - k;
+ cmyk->k = k;
+}
+
+
+
+void GfxLabColorSpace::getDefaultColor(GfxColor *color) {
+ color->c[0] = 0;
+ if (aMin > 0) {
+ color->c[1] = dblToCol(aMin);
+ } else if (aMax < 0) {
+ color->c[1] = dblToCol(aMax);
+ } else {
+ color->c[1] = 0;
+ }
+ if (bMin > 0) {
+ color->c[2] = dblToCol(bMin);
+ } else if (bMax < 0) {
+ color->c[2] = dblToCol(bMax);
+ } else {
+ color->c[2] = 0;
+ }
+}
+
+void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
+ int maxImgPixel) {
+ decodeLow[0] = 0;
+ decodeRange[0] = 100;
+ decodeLow[1] = aMin;
+ decodeRange[1] = aMax - aMin;
+ decodeLow[2] = bMin;
+ decodeRange[2] = bMax - bMin;
+}
+
+//------------------------------------------------------------------------
+// GfxICCBasedColorSpace
+//------------------------------------------------------------------------
+
+GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
+ Ref *iccProfileStreamA) {
+ nComps = nCompsA;
+ alt = altA;
+ iccProfileStream = *iccProfileStreamA;
+ rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0;
+ rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1;
+}
+
+GfxICCBasedColorSpace::~GfxICCBasedColorSpace() {
+ delete alt;
+}
+
+GfxColorSpace *GfxICCBasedColorSpace::copy() {
+ GfxICCBasedColorSpace *cs;
+ int i;
+
+ cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream);
+ for (i = 0; i < 4; ++i) {
+ cs->rangeMin[i] = rangeMin[i];
+ cs->rangeMax[i] = rangeMax[i];
+ }
+ return cs;
+}
+
+GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr,
+ int recursion) {
+ GfxICCBasedColorSpace *cs;
+ Ref iccProfileStreamA;
+ int nCompsA;
+ GfxColorSpace *altA;
+ Dict *dict;
+ Object obj1, obj2, obj3;
+ int i;
+
+ if (arr->getLength() < 2) {
+ error(errSyntaxError, -1, "Bad ICCBased color space");
+ return NULL;
+ }
+ arr->getNF(1, &obj1);
+ if (obj1.isRef()) {
+ iccProfileStreamA = obj1.getRef();
+ } else {
+ iccProfileStreamA.num = 0;
+ iccProfileStreamA.gen = 0;
+ }
+ obj1.free();
+ arr->get(1, &obj1);
+ if (!obj1.isStream()) {
+ error(errSyntaxError, -1, "Bad ICCBased color space (stream)");
+ obj1.free();
+ return NULL;
+ }
+ dict = obj1.streamGetDict();
+ if (!dict->lookup("N", &obj2)->isInt()) {
+ error(errSyntaxError, -1, "Bad ICCBased color space (N)");
+ obj2.free();
+ obj1.free();
+ return NULL;
+ }
+ nCompsA = obj2.getInt();
+ obj2.free();
+ if (nCompsA > 4) {
+ error(errSyntaxError, -1,
+ "ICCBased color space with too many ({0:d} > 4) components",
+ nCompsA);
+ nCompsA = 4;
+ }
+ if (dict->lookup("Alternate", &obj2)->isNull() ||
+ !(altA = GfxColorSpace::parse(&obj2,
+ recursion + 1))) {
+ switch (nCompsA) {
+ case 1:
+ altA = GfxColorSpace::create(csDeviceGray);
+ break;
+ case 3:
+ altA = GfxColorSpace::create(csDeviceRGB);
+ break;
+ case 4:
+ altA = GfxColorSpace::create(csDeviceCMYK);
+ break;
+ default:
+ error(errSyntaxError, -1, "Bad ICCBased color space - invalid N");
+ obj2.free();
+ obj1.free();
+ return NULL;
+ }
+ }
+ obj2.free();
+ cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA);
+ if (dict->lookup("Range", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 2 * nCompsA) {
+ for (i = 0; i < nCompsA; ++i) {
+ obj2.arrayGet(2*i, &obj3);
+ cs->rangeMin[i] = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2*i+1, &obj3);
+ cs->rangeMax[i] = obj3.getNum();
+ obj3.free();
+ }
+ }
+ obj2.free();
+ obj1.free();
+ return cs;
+}
+
+
+void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray,
+ GfxRenderingIntent ri) {
+ alt->getGray(color, gray, ri);
+}
+
+void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb,
+ GfxRenderingIntent ri) {
+ alt->getRGB(color, rgb, ri);
+}
+
+void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) {
+ alt->getCMYK(color, cmyk, ri);
+}
+
+
+
+void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) {
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ if (rangeMin[i] > 0) {
+ color->c[i] = dblToCol(rangeMin[i]);
+ } else if (rangeMax[i] < 0) {
+ color->c[i] = dblToCol(rangeMax[i]);
+ } else {
+ color->c[i] = 0;
+ }
+ }
+}
+
+void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow,
+ double *decodeRange,
+ int maxImgPixel) {
+ alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel);
+
+#if 0
+ // this is nominally correct, but some PDF files don't set the
+ // correct ranges in the ICCBased dict
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ decodeLow[i] = rangeMin[i];
+ decodeRange[i] = rangeMax[i] - rangeMin[i];
+ }
+#endif
+}
+
+
+//------------------------------------------------------------------------
+// GfxIndexedColorSpace
+//------------------------------------------------------------------------
+
+GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA,
+ int indexHighA) {
+ base = baseA;
+ indexHigh = indexHighA;
+ lookup = (Guchar *)gmallocn((indexHigh + 1) * base->getNComps(),
+ sizeof(Guchar));
+ overprintMask = base->getOverprintMask();
+}
+
+GfxIndexedColorSpace::~GfxIndexedColorSpace() {
+ delete base;
+ gfree(lookup);
+}
+
+GfxColorSpace *GfxIndexedColorSpace::copy() {
+ GfxIndexedColorSpace *cs;
+
+ cs = new GfxIndexedColorSpace(base->copy(), indexHigh);
+ memcpy(cs->lookup, lookup,
+ (indexHigh + 1) * base->getNComps() * sizeof(Guchar));
+ return cs;
+}
+
+GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr,
+ int recursion) {
+ GfxIndexedColorSpace *cs;
+ GfxColorSpace *baseA;
+ int indexHighA;
+ Object obj1;
+ int x;
+ char *s;
+ int n, i, j;
+
+ if (arr->getLength() != 4) {
+ error(errSyntaxError, -1, "Bad Indexed color space");
+ goto err1;
+ }
+ arr->get(1, &obj1);
+ if (!(baseA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
+ error(errSyntaxError, -1, "Bad Indexed color space (base color space)");
+ goto err2;
+ }
+ obj1.free();
+ if (!arr->get(2, &obj1)->isInt()) {
+ error(errSyntaxError, -1, "Bad Indexed color space (hival)");
+ delete baseA;
+ goto err2;
+ }
+ indexHighA = obj1.getInt();
+ if (indexHighA < 0 || indexHighA > 255) {
+ // the PDF spec requires indexHigh to be in [0,255] -- allowing
+ // values larger than 255 creates a security hole: if nComps *
+ // indexHigh is greater than 2^31, the loop below may overwrite
+ // past the end of the array
+ error(errSyntaxError, -1,
+ "Bad Indexed color space (invalid indexHigh value)");
+ delete baseA;
+ goto err2;
+ }
+ obj1.free();
+ cs = new GfxIndexedColorSpace(baseA, indexHighA);
+ arr->get(3, &obj1);
+ n = baseA->getNComps();
+ if (obj1.isStream()) {
+ obj1.streamReset();
+ for (i = 0; i <= indexHighA; ++i) {
+ for (j = 0; j < n; ++j) {
+ if ((x = obj1.streamGetChar()) == EOF) {
+ error(errSyntaxError, -1,
+ "Bad Indexed color space (lookup table stream too short)");
+ cs->indexHigh = indexHighA = i - 1;
+ if (cs->indexHigh < 0) {
+ goto err3;
+ }
+ }
+ cs->lookup[i*n + j] = (Guchar)x;
+ }
+ }
+ obj1.streamClose();
+ } else if (obj1.isString()) {
+ if (obj1.getString()->getLength() < (indexHighA + 1) * n) {
+ error(errSyntaxError, -1,
+ "Bad Indexed color space (lookup table string too short)");
+ cs->indexHigh = indexHighA = obj1.getString()->getLength() / n - 1;
+ if (cs->indexHigh < 0) {
+ goto err3;
+ }
+ }
+ s = obj1.getString()->getCString();
+ for (i = 0; i <= indexHighA; ++i) {
+ for (j = 0; j < n; ++j) {
+ cs->lookup[i*n + j] = (Guchar)*s++;
+ }
+ }
+ } else {
+ error(errSyntaxError, -1, "Bad Indexed color space (lookup table)");
+ goto err3;
+ }
+ obj1.free();
+ return cs;
+
+ err3:
+ delete cs;
+ err2:
+ obj1.free();
+ err1:
+ return NULL;
+}
+
+
+GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
+ GfxColor *baseColor) {
+ Guchar *p;
+ double low[gfxColorMaxComps], range[gfxColorMaxComps];
+ int n, i, k;
+
+ n = base->getNComps();
+ base->getDefaultRanges(low, range, indexHigh);
+ k = (int)(colToDbl(color->c[0]) + 0.5);
+ if (k < 0) {
+ k = 0;
+ } else if (k > indexHigh) {
+ k = indexHigh;
+ }
+ p = &lookup[k * n];
+ for (i = 0; i < n; ++i) {
+ baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]);
+ }
+ return baseColor;
+}
+
+void GfxIndexedColorSpace::getGray(GfxColor *color, GfxGray *gray,
+ GfxRenderingIntent ri) {
+ GfxColor color2;
+
+ base->getGray(mapColorToBase(color, &color2), gray, ri);
+}
+
+void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb,
+ GfxRenderingIntent ri) {
+ GfxColor color2;
+
+ base->getRGB(mapColorToBase(color, &color2), rgb, ri);
+}
+
+void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) {
+ GfxColor color2;
+
+ base->getCMYK(mapColorToBase(color, &color2), cmyk, ri);
+}
+
+
+
+void GfxIndexedColorSpace::getDefaultColor(GfxColor *color) {
+ color->c[0] = 0;
+}
+
+void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
+ double *decodeRange,
+ int maxImgPixel) {
+ decodeLow[0] = 0;
+ decodeRange[0] = maxImgPixel;
+}
+
+//------------------------------------------------------------------------
+// GfxSeparationColorSpace
+//------------------------------------------------------------------------
+
+GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA,
+ GfxColorSpace *altA,
+ Function *funcA) {
+ name = nameA;
+ alt = altA;
+ func = funcA;
+ nonMarking = !name->cmp("None");
+ if (!name->cmp("Cyan")) {
+ overprintMask = 0x01;
+ } else if (!name->cmp("Magenta")) {
+ overprintMask = 0x02;
+ } else if (!name->cmp("Yellow")) {
+ overprintMask = 0x04;
+ } else if (!name->cmp("Black")) {
+ overprintMask = 0x08;
+ }
+}
+
+GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA,
+ GfxColorSpace *altA,
+ Function *funcA,
+ GBool nonMarkingA,
+ Guint overprintMaskA) {
+ name = nameA;
+ alt = altA;
+ func = funcA;
+ nonMarking = nonMarkingA;
+ overprintMask = overprintMaskA;
+}
+
+GfxSeparationColorSpace::~GfxSeparationColorSpace() {
+ delete name;
+ delete alt;
+ delete func;
+}
+
+GfxColorSpace *GfxSeparationColorSpace::copy() {
+ GfxSeparationColorSpace *cs;
+
+ cs = new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
+ nonMarking, overprintMask);
+ return cs;
+}
+
+GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr,
+ int recursion) {
+ GfxSeparationColorSpace *cs;
+ GString *nameA;
+ GfxColorSpace *altA;
+ Function *funcA;
+ Object obj1, obj2;
+
+ if (arr->getLength() != 4) {
+ error(errSyntaxError, -1, "Bad Separation color space");
+ goto err1;
+ }
+ if (!arr->get(1, &obj1)->isName()) {
+ error(errSyntaxError, -1, "Bad Separation color space (name)");
+ goto err2;
+ }
+ nameA = new GString(obj1.getName());
+ obj1.free();
+ arr->get(2, &obj1);
+ // some PDF generators use an ICC profile stream here; Adobe
+ // apparently looks at the /Alternate entry in the stream dictionary
+ if (obj1.isStream() &&
+ !obj1.streamGetDict()->lookup("Alternate", &obj2)->isNull()) {
+ obj1.free();
+ obj1 = obj2;
+ }
+ if (!(altA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
+ error(errSyntaxError, -1,
+ "Bad Separation color space (alternate color space)");
+ goto err3;
+ }
+ obj1.free();
+ arr->get(3, &obj1);
+ if (!(funcA = Function::parse(&obj1, 1, altA->getNComps()))) {
+ goto err4;
+ }
+ obj1.free();
+ cs = new GfxSeparationColorSpace(nameA, altA, funcA);
+ return cs;
+
+ err4:
+ delete altA;
+ err3:
+ delete nameA;
+ err2:
+ obj1.free();
+ err1:
+ return NULL;
+}
+
+
+void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray,
+ GfxRenderingIntent ri) {
+ double x;
+ double c[gfxColorMaxComps];
+ GfxColor color2;
+ int i;
+
+ x = colToDbl(color->c[0]);
+ func->transform(&x, c);
+ for (i = 0; i < alt->getNComps(); ++i) {
+ color2.c[i] = dblToCol(c[i]);
+ }
+ alt->getGray(&color2, gray, ri);
+}
+
+void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb,
+ GfxRenderingIntent ri) {
+ double x;
+ double c[gfxColorMaxComps];
+ GfxColor color2;
+ int i;
+
+ x = colToDbl(color->c[0]);
+ func->transform(&x, c);
+ for (i = 0; i < alt->getNComps(); ++i) {
+ color2.c[i] = dblToCol(c[i]);
+ }
+ alt->getRGB(&color2, rgb, ri);
+}
+
+void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) {
+ double x;
+ double c[gfxColorMaxComps];
+ GfxColor color2;
+ int i;
+
+ x = colToDbl(color->c[0]);
+ func->transform(&x, c);
+ for (i = 0; i < alt->getNComps(); ++i) {
+ color2.c[i] = dblToCol(c[i]);
+ }
+ alt->getCMYK(&color2, cmyk, ri);
+}
+
+
+
+void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) {
+ color->c[0] = gfxColorComp1;
+}
+
+//------------------------------------------------------------------------
+// GfxDeviceNColorSpace
+//------------------------------------------------------------------------
+
+GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
+ GString **namesA,
+ GfxColorSpace *altA,
+ Function *funcA,
+ Object *attrsA) {
+ int i;
+
+ nComps = nCompsA;
+ alt = altA;
+ func = funcA;
+ attrsA->copy(&attrs);
+ nonMarking = gTrue;
+ overprintMask = 0;
+ for (i = 0; i < nComps; ++i) {
+ names[i] = namesA[i];
+ if (names[i]->cmp("None")) {
+ nonMarking = gFalse;
+ }
+ if (!names[i]->cmp("Cyan")) {
+ overprintMask |= 0x01;
+ } else if (!names[i]->cmp("Magenta")) {
+ overprintMask |= 0x02;
+ } else if (!names[i]->cmp("Yellow")) {
+ overprintMask |= 0x04;
+ } else if (!names[i]->cmp("Black")) {
+ overprintMask |= 0x08;
+ } else {
+ overprintMask = 0x0f;
+ }
+ }
+}
+
+GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
+ GString **namesA,
+ GfxColorSpace *altA,
+ Function *funcA,
+ Object *attrsA,
+ GBool nonMarkingA,
+ Guint overprintMaskA) {
+ int i;
+
+ nComps = nCompsA;
+ alt = altA;
+ func = funcA;
+ attrsA->copy(&attrs);
+ nonMarking = nonMarkingA;
+ overprintMask = overprintMaskA;
+ for (i = 0; i < nComps; ++i) {
+ names[i] = namesA[i]->copy();
+ }
+}
+
+GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ delete names[i];
+ }
+ delete alt;
+ delete func;
+ attrs.free();
+}
+
+GfxColorSpace *GfxDeviceNColorSpace::copy() {
+ GfxDeviceNColorSpace *cs;
+
+ cs = new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
+ &attrs, nonMarking, overprintMask);
+ return cs;
+}
+
+GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr,
+ int recursion) {
+ GfxDeviceNColorSpace *cs;
+ int nCompsA;
+ GString *namesA[gfxColorMaxComps];
+ GfxColorSpace *altA;
+ Function *funcA;
+ Object attrsA, obj1, obj2;
+ int i;
+
+ if (arr->getLength() != 4 && arr->getLength() != 5) {
+ error(errSyntaxError, -1, "Bad DeviceN color space");
+ goto err1;
+ }
+ if (!arr->get(1, &obj1)->isArray()) {
+ error(errSyntaxError, -1, "Bad DeviceN color space (names)");
+ goto err2;
+ }
+ nCompsA = obj1.arrayGetLength();
+ if (nCompsA > gfxColorMaxComps) {
+ error(errSyntaxError, -1,
+ "DeviceN color space with too many ({0:d} > {1:d}) components",
+ nCompsA, gfxColorMaxComps);
+ nCompsA = gfxColorMaxComps;
+ }
+ for (i = 0; i < nCompsA; ++i) {
+ if (!obj1.arrayGet(i, &obj2)->isName()) {
+ error(errSyntaxError, -1, "Bad DeviceN color space (names)");
+ obj2.free();
+ goto err2;
+ }
+ namesA[i] = new GString(obj2.getName());
+ obj2.free();
+ }
+ obj1.free();
+ arr->get(2, &obj1);
+ // some PDF generators use an ICC profile stream here; Adobe
+ // apparently looks at the /Alternate entry in the stream dictionary
+ if (obj1.isStream() &&
+ !obj1.streamGetDict()->lookup("Alternate", &obj2)->isNull()) {
+ obj1.free();
+ obj1 = obj2;
+ }
+ if (!(altA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
+ error(errSyntaxError, -1,
+ "Bad DeviceN color space (alternate color space)");
+ goto err3;
+ }
+ obj1.free();
+ arr->get(3, &obj1);
+ if (!(funcA = Function::parse(&obj1, nCompsA, altA->getNComps()))) {
+ goto err4;
+ }
+ obj1.free();
+ if (arr->getLength() == 5) {
+ arr->get(4, &attrsA);
+ } else {
+ attrsA.initNull();
+ }
+ cs = new GfxDeviceNColorSpace(nCompsA, namesA, altA, funcA, &attrsA);
+ attrsA.free();
+ return cs;
+
+ err4:
+ delete altA;
+ err3:
+ for (i = 0; i < nCompsA; ++i) {
+ delete namesA[i];
+ }
+ err2:
+ obj1.free();
+ err1:
+ return NULL;
+}
+
+
+void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray,
+ GfxRenderingIntent ri) {
+ double x[gfxColorMaxComps], c[gfxColorMaxComps];
+ GfxColor color2;
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ x[i] = colToDbl(color->c[i]);
+ }
+ func->transform(x, c);
+ for (i = 0; i < alt->getNComps(); ++i) {
+ color2.c[i] = dblToCol(c[i]);
+ }
+ alt->getGray(&color2, gray, ri);
+}
+
+void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb,
+ GfxRenderingIntent ri) {
+ double x[gfxColorMaxComps], c[gfxColorMaxComps];
+ GfxColor color2;
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ x[i] = colToDbl(color->c[i]);
+ }
+ func->transform(x, c);
+ for (i = 0; i < alt->getNComps(); ++i) {
+ color2.c[i] = dblToCol(c[i]);
+ }
+ alt->getRGB(&color2, rgb, ri);
+}
+
+void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) {
+ double x[gfxColorMaxComps], c[gfxColorMaxComps];
+ GfxColor color2;
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ x[i] = colToDbl(color->c[i]);
+ }
+ func->transform(x, c);
+ for (i = 0; i < alt->getNComps(); ++i) {
+ color2.c[i] = dblToCol(c[i]);
+ }
+ alt->getCMYK(&color2, cmyk, ri);
+}
+
+
+
+void GfxDeviceNColorSpace::getDefaultColor(GfxColor *color) {
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ color->c[i] = gfxColorComp1;
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxPatternColorSpace
+//------------------------------------------------------------------------
+
+GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) {
+ under = underA;
+}
+
+GfxPatternColorSpace::~GfxPatternColorSpace() {
+ if (under) {
+ delete under;
+ }
+}
+
+GfxColorSpace *GfxPatternColorSpace::copy() {
+ GfxPatternColorSpace *cs;
+
+ cs = new GfxPatternColorSpace(under ? under->copy() :
+ (GfxColorSpace *)NULL);
+ return cs;
+}
+
+GfxColorSpace *GfxPatternColorSpace::parse(Array *arr,
+ int recursion) {
+ GfxPatternColorSpace *cs;
+ GfxColorSpace *underA;
+ Object obj1;
+
+ if (arr->getLength() != 1 && arr->getLength() != 2) {
+ error(errSyntaxError, -1, "Bad Pattern color space");
+ return NULL;
+ }
+ underA = NULL;
+ if (arr->getLength() == 2) {
+ arr->get(1, &obj1);
+ if (!(underA = GfxColorSpace::parse(&obj1,
+ recursion + 1))) {
+ error(errSyntaxError, -1,
+ "Bad Pattern color space (underlying color space)");
+ obj1.free();
+ return NULL;
+ }
+ obj1.free();
+ }
+ cs = new GfxPatternColorSpace(underA);
+ return cs;
+}
+
+
+void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray,
+ GfxRenderingIntent ri) {
+ *gray = 0;
+}
+
+void GfxPatternColorSpace::getRGB(GfxColor *color, GfxRGB *rgb,
+ GfxRenderingIntent ri) {
+ rgb->r = rgb->g = rgb->b = 0;
+}
+
+void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) {
+ cmyk->c = cmyk->m = cmyk->y = 0;
+ cmyk->k = 1;
+}
+
+
+
+void GfxPatternColorSpace::getDefaultColor(GfxColor *color) {
+ // not used
+}
+
+//------------------------------------------------------------------------
+// Pattern
+//------------------------------------------------------------------------
+
+GfxPattern::GfxPattern(int typeA) {
+ type = typeA;
+}
+
+GfxPattern::~GfxPattern() {
+}
+
+GfxPattern *GfxPattern::parse(Object *objRef, Object *obj
+ ) {
+ GfxPattern *pattern;
+ Object typeObj;
+
+ if (obj->isDict()) {
+ obj->dictLookup("PatternType", &typeObj);
+ } else if (obj->isStream()) {
+ obj->streamGetDict()->lookup("PatternType", &typeObj);
+ } else {
+ return NULL;
+ }
+ pattern = NULL;
+ if (typeObj.isInt() && typeObj.getInt() == 1) {
+ pattern = GfxTilingPattern::parse(objRef, obj);
+ } else if (typeObj.isInt() && typeObj.getInt() == 2) {
+ pattern = GfxShadingPattern::parse(obj
+ );
+ }
+ typeObj.free();
+ return pattern;
+}
+
+//------------------------------------------------------------------------
+// GfxTilingPattern
+//------------------------------------------------------------------------
+
+GfxTilingPattern *GfxTilingPattern::parse(Object *patObjRef, Object *patObj) {
+ GfxTilingPattern *pat;
+ Dict *dict;
+ int paintTypeA, tilingTypeA;
+ double bboxA[4], matrixA[6];
+ double xStepA, yStepA;
+ Object resDictA;
+ Object obj1, obj2;
+ int i;
+
+ if (!patObj->isStream()) {
+ return NULL;
+ }
+ dict = patObj->streamGetDict();
+
+ if (dict->lookup("PaintType", &obj1)->isInt()) {
+ paintTypeA = obj1.getInt();
+ } else {
+ paintTypeA = 1;
+ error(errSyntaxWarning, -1, "Invalid or missing PaintType in pattern");
+ }
+ obj1.free();
+ if (dict->lookup("TilingType", &obj1)->isInt()) {
+ tilingTypeA = obj1.getInt();
+ } else {
+ tilingTypeA = 1;
+ error(errSyntaxWarning, -1, "Invalid or missing TilingType in pattern");
+ }
+ obj1.free();
+ bboxA[0] = bboxA[1] = 0;
+ bboxA[2] = bboxA[3] = 1;
+ if (dict->lookup("BBox", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ for (i = 0; i < 4; ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ bboxA[i] = obj2.getNum();
+ }
+ obj2.free();
+ }
+ } else {
+ error(errSyntaxError, -1, "Invalid or missing BBox in pattern");
+ }
+ obj1.free();
+ if (dict->lookup("XStep", &obj1)->isNum()) {
+ xStepA = obj1.getNum();
+ } else {
+ xStepA = 1;
+ error(errSyntaxError, -1, "Invalid or missing XStep in pattern");
+ }
+ obj1.free();
+ if (dict->lookup("YStep", &obj1)->isNum()) {
+ yStepA = obj1.getNum();
+ } else {
+ yStepA = 1;
+ error(errSyntaxError, -1, "Invalid or missing YStep in pattern");
+ }
+ obj1.free();
+ if (!dict->lookup("Resources", &resDictA)->isDict()) {
+ resDictA.free();
+ resDictA.initNull();
+ error(errSyntaxError, -1, "Invalid or missing Resources in pattern");
+ }
+ matrixA[0] = 1; matrixA[1] = 0;
+ matrixA[2] = 0; matrixA[3] = 1;
+ matrixA[4] = 0; matrixA[5] = 0;
+ if (dict->lookup("Matrix", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 6) {
+ for (i = 0; i < 6; ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ matrixA[i] = obj2.getNum();
+ }
+ obj2.free();
+ }
+ }
+ obj1.free();
+
+ pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA,
+ &resDictA, matrixA, patObjRef);
+ resDictA.free();
+ return pat;
+}
+
+GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
+ double *bboxA, double xStepA, double yStepA,
+ Object *resDictA, double *matrixA,
+ Object *contentStreamRefA):
+ GfxPattern(1)
+{
+ int i;
+
+ paintType = paintTypeA;
+ tilingType = tilingTypeA;
+ for (i = 0; i < 4; ++i) {
+ bbox[i] = bboxA[i];
+ }
+ xStep = xStepA;
+ yStep = yStepA;
+ resDictA->copy(&resDict);
+ for (i = 0; i < 6; ++i) {
+ matrix[i] = matrixA[i];
+ }
+ contentStreamRefA->copy(&contentStreamRef);
+}
+
+GfxTilingPattern::~GfxTilingPattern() {
+ resDict.free();
+ contentStreamRef.free();
+}
+
+GfxPattern *GfxTilingPattern::copy() {
+ return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
+ &resDict, matrix, &contentStreamRef);
+}
+
+//------------------------------------------------------------------------
+// GfxShadingPattern
+//------------------------------------------------------------------------
+
+GfxShadingPattern *GfxShadingPattern::parse(Object *patObj
+ ) {
+ Dict *dict;
+ GfxShading *shadingA;
+ double matrixA[6];
+ Object obj1, obj2;
+ int i;
+
+ if (!patObj->isDict()) {
+ return NULL;
+ }
+ dict = patObj->getDict();
+
+ dict->lookup("Shading", &obj1);
+ shadingA = GfxShading::parse(&obj1
+ );
+ obj1.free();
+ if (!shadingA) {
+ return NULL;
+ }
+
+ matrixA[0] = 1; matrixA[1] = 0;
+ matrixA[2] = 0; matrixA[3] = 1;
+ matrixA[4] = 0; matrixA[5] = 0;
+ if (dict->lookup("Matrix", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 6) {
+ for (i = 0; i < 6; ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ matrixA[i] = obj2.getNum();
+ }
+ obj2.free();
+ }
+ }
+ obj1.free();
+
+ return new GfxShadingPattern(shadingA, matrixA);
+}
+
+GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA):
+ GfxPattern(2)
+{
+ int i;
+
+ shading = shadingA;
+ for (i = 0; i < 6; ++i) {
+ matrix[i] = matrixA[i];
+ }
+}
+
+GfxShadingPattern::~GfxShadingPattern() {
+ delete shading;
+}
+
+GfxPattern *GfxShadingPattern::copy() {
+ return new GfxShadingPattern(shading->copy(), matrix);
+}
+
+//------------------------------------------------------------------------
+// GfxShading
+//------------------------------------------------------------------------
+
+GfxShading::GfxShading(int typeA) {
+ type = typeA;
+ colorSpace = NULL;
+}
+
+GfxShading::GfxShading(GfxShading *shading) {
+ int i;
+
+ type = shading->type;
+ colorSpace = shading->colorSpace->copy();
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ background.c[i] = shading->background.c[i];
+ }
+ hasBackground = shading->hasBackground;
+ xMin = shading->xMin;
+ yMin = shading->yMin;
+ xMax = shading->xMax;
+ yMax = shading->yMax;
+ hasBBox = shading->hasBBox;
+}
+
+GfxShading::~GfxShading() {
+ if (colorSpace) {
+ delete colorSpace;
+ }
+}
+
+GfxShading *GfxShading::parse(Object *obj
+ ) {
+ GfxShading *shading;
+ Dict *dict;
+ int typeA;
+ Object obj1;
+
+ if (obj->isDict()) {
+ dict = obj->getDict();
+ } else if (obj->isStream()) {
+ dict = obj->streamGetDict();
+ } else {
+ return NULL;
+ }
+
+ if (!dict->lookup("ShadingType", &obj1)->isInt()) {
+ error(errSyntaxError, -1, "Invalid ShadingType in shading dictionary");
+ obj1.free();
+ return NULL;
+ }
+ typeA = obj1.getInt();
+ obj1.free();
+
+ switch (typeA) {
+ case 1:
+ shading = GfxFunctionShading::parse(dict
+ );
+ break;
+ case 2:
+ shading = GfxAxialShading::parse(dict
+ );
+ break;
+ case 3:
+ shading = GfxRadialShading::parse(dict
+ );
+ break;
+ case 4:
+ if (obj->isStream()) {
+ shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream()
+ );
+ } else {
+ error(errSyntaxError, -1, "Invalid Type 4 shading object");
+ goto err1;
+ }
+ break;
+ case 5:
+ if (obj->isStream()) {
+ shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream()
+ );
+ } else {
+ error(errSyntaxError, -1, "Invalid Type 5 shading object");
+ goto err1;
+ }
+ break;
+ case 6:
+ if (obj->isStream()) {
+ shading = GfxPatchMeshShading::parse(6, dict, obj->getStream()
+ );
+ } else {
+ error(errSyntaxError, -1, "Invalid Type 6 shading object");
+ goto err1;
+ }
+ break;
+ case 7:
+ if (obj->isStream()) {
+ shading = GfxPatchMeshShading::parse(7, dict, obj->getStream()
+ );
+ } else {
+ error(errSyntaxError, -1, "Invalid Type 7 shading object");
+ goto err1;
+ }
+ break;
+ default:
+ error(errSyntaxError, -1, "Unknown shading type {0:d}", typeA);
+ goto err1;
+ }
+
+ return shading;
+
+ err1:
+ return NULL;
+}
+
+GBool GfxShading::init(Dict *dict
+ ) {
+ Object obj1, obj2;
+ int i;
+
+ dict->lookup("ColorSpace", &obj1);
+ if (!(colorSpace = GfxColorSpace::parse(&obj1
+ ))) {
+ error(errSyntaxError, -1, "Bad color space in shading dictionary");
+ obj1.free();
+ return gFalse;
+ }
+ obj1.free();
+
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ background.c[i] = 0;
+ }
+ hasBackground = gFalse;
+ if (dict->lookup("Background", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() == colorSpace->getNComps()) {
+ hasBackground = gTrue;
+ for (i = 0; i < colorSpace->getNComps(); ++i) {
+ background.c[i] = dblToCol(obj1.arrayGet(i, &obj2)->getNum());
+ obj2.free();
+ }
+ } else {
+ error(errSyntaxError, -1, "Bad Background in shading dictionary");
+ }
+ }
+ obj1.free();
+
+ xMin = yMin = xMax = yMax = 0;
+ hasBBox = gFalse;
+ if (dict->lookup("BBox", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() == 4) {
+ hasBBox = gTrue;
+ xMin = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ yMin = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ xMax = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ yMax = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ } else {
+ error(errSyntaxError, -1, "Bad BBox in shading dictionary");
+ }
+ }
+ obj1.free();
+
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// GfxFunctionShading
+//------------------------------------------------------------------------
+
+GfxFunctionShading::GfxFunctionShading(double x0A, double y0A,
+ double x1A, double y1A,
+ double *matrixA,
+ Function **funcsA, int nFuncsA):
+ GfxShading(1)
+{
+ int i;
+
+ x0 = x0A;
+ y0 = y0A;
+ x1 = x1A;
+ y1 = y1A;
+ for (i = 0; i < 6; ++i) {
+ matrix[i] = matrixA[i];
+ }
+ nFuncs = nFuncsA;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = funcsA[i];
+ }
+}
+
+GfxFunctionShading::GfxFunctionShading(GfxFunctionShading *shading):
+ GfxShading(shading)
+{
+ int i;
+
+ x0 = shading->x0;
+ y0 = shading->y0;
+ x1 = shading->x1;
+ y1 = shading->y1;
+ for (i = 0; i < 6; ++i) {
+ matrix[i] = shading->matrix[i];
+ }
+ nFuncs = shading->nFuncs;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = shading->funcs[i]->copy();
+ }
+}
+
+GfxFunctionShading::~GfxFunctionShading() {
+ int i;
+
+ for (i = 0; i < nFuncs; ++i) {
+ delete funcs[i];
+ }
+}
+
+GfxFunctionShading *GfxFunctionShading::parse(Dict *dict
+ ) {
+ GfxFunctionShading *shading;
+ double x0A, y0A, x1A, y1A;
+ double matrixA[6];
+ Function *funcsA[gfxColorMaxComps];
+ int nFuncsA;
+ Object obj1, obj2;
+ int i;
+
+ x0A = y0A = 0;
+ x1A = y1A = 1;
+ if (dict->lookup("Domain", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ x0A = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ x1A = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ y0A = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ y1A = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ matrixA[0] = 1; matrixA[1] = 0;
+ matrixA[2] = 0; matrixA[3] = 1;
+ matrixA[4] = 0; matrixA[5] = 0;
+ if (dict->lookup("Matrix", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 6) {
+ matrixA[0] = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ matrixA[1] = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ matrixA[2] = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ matrixA[3] = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ matrixA[4] = obj1.arrayGet(4, &obj2)->getNum();
+ obj2.free();
+ matrixA[5] = obj1.arrayGet(5, &obj2)->getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ dict->lookup("Function", &obj1);
+ if (obj1.isArray()) {
+ nFuncsA = obj1.arrayGetLength();
+ if (nFuncsA > gfxColorMaxComps) {
+ error(errSyntaxError, -1,
+ "Invalid Function array in shading dictionary");
+ goto err1;
+ }
+ for (i = 0; i < nFuncsA; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!(funcsA[i] = Function::parse(&obj2, 2, 1))) {
+ goto err2;
+ }
+ obj2.free();
+ }
+ } else {
+ nFuncsA = 1;
+ if (!(funcsA[0] = Function::parse(&obj1, 2, -1))) {
+ goto err1;
+ }
+ }
+ obj1.free();
+
+ shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
+ funcsA, nFuncsA);
+ if (!shading->init(dict
+ )) {
+ delete shading;
+ return NULL;
+ }
+
+ for (i = 0; i < shading->nFuncs; ++i) {
+ if (shading->funcs[i]->getOutputSize()
+ != shading->getColorSpace()->getNComps()) {
+ error(errSyntaxError, -1, "Invalid function in shading dictionary");
+ delete shading;
+ return NULL;
+ }
+ }
+
+ return shading;
+
+ err2:
+ obj2.free();
+ err1:
+ obj1.free();
+ return NULL;
+}
+
+GfxShading *GfxFunctionShading::copy() {
+ return new GfxFunctionShading(this);
+}
+
+void GfxFunctionShading::getColor(double x, double y, GfxColor *color) {
+ double in[2], out[gfxColorMaxComps];
+ int i;
+
+ // NB: there can be one function with n outputs or n functions with
+ // one output each (where n = number of color components)
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ out[i] = 0;
+ }
+ in[0] = x;
+ in[1] = y;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(in, &out[i]);
+ }
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ color->c[i] = dblToCol(out[i]);
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxAxialShading
+//------------------------------------------------------------------------
+
+GfxAxialShading::GfxAxialShading(double x0A, double y0A,
+ double x1A, double y1A,
+ double t0A, double t1A,
+ Function **funcsA, int nFuncsA,
+ GBool extend0A, GBool extend1A):
+ GfxShading(2)
+{
+ int i;
+
+ x0 = x0A;
+ y0 = y0A;
+ x1 = x1A;
+ y1 = y1A;
+ t0 = t0A;
+ t1 = t1A;
+ nFuncs = nFuncsA;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = funcsA[i];
+ }
+ extend0 = extend0A;
+ extend1 = extend1A;
+}
+
+GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
+ GfxShading(shading)
+{
+ int i;
+
+ x0 = shading->x0;
+ y0 = shading->y0;
+ x1 = shading->x1;
+ y1 = shading->y1;
+ t0 = shading->t0;
+ t1 = shading->t1;
+ nFuncs = shading->nFuncs;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = shading->funcs[i]->copy();
+ }
+ extend0 = shading->extend0;
+ extend1 = shading->extend1;
+}
+
+GfxAxialShading::~GfxAxialShading() {
+ int i;
+
+ for (i = 0; i < nFuncs; ++i) {
+ delete funcs[i];
+ }
+}
+
+GfxAxialShading *GfxAxialShading::parse(Dict *dict
+ ) {
+ GfxAxialShading *shading;
+ double x0A, y0A, x1A, y1A;
+ double t0A, t1A;
+ Function *funcsA[gfxColorMaxComps];
+ int nFuncsA;
+ GBool extend0A, extend1A;
+ Object obj1, obj2;
+ int i;
+
+ x0A = y0A = x1A = y1A = 0;
+ if (dict->lookup("Coords", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ x0A = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ y0A = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ x1A = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ y1A = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ } else {
+ error(errSyntaxError, -1,
+ "Missing or invalid Coords in shading dictionary");
+ obj1.free();
+ goto err1;
+ }
+ obj1.free();
+
+ t0A = 0;
+ t1A = 1;
+ if (dict->lookup("Domain", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ t0A = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ t1A = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ dict->lookup("Function", &obj1);
+ if (obj1.isArray()) {
+ nFuncsA = obj1.arrayGetLength();
+ if (nFuncsA > gfxColorMaxComps) {
+ error(errSyntaxError, -1,
+ "Invalid Function array in shading dictionary");
+ goto err1;
+ }
+ for (i = 0; i < nFuncsA; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!(funcsA[i] = Function::parse(&obj2, 1, 1))) {
+ obj1.free();
+ obj2.free();
+ goto err1;
+ }
+ obj2.free();
+ }
+ } else {
+ nFuncsA = 1;
+ if (!(funcsA[0] = Function::parse(&obj1, 1, -1))) {
+ obj1.free();
+ goto err1;
+ }
+ }
+ obj1.free();
+
+ extend0A = extend1A = gFalse;
+ if (dict->lookup("Extend", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ extend0A = obj1.arrayGet(0, &obj2)->getBool();
+ obj2.free();
+ extend1A = obj1.arrayGet(1, &obj2)->getBool();
+ obj2.free();
+ }
+ obj1.free();
+
+ shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
+ funcsA, nFuncsA, extend0A, extend1A);
+ if (!shading->init(dict
+ )) {
+ delete shading;
+ return NULL;
+ }
+
+ for (i = 0; i < shading->nFuncs; ++i) {
+ if (shading->funcs[i]->getOutputSize()
+ != shading->getColorSpace()->getNComps()) {
+ error(errSyntaxError, -1, "Invalid function in shading dictionary");
+ delete shading;
+ return NULL;
+ }
+ }
+
+ return shading;
+
+ err1:
+ return NULL;
+}
+
+GfxShading *GfxAxialShading::copy() {
+ return new GfxAxialShading(this);
+}
+
+void GfxAxialShading::getColor(double t, GfxColor *color) {
+ double out[gfxColorMaxComps];
+ int i;
+
+ // NB: there can be one function with n outputs or n functions with
+ // one output each (where n = number of color components)
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ out[i] = 0;
+ }
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(&t, &out[i]);
+ }
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ color->c[i] = dblToCol(out[i]);
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxRadialShading
+//------------------------------------------------------------------------
+
+GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
+ double x1A, double y1A, double r1A,
+ double t0A, double t1A,
+ Function **funcsA, int nFuncsA,
+ GBool extend0A, GBool extend1A):
+ GfxShading(3)
+{
+ int i;
+
+ x0 = x0A;
+ y0 = y0A;
+ r0 = r0A;
+ x1 = x1A;
+ y1 = y1A;
+ r1 = r1A;
+ t0 = t0A;
+ t1 = t1A;
+ nFuncs = nFuncsA;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = funcsA[i];
+ }
+ extend0 = extend0A;
+ extend1 = extend1A;
+}
+
+GfxRadialShading::GfxRadialShading(GfxRadialShading *shading):
+ GfxShading(shading)
+{
+ int i;
+
+ x0 = shading->x0;
+ y0 = shading->y0;
+ r0 = shading->r0;
+ x1 = shading->x1;
+ y1 = shading->y1;
+ r1 = shading->r1;
+ t0 = shading->t0;
+ t1 = shading->t1;
+ nFuncs = shading->nFuncs;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = shading->funcs[i]->copy();
+ }
+ extend0 = shading->extend0;
+ extend1 = shading->extend1;
+}
+
+GfxRadialShading::~GfxRadialShading() {
+ int i;
+
+ for (i = 0; i < nFuncs; ++i) {
+ delete funcs[i];
+ }
+}
+
+GfxRadialShading *GfxRadialShading::parse(Dict *dict
+ ) {
+ GfxRadialShading *shading;
+ double x0A, y0A, r0A, x1A, y1A, r1A;
+ double t0A, t1A;
+ Function *funcsA[gfxColorMaxComps];
+ int nFuncsA;
+ GBool extend0A, extend1A;
+ Object obj1, obj2;
+ int i;
+
+ x0A = y0A = r0A = x1A = y1A = r1A = 0;
+ if (dict->lookup("Coords", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 6) {
+ x0A = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ y0A = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ r0A = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ x1A = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ y1A = obj1.arrayGet(4, &obj2)->getNum();
+ obj2.free();
+ r1A = obj1.arrayGet(5, &obj2)->getNum();
+ obj2.free();
+ } else {
+ error(errSyntaxError, -1,
+ "Missing or invalid Coords in shading dictionary");
+ goto err1;
+ }
+ obj1.free();
+
+ t0A = 0;
+ t1A = 1;
+ if (dict->lookup("Domain", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ t0A = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ t1A = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ dict->lookup("Function", &obj1);
+ if (obj1.isArray()) {
+ nFuncsA = obj1.arrayGetLength();
+ if (nFuncsA > gfxColorMaxComps) {
+ error(errSyntaxError, -1,
+ "Invalid Function array in shading dictionary");
+ goto err1;
+ }
+ for (i = 0; i < nFuncsA; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!(funcsA[i] = Function::parse(&obj2, 1, 1))) {
+ obj1.free();
+ obj2.free();
+ goto err1;
+ }
+ obj2.free();
+ }
+ } else {
+ nFuncsA = 1;
+ if (!(funcsA[0] = Function::parse(&obj1, 1, -1))) {
+ obj1.free();
+ goto err1;
+ }
+ }
+ obj1.free();
+
+ extend0A = extend1A = gFalse;
+ if (dict->lookup("Extend", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ extend0A = obj1.arrayGet(0, &obj2)->getBool();
+ obj2.free();
+ extend1A = obj1.arrayGet(1, &obj2)->getBool();
+ obj2.free();
+ }
+ obj1.free();
+
+ shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
+ funcsA, nFuncsA, extend0A, extend1A);
+ if (!shading->init(dict
+ )) {
+ delete shading;
+ return NULL;
+ }
+
+ for (i = 0; i < shading->nFuncs; ++i) {
+ if (shading->funcs[i]->getOutputSize()
+ != shading->getColorSpace()->getNComps()) {
+ error(errSyntaxError, -1, "Invalid function in shading dictionary");
+ delete shading;
+ return NULL;
+ }
+ }
+
+ return shading;
+
+ err1:
+ return NULL;
+}
+
+GfxShading *GfxRadialShading::copy() {
+ return new GfxRadialShading(this);
+}
+
+void GfxRadialShading::getColor(double t, GfxColor *color) {
+ double out[gfxColorMaxComps];
+ int i;
+
+ // NB: there can be one function with n outputs or n functions with
+ // one output each (where n = number of color components)
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ out[i] = 0;
+ }
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(&t, &out[i]);
+ }
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ color->c[i] = dblToCol(out[i]);
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxShadingBitBuf
+//------------------------------------------------------------------------
+
+class GfxShadingBitBuf {
+public:
+
+ GfxShadingBitBuf(Stream *strA);
+ ~GfxShadingBitBuf();
+ GBool getBits(int n, Guint *val);
+ void flushBits();
+
+private:
+
+ Stream *str;
+ int bitBuf;
+ int nBits;
+};
+
+GfxShadingBitBuf::GfxShadingBitBuf(Stream *strA) {
+ str = strA;
+ str->reset();
+ bitBuf = 0;
+ nBits = 0;
+}
+
+GfxShadingBitBuf::~GfxShadingBitBuf() {
+ str->close();
+}
+
+GBool GfxShadingBitBuf::getBits(int n, Guint *val) {
+ int x;
+
+ if (nBits >= n) {
+ x = (bitBuf >> (nBits - n)) & ((1 << n) - 1);
+ nBits -= n;
+ } else {
+ x = 0;
+ if (nBits > 0) {
+ x = bitBuf & ((1 << nBits) - 1);
+ n -= nBits;
+ nBits = 0;
+ }
+ while (n > 0) {
+ if ((bitBuf = str->getChar()) == EOF) {
+ nBits = 0;
+ return gFalse;
+ }
+ if (n >= 8) {
+ x = (x << 8) | bitBuf;
+ n -= 8;
+ } else {
+ x = (x << n) | (bitBuf >> (8 - n));
+ nBits = 8 - n;
+ n = 0;
+ }
+ }
+ }
+ *val = x;
+ return gTrue;
+}
+
+void GfxShadingBitBuf::flushBits() {
+ bitBuf = 0;
+ nBits = 0;
+}
+
+//------------------------------------------------------------------------
+// GfxGouraudTriangleShading
+//------------------------------------------------------------------------
+
+GfxGouraudTriangleShading::GfxGouraudTriangleShading(
+ int typeA,
+ GfxGouraudVertex *verticesA, int nVerticesA,
+ int (*trianglesA)[3], int nTrianglesA,
+ int nCompsA, Function **funcsA, int nFuncsA):
+ GfxShading(typeA)
+{
+ int i;
+
+ vertices = verticesA;
+ nVertices = nVerticesA;
+ triangles = trianglesA;
+ nTriangles = nTrianglesA;
+ nComps = nCompsA;
+ nFuncs = nFuncsA;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = funcsA[i];
+ }
+}
+
+GfxGouraudTriangleShading::GfxGouraudTriangleShading(
+ GfxGouraudTriangleShading *shading):
+ GfxShading(shading)
+{
+ int i;
+
+ nVertices = shading->nVertices;
+ vertices = (GfxGouraudVertex *)gmallocn(nVertices, sizeof(GfxGouraudVertex));
+ memcpy(vertices, shading->vertices, nVertices * sizeof(GfxGouraudVertex));
+ nTriangles = shading->nTriangles;
+ triangles = (int (*)[3])gmallocn(nTriangles * 3, sizeof(int));
+ memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int));
+ nComps = shading->nComps;
+ nFuncs = shading->nFuncs;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = shading->funcs[i]->copy();
+ }
+}
+
+GfxGouraudTriangleShading::~GfxGouraudTriangleShading() {
+ int i;
+
+ gfree(vertices);
+ gfree(triangles);
+ for (i = 0; i < nFuncs; ++i) {
+ delete funcs[i];
+ }
+}
+
+GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(
+ int typeA, Dict *dict, Stream *str
+ ) {
+ GfxGouraudTriangleShading *shading;
+ Function *funcsA[gfxColorMaxComps];
+ int nFuncsA;
+ int coordBits, compBits, flagBits, vertsPerRow, nRows;
+ double xMin, xMax, yMin, yMax;
+ double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
+ double xMul, yMul;
+ double cMul[gfxColorMaxComps];
+ GfxGouraudVertex *verticesA;
+ int (*trianglesA)[3];
+ int nCompsA, nVerticesA, nTrianglesA, vertSize, triSize;
+ Guint x, y, flag;
+ Guint c[gfxColorMaxComps];
+ GfxShadingBitBuf *bitBuf;
+ Object obj1, obj2;
+ int i, j, k, state;
+
+ if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
+ coordBits = obj1.getInt();
+ } else {
+ error(errSyntaxError, -1,
+ "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();
+ } else {
+ error(errSyntaxError, -1,
+ "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) {
+ if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
+ flagBits = obj1.getInt();
+ } else {
+ error(errSyntaxError, -1,
+ "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()) {
+ vertsPerRow = obj1.getInt();
+ } else {
+ error(errSyntaxError, -1,
+ "Missing or invalid VerticesPerRow in shading dictionary");
+ 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) {
+ xMin = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ xMax = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
+ yMin = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ yMax = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
+ for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
+ cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
+ obj2.free();
+ cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
+ obj2.free();
+ cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
+ }
+ nCompsA = i;
+ } else {
+ error(errSyntaxError, -1,
+ "Missing or invalid Decode array in shading dictionary");
+ goto err2;
+ }
+ obj1.free();
+
+ if (!dict->lookup("Function", &obj1)->isNull()) {
+ if (obj1.isArray()) {
+ nFuncsA = obj1.arrayGetLength();
+ if (nFuncsA > gfxColorMaxComps) {
+ error(errSyntaxError, -1,
+ "Invalid Function array in shading dictionary");
+ goto err1;
+ }
+ for (i = 0; i < nFuncsA; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!(funcsA[i] = Function::parse(&obj2, 1, 1))) {
+ obj1.free();
+ obj2.free();
+ goto err1;
+ }
+ obj2.free();
+ }
+ } else {
+ nFuncsA = 1;
+ if (!(funcsA[0] = Function::parse(&obj1, 1, -1))) {
+ obj1.free();
+ goto err1;
+ }
+ }
+ } else {
+ nFuncsA = 0;
+ }
+ obj1.free();
+
+ nVerticesA = nTrianglesA = 0;
+ verticesA = NULL;
+ trianglesA = NULL;
+ vertSize = triSize = 0;
+ state = 0;
+ flag = 0; // make gcc happy
+ bitBuf = new GfxShadingBitBuf(str);
+ while (1) {
+ if (typeA == 4) {
+ if (!bitBuf->getBits(flagBits, &flag)) {
+ break;
+ }
+ }
+ if (!bitBuf->getBits(coordBits, &x) ||
+ !bitBuf->getBits(coordBits, &y)) {
+ break;
+ }
+ for (i = 0; i < nCompsA; ++i) {
+ if (!bitBuf->getBits(compBits, &c[i])) {
+ break;
+ }
+ }
+ if (i < nCompsA) {
+ break;
+ }
+ if (nVerticesA == vertSize) {
+ vertSize = (vertSize == 0) ? 16 : 2 * vertSize;
+ verticesA = (GfxGouraudVertex *)
+ greallocn(verticesA, vertSize, sizeof(GfxGouraudVertex));
+ }
+ verticesA[nVerticesA].x = xMin + xMul * (double)x;
+ verticesA[nVerticesA].y = yMin + yMul * (double)y;
+ for (i = 0; i < nCompsA; ++i) {
+ verticesA[nVerticesA].color[i] = cMin[i] + cMul[i] * (double)c[i];
+ }
+ ++nVerticesA;
+ bitBuf->flushBits();
+ if (typeA == 4) {
+ if (state == 0 || state == 1) {
+ ++state;
+ } else if (state == 2 || flag > 0) {
+ if (nTrianglesA == triSize) {
+ triSize = (triSize == 0) ? 16 : 2 * triSize;
+ trianglesA = (int (*)[3])
+ greallocn(trianglesA, triSize * 3, sizeof(int));
+ }
+ if (state == 2) {
+ trianglesA[nTrianglesA][0] = nVerticesA - 3;
+ trianglesA[nTrianglesA][1] = nVerticesA - 2;
+ trianglesA[nTrianglesA][2] = nVerticesA - 1;
+ ++state;
+ } else if (flag == 1) {
+ trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][1];
+ trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
+ trianglesA[nTrianglesA][2] = nVerticesA - 1;
+ } else { // flag == 2
+ trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][0];
+ trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
+ trianglesA[nTrianglesA][2] = nVerticesA - 1;
+ }
+ ++nTrianglesA;
+ } else { // state == 3 && flag == 0
+ state = 1;
+ }
+ }
+ }
+ delete bitBuf;
+ if (typeA == 5) {
+ nRows = nVerticesA / vertsPerRow;
+ nTrianglesA = (nRows - 1) * 2 * (vertsPerRow - 1);
+ trianglesA = (int (*)[3])gmallocn(nTrianglesA * 3, sizeof(int));
+ k = 0;
+ for (i = 0; i < nRows - 1; ++i) {
+ for (j = 0; j < vertsPerRow - 1; ++j) {
+ trianglesA[k][0] = i * vertsPerRow + j;
+ trianglesA[k][1] = i * vertsPerRow + j+1;
+ trianglesA[k][2] = (i+1) * vertsPerRow + j;
+ ++k;
+ trianglesA[k][0] = i * vertsPerRow + j+1;
+ trianglesA[k][1] = (i+1) * vertsPerRow + j;
+ trianglesA[k][2] = (i+1) * vertsPerRow + j+1;
+ ++k;
+ }
+ }
+ }
+
+ shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA,
+ trianglesA, nTrianglesA,
+ nCompsA, funcsA, nFuncsA);
+ if (!shading->init(dict
+ )) {
+ delete shading;
+ return NULL;
+ }
+
+ for (i = 0; i < shading->nFuncs; ++i) {
+ if (shading->funcs[i]->getOutputSize()
+ != shading->getColorSpace()->getNComps()) {
+ error(errSyntaxError, -1, "Invalid function in shading dictionary");
+ delete shading;
+ return NULL;
+ }
+ }
+
+ return shading;
+
+ err2:
+ obj1.free();
+ err1:
+ return NULL;
+}
+
+GfxShading *GfxGouraudTriangleShading::copy() {
+ return new GfxGouraudTriangleShading(this);
+}
+
+void GfxGouraudTriangleShading::getTriangle(
+ int i,
+ double *x0, double *y0, double *color0,
+ double *x1, double *y1, double *color1,
+ double *x2, double *y2, double *color2) {
+ int v, j;
+
+ v = triangles[i][0];
+ *x0 = vertices[v].x;
+ *y0 = vertices[v].y;
+ for (j = 0; j < nComps; ++j) {
+ color0[j] = vertices[v].color[j];
+ }
+ v = triangles[i][1];
+ *x1 = vertices[v].x;
+ *y1 = vertices[v].y;
+ for (j = 0; j < nComps; ++j) {
+ color1[j] = vertices[v].color[j];
+ }
+ v = triangles[i][2];
+ *x2 = vertices[v].x;
+ *y2 = vertices[v].y;
+ for (j = 0; j < nComps; ++j) {
+ color2[j] = vertices[v].color[j];
+ }
+}
+
+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;
+
+ if (nFuncs > 0) {
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(in, &c[i]);
+ }
+ for (i = 0; i < colorSpace->getNComps(); ++i) {
+ out->c[i] = dblToCol(c[i]);
+ }
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ out->c[i] = dblToCol(in[i]);
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxPatchMeshShading
+//------------------------------------------------------------------------
+
+GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
+ GfxPatch *patchesA, int nPatchesA,
+ int nCompsA,
+ Function **funcsA, int nFuncsA):
+ GfxShading(typeA)
+{
+ int i;
+
+ patches = patchesA;
+ nPatches = nPatchesA;
+ nComps = nCompsA;
+ nFuncs = nFuncsA;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = funcsA[i];
+ }
+}
+
+GfxPatchMeshShading::GfxPatchMeshShading(GfxPatchMeshShading *shading):
+ GfxShading(shading)
+{
+ int i;
+
+ nPatches = shading->nPatches;
+ patches = (GfxPatch *)gmallocn(nPatches, sizeof(GfxPatch));
+ memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch));
+ nComps = shading->nComps;
+ nFuncs = shading->nFuncs;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = shading->funcs[i]->copy();
+ }
+}
+
+GfxPatchMeshShading::~GfxPatchMeshShading() {
+ int i;
+
+ gfree(patches);
+ for (i = 0; i < nFuncs; ++i) {
+ delete funcs[i];
+ }
+}
+
+GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
+ Stream *str
+ ) {
+ GfxPatchMeshShading *shading;
+ Function *funcsA[gfxColorMaxComps];
+ int nFuncsA;
+ int coordBits, compBits, flagBits;
+ double xMin, xMax, yMin, yMax;
+ double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
+ double xMul, yMul;
+ double cMul[gfxColorMaxComps];
+ GfxPatch *patchesA, *p;
+ int nCompsA, nPatchesA, patchesSize, nPts, nColors;
+ Guint flag;
+ double x[16], y[16];
+ Guint xi, yi;
+ double c[4][gfxColorMaxComps];
+ Guint ci;
+ GfxShadingBitBuf *bitBuf;
+ Object obj1, obj2;
+ int i, j;
+
+ nPatchesA = 0;
+ patchesA = NULL;
+ patchesSize = 0;
+
+ if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
+ coordBits = obj1.getInt();
+ } else {
+ error(errSyntaxError, -1,
+ "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();
+ } else {
+ error(errSyntaxError, -1,
+ "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();
+ } else {
+ error(errSyntaxError, -1,
+ "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) {
+ xMin = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ xMax = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
+ yMin = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ yMax = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
+ for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
+ cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
+ obj2.free();
+ cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
+ obj2.free();
+ cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
+ }
+ nCompsA = i;
+ } else {
+ error(errSyntaxError, -1,
+ "Missing or invalid Decode array in shading dictionary");
+ goto err2;
+ }
+ obj1.free();
+
+ if (!dict->lookup("Function", &obj1)->isNull()) {
+ if (obj1.isArray()) {
+ nFuncsA = obj1.arrayGetLength();
+ if (nFuncsA > gfxColorMaxComps) {
+ error(errSyntaxError, -1,
+ "Invalid Function array in shading dictionary");
+ goto err1;
+ }
+ for (i = 0; i < nFuncsA; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!(funcsA[i] = Function::parse(&obj2, 1, 1))) {
+ obj1.free();
+ obj2.free();
+ goto err1;
+ }
+ obj2.free();
+ }
+ } else {
+ nFuncsA = 1;
+ if (!(funcsA[0] = Function::parse(&obj1, 1, -1))) {
+ obj1.free();
+ goto err1;
+ }
+ }
+ } else {
+ nFuncsA = 0;
+ }
+ obj1.free();
+
+ bitBuf = new GfxShadingBitBuf(str);
+ while (1) {
+ if (!bitBuf->getBits(flagBits, &flag)) {
+ break;
+ }
+ flag &= 3;
+ if (flag != 0 && nPatchesA == 0) {
+ error(errSyntaxError, -1, "Invalid patch in patch mesh shading");
+ delete bitBuf;
+ goto err1;
+ }
+ if (typeA == 6) {
+ switch (flag) {
+ case 0: nPts = 12; nColors = 4; break;
+ case 1:
+ case 2:
+ case 3:
+ default: nPts = 8; nColors = 2; break;
+ }
+ } else {
+ switch (flag) {
+ case 0: nPts = 16; nColors = 4; break;
+ case 1:
+ case 2:
+ case 3:
+ default: nPts = 12; nColors = 2; break;
+ }
+ }
+ for (i = 0; i < nPts; ++i) {
+ if (!bitBuf->getBits(coordBits, &xi) ||
+ !bitBuf->getBits(coordBits, &yi)) {
+ break;
+ }
+ x[i] = xMin + xMul * (double)xi;
+ y[i] = yMin + yMul * (double)yi;
+ }
+ if (i < nPts) {
+ break;
+ }
+ for (i = 0; i < nColors; ++i) {
+ for (j = 0; j < nCompsA; ++j) {
+ if (!bitBuf->getBits(compBits, &ci)) {
+ break;
+ }
+ c[i][j] = cMin[j] + cMul[j] * (double)ci;
+ }
+ if (j < nCompsA) {
+ break;
+ }
+ }
+ if (i < nColors) {
+ break;
+ }
+ if (nPatchesA == patchesSize) {
+ patchesSize = (patchesSize == 0) ? 16 : 2 * patchesSize;
+ patchesA = (GfxPatch *)greallocn(patchesA,
+ patchesSize, sizeof(GfxPatch));
+ }
+ p = &patchesA[nPatchesA];
+ if (typeA == 6) {
+ switch (flag) {
+ case 0:
+ p->x[0][0] = x[0];
+ p->y[0][0] = y[0];
+ p->x[0][1] = x[1];
+ p->y[0][1] = y[1];
+ p->x[0][2] = x[2];
+ p->y[0][2] = y[2];
+ p->x[0][3] = x[3];
+ p->y[0][3] = y[3];
+ p->x[1][3] = x[4];
+ p->y[1][3] = y[4];
+ p->x[2][3] = x[5];
+ p->y[2][3] = y[5];
+ p->x[3][3] = x[6];
+ p->y[3][3] = y[6];
+ p->x[3][2] = x[7];
+ p->y[3][2] = y[7];
+ p->x[3][1] = x[8];
+ p->y[3][1] = y[8];
+ p->x[3][0] = x[9];
+ p->y[3][0] = y[9];
+ p->x[2][0] = x[10];
+ p->y[2][0] = y[10];
+ p->x[1][0] = x[11];
+ p->y[1][0] = y[11];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = c[0][j];
+ p->color[0][1][j] = c[1][j];
+ p->color[1][1][j] = c[2][j];
+ p->color[1][0][j] = c[3][j];
+ }
+ break;
+ case 1:
+ p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
+ p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
+ p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
+ p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
+ p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
+ p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
+ p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
+ p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
+ p->x[1][3] = x[0];
+ p->y[1][3] = y[0];
+ p->x[2][3] = x[1];
+ p->y[2][3] = y[1];
+ p->x[3][3] = x[2];
+ p->y[3][3] = y[2];
+ p->x[3][2] = x[3];
+ p->y[3][2] = y[3];
+ p->x[3][1] = x[4];
+ p->y[3][1] = y[4];
+ p->x[3][0] = x[5];
+ p->y[3][0] = y[5];
+ p->x[2][0] = x[6];
+ p->y[2][0] = y[6];
+ p->x[1][0] = x[7];
+ p->y[1][0] = y[7];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[0][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
+ }
+ break;
+ case 2:
+ p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
+ p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
+ p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
+ p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
+ p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
+ p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
+ p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
+ p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
+ p->x[1][3] = x[0];
+ p->y[1][3] = y[0];
+ p->x[2][3] = x[1];
+ p->y[2][3] = y[1];
+ p->x[3][3] = x[2];
+ p->y[3][3] = y[2];
+ p->x[3][2] = x[3];
+ p->y[3][2] = y[3];
+ p->x[3][1] = x[4];
+ p->y[3][1] = y[4];
+ p->x[3][0] = x[5];
+ p->y[3][0] = y[5];
+ p->x[2][0] = x[6];
+ p->y[2][0] = y[6];
+ p->x[1][0] = x[7];
+ p->y[1][0] = y[7];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
+ }
+ break;
+ case 3:
+ p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
+ p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
+ p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
+ p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
+ p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
+ p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
+ p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
+ p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
+ p->x[1][3] = x[0];
+ p->y[1][3] = y[0];
+ p->x[2][3] = x[1];
+ p->y[2][3] = y[1];
+ p->x[3][3] = x[2];
+ p->y[3][3] = y[2];
+ p->x[3][2] = x[3];
+ p->y[3][2] = y[3];
+ p->x[3][1] = x[4];
+ p->y[3][1] = y[4];
+ p->x[3][0] = x[5];
+ p->y[3][0] = y[5];
+ p->x[2][0] = x[6];
+ p->y[2][0] = y[6];
+ p->x[1][0] = x[7];
+ p->y[1][0] = y[7];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[0][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
+ }
+ break;
+ }
+ } else {
+ switch (flag) {
+ case 0:
+ p->x[0][0] = x[0];
+ p->y[0][0] = y[0];
+ p->x[0][1] = x[1];
+ p->y[0][1] = y[1];
+ p->x[0][2] = x[2];
+ p->y[0][2] = y[2];
+ p->x[0][3] = x[3];
+ p->y[0][3] = y[3];
+ p->x[1][3] = x[4];
+ p->y[1][3] = y[4];
+ p->x[2][3] = x[5];
+ p->y[2][3] = y[5];
+ p->x[3][3] = x[6];
+ p->y[3][3] = y[6];
+ p->x[3][2] = x[7];
+ p->y[3][2] = y[7];
+ p->x[3][1] = x[8];
+ p->y[3][1] = y[8];
+ p->x[3][0] = x[9];
+ p->y[3][0] = y[9];
+ p->x[2][0] = x[10];
+ p->y[2][0] = y[10];
+ p->x[1][0] = x[11];
+ p->y[1][0] = y[11];
+ p->x[1][1] = x[12];
+ p->y[1][1] = y[12];
+ p->x[1][2] = x[13];
+ p->y[1][2] = y[13];
+ p->x[2][2] = x[14];
+ p->y[2][2] = y[14];
+ p->x[2][1] = x[15];
+ p->y[2][1] = y[15];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = c[0][j];
+ p->color[0][1][j] = c[1][j];
+ p->color[1][1][j] = c[2][j];
+ p->color[1][0][j] = c[3][j];
+ }
+ break;
+ case 1:
+ p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
+ p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
+ p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
+ p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
+ p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
+ p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
+ p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
+ p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
+ p->x[1][3] = x[0];
+ p->y[1][3] = y[0];
+ p->x[2][3] = x[1];
+ p->y[2][3] = y[1];
+ p->x[3][3] = x[2];
+ p->y[3][3] = y[2];
+ p->x[3][2] = x[3];
+ p->y[3][2] = y[3];
+ p->x[3][1] = x[4];
+ p->y[3][1] = y[4];
+ p->x[3][0] = x[5];
+ p->y[3][0] = y[5];
+ p->x[2][0] = x[6];
+ p->y[2][0] = y[6];
+ p->x[1][0] = x[7];
+ p->y[1][0] = y[7];
+ p->x[1][1] = x[8];
+ p->y[1][1] = y[8];
+ p->x[1][2] = x[9];
+ p->y[1][2] = y[9];
+ p->x[2][2] = x[10];
+ p->y[2][2] = y[10];
+ p->x[2][1] = x[11];
+ p->y[2][1] = y[11];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[0][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
+ }
+ break;
+ case 2:
+ p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
+ p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
+ p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
+ p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
+ p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
+ p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
+ p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
+ p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
+ p->x[1][3] = x[0];
+ p->y[1][3] = y[0];
+ p->x[2][3] = x[1];
+ p->y[2][3] = y[1];
+ p->x[3][3] = x[2];
+ p->y[3][3] = y[2];
+ p->x[3][2] = x[3];
+ p->y[3][2] = y[3];
+ p->x[3][1] = x[4];
+ p->y[3][1] = y[4];
+ p->x[3][0] = x[5];
+ p->y[3][0] = y[5];
+ p->x[2][0] = x[6];
+ p->y[2][0] = y[6];
+ p->x[1][0] = x[7];
+ p->y[1][0] = y[7];
+ p->x[1][1] = x[8];
+ p->y[1][1] = y[8];
+ p->x[1][2] = x[9];
+ p->y[1][2] = y[9];
+ p->x[2][2] = x[10];
+ p->y[2][2] = y[10];
+ p->x[2][1] = x[11];
+ p->y[2][1] = y[11];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][1][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
+ }
+ break;
+ case 3:
+ p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
+ p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
+ p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
+ p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
+ p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
+ p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
+ p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
+ p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
+ p->x[1][3] = x[0];
+ p->y[1][3] = y[0];
+ p->x[2][3] = x[1];
+ p->y[2][3] = y[1];
+ p->x[3][3] = x[2];
+ p->y[3][3] = y[2];
+ p->x[3][2] = x[3];
+ p->y[3][2] = y[3];
+ p->x[3][1] = x[4];
+ p->y[3][1] = y[4];
+ p->x[3][0] = x[5];
+ p->y[3][0] = y[5];
+ p->x[2][0] = x[6];
+ p->y[2][0] = y[6];
+ p->x[1][0] = x[7];
+ p->y[1][0] = y[7];
+ p->x[1][1] = x[8];
+ p->y[1][1] = y[8];
+ p->x[1][2] = x[9];
+ p->y[1][2] = y[9];
+ p->x[2][2] = x[10];
+ p->y[2][2] = y[10];
+ p->x[2][1] = x[11];
+ p->y[2][1] = y[11];
+ for (j = 0; j < nCompsA; ++j) {
+ p->color[0][0][j] = patchesA[nPatchesA-1].color[1][0][j];
+ p->color[0][1][j] = patchesA[nPatchesA-1].color[0][0][j];
+ p->color[1][1][j] = c[0][j];
+ p->color[1][0][j] = c[1][j];
+ }
+ break;
+ }
+ }
+ ++nPatchesA;
+ bitBuf->flushBits();
+ }
+ delete bitBuf;
+
+ if (typeA == 6) {
+ for (i = 0; i < nPatchesA; ++i) {
+ p = &patchesA[i];
+ p->x[1][1] = (-4 * p->x[0][0]
+ +6 * (p->x[0][1] + p->x[1][0])
+ -2 * (p->x[0][3] + p->x[3][0])
+ +3 * (p->x[3][1] + p->x[1][3])
+ - p->x[3][3]) / 9;
+ p->y[1][1] = (-4 * p->y[0][0]
+ +6 * (p->y[0][1] + p->y[1][0])
+ -2 * (p->y[0][3] + p->y[3][0])
+ +3 * (p->y[3][1] + p->y[1][3])
+ - p->y[3][3]) / 9;
+ p->x[1][2] = (-4 * p->x[0][3]
+ +6 * (p->x[0][2] + p->x[1][3])
+ -2 * (p->x[0][0] + p->x[3][3])
+ +3 * (p->x[3][2] + p->x[1][0])
+ - p->x[3][0]) / 9;
+ p->y[1][2] = (-4 * p->y[0][3]
+ +6 * (p->y[0][2] + p->y[1][3])
+ -2 * (p->y[0][0] + p->y[3][3])
+ +3 * (p->y[3][2] + p->y[1][0])
+ - p->y[3][0]) / 9;
+ p->x[2][1] = (-4 * p->x[3][0]
+ +6 * (p->x[3][1] + p->x[2][0])
+ -2 * (p->x[3][3] + p->x[0][0])
+ +3 * (p->x[0][1] + p->x[2][3])
+ - p->x[0][3]) / 9;
+ p->y[2][1] = (-4 * p->y[3][0]
+ +6 * (p->y[3][1] + p->y[2][0])
+ -2 * (p->y[3][3] + p->y[0][0])
+ +3 * (p->y[0][1] + p->y[2][3])
+ - p->y[0][3]) / 9;
+ p->x[2][2] = (-4 * p->x[3][3]
+ +6 * (p->x[3][2] + p->x[2][3])
+ -2 * (p->x[3][0] + p->x[0][3])
+ +3 * (p->x[0][2] + p->x[2][0])
+ - p->x[0][0]) / 9;
+ p->y[2][2] = (-4 * p->y[3][3]
+ +6 * (p->y[3][2] + p->y[2][3])
+ -2 * (p->y[3][0] + p->y[0][3])
+ +3 * (p->y[0][2] + p->y[2][0])
+ - p->y[0][0]) / 9;
+ }
+ }
+
+ shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA,
+ nCompsA, funcsA, nFuncsA);
+ if (!shading->init(dict
+ )) {
+ delete shading;
+ return NULL;
+ }
+
+ for (i = 0; i < shading->nFuncs; ++i) {
+ if (shading->funcs[i]->getOutputSize()
+ != shading->getColorSpace()->getNComps()) {
+ error(errSyntaxError, -1, "Invalid function in shading dictionary");
+ delete shading;
+ return NULL;
+ }
+ }
+
+ return shading;
+
+ err2:
+ obj1.free();
+ err1:
+ if (patchesA) {
+ gfree(patchesA);
+ }
+ return NULL;
+}
+
+GfxShading *GfxPatchMeshShading::copy() {
+ return new GfxPatchMeshShading(this);
+}
+
+void GfxPatchMeshShading::getBBox(double *xMin, double *yMin,
+ double *xMax, double *yMax) {
+ double xxMin = 0;
+ double yyMin = 0;
+ double xxMax = 0;
+ double yyMax = 0;
+ if (nPatches > 0) {
+ xxMin = patches[0].x[0][0];
+ yyMin = patches[0].y[0][0];
+ }
+ for (int i = 0; i < nPatches; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ for (int k = 0; k < 4; ++k) {
+ if (patches[i].x[j][k] < xxMin) {
+ xxMin = patches[i].x[j][k];
+ } else if (patches[i].x[j][k] > xxMax) {
+ xxMax = patches[i].x[j][k];
+ }
+ if (patches[i].y[j][k] < yyMin) {
+ yyMin = patches[i].y[j][k];
+ } else if (patches[i].y[j][k] > yyMax) {
+ yyMax = patches[i].y[j][k];
+ }
+ }
+ }
+ }
+ *xMin = xxMin;
+ *yMin = yyMin;
+ *xMax = xxMax;
+ *yMax = yyMax;
+}
+
+void GfxPatchMeshShading::getColor(double *in, GfxColor *out) {
+ double c[gfxColorMaxComps];
+ int i;
+
+ if (nFuncs > 0) {
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(in, &c[i]);
+ }
+ for (i = 0; i < colorSpace->getNComps(); ++i) {
+ out->c[i] = dblToCol(c[i]);
+ }
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ out->c[i] = dblToCol(in[i]);
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxImageColorMap
+//------------------------------------------------------------------------
+
+GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
+ GfxColorSpace *colorSpaceA,
+ int maxAllowedBits) {
+ GfxIndexedColorSpace *indexedCS;
+ GfxSeparationColorSpace *sepCS;
+ int maxPixel, indexHigh;
+ Guchar *indexedLookup;
+ Function *sepFunc;
+ Object obj;
+ double defaultLow[gfxColorMaxComps], defaultRange[gfxColorMaxComps];
+ double x[gfxColorMaxComps], y[gfxColorMaxComps];
+ int i, j, k;
+
+ ok = gTrue;
+
+ // bits per component and color space
+ bits = bitsA;
+ if (bits <= maxAllowedBits) {
+ maxPixel = (1 << bits) - 1;
+ } else {
+ maxPixel = (1 << maxAllowedBits) - 1;
+ }
+ colorSpace = colorSpaceA;
+
+ // initialize
+ for (k = 0; k < gfxColorMaxComps; ++k) {
+ lookup[k] = NULL;
+ lookup2[k] = NULL;
+ }
+
+ // get decode map
+ colorSpace->getDefaultRanges(defaultLow, defaultRange, maxPixel);
+ if (decode->isNull()) {
+ nComps = colorSpace->getNComps();
+ for (i = 0; i < nComps; ++i) {
+ decodeLow[i] = defaultLow[i];
+ decodeRange[i] = defaultRange[i];
+ }
+ } else if (decode->isArray()) {
+ nComps = decode->arrayGetLength() / 2;
+ if (nComps < colorSpace->getNComps()) {
+ goto err1;
+ }
+ if (nComps > colorSpace->getNComps()) {
+ error(errSyntaxWarning, -1, "Too many elements in Decode array");
+ nComps = colorSpace->getNComps();
+ }
+ for (i = 0; i < nComps; ++i) {
+ decode->arrayGet(2*i, &obj);
+ if (!obj.isNum()) {
+ goto err2;
+ }
+ decodeLow[i] = obj.getNum();
+ obj.free();
+ decode->arrayGet(2*i+1, &obj);
+ if (!obj.isNum()) {
+ goto err2;
+ }
+ decodeRange[i] = obj.getNum() - decodeLow[i];
+ obj.free();
+ }
+ } else {
+ goto err1;
+ }
+
+ // Construct a lookup table -- this stores pre-computed decoded
+ // values for each component, i.e., the result of applying the
+ // decode mapping to each possible image pixel component value.
+ for (k = 0; k < nComps; ++k) {
+ lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
+ sizeof(GfxColorComp));
+ for (i = 0; i <= maxPixel; ++i) {
+ double t = decodeLow[k] + (i * decodeRange[k]) / maxPixel;
+ if (t < defaultLow[k]) {
+ t = defaultLow[k];
+ } else if (t > defaultLow[k] + defaultRange[k]) {
+ t = defaultLow[k] + defaultRange[k];
+ }
+ lookup[k][i] = dblToCol(t);
+ }
+ }
+
+ // Optimization: for Indexed and Separation color spaces (which have
+ // only one component), we pre-compute a second lookup table with
+ // color values
+ colorSpace2 = NULL;
+ nComps2 = 0;
+ if (colorSpace->getMode() == csIndexed) {
+ // Note that indexHigh may not be the same as maxPixel --
+ // Distiller will remove unused palette entries, resulting in
+ // indexHigh < maxPixel.
+ indexedCS = (GfxIndexedColorSpace *)colorSpace;
+ colorSpace2 = indexedCS->getBase();
+ indexHigh = indexedCS->getIndexHigh();
+ nComps2 = colorSpace2->getNComps();
+ indexedLookup = indexedCS->getLookup();
+ colorSpace2->getDefaultRanges(x, y, indexHigh);
+ for (k = 0; k < nComps2; ++k) {
+ lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
+ sizeof(GfxColorComp));
+ }
+ for (i = 0; i <= maxPixel; ++i) {
+ j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5);
+ if (j < 0) {
+ j = 0;
+ } else if (j > indexHigh) {
+ j = indexHigh;
+ }
+ for (k = 0; k < nComps2; ++k) {
+ lookup2[k][i] =
+ dblToCol(x[k] + (indexedLookup[j*nComps2 + k] / 255.0) * y[k]);
+ }
+ }
+ } else if (colorSpace->getMode() == csSeparation) {
+ sepCS = (GfxSeparationColorSpace *)colorSpace;
+ colorSpace2 = sepCS->getAlt();
+ nComps2 = colorSpace2->getNComps();
+ sepFunc = sepCS->getFunc();
+ for (k = 0; k < nComps2; ++k) {
+ lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
+ sizeof(GfxColorComp));
+ }
+ for (i = 0; i <= maxPixel; ++i) {
+ double t = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
+ if (t < defaultLow[0]) {
+ t = defaultLow[0];
+ } else if (t > defaultLow[0] + defaultRange[0]) {
+ t = defaultLow[0] + defaultRange[0];
+ }
+ x[0] = t;
+ sepFunc->transform(x, y);
+ for (k = 0; k < nComps2; ++k) {
+ lookup2[k][i] = dblToCol(y[k]);
+ }
+ }
+ }
+
+ return;
+
+ err2:
+ obj.free();
+ err1:
+ ok = gFalse;
+}
+
+GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
+ int n, i, k;
+
+ colorSpace = colorMap->colorSpace->copy();
+ bits = colorMap->bits;
+ nComps = colorMap->nComps;
+ nComps2 = colorMap->nComps2;
+ colorSpace2 = NULL;
+ for (k = 0; k < gfxColorMaxComps; ++k) {
+ lookup[k] = NULL;
+ lookup2[k] = NULL;
+ }
+ if (bits <= 8) {
+ n = 1 << bits;
+ } else {
+ n = 256;
+ }
+ for (k = 0; k < nComps; ++k) {
+ lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
+ memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
+ }
+ if (colorSpace->getMode() == csIndexed) {
+ colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase();
+ for (k = 0; k < nComps2; ++k) {
+ lookup2[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
+ memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp));
+ }
+ } else if (colorSpace->getMode() == csSeparation) {
+ colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt();
+ for (k = 0; k < nComps2; ++k) {
+ lookup2[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
+ memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp));
+ }
+ }
+ for (i = 0; i < nComps; ++i) {
+ decodeLow[i] = colorMap->decodeLow[i];
+ decodeRange[i] = colorMap->decodeRange[i];
+ }
+ ok = gTrue;
+}
+
+GfxImageColorMap::~GfxImageColorMap() {
+ int i;
+
+ delete colorSpace;
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ gfree(lookup[i]);
+ gfree(lookup2[i]);
+ }
+}
+
+void GfxImageColorMap::getGray(Guchar *x, GfxGray *gray,
+ GfxRenderingIntent ri) {
+ GfxColor color;
+ int i;
+
+ if (colorSpace2) {
+ for (i = 0; i < nComps2; ++i) {
+ color.c[i] = lookup2[i][x[0]];
+ }
+ colorSpace2->getGray(&color, gray, ri);
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ color.c[i] = lookup[i][x[i]];
+ }
+ colorSpace->getGray(&color, gray, ri);
+ }
+}
+
+void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb, GfxRenderingIntent ri) {
+ GfxColor color;
+ int i;
+
+ if (colorSpace2) {
+ for (i = 0; i < nComps2; ++i) {
+ color.c[i] = lookup2[i][x[0]];
+ }
+ colorSpace2->getRGB(&color, rgb, ri);
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ color.c[i] = lookup[i][x[i]];
+ }
+ colorSpace->getRGB(&color, rgb, ri);
+ }
+}
+
+void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) {
+ GfxColor color;
+ int i;
+
+ if (colorSpace2) {
+ for (i = 0; i < nComps2; ++i) {
+ color.c[i] = lookup2[i][x[0]];
+ }
+ colorSpace2->getCMYK(&color, cmyk, ri);
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ color.c[i] = lookup[i][x[i]];
+ }
+ colorSpace->getCMYK(&color, cmyk, ri);
+ }
+}
+
+
+void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ color->c[i] = lookup[i][x[i]];
+ }
+}
+
+void GfxImageColorMap::getGrayByteLine(Guchar *in, Guchar *out, int n,
+ GfxRenderingIntent ri) {
+ GfxColor color;
+ GfxGray gray;
+ int i, j;
+
+ if (colorSpace2) {
+ for (j = 0; j < n; ++j) {
+ for (i = 0; i < nComps2; ++i) {
+ color.c[i] = lookup2[i][in[j]];
+ }
+ colorSpace2->getGray(&color, &gray, ri);
+ out[j] = colToByte(gray);
+ }
+ } else {
+ for (j = 0; j < n; ++j) {
+ for (i = 0; i < nComps; ++i) {
+ color.c[i] = lookup[i][in[j * nComps + i]];
+ }
+ colorSpace->getGray(&color, &gray, ri);
+ out[j] = colToByte(gray);
+ }
+ }
+}
+
+void GfxImageColorMap::getRGBByteLine(Guchar *in, Guchar *out, int n,
+ GfxRenderingIntent ri) {
+ GfxColor color;
+ GfxRGB rgb;
+ int i, j;
+
+ if (colorSpace2) {
+ for (j = 0; j < n; ++j) {
+ for (i = 0; i < nComps2; ++i) {
+ color.c[i] = lookup2[i][in[j]];
+ }
+ colorSpace2->getRGB(&color, &rgb, ri);
+ out[j*3] = colToByte(rgb.r);
+ out[j*3 + 1] = colToByte(rgb.g);
+ out[j*3 + 2] = colToByte(rgb.b);
+ }
+ } else {
+ for (j = 0; j < n; ++j) {
+ for (i = 0; i < nComps; ++i) {
+ color.c[i] = lookup[i][in[j * nComps + i]];
+ }
+ colorSpace->getRGB(&color, &rgb, ri);
+ out[j*3] = colToByte(rgb.r);
+ out[j*3 + 1] = colToByte(rgb.g);
+ out[j*3 + 2] = colToByte(rgb.b);
+ }
+ }
+}
+
+void GfxImageColorMap::getCMYKByteLine(Guchar *in, Guchar *out, int n,
+ GfxRenderingIntent ri) {
+ GfxColor color;
+ GfxCMYK cmyk;
+ int i, j;
+
+ if (colorSpace2) {
+ for (j = 0; j < n; ++j) {
+ for (i = 0; i < nComps2; ++i) {
+ color.c[i] = lookup2[i][in[j]];
+ }
+ colorSpace2->getCMYK(&color, &cmyk, ri);
+ out[j*4] = colToByte(cmyk.c);
+ out[j*4 + 1] = colToByte(cmyk.m);
+ out[j*4 + 2] = colToByte(cmyk.y);
+ out[j*4 + 3] = colToByte(cmyk.k);
+ }
+ } else {
+ for (j = 0; j < n; ++j) {
+ for (i = 0; i < nComps; ++i) {
+ color.c[i] = lookup[i][in[j * nComps + i]];
+ }
+ colorSpace->getCMYK(&color, &cmyk, ri);
+ out[j*4] = colToByte(cmyk.c);
+ out[j*4 + 1] = colToByte(cmyk.m);
+ out[j*4 + 2] = colToByte(cmyk.y);
+ out[j*4 + 3] = colToByte(cmyk.k);
+ }
+ }
+}
+
+
+//------------------------------------------------------------------------
+// GfxSubpath and GfxPath
+//------------------------------------------------------------------------
+
+GfxSubpath::GfxSubpath(double x1, double y1) {
+ size = 16;
+ x = (double *)gmallocn(size, sizeof(double));
+ y = (double *)gmallocn(size, sizeof(double));
+ curve = (GBool *)gmallocn(size, sizeof(GBool));
+ n = 1;
+ x[0] = x1;
+ y[0] = y1;
+ curve[0] = gFalse;
+ closed = gFalse;
+}
+
+GfxSubpath::~GfxSubpath() {
+ gfree(x);
+ gfree(y);
+ gfree(curve);
+}
+
+// Used for copy().
+GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
+ size = subpath->size;
+ n = subpath->n;
+ x = (double *)gmallocn(size, sizeof(double));
+ y = (double *)gmallocn(size, sizeof(double));
+ curve = (GBool *)gmallocn(size, sizeof(GBool));
+ memcpy(x, subpath->x, n * sizeof(double));
+ memcpy(y, subpath->y, n * sizeof(double));
+ memcpy(curve, subpath->curve, n * sizeof(GBool));
+ closed = subpath->closed;
+}
+
+void GfxSubpath::lineTo(double x1, double y1) {
+ if (n >= size) {
+ size *= 2;
+ x = (double *)greallocn(x, size, sizeof(double));
+ y = (double *)greallocn(y, size, sizeof(double));
+ curve = (GBool *)greallocn(curve, size, sizeof(GBool));
+ }
+ x[n] = x1;
+ y[n] = y1;
+ curve[n] = gFalse;
+ ++n;
+}
+
+void GfxSubpath::curveTo(double x1, double y1, double x2, double y2,
+ double x3, double y3) {
+ if (n+3 > size) {
+ size *= 2;
+ x = (double *)greallocn(x, size, sizeof(double));
+ y = (double *)greallocn(y, size, sizeof(double));
+ curve = (GBool *)greallocn(curve, size, sizeof(GBool));
+ }
+ x[n] = x1;
+ y[n] = y1;
+ x[n+1] = x2;
+ y[n+1] = y2;
+ x[n+2] = x3;
+ y[n+2] = y3;
+ curve[n] = curve[n+1] = gTrue;
+ curve[n+2] = gFalse;
+ n += 3;
+}
+
+void GfxSubpath::close() {
+ if (x[n-1] != x[0] || y[n-1] != y[0]) {
+ lineTo(x[0], y[0]);
+ }
+ closed = gTrue;
+}
+
+void GfxSubpath::offset(double dx, double dy) {
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ x[i] += dx;
+ y[i] += dy;
+ }
+}
+
+GfxPath::GfxPath() {
+ justMoved = gFalse;
+ size = 16;
+ n = 0;
+ firstX = firstY = 0;
+ subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
+}
+
+GfxPath::~GfxPath() {
+ int i;
+
+ for (i = 0; i < n; ++i)
+ delete subpaths[i];
+ gfree(subpaths);
+}
+
+// Used for copy().
+GfxPath::GfxPath(GBool justMoved1, double firstX1, double firstY1,
+ GfxSubpath **subpaths1, int n1, int size1) {
+ int i;
+
+ justMoved = justMoved1;
+ firstX = firstX1;
+ firstY = firstY1;
+ size = size1;
+ n = n1;
+ subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
+ for (i = 0; i < n; ++i)
+ subpaths[i] = subpaths1[i]->copy();
+}
+
+void GfxPath::moveTo(double x, double y) {
+ justMoved = gTrue;
+ firstX = x;
+ firstY = y;
+}
+
+void GfxPath::lineTo(double x, double y) {
+ if (justMoved || (n > 0 && subpaths[n-1]->isClosed())) {
+ if (n >= size) {
+ size *= 2;
+ subpaths = (GfxSubpath **)
+ greallocn(subpaths, size, sizeof(GfxSubpath *));
+ }
+ if (justMoved) {
+ subpaths[n] = new GfxSubpath(firstX, firstY);
+ } else {
+ subpaths[n] = new GfxSubpath(subpaths[n-1]->getLastX(),
+ subpaths[n-1]->getLastY());
+ }
+ ++n;
+ justMoved = gFalse;
+ }
+ subpaths[n-1]->lineTo(x, y);
+}
+
+void GfxPath::curveTo(double x1, double y1, double x2, double y2,
+ double x3, double y3) {
+ if (justMoved || (n > 0 && subpaths[n-1]->isClosed())) {
+ if (n >= size) {
+ size *= 2;
+ subpaths = (GfxSubpath **)
+ greallocn(subpaths, size, sizeof(GfxSubpath *));
+ }
+ if (justMoved) {
+ subpaths[n] = new GfxSubpath(firstX, firstY);
+ } else {
+ subpaths[n] = new GfxSubpath(subpaths[n-1]->getLastX(),
+ subpaths[n-1]->getLastY());
+ }
+ ++n;
+ justMoved = gFalse;
+ }
+ subpaths[n-1]->curveTo(x1, y1, x2, y2, x3, y3);
+}
+
+void GfxPath::close() {
+ // this is necessary to handle the pathological case of
+ // moveto/closepath/clip, which defines an empty clipping region
+ if (justMoved) {
+ if (n >= size) {
+ size *= 2;
+ subpaths = (GfxSubpath **)
+ greallocn(subpaths, size, sizeof(GfxSubpath *));
+ }
+ subpaths[n] = new GfxSubpath(firstX, firstY);
+ ++n;
+ justMoved = gFalse;
+ }
+ subpaths[n-1]->close();
+}
+
+void GfxPath::append(GfxPath *path) {
+ int i;
+
+ if (n + path->n > size) {
+ size = n + path->n;
+ subpaths = (GfxSubpath **)
+ greallocn(subpaths, size, sizeof(GfxSubpath *));
+ }
+ for (i = 0; i < path->n; ++i) {
+ subpaths[n++] = path->subpaths[i]->copy();
+ }
+ justMoved = gFalse;
+}
+
+void GfxPath::offset(double dx, double dy) {
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ subpaths[i]->offset(dx, dy);
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxState
+//------------------------------------------------------------------------
+
+GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
+ int rotateA, GBool upsideDown
+ ) {
+ double kx, ky;
+
+ hDPI = hDPIA;
+ vDPI = vDPIA;
+ rotate = rotateA;
+ px1 = pageBox->x1;
+ py1 = pageBox->y1;
+ px2 = pageBox->x2;
+ py2 = pageBox->y2;
+ kx = hDPI / 72.0;
+ ky = vDPI / 72.0;
+ if (rotate == 90) {
+ ctm[0] = 0;
+ ctm[1] = upsideDown ? ky : -ky;
+ ctm[2] = kx;
+ ctm[3] = 0;
+ ctm[4] = -kx * py1;
+ ctm[5] = ky * (upsideDown ? -px1 : px2);
+ pageWidth = kx * (py2 - py1);
+ pageHeight = ky * (px2 - px1);
+ } else if (rotate == 180) {
+ ctm[0] = -kx;
+ ctm[1] = 0;
+ ctm[2] = 0;
+ ctm[3] = upsideDown ? ky : -ky;
+ ctm[4] = kx * px2;
+ ctm[5] = ky * (upsideDown ? -py1 : py2);
+ pageWidth = kx * (px2 - px1);
+ pageHeight = ky * (py2 - py1);
+ } else if (rotate == 270) {
+ ctm[0] = 0;
+ ctm[1] = upsideDown ? -ky : ky;
+ ctm[2] = -kx;
+ ctm[3] = 0;
+ ctm[4] = kx * py2;
+ ctm[5] = ky * (upsideDown ? px2 : -px1);
+ pageWidth = kx * (py2 - py1);
+ pageHeight = ky * (px2 - px1);
+ } else {
+ ctm[0] = kx;
+ ctm[1] = 0;
+ ctm[2] = 0;
+ ctm[3] = upsideDown ? -ky : ky;
+ ctm[4] = -kx * px1;
+ ctm[5] = ky * (upsideDown ? py2 : -py1);
+ pageWidth = kx * (px2 - px1);
+ pageHeight = ky * (py2 - py1);
+ }
+
+ fillColorSpace = GfxColorSpace::create(csDeviceGray);
+ strokeColorSpace = GfxColorSpace::create(csDeviceGray);
+ fillColor.c[0] = 0;
+ strokeColor.c[0] = 0;
+ fillPattern = NULL;
+ strokePattern = NULL;
+ blendMode = gfxBlendNormal;
+ fillOpacity = 1;
+ strokeOpacity = 1;
+ fillOverprint = gFalse;
+ strokeOverprint = gFalse;
+ renderingIntent = gfxRenderingIntentRelativeColorimetric;
+ overprintMode = 0;
+ transfer[0] = transfer[1] = transfer[2] = transfer[3] = NULL;
+
+ lineWidth = 1;
+ lineDash = NULL;
+ lineDashLength = 0;
+ lineDashStart = 0;
+ flatness = 1;
+ lineJoin = 0;
+ lineCap = 0;
+ miterLimit = 10;
+ strokeAdjust = gFalse;
+
+ font = NULL;
+ fontSize = 0;
+ textMat[0] = 1; textMat[1] = 0;
+ textMat[2] = 0; textMat[3] = 1;
+ textMat[4] = 0; textMat[5] = 0;
+ charSpace = 0;
+ wordSpace = 0;
+ horizScaling = 1;
+ leading = 0;
+ rise = 0;
+ render = 0;
+
+ path = new GfxPath();
+ curX = curY = 0;
+ lineX = lineY = 0;
+
+ clipXMin = 0;
+ clipYMin = 0;
+ clipXMax = pageWidth;
+ clipYMax = pageHeight;
+
+ ignoreColorOps = gFalse;
+
+ saved = NULL;
+}
+
+GfxState::~GfxState() {
+ int i;
+
+ if (fillColorSpace) {
+ delete fillColorSpace;
+ }
+ if (strokeColorSpace) {
+ delete strokeColorSpace;
+ }
+ if (fillPattern) {
+ delete fillPattern;
+ }
+ if (strokePattern) {
+ delete strokePattern;
+ }
+ for (i = 0; i < 4; ++i) {
+ if (transfer[i]) {
+ delete transfer[i];
+ }
+ }
+ gfree(lineDash);
+ if (path) {
+ // this gets set to NULL by restore()
+ delete path;
+ }
+}
+
+// Used for copy();
+GfxState::GfxState(GfxState *state, GBool copyPath) {
+ int i;
+
+ memcpy(this, state, sizeof(GfxState));
+ if (fillColorSpace) {
+ fillColorSpace = state->fillColorSpace->copy();
+ }
+ if (strokeColorSpace) {
+ strokeColorSpace = state->strokeColorSpace->copy();
+ }
+ if (fillPattern) {
+ fillPattern = state->fillPattern->copy();
+ }
+ if (strokePattern) {
+ strokePattern = state->strokePattern->copy();
+ }
+ for (i = 0; i < 4; ++i) {
+ if (transfer[i]) {
+ transfer[i] = state->transfer[i]->copy();
+ }
+ }
+ if (lineDashLength > 0) {
+ lineDash = (double *)gmallocn(lineDashLength, sizeof(double));
+ memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double));
+ }
+ if (copyPath) {
+ path = state->path->copy();
+ }
+ saved = NULL;
+}
+
+void GfxState::setPath(GfxPath *pathA) {
+ delete path;
+ path = pathA;
+}
+
+void GfxState::getUserClipBBox(double *xMin, double *yMin,
+ double *xMax, double *yMax) {
+ double ictm[6];
+ double xMin1, yMin1, xMax1, yMax1, det, tx, ty;
+
+ // invert the 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;
+
+ // transform all four corners of the clip bbox; find the min and max
+ // x and y values
+ xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4];
+ yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5];
+ tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4];
+ ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5];
+ if (tx < xMin1) {
+ xMin1 = tx;
+ } else if (tx > xMax1) {
+ xMax1 = tx;
+ }
+ if (ty < yMin1) {
+ yMin1 = ty;
+ } else if (ty > yMax1) {
+ yMax1 = ty;
+ }
+ tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4];
+ ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5];
+ if (tx < xMin1) {
+ xMin1 = tx;
+ } else if (tx > xMax1) {
+ xMax1 = tx;
+ }
+ if (ty < yMin1) {
+ yMin1 = ty;
+ } else if (ty > yMax1) {
+ yMax1 = ty;
+ }
+ tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4];
+ ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5];
+ if (tx < xMin1) {
+ xMin1 = tx;
+ } else if (tx > xMax1) {
+ xMax1 = tx;
+ }
+ if (ty < yMin1) {
+ yMin1 = ty;
+ } else if (ty > yMax1) {
+ yMax1 = ty;
+ }
+
+ *xMin = xMin1;
+ *yMin = yMin1;
+ *xMax = xMax1;
+ *yMax = yMax1;
+}
+
+double GfxState::transformWidth(double w) {
+ double x, y;
+
+ x = ctm[0] + ctm[2];
+ y = ctm[1] + ctm[3];
+ return w * sqrt(0.5 * (x * x + y * y));
+}
+
+double GfxState::getTransformedFontSize() {
+ double x1, y1, x2, y2;
+
+ x1 = textMat[2] * fontSize;
+ y1 = textMat[3] * fontSize;
+ x2 = ctm[0] * x1 + ctm[2] * y1;
+ y2 = ctm[1] * x1 + ctm[3] * y1;
+ return sqrt(x2 * x2 + y2 * y2);
+}
+
+void GfxState::getFontTransMat(double *m11, double *m12,
+ double *m21, double *m22) {
+ *m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize * horizScaling;
+ *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize * horizScaling;
+ *m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize;
+ *m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize;
+}
+
+void GfxState::setCTM(double a, double b, double c,
+ double d, double e, double f) {
+ int i;
+
+ ctm[0] = a;
+ ctm[1] = b;
+ ctm[2] = c;
+ ctm[3] = d;
+ ctm[4] = e;
+ ctm[5] = f;
+
+ // avoid FP exceptions on badly messed up PDF files
+ for (i = 0; i < 6; ++i) {
+ if (ctm[i] > 1e10) {
+ ctm[i] = 1e10;
+ } else if (ctm[i] < -1e10) {
+ ctm[i] = -1e10;
+ }
+ }
+}
+
+void GfxState::concatCTM(double a, double b, double c,
+ double d, double e, double f) {
+ double a1 = ctm[0];
+ double b1 = ctm[1];
+ double c1 = ctm[2];
+ double d1 = ctm[3];
+ int i;
+
+ ctm[0] = a * a1 + b * c1;
+ ctm[1] = a * b1 + b * d1;
+ ctm[2] = c * a1 + d * c1;
+ ctm[3] = c * b1 + d * d1;
+ ctm[4] = e * a1 + f * c1 + ctm[4];
+ ctm[5] = e * b1 + f * d1 + ctm[5];
+
+ // avoid FP exceptions on badly messed up PDF files
+ for (i = 0; i < 6; ++i) {
+ if (ctm[i] > 1e10) {
+ ctm[i] = 1e10;
+ } else if (ctm[i] < -1e10) {
+ ctm[i] = -1e10;
+ }
+ }
+}
+
+void GfxState::shiftCTM(double tx, double ty) {
+ ctm[4] += tx;
+ ctm[5] += ty;
+ clipXMin += tx;
+ clipYMin += ty;
+ clipXMax += tx;
+ clipYMax += ty;
+}
+
+void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) {
+ if (fillColorSpace) {
+ delete fillColorSpace;
+ }
+ fillColorSpace = colorSpace;
+}
+
+void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) {
+ if (strokeColorSpace) {
+ delete strokeColorSpace;
+ }
+ strokeColorSpace = colorSpace;
+}
+
+void GfxState::setFillPattern(GfxPattern *pattern) {
+ if (fillPattern) {
+ delete fillPattern;
+ }
+ fillPattern = pattern;
+}
+
+void GfxState::setStrokePattern(GfxPattern *pattern) {
+ if (strokePattern) {
+ delete strokePattern;
+ }
+ strokePattern = pattern;
+}
+
+void GfxState::setTransfer(Function **funcs) {
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ if (transfer[i]) {
+ delete transfer[i];
+ }
+ transfer[i] = funcs[i];
+ }
+}
+
+void GfxState::setLineDash(double *dash, int length, double start) {
+ if (lineDash)
+ gfree(lineDash);
+ lineDash = dash;
+ lineDashLength = length;
+ lineDashStart = start;
+}
+
+void GfxState::clearPath() {
+ delete path;
+ path = new GfxPath();
+}
+
+void GfxState::clip() {
+ double xMin, yMin, xMax, yMax, x, y;
+ GfxSubpath *subpath;
+ int i, j;
+
+ xMin = xMax = yMin = yMax = 0; // make gcc happy
+ for (i = 0; i < path->getNumSubpaths(); ++i) {
+ subpath = path->getSubpath(i);
+ for (j = 0; j < subpath->getNumPoints(); ++j) {
+ transform(subpath->getX(j), subpath->getY(j), &x, &y);
+ if (i == 0 && j == 0) {
+ xMin = xMax = x;
+ yMin = yMax = y;
+ } else {
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ }
+ }
+ }
+ if (xMin > clipXMin) {
+ clipXMin = xMin;
+ }
+ if (yMin > clipYMin) {
+ clipYMin = yMin;
+ }
+ if (xMax < clipXMax) {
+ clipXMax = xMax;
+ }
+ if (yMax < clipYMax) {
+ clipYMax = yMax;
+ }
+}
+
+void GfxState::clipToStrokePath() {
+ double xMin, yMin, xMax, yMax, x, y, t0, t1;
+ GfxSubpath *subpath;
+ int i, j;
+
+ xMin = xMax = yMin = yMax = 0; // make gcc happy
+ for (i = 0; i < path->getNumSubpaths(); ++i) {
+ subpath = path->getSubpath(i);
+ for (j = 0; j < subpath->getNumPoints(); ++j) {
+ transform(subpath->getX(j), subpath->getY(j), &x, &y);
+ if (i == 0 && j == 0) {
+ xMin = xMax = x;
+ yMin = yMax = y;
+ } else {
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ }
+ }
+ }
+
+ // allow for the line width
+ //~ miter joins can extend farther than this
+ t0 = fabs(ctm[0]);
+ t1 = fabs(ctm[2]);
+ if (t0 > t1) {
+ xMin -= 0.5 * lineWidth * t0;
+ xMax += 0.5 * lineWidth * t0;
+ } else {
+ xMin -= 0.5 * lineWidth * t1;
+ xMax += 0.5 * lineWidth * t1;
+ }
+ t0 = fabs(ctm[0]);
+ t1 = fabs(ctm[3]);
+ if (t0 > t1) {
+ yMin -= 0.5 * lineWidth * t0;
+ yMax += 0.5 * lineWidth * t0;
+ } else {
+ yMin -= 0.5 * lineWidth * t1;
+ yMax += 0.5 * lineWidth * t1;
+ }
+
+ if (xMin > clipXMin) {
+ clipXMin = xMin;
+ }
+ if (yMin > clipYMin) {
+ clipYMin = yMin;
+ }
+ if (xMax < clipXMax) {
+ clipXMax = xMax;
+ }
+ if (yMax < clipYMax) {
+ clipYMax = yMax;
+ }
+}
+
+void GfxState::clipToRect(double xMin, double yMin, double xMax, double yMax) {
+ double x, y, xMin1, yMin1, xMax1, yMax1;
+
+ transform(xMin, yMin, &x, &y);
+ xMin1 = xMax1 = x;
+ yMin1 = yMax1 = y;
+ transform(xMax, yMin, &x, &y);
+ if (x < xMin1) {
+ xMin1 = x;
+ } else if (x > xMax1) {
+ xMax1 = x;
+ }
+ if (y < yMin1) {
+ yMin1 = y;
+ } else if (y > yMax1) {
+ yMax1 = y;
+ }
+ transform(xMax, yMax, &x, &y);
+ if (x < xMin1) {
+ xMin1 = x;
+ } else if (x > xMax1) {
+ xMax1 = x;
+ }
+ if (y < yMin1) {
+ yMin1 = y;
+ } else if (y > yMax1) {
+ yMax1 = y;
+ }
+ transform(xMin, yMax, &x, &y);
+ if (x < xMin1) {
+ xMin1 = x;
+ } else if (x > xMax1) {
+ xMax1 = x;
+ }
+ if (y < yMin1) {
+ yMin1 = y;
+ } else if (y > yMax1) {
+ yMax1 = y;
+ }
+
+ if (xMin1 > clipXMin) {
+ clipXMin = xMin1;
+ }
+ if (yMin1 > clipYMin) {
+ clipYMin = yMin1;
+ }
+ if (xMax1 < clipXMax) {
+ clipXMax = xMax1;
+ }
+ if (yMax1 < clipYMax) {
+ clipYMax = yMax1;
+ }
+}
+
+void GfxState::textShift(double tx, double ty) {
+ double dx, dy;
+
+ textTransformDelta(tx, ty, &dx, &dy);
+ curX += dx;
+ curY += dy;
+}
+
+void GfxState::shift(double dx, double dy) {
+ curX += dx;
+ curY += dy;
+}
+
+GfxState *GfxState::save() {
+ GfxState *newState;
+
+ newState = copy();
+ newState->saved = this;
+ return newState;
+}
+
+GfxState *GfxState::restore() {
+ GfxState *oldState;
+
+ if (saved) {
+ oldState = saved;
+
+ // these attributes aren't saved/restored by the q/Q operators
+ oldState->path = path;
+ oldState->curX = curX;
+ oldState->curY = curY;
+ oldState->lineX = lineX;
+ oldState->lineY = lineY;
+
+ path = NULL;
+ saved = NULL;
+ delete this;
+
+ } else {
+ oldState = this;
+ }
+
+ return oldState;
+}
+
+GBool GfxState::parseBlendMode(Object *obj, GfxBlendMode *mode) {
+ Object obj2;
+ int i, j;
+
+ if (obj->isName()) {
+ for (i = 0; i < nGfxBlendModeNames; ++i) {
+ if (!strcmp(obj->getName(), gfxBlendModeNames[i].name)) {
+ *mode = gfxBlendModeNames[i].mode;
+ return gTrue;
+ }
+ }
+ return gFalse;
+ } else if (obj->isArray()) {
+ for (i = 0; i < obj->arrayGetLength(); ++i) {
+ obj->arrayGet(i, &obj2);
+ if (!obj2.isName()) {
+ obj2.free();
+ return gFalse;
+ }
+ for (j = 0; j < nGfxBlendModeNames; ++j) {
+ if (!strcmp(obj2.getName(), gfxBlendModeNames[j].name)) {
+ obj2.free();
+ *mode = gfxBlendModeNames[j].mode;
+ return gTrue;
+ }
+ }
+ obj2.free();
+ }
+ *mode = gfxBlendNormal;
+ return gTrue;
+ } else {
+ return gFalse;
+ }
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h
new file mode 100644
index 00000000000..f9c0cfcad6e
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GfxState.h
@@ -0,0 +1,1365 @@
+//========================================================================
+//
+// GfxState.h
+//
+// Copyright 1996-2016 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GFXSTATE_H
+#define GFXSTATE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+#include "Function.h"
+
+class Array;
+class GfxFont;
+class PDFRectangle;
+class GfxDeviceNColorSpace;
+class GfxSeparationColorSpace;
+class GfxShading;
+class GfxState;
+
+//------------------------------------------------------------------------
+// GfxBlendMode
+//------------------------------------------------------------------------
+
+enum GfxBlendMode {
+ gfxBlendNormal,
+ gfxBlendMultiply,
+ gfxBlendScreen,
+ gfxBlendOverlay,
+ gfxBlendDarken,
+ gfxBlendLighten,
+ gfxBlendColorDodge,
+ gfxBlendColorBurn,
+ gfxBlendHardLight,
+ gfxBlendSoftLight,
+ gfxBlendDifference,
+ gfxBlendExclusion,
+ gfxBlendHue,
+ gfxBlendSaturation,
+ gfxBlendColor,
+ gfxBlendLuminosity
+};
+
+//------------------------------------------------------------------------
+// GfxRenderingIntent
+//------------------------------------------------------------------------
+
+enum GfxRenderingIntent {
+ gfxRenderingIntentAbsoluteColorimetric,
+ gfxRenderingIntentRelativeColorimetric,
+ gfxRenderingIntentSaturation,
+ gfxRenderingIntentPerceptual
+};
+
+#define gfxNumRenderingIntents 4
+
+//------------------------------------------------------------------------
+// GfxColorComp
+//------------------------------------------------------------------------
+
+// 16.16 fixed point color component
+typedef int GfxColorComp;
+
+#define gfxColorComp1 0x10000
+
+static inline GfxColorComp dblToCol(double x) {
+ return (GfxColorComp)(x * gfxColorComp1);
+}
+
+static inline double colToDbl(GfxColorComp x) {
+ return (double)x / (double)gfxColorComp1;
+}
+
+static inline GfxColorComp byteToCol(Guchar x) {
+ // (x / 255) << 16 = (0.0000000100000001... * x) << 16
+ // = ((x << 8) + (x) + (x >> 8) + ...)
+ // = (x << 8) + (x) + (x >> 7)
+ // [for rounding]
+ return (GfxColorComp)((x << 8) + x + (x >> 7));
+}
+
+static inline GfxColorComp wordToCol(Gushort x) {
+ // (x / 65535) << 16 = (0.0000000000000001... * x) << 16
+ // = x + (x >> 15)
+ // [for rounding]
+ return (GfxColorComp)(x + (x >> 15));
+}
+
+static inline Guchar colToByte(GfxColorComp x) {
+ // 255 * x + 0.5 = 256 * x - x + 0.5
+ // = [256 * (x << 16) - (x << 16) + (1 << 15)] >> 16
+ return (Guchar)(((x << 8) - x + 0x8000) >> 16);
+}
+
+static inline Gushort colToWord(GfxColorComp x) {
+ // 65535 * x + 0.5 = 65536 * x - x + 0.5
+ // = [65536 * (x << 16) - (x << 16) + (1 << 15)] >> 16
+ return (Gushort)(((x << 16) - x + 0x8000) >> 16);
+}
+
+//------------------------------------------------------------------------
+// GfxColor
+//------------------------------------------------------------------------
+
+#define gfxColorMaxComps funcMaxOutputs
+
+struct GfxColor {
+ GfxColorComp c[gfxColorMaxComps];
+};
+
+//------------------------------------------------------------------------
+// GfxGray
+//------------------------------------------------------------------------
+
+typedef GfxColorComp GfxGray;
+
+//------------------------------------------------------------------------
+// GfxRGB
+//------------------------------------------------------------------------
+
+struct GfxRGB {
+ GfxColorComp r, g, b;
+};
+
+//------------------------------------------------------------------------
+// GfxCMYK
+//------------------------------------------------------------------------
+
+struct GfxCMYK {
+ GfxColorComp c, m, y, k;
+};
+
+
+
+
+
+//------------------------------------------------------------------------
+// GfxColorSpace
+//------------------------------------------------------------------------
+
+// NB: The nGfxColorSpaceModes constant and the gfxColorSpaceModeNames
+// array defined in GfxState.cc must match this enum.
+enum GfxColorSpaceMode {
+ csDeviceGray,
+ csCalGray,
+ csDeviceRGB,
+ csCalRGB,
+ csDeviceCMYK,
+ csLab,
+ csICCBased,
+ csIndexed,
+ csSeparation,
+ csDeviceN,
+ csPattern
+};
+
+class GfxColorSpace {
+public:
+
+ GfxColorSpace();
+ virtual ~GfxColorSpace();
+ virtual GfxColorSpace *copy() = 0;
+ virtual GfxColorSpaceMode getMode() = 0;
+
+ // Construct a color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Object *csObj,
+ int recursion = 0);
+
+ // Construct a simple color space. The <mode> argument can be
+ // csDeviceGray, csDeviceRGB, or csDeviceCMYK.
+ static GfxColorSpace *create(GfxColorSpaceMode mode);
+
+
+ // Convert to gray, RGB, or CMYK.
+ virtual void getGray(GfxColor *color, GfxGray *gray,
+ GfxRenderingIntent ri) = 0;
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb,
+ GfxRenderingIntent ri) = 0;
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk,
+ GfxRenderingIntent ri) = 0;
+
+ // Return the number of color components.
+ virtual int getNComps() = 0;
+
+ // Get this color space's default color.
+ virtual void getDefaultColor(GfxColor *color) = 0;
+
+ // Return the default ranges for each component, assuming an image
+ // with a max pixel value of <maxImgPixel>.
+ virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
+ int maxImgPixel);
+
+ // Returns true if painting operations in this color space never
+ // mark the page (e.g., the "None" colorant).
+ virtual GBool isNonMarking() { return gFalse; }
+
+
+ // 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();
+
+ // Return the name of the <idx>th color space mode.
+ static const char *getColorSpaceModeName(int idx);
+
+protected:
+
+ Guint overprintMask;
+ GBool defaultColorSpace;
+};
+
+//------------------------------------------------------------------------
+// GfxDeviceGrayColorSpace
+//------------------------------------------------------------------------
+
+class GfxDeviceGrayColorSpace: public GfxColorSpace {
+public:
+
+ GfxDeviceGrayColorSpace();
+ virtual ~GfxDeviceGrayColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csDeviceGray; }
+
+ virtual void getGray(GfxColor *color, GfxGray *gray, GfxRenderingIntent ri);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb, GfxRenderingIntent ri);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk, GfxRenderingIntent ri);
+
+ virtual int getNComps() { return 1; }
+ virtual void getDefaultColor(GfxColor *color);
+
+private:
+};
+
+//------------------------------------------------------------------------
+// GfxCalGrayColorSpace
+//------------------------------------------------------------------------
+
+class GfxCalGrayColorSpace: public GfxColorSpace {
+public:
+
+ GfxCalGrayColorSpace();
+ virtual ~GfxCalGrayColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csCalGray; }
+
+ // Construct a CalGray color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr, int recursion);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray, GfxRenderingIntent ri);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb, GfxRenderingIntent ri);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk, GfxRenderingIntent ri);
+
+ virtual int getNComps() { return 1; }
+ virtual void getDefaultColor(GfxColor *color);
+
+ // CalGray-specific access.
+ double getWhiteX() { return whiteX; }
+ double getWhiteY() { return whiteY; }
+ double getWhiteZ() { return whiteZ; }
+ double getBlackX() { return blackX; }
+ double getBlackY() { return blackY; }
+ double getBlackZ() { return blackZ; }
+ double getGamma() { return gamma; }
+
+private:
+
+ double whiteX, whiteY, whiteZ; // white point
+ double blackX, blackY, blackZ; // black point
+ double gamma; // gamma value
+};
+
+//------------------------------------------------------------------------
+// GfxDeviceRGBColorSpace
+//------------------------------------------------------------------------
+
+class GfxDeviceRGBColorSpace: public GfxColorSpace {
+public:
+
+ GfxDeviceRGBColorSpace();
+ virtual ~GfxDeviceRGBColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csDeviceRGB; }
+
+ virtual void getGray(GfxColor *color, GfxGray *gray, GfxRenderingIntent ri);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb, GfxRenderingIntent ri);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk, GfxRenderingIntent ri);
+
+ virtual int getNComps() { return 3; }
+ virtual void getDefaultColor(GfxColor *color);
+
+private:
+};
+
+//------------------------------------------------------------------------
+// GfxCalRGBColorSpace
+//------------------------------------------------------------------------
+
+class GfxCalRGBColorSpace: public GfxColorSpace {
+public:
+
+ GfxCalRGBColorSpace();
+ virtual ~GfxCalRGBColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csCalRGB; }
+
+ // Construct a CalRGB color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr, int recursion);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray, GfxRenderingIntent ri);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb, GfxRenderingIntent ri);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk, GfxRenderingIntent ri);
+
+ virtual int getNComps() { return 3; }
+ virtual void getDefaultColor(GfxColor *color);
+
+ // CalRGB-specific access.
+ double getWhiteX() { return whiteX; }
+ double getWhiteY() { return whiteY; }
+ double getWhiteZ() { return whiteZ; }
+ double getBlackX() { return blackX; }
+ double getBlackY() { return blackY; }
+ double getBlackZ() { return blackZ; }
+ double getGammaR() { return gammaR; }
+ double getGammaG() { return gammaG; }
+ double getGammaB() { return gammaB; }
+ double *getMatrix() { return mat; }
+
+private:
+
+ double whiteX, whiteY, whiteZ; // white point
+ double blackX, blackY, blackZ; // black point
+ double gammaR, gammaG, gammaB; // gamma values
+ double mat[9]; // ABC -> XYZ transform matrix
+};
+
+//------------------------------------------------------------------------
+// GfxDeviceCMYKColorSpace
+//------------------------------------------------------------------------
+
+class GfxDeviceCMYKColorSpace: public GfxColorSpace {
+public:
+
+ GfxDeviceCMYKColorSpace();
+ virtual ~GfxDeviceCMYKColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csDeviceCMYK; }
+
+ virtual void getGray(GfxColor *color, GfxGray *gray, GfxRenderingIntent ri);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb, GfxRenderingIntent ri);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk, GfxRenderingIntent ri);
+
+ virtual int getNComps() { return 4; }
+ virtual void getDefaultColor(GfxColor *color);
+
+
+private:
+};
+
+//------------------------------------------------------------------------
+// GfxLabColorSpace
+//------------------------------------------------------------------------
+
+class GfxLabColorSpace: public GfxColorSpace {
+public:
+
+ GfxLabColorSpace();
+ virtual ~GfxLabColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csLab; }
+
+ // Construct a Lab color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr, int recursion);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray, GfxRenderingIntent ri);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb, GfxRenderingIntent ri);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk, GfxRenderingIntent ri);
+
+ virtual int getNComps() { return 3; }
+ virtual void getDefaultColor(GfxColor *color);
+
+ virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
+ int maxImgPixel);
+
+ // Lab-specific access.
+ double getWhiteX() { return whiteX; }
+ double getWhiteY() { return whiteY; }
+ double getWhiteZ() { return whiteZ; }
+ double getBlackX() { return blackX; }
+ double getBlackY() { return blackY; }
+ double getBlackZ() { return blackZ; }
+ double getAMin() { return aMin; }
+ double getAMax() { return aMax; }
+ double getBMin() { return bMin; }
+ double getBMax() { return bMax; }
+
+private:
+
+ double whiteX, whiteY, whiteZ; // white point
+ double blackX, blackY, blackZ; // black point
+ double aMin, aMax, bMin, bMax; // range for the a and b components
+ double kr, kg, kb; // gamut mapping mulitpliers
+};
+
+//------------------------------------------------------------------------
+// GfxICCBasedColorSpace
+//------------------------------------------------------------------------
+
+class GfxICCBasedColorSpace: public GfxColorSpace {
+public:
+
+ GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
+ Ref *iccProfileStreamA);
+ virtual ~GfxICCBasedColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csICCBased; }
+
+ // Construct an ICCBased color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray, GfxRenderingIntent ri);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb, GfxRenderingIntent ri);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk, GfxRenderingIntent ri);
+
+ virtual int getNComps() { return nComps; }
+ virtual void getDefaultColor(GfxColor *color);
+
+ virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
+ int maxImgPixel);
+
+
+ // ICCBased-specific access.
+ GfxColorSpace *getAlt() { return alt; }
+ Ref getICCProfileStreamRef() { return iccProfileStream; }
+
+private:
+
+ int nComps; // number of color components (1, 3, or 4)
+ GfxColorSpace *alt; // alternate color space
+ double rangeMin[4]; // min values for each component
+ double rangeMax[4]; // max values for each component
+ Ref iccProfileStream; // the ICC profile
+};
+
+//------------------------------------------------------------------------
+// GfxIndexedColorSpace
+//------------------------------------------------------------------------
+
+class GfxIndexedColorSpace: public GfxColorSpace {
+public:
+
+ GfxIndexedColorSpace(GfxColorSpace *baseA, int indexHighA);
+ virtual ~GfxIndexedColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csIndexed; }
+
+ // Construct an Indexed color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray, GfxRenderingIntent ri);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb, GfxRenderingIntent ri);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk, GfxRenderingIntent ri);
+
+ virtual int getNComps() { return 1; }
+ virtual void getDefaultColor(GfxColor *color);
+
+ virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
+ int maxImgPixel);
+
+ // Indexed-specific access.
+ GfxColorSpace *getBase() { return base; }
+ int getIndexHigh() { return indexHigh; }
+ Guchar *getLookup() { return lookup; }
+ GfxColor *mapColorToBase(GfxColor *color, GfxColor *baseColor);
+
+private:
+
+ GfxColorSpace *base; // base color space
+ int indexHigh; // max pixel value
+ Guchar *lookup; // lookup table
+};
+
+//------------------------------------------------------------------------
+// GfxSeparationColorSpace
+//------------------------------------------------------------------------
+
+class GfxSeparationColorSpace: public GfxColorSpace {
+public:
+
+ GfxSeparationColorSpace(GString *nameA, GfxColorSpace *altA,
+ Function *funcA);
+ virtual ~GfxSeparationColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csSeparation; }
+
+ // Construct a Separation color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray, GfxRenderingIntent ri);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb, GfxRenderingIntent ri);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk, GfxRenderingIntent ri);
+
+ virtual int getNComps() { return 1; }
+ virtual void getDefaultColor(GfxColor *color);
+
+ virtual GBool isNonMarking() { return nonMarking; }
+
+ // Separation-specific access.
+ GString *getName() { return name; }
+ GfxColorSpace *getAlt() { return alt; }
+ Function *getFunc() { return func; }
+
+private:
+
+ GfxSeparationColorSpace(GString *nameA, GfxColorSpace *altA,
+ Function *funcA, GBool nonMarkingA,
+ Guint overprintMaskA);
+
+ GString *name; // colorant name
+ GfxColorSpace *alt; // alternate color space
+ Function *func; // tint transform (into alternate color space)
+ GBool nonMarking;
+};
+
+//------------------------------------------------------------------------
+// GfxDeviceNColorSpace
+//------------------------------------------------------------------------
+
+class GfxDeviceNColorSpace: public GfxColorSpace {
+public:
+
+ GfxDeviceNColorSpace(int nCompsA, GString **namesA,
+ GfxColorSpace *alt, Function *func,
+ Object *attrsA);
+ virtual ~GfxDeviceNColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csDeviceN; }
+
+ // Construct a DeviceN color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray, GfxRenderingIntent ri);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb, GfxRenderingIntent ri);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk, GfxRenderingIntent ri);
+
+ virtual int getNComps() { return nComps; }
+ virtual void getDefaultColor(GfxColor *color);
+
+ virtual GBool isNonMarking() { return nonMarking; }
+
+ // DeviceN-specific access.
+ GString *getColorantName(int i) { return names[i]; }
+ GfxColorSpace *getAlt() { return alt; }
+ Function *getTintTransformFunc() { return func; }
+ Object *getAttrs() { return &attrs; }
+
+private:
+
+ GfxDeviceNColorSpace(int nCompsA, GString **namesA,
+ GfxColorSpace *alt, Function *func,
+ Object *attrsA,
+ GBool nonMarkingA, Guint overprintMaskA);
+
+ int nComps; // number of components
+ GString // colorant names
+ *names[gfxColorMaxComps];
+ GfxColorSpace *alt; // alternate color space
+ Function *func; // tint transform (into alternate color space)
+ Object attrs;
+ GBool nonMarking;
+};
+
+//------------------------------------------------------------------------
+// GfxPatternColorSpace
+//------------------------------------------------------------------------
+
+class GfxPatternColorSpace: public GfxColorSpace {
+public:
+
+ GfxPatternColorSpace(GfxColorSpace *underA);
+ virtual ~GfxPatternColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csPattern; }
+
+ // Construct a Pattern color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr,
+ int recursion);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray, GfxRenderingIntent ri);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb, GfxRenderingIntent ri);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk, GfxRenderingIntent ri);
+
+ virtual int getNComps() { return 0; }
+ virtual void getDefaultColor(GfxColor *color);
+
+ // Pattern-specific access.
+ GfxColorSpace *getUnder() { return under; }
+
+private:
+
+ GfxColorSpace *under; // underlying color space (for uncolored
+ // patterns)
+};
+
+//------------------------------------------------------------------------
+// GfxPattern
+//------------------------------------------------------------------------
+
+class GfxPattern {
+public:
+
+ GfxPattern(int typeA);
+ virtual ~GfxPattern();
+
+ static GfxPattern *parse(Object *objRef, Object *obj
+ );
+
+ virtual GfxPattern *copy() = 0;
+
+ int getType() { return type; }
+
+private:
+
+ int type;
+};
+
+//------------------------------------------------------------------------
+// GfxTilingPattern
+//------------------------------------------------------------------------
+
+class GfxTilingPattern: public GfxPattern {
+public:
+
+ static GfxTilingPattern *parse(Object *patObjRef, Object *patObj);
+ virtual ~GfxTilingPattern();
+
+ virtual GfxPattern *copy();
+
+ int getPaintType() { return paintType; }
+ int getTilingType() { return tilingType; }
+ double *getBBox() { return bbox; }
+ double getXStep() { return xStep; }
+ double getYStep() { return yStep; }
+ Dict *getResDict()
+ { return resDict.isDict() ? resDict.getDict() : (Dict *)NULL; }
+ double *getMatrix() { return matrix; }
+ Object *getContentStreamRef() { return &contentStreamRef; }
+
+private:
+
+ GfxTilingPattern(int paintTypeA, int tilingTypeA,
+ double *bboxA, double xStepA, double yStepA,
+ Object *resDictA, double *matrixA,
+ Object *contentStreamA);
+
+ int paintType;
+ int tilingType;
+ double bbox[4];
+ double xStep, yStep;
+ Object resDict;
+ double matrix[6];
+ Object contentStreamRef;
+};
+
+//------------------------------------------------------------------------
+// GfxShadingPattern
+//------------------------------------------------------------------------
+
+class GfxShadingPattern: public GfxPattern {
+public:
+
+ static GfxShadingPattern *parse(Object *patObj
+ );
+ virtual ~GfxShadingPattern();
+
+ virtual GfxPattern *copy();
+
+ GfxShading *getShading() { return shading; }
+ double *getMatrix() { return matrix; }
+
+private:
+
+ GfxShadingPattern(GfxShading *shadingA, double *matrixA);
+
+ GfxShading *shading;
+ double matrix[6];
+};
+
+//------------------------------------------------------------------------
+// GfxShading
+//------------------------------------------------------------------------
+
+class GfxShading {
+public:
+
+ GfxShading(int typeA);
+ GfxShading(GfxShading *shading);
+ virtual ~GfxShading();
+
+ static GfxShading *parse(Object *obj
+ );
+
+ virtual GfxShading *copy() = 0;
+
+ int getType() { return type; }
+ GfxColorSpace *getColorSpace() { return colorSpace; }
+ GfxColor *getBackground() { return &background; }
+ GBool getHasBackground() { return hasBackground; }
+ void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA)
+ { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
+ GBool getHasBBox() { return hasBBox; }
+
+protected:
+
+ GBool init(Dict *dict
+ );
+
+ int type;
+ GfxColorSpace *colorSpace;
+ GfxColor background;
+ GBool hasBackground;
+ double xMin, yMin, xMax, yMax;
+ GBool hasBBox;
+};
+
+//------------------------------------------------------------------------
+// GfxFunctionShading
+//------------------------------------------------------------------------
+
+class GfxFunctionShading: public GfxShading {
+public:
+
+ GfxFunctionShading(double x0A, double y0A,
+ double x1A, double y1A,
+ double *matrixA,
+ Function **funcsA, int nFuncsA);
+ GfxFunctionShading(GfxFunctionShading *shading);
+ virtual ~GfxFunctionShading();
+
+ static GfxFunctionShading *parse(Dict *dict
+ );
+
+ virtual GfxShading *copy();
+
+ void getDomain(double *x0A, double *y0A, double *x1A, double *y1A)
+ { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
+ double *getMatrix() { return matrix; }
+ int getNFuncs() { return nFuncs; }
+ Function *getFunc(int i) { return funcs[i]; }
+ void getColor(double x, double y, GfxColor *color);
+
+private:
+
+ double x0, y0, x1, y1;
+ double matrix[6];
+ Function *funcs[gfxColorMaxComps];
+ int nFuncs;
+};
+
+//------------------------------------------------------------------------
+// GfxAxialShading
+//------------------------------------------------------------------------
+
+class GfxAxialShading: public GfxShading {
+public:
+
+ GfxAxialShading(double x0A, double y0A,
+ double x1A, double y1A,
+ double t0A, double t1A,
+ Function **funcsA, int nFuncsA,
+ GBool extend0A, GBool extend1A);
+ GfxAxialShading(GfxAxialShading *shading);
+ virtual ~GfxAxialShading();
+
+ static GfxAxialShading *parse(Dict *dict
+ );
+
+ virtual GfxShading *copy();
+
+ void getCoords(double *x0A, double *y0A, double *x1A, double *y1A)
+ { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
+ double getDomain0() { return t0; }
+ double getDomain1() { return t1; }
+ GBool getExtend0() { return extend0; }
+ GBool getExtend1() { return extend1; }
+ int getNFuncs() { return nFuncs; }
+ Function *getFunc(int i) { return funcs[i]; }
+ void getColor(double t, GfxColor *color);
+
+private:
+
+ double x0, y0, x1, y1;
+ double t0, t1;
+ Function *funcs[gfxColorMaxComps];
+ int nFuncs;
+ GBool extend0, extend1;
+};
+
+//------------------------------------------------------------------------
+// GfxRadialShading
+//------------------------------------------------------------------------
+
+class GfxRadialShading: public GfxShading {
+public:
+
+ GfxRadialShading(double x0A, double y0A, double r0A,
+ double x1A, double y1A, double r1A,
+ double t0A, double t1A,
+ Function **funcsA, int nFuncsA,
+ GBool extend0A, GBool extend1A);
+ GfxRadialShading(GfxRadialShading *shading);
+ virtual ~GfxRadialShading();
+
+ static GfxRadialShading *parse(Dict *dict
+ );
+
+ virtual GfxShading *copy();
+
+ void getCoords(double *x0A, double *y0A, double *r0A,
+ double *x1A, double *y1A, double *r1A)
+ { *x0A = x0; *y0A = y0; *r0A = r0; *x1A = x1; *y1A = y1; *r1A = r1; }
+ double getDomain0() { return t0; }
+ double getDomain1() { return t1; }
+ GBool getExtend0() { return extend0; }
+ GBool getExtend1() { return extend1; }
+ int getNFuncs() { return nFuncs; }
+ Function *getFunc(int i) { return funcs[i]; }
+ void getColor(double t, GfxColor *color);
+
+private:
+
+ double x0, y0, r0, x1, y1, r1;
+ double t0, t1;
+ Function *funcs[gfxColorMaxComps];
+ int nFuncs;
+ GBool extend0, extend1;
+};
+
+//------------------------------------------------------------------------
+// GfxGouraudTriangleShading
+//------------------------------------------------------------------------
+
+struct GfxGouraudVertex {
+ double x, y;
+ double color[gfxColorMaxComps];
+};
+
+class GfxGouraudTriangleShading: public GfxShading {
+public:
+
+ GfxGouraudTriangleShading(int typeA,
+ GfxGouraudVertex *verticesA, int nVerticesA,
+ int (*trianglesA)[3], int nTrianglesA,
+ int nCompsA, Function **funcsA, int nFuncsA);
+ GfxGouraudTriangleShading(GfxGouraudTriangleShading *shading);
+ virtual ~GfxGouraudTriangleShading();
+
+ static GfxGouraudTriangleShading *parse(int typeA, Dict *dict, Stream *str
+ );
+
+ virtual GfxShading *copy();
+
+ int getNComps() { return nComps; }
+ int getNTriangles() { return nTriangles; }
+ 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:
+
+ GfxGouraudVertex *vertices;
+ int nVertices;
+ int (*triangles)[3];
+ int nTriangles;
+ Function *funcs[gfxColorMaxComps];
+ int nComps; // number of color components (1 if nFuncs > 0)
+ int nFuncs;
+};
+
+//------------------------------------------------------------------------
+// GfxPatchMeshShading
+//------------------------------------------------------------------------
+
+struct GfxPatch {
+ double x[4][4];
+ double y[4][4];
+ double color[2][2][gfxColorMaxComps];
+};
+
+class GfxPatchMeshShading: public GfxShading {
+public:
+
+ GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA,
+ int nCompsA, Function **funcsA, int nFuncsA);
+ GfxPatchMeshShading(GfxPatchMeshShading *shading);
+ virtual ~GfxPatchMeshShading();
+
+ static GfxPatchMeshShading *parse(int typeA, Dict *dict, Stream *str
+ );
+
+ virtual GfxShading *copy();
+
+ 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:
+
+ GfxPatch *patches;
+ int nPatches;
+ Function *funcs[gfxColorMaxComps];
+ int nComps; // number of color components (1 if nFuncs > 0)
+ int nFuncs;
+};
+
+//------------------------------------------------------------------------
+// GfxImageColorMap
+//------------------------------------------------------------------------
+
+class GfxImageColorMap {
+public:
+
+ // Constructor.
+ GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *colorSpaceA,
+ int maxAllowedBits = 8);
+
+ // Destructor.
+ ~GfxImageColorMap();
+
+ // Return a copy of this color map.
+ GfxImageColorMap *copy() { return new GfxImageColorMap(this); }
+
+ // Is color map valid?
+ GBool isOk() { return ok; }
+
+ // Get the color space.
+ GfxColorSpace *getColorSpace() { return colorSpace; }
+
+ // Get stream decoding info.
+ int getNumPixelComps() { return nComps; }
+ int getBits() { return bits; }
+
+ // Get decode table.
+ double getDecodeLow(int i) { return decodeLow[i]; }
+ double getDecodeHigh(int i) { return decodeLow[i] + decodeRange[i]; }
+
+ // Convert an image pixel to a color.
+ void getGray(Guchar *x, GfxGray *gray, GfxRenderingIntent ri);
+ void getRGB(Guchar *x, GfxRGB *rgb, GfxRenderingIntent ri);
+ void getCMYK(Guchar *x, GfxCMYK *cmyk, GfxRenderingIntent ri);
+ void getColor(Guchar *x, GfxColor *color);
+
+ // Convert a line of <n> pixels to 8-bit colors.
+ void getGrayByteLine(Guchar *in, Guchar *out, int n, GfxRenderingIntent ri);
+ void getRGBByteLine(Guchar *in, Guchar *out, int n, GfxRenderingIntent ri);
+ void getCMYKByteLine(Guchar *in, Guchar *out, int n, GfxRenderingIntent ri);
+
+private:
+
+ GfxImageColorMap(GfxImageColorMap *colorMap);
+
+ GfxColorSpace *colorSpace; // the image color space
+ int bits; // bits per component
+ int nComps; // number of components in a pixel
+ GfxColorSpace *colorSpace2; // secondary color space
+ int nComps2; // number of components in colorSpace2
+ GfxColorComp * // lookup table
+ lookup[gfxColorMaxComps];
+ GfxColorComp * // optimized case lookup table
+ lookup2[gfxColorMaxComps];
+ double // minimum values for each component
+ decodeLow[gfxColorMaxComps];
+ double // max - min value for each component
+ decodeRange[gfxColorMaxComps];
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// GfxSubpath and GfxPath
+//------------------------------------------------------------------------
+
+class GfxSubpath {
+public:
+
+ // Constructor.
+ GfxSubpath(double x1, double y1);
+
+ // Destructor.
+ ~GfxSubpath();
+
+ // Copy.
+ GfxSubpath *copy() { return new GfxSubpath(this); }
+
+ // Get points.
+ int getNumPoints() { return n; }
+ double getX(int i) { return x[i]; }
+ double getY(int i) { return y[i]; }
+ GBool getCurve(int i) { return curve[i]; }
+
+ // Get last point.
+ double getLastX() { return x[n-1]; }
+ double getLastY() { return y[n-1]; }
+
+ // Add a line segment.
+ void lineTo(double x1, double y1);
+
+ // Add a Bezier curve.
+ void curveTo(double x1, double y1, double x2, double y2,
+ double x3, double y3);
+
+ // Close the subpath.
+ void close();
+ GBool isClosed() { return closed; }
+
+ // Add (<dx>, <dy>) to each point in the subpath.
+ void offset(double dx, double dy);
+
+private:
+
+ double *x, *y; // points
+ GBool *curve; // curve[i] => point i is a control point
+ // for a Bezier curve
+ int n; // number of points
+ int size; // size of x/y arrays
+ GBool closed; // set if path is closed
+
+ GfxSubpath(GfxSubpath *subpath);
+};
+
+class GfxPath {
+public:
+
+ // Constructor.
+ GfxPath();
+
+ // Destructor.
+ ~GfxPath();
+
+ // Copy.
+ GfxPath *copy()
+ { return new GfxPath(justMoved, firstX, firstY, subpaths, n, size); }
+
+ // Is there a current point?
+ GBool isCurPt() { return n > 0 || justMoved; }
+
+ // Is the path non-empty, i.e., is there at least one segment?
+ GBool isPath() { return n > 0; }
+
+ // Get subpaths.
+ int getNumSubpaths() { return n; }
+ GfxSubpath *getSubpath(int i) { return subpaths[i]; }
+
+ // Get last point on last subpath.
+ double getLastX() { return subpaths[n-1]->getLastX(); }
+ double getLastY() { return subpaths[n-1]->getLastY(); }
+
+ // Move the current point.
+ void moveTo(double x, double y);
+
+ // Add a segment to the last subpath.
+ void lineTo(double x, double y);
+
+ // Add a Bezier curve to the last subpath
+ void curveTo(double x1, double y1, double x2, double y2,
+ double x3, double y3);
+
+ // Close the last subpath.
+ void close();
+
+ // Append <path> to <this>.
+ void append(GfxPath *path);
+
+ // Add (<dx>, <dy>) to each point in the path.
+ void offset(double dx, double dy);
+
+private:
+
+ GBool justMoved; // set if a new subpath was just started
+ double firstX, firstY; // first point in new subpath
+ GfxSubpath **subpaths; // subpaths
+ int n; // number of subpaths
+ int size; // size of subpaths array
+
+ GfxPath(GBool justMoved1, double firstX1, double firstY1,
+ GfxSubpath **subpaths1, int n1, int size1);
+};
+
+//------------------------------------------------------------------------
+// GfxState
+//------------------------------------------------------------------------
+
+class GfxState {
+public:
+
+ // Construct a default GfxState, for a device with resolution <hDPI>
+ // x <vDPI>, page box <pageBox>, page rotation <rotateA>, and
+ // coordinate system specified by <upsideDown>.
+ GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
+ int rotateA, GBool upsideDown
+ );
+
+ // Destructor.
+ ~GfxState();
+
+ // Copy.
+ GfxState *copy(GBool copyPath = gFalse)
+ { return new GfxState(this, copyPath); }
+
+ // Accessors.
+ double getHDPI() { return hDPI; }
+ double getVDPI() { return vDPI; }
+ double *getCTM() { return ctm; }
+ double getX1() { return px1; }
+ double getY1() { return py1; }
+ double getX2() { return px2; }
+ double getY2() { return py2; }
+ double getPageWidth() { return pageWidth; }
+ double getPageHeight() { return pageHeight; }
+ int getRotate() { return rotate; }
+ GfxColor *getFillColor() { return &fillColor; }
+ GfxColor *getStrokeColor() { return &strokeColor; }
+ void getFillGray(GfxGray *gray)
+ { fillColorSpace->getGray(&fillColor, gray, renderingIntent); }
+ void getStrokeGray(GfxGray *gray)
+ { strokeColorSpace->getGray(&strokeColor, gray, renderingIntent); }
+ void getFillRGB(GfxRGB *rgb)
+ { fillColorSpace->getRGB(&fillColor, rgb, renderingIntent); }
+ void getStrokeRGB(GfxRGB *rgb)
+ { strokeColorSpace->getRGB(&strokeColor, rgb, renderingIntent); }
+ void getFillCMYK(GfxCMYK *cmyk)
+ { fillColorSpace->getCMYK(&fillColor, cmyk, renderingIntent); }
+ void getStrokeCMYK(GfxCMYK *cmyk)
+ { strokeColorSpace->getCMYK(&strokeColor, cmyk, renderingIntent); }
+ GfxColorSpace *getFillColorSpace() { return fillColorSpace; }
+ GfxColorSpace *getStrokeColorSpace() { return strokeColorSpace; }
+ GfxPattern *getFillPattern() { return fillPattern; }
+ GfxPattern *getStrokePattern() { return strokePattern; }
+ GfxBlendMode getBlendMode() { return blendMode; }
+ double getFillOpacity() { return fillOpacity; }
+ double getStrokeOpacity() { return strokeOpacity; }
+ GBool getFillOverprint() { return fillOverprint; }
+ GBool getStrokeOverprint() { return strokeOverprint; }
+ int getOverprintMode() { return overprintMode; }
+ GfxRenderingIntent getRenderingIntent() { return renderingIntent; }
+ Function **getTransfer() { return transfer; }
+ double getLineWidth() { return lineWidth; }
+ void getLineDash(double **dash, int *length, double *start)
+ { *dash = lineDash; *length = lineDashLength; *start = lineDashStart; }
+ double getFlatness() { return flatness; }
+ int getLineJoin() { return lineJoin; }
+ int getLineCap() { return lineCap; }
+ double getMiterLimit() { return miterLimit; }
+ GBool getStrokeAdjust() { return strokeAdjust; }
+ GfxFont *getFont() { return font; }
+ double getFontSize() { return fontSize; }
+ double *getTextMat() { return textMat; }
+ double getCharSpace() { return charSpace; }
+ double getWordSpace() { return wordSpace; }
+ double getHorizScaling() { return horizScaling; }
+ double getLeading() { return leading; }
+ double getRise() { return rise; }
+ int getRender() { return render; }
+ GfxPath *getPath() { return path; }
+ void setPath(GfxPath *pathA);
+ double getCurX() { return curX; }
+ double getCurY() { return curY; }
+ void getClipBBox(double *xMin, double *yMin, double *xMax, double *yMax)
+ { *xMin = clipXMin; *yMin = clipYMin; *xMax = clipXMax; *yMax = clipYMax; }
+ void getUserClipBBox(double *xMin, double *yMin, double *xMax, double *yMax);
+ double getLineX() { return lineX; }
+ double getLineY() { return lineY; }
+ GBool getIgnoreColorOps() { return ignoreColorOps; }
+
+ // Is there a current point/path?
+ GBool isCurPt() { return path->isCurPt(); }
+ GBool isPath() { return path->isPath(); }
+
+ // Transforms.
+ void transform(double x1, double y1, double *x2, double *y2)
+ { *x2 = ctm[0] * x1 + ctm[2] * y1 + ctm[4];
+ *y2 = ctm[1] * x1 + ctm[3] * y1 + ctm[5]; }
+ void transformDelta(double x1, double y1, double *x2, double *y2)
+ { *x2 = ctm[0] * x1 + ctm[2] * y1;
+ *y2 = ctm[1] * x1 + ctm[3] * y1; }
+ void textTransform(double x1, double y1, double *x2, double *y2)
+ { *x2 = textMat[0] * x1 + textMat[2] * y1 + textMat[4];
+ *y2 = textMat[1] * x1 + textMat[3] * y1 + textMat[5]; }
+ void textTransformDelta(double x1, double y1, double *x2, double *y2)
+ { *x2 = textMat[0] * x1 + textMat[2] * y1;
+ *y2 = textMat[1] * x1 + textMat[3] * y1; }
+ double transformWidth(double w);
+ double getTransformedLineWidth()
+ { return transformWidth(lineWidth); }
+ double getTransformedFontSize();
+ void getFontTransMat(double *m11, double *m12, double *m21, double *m22);
+
+ // Change state parameters.
+ void setCTM(double a, double b, double c,
+ double d, double e, double f);
+ void concatCTM(double a, double b, double c,
+ double d, double e, double f);
+ void shiftCTM(double tx, double ty);
+ void setFillColorSpace(GfxColorSpace *colorSpace);
+ void setStrokeColorSpace(GfxColorSpace *colorSpace);
+ void setFillColor(GfxColor *color) { fillColor = *color; }
+ void setStrokeColor(GfxColor *color) { strokeColor = *color; }
+ void setFillPattern(GfxPattern *pattern);
+ void setStrokePattern(GfxPattern *pattern);
+ void setBlendMode(GfxBlendMode mode) { blendMode = mode; }
+ void setFillOpacity(double opac) { fillOpacity = opac; }
+ void setStrokeOpacity(double opac) { strokeOpacity = opac; }
+ void setFillOverprint(GBool op) { fillOverprint = op; }
+ void setStrokeOverprint(GBool op) { strokeOverprint = op; }
+ void setOverprintMode(int opm) { overprintMode = opm; }
+ void setRenderingIntent(GfxRenderingIntent ri) { renderingIntent = ri; }
+ void setTransfer(Function **funcs);
+ void setLineWidth(double width) { lineWidth = width; }
+ void setLineDash(double *dash, int length, double start);
+ void setFlatness(double flatness1) { flatness = flatness1; }
+ void setLineJoin(int lineJoin1) { lineJoin = lineJoin1; }
+ void setLineCap(int lineCap1) { lineCap = lineCap1; }
+ void setMiterLimit(double limit) { miterLimit = limit; }
+ void setStrokeAdjust(GBool sa) { strokeAdjust = sa; }
+ void setFont(GfxFont *fontA, double fontSizeA)
+ { font = fontA; fontSize = fontSizeA; }
+ void setTextMat(double a, double b, double c,
+ double d, double e, double f)
+ { textMat[0] = a; textMat[1] = b; textMat[2] = c;
+ textMat[3] = d; textMat[4] = e; textMat[5] = f; }
+ void setCharSpace(double space)
+ { charSpace = space; }
+ void setWordSpace(double space)
+ { wordSpace = space; }
+ void setHorizScaling(double scale)
+ { horizScaling = 0.01 * scale; }
+ void setLeading(double leadingA)
+ { leading = leadingA; }
+ void setRise(double riseA)
+ { rise = riseA; }
+ void setRender(int renderA)
+ { render = renderA; }
+
+ // Add to path.
+ void moveTo(double x, double y)
+ { path->moveTo(curX = x, curY = y); }
+ void lineTo(double x, double y)
+ { path->lineTo(curX = x, curY = y); }
+ void curveTo(double x1, double y1, double x2, double y2,
+ double x3, double y3)
+ { path->curveTo(x1, y1, x2, y2, curX = x3, curY = y3); }
+ void closePath()
+ { path->close(); curX = path->getLastX(); curY = path->getLastY(); }
+ void clearPath();
+
+ // Update clip region.
+ void clip();
+ void clipToStrokePath();
+ void clipToRect(double xMin, double yMin, double xMax, double yMax);
+ void resetDevClipRect(double xMin, double yMin, double xMax, double yMax)
+ { clipXMin = xMin; clipYMin = yMin; clipXMax = xMax; clipYMax = yMax; }
+
+ // Text position.
+ void textSetPos(double tx, double ty) { lineX = tx; lineY = ty; }
+ void textMoveTo(double tx, double ty)
+ { lineX = tx; lineY = ty; textTransform(tx, ty, &curX, &curY); }
+ void textShift(double tx, double ty);
+ void shift(double dx, double dy);
+
+ // Ignore color operators (in cached/uncolored Type 3 chars, and
+ // uncolored tiling patterns). Cached Type 3 char status.
+ void setIgnoreColorOps(GBool ignore) { ignoreColorOps = ignore; }
+
+ // Push/pop GfxState on/off stack.
+ GfxState *save();
+ GfxState *restore();
+ GBool hasSaves() { return saved != NULL; }
+
+ // Misc
+ GBool parseBlendMode(Object *obj, GfxBlendMode *mode);
+
+private:
+
+ double hDPI, vDPI; // resolution
+ double ctm[6]; // coord transform matrix
+ double px1, py1, px2, py2; // page corners (user coords)
+ double pageWidth, pageHeight; // page size (pixels)
+ int rotate; // page rotation angle
+
+ GfxColorSpace *fillColorSpace; // fill color space
+ GfxColorSpace *strokeColorSpace; // stroke color space
+ GfxColor fillColor; // fill color
+ GfxColor strokeColor; // stroke color
+ GfxPattern *fillPattern; // fill pattern
+ GfxPattern *strokePattern; // stroke pattern
+ GfxBlendMode blendMode; // transparency blend mode
+ double fillOpacity; // fill opacity
+ double strokeOpacity; // stroke opacity
+ GBool fillOverprint; // fill overprint
+ GBool strokeOverprint; // stroke overprint
+ int overprintMode; // overprint mode ("OPM")
+ GfxRenderingIntent renderingIntent; // rendering intent
+ Function *transfer[4]; // transfer function (entries may be: all
+ // NULL = identity; last three NULL =
+ // single function; all four non-NULL =
+ // R,G,B,gray functions)
+
+ double lineWidth; // line width
+ double *lineDash; // line dash
+ int lineDashLength;
+ double lineDashStart;
+ double flatness; // curve flatness
+ int lineJoin; // line join style
+ int lineCap; // line cap style
+ double miterLimit; // line miter limit
+ GBool strokeAdjust; // stroke adjustment
+
+ GfxFont *font; // font
+ double fontSize; // font size
+ double textMat[6]; // text matrix
+ double charSpace; // character spacing
+ double wordSpace; // word spacing
+ double horizScaling; // horizontal scaling
+ double leading; // text leading
+ double rise; // text rise
+ int render; // text rendering mode
+
+ GfxPath *path; // array of path elements
+ double curX, curY; // current point (user coords)
+ double lineX, lineY; // start of current text line (text coords)
+
+ double clipXMin, clipYMin, // bounding box for clip region
+ clipXMax, clipYMax;
+
+ GBool ignoreColorOps; // ignore color ops (in cached/uncolored
+ // Type 3 chars, and uncolored tiling
+ // patterns)
+
+ GfxState *saved; // next GfxState on stack
+
+ GfxState(GfxState *state, GBool copyPath);
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc
new file mode 100644
index 00000000000..5ff932479d1
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.cc
@@ -0,0 +1,3799 @@
+//========================================================================
+//
+// GlobalParams.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+// Modified for TeX Live by Peter Breitenlohner <tex-live@tug.org>
+// See top-level ChangeLog for a list of all modifications
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <time.h>
+#ifdef _WIN32
+# include <shlobj.h>
+#endif
+#if HAVE_PAPER_H
+#include <paper.h>
+#endif
+#if HAVE_FONTCONFIG
+# include <fontconfig/fontconfig.h>
+#endif
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "GHash.h"
+#include "gfile.h"
+#include "FoFiIdentifier.h"
+#include "Error.h"
+#include "NameToCharCode.h"
+#include "CharCodeToUnicode.h"
+#include "UnicodeRemapping.h"
+#include "UnicodeMap.h"
+#include "CMap.h"
+#include "BuiltinFontTables.h"
+#include "FontEncodingTables.h"
+#include "GlobalParams.h"
+
+#ifdef _WIN32
+# undef strcasecmp
+# undef strncasecmp
+# define strcasecmp _stricmp
+# define strncasecmp _strnicmp
+#else
+# include <strings.h>
+#endif
+
+#if MULTITHREADED
+# define lockGlobalParams gLockMutex(&mutex)
+# define lockUnicodeMapCache gLockMutex(&unicodeMapCacheMutex)
+# define lockCMapCache gLockMutex(&cMapCacheMutex)
+# define unlockGlobalParams gUnlockMutex(&mutex)
+# define unlockUnicodeMapCache gUnlockMutex(&unicodeMapCacheMutex)
+# define unlockCMapCache gUnlockMutex(&cMapCacheMutex)
+#else
+# define lockGlobalParams
+# define lockUnicodeMapCache
+# define lockCMapCache
+# define unlockGlobalParams
+# define unlockUnicodeMapCache
+# define unlockCMapCache
+#endif
+
+#include "NameToUnicodeTable.h"
+#include "UnicodeMapTables.h"
+#include "UTF8.h"
+
+//------------------------------------------------------------------------
+
+#define cidToUnicodeCacheSize 4
+#define unicodeToUnicodeCacheSize 4
+
+//------------------------------------------------------------------------
+
+static struct {
+ const char *name;
+ const char *t1FileName;
+ const char *ttFileName;
+ const char *macFileName; // may be .dfont, .ttf, or .ttc
+ const char *macFontName; // font name inside .dfont or .ttc
+ const char *obliqueFont; // name of font to oblique
+ double obliqueFactor; // oblique sheer factor
+} displayFontTab[] = {
+ {"Courier", "n022003l.pfb", "cour.ttf", "Courier", "Courier", NULL, 0},
+ {"Courier-Bold", "n022004l.pfb", "courbd.ttf", "Courier", "Courier Bold", NULL, 0},
+ {"Courier-BoldOblique", "n022024l.pfb", "courbi.ttf", "Courier", "Courier Bold Oblique", "Courier-Bold", 0.212557},
+ {"Courier-Oblique", "n022023l.pfb", "couri.ttf", "Courier", "Courier Oblique", "Courier", 0.212557},
+ {"Helvetica", "n019003l.pfb", "arial.ttf", "Helvetica", "Helvetica", NULL, 0},
+ {"Helvetica-Bold", "n019004l.pfb", "arialbd.ttf", "Helvetica", "Helvetica Bold", NULL, 0},
+ {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf", "Helvetica", "Helvetica Bold Oblique", "Helvetica-Bold", 0.212557},
+ {"Helvetica-Oblique", "n019023l.pfb", "ariali.ttf", "Helvetica", "Helvetica Oblique", "Helvetica", 0.212557},
+ {"Symbol", "s050000l.pfb", NULL, "Symbol", "Symbol", NULL, 0},
+ {"Times-Bold", "n021004l.pfb", "timesbd.ttf", "Times", "Times Bold", NULL, 0},
+ {"Times-BoldItalic", "n021024l.pfb", "timesbi.ttf", "Times", "Times Bold Italic", NULL, 0},
+ {"Times-Italic", "n021023l.pfb", "timesi.ttf", "Times", "Times Italic", NULL, 0},
+ {"Times-Roman", "n021003l.pfb", "times.ttf", "Times", "Times Roman", NULL, 0},
+ {"ZapfDingbats", "d050000l.pfb", NULL, "ZapfDingbats", "Zapf Dingbats", NULL, 0},
+ {NULL}
+};
+
+#ifdef _WIN32
+static const char *displayFontDirs[] = {
+ "c:/windows/fonts",
+ "c:/winnt/fonts",
+ NULL
+};
+#else
+static const char *displayFontDirs[] = {
+ "/usr/share/ghostscript/fonts",
+ "/usr/local/share/ghostscript/fonts",
+ "/usr/share/fonts/default/Type1",
+ "/usr/share/fonts/default/ghostscript",
+ "/usr/share/fonts/type1/gsfonts",
+#if defined(__sun) && defined(__SVR4)
+ "/usr/sfw/share/ghostscript/fonts",
+#endif
+ NULL
+};
+#endif
+
+#ifdef __APPLE__
+static const char *macSystemFontPath = "/System/Library/Fonts";
+#endif
+
+struct Base14FontInfo {
+ Base14FontInfo(GString *fileNameA, int fontNumA, double obliqueA) {
+ fileName = fileNameA;
+ fontNum = fontNumA;
+ oblique = obliqueA;
+ }
+ ~Base14FontInfo() {
+ delete fileName;
+ }
+ GString *fileName;
+ int fontNum;
+ double oblique;
+};
+
+//------------------------------------------------------------------------
+
+GlobalParams *globalParams = NULL;
+
+const char *GlobalParams::defaultTextEncoding = "Latin1";
+
+//------------------------------------------------------------------------
+// PSFontParam16
+//------------------------------------------------------------------------
+
+PSFontParam16::PSFontParam16(GString *nameA, int wModeA,
+ GString *psFontNameA, GString *encodingA) {
+ name = nameA;
+ wMode = wModeA;
+ psFontName = psFontNameA;
+ encoding = encodingA;
+}
+
+PSFontParam16::~PSFontParam16() {
+ delete name;
+ delete psFontName;
+ delete encoding;
+}
+
+//------------------------------------------------------------------------
+// SysFontInfo
+//------------------------------------------------------------------------
+
+class SysFontInfo {
+public:
+
+ GString *name;
+ GString *path;
+ SysFontType type;
+ int fontNum; // for TrueType collections
+
+ SysFontInfo(GString *nameA, GString *pathA, SysFontType typeA, int fontNumA);
+ ~SysFontInfo();
+ GString *mungeName1(GString *in);
+ GString *mungeName2(GString *in);
+ void mungeName3(GString *name, GBool *bold, GBool *italic);
+ int match(GString *nameA);
+};
+
+SysFontInfo::SysFontInfo(GString *nameA, GString *pathA,
+ SysFontType typeA, int fontNumA) {
+ name = nameA;
+ path = pathA;
+ type = typeA;
+ fontNum = fontNumA;
+}
+
+SysFontInfo::~SysFontInfo() {
+ delete name;
+ delete path;
+}
+
+// Remove space/comma/dash/underscore chars.
+// Uppercase the name.
+GString *SysFontInfo::mungeName1(GString *in) {
+ GString *out = new GString();
+ for (char *p = in->getCString(); *p; ++p) {
+ if (*p == ' ' || *p == ',' || *p == '-' || *p == '_') {
+ // skip
+ } else if (*p >= 'a' && *p <= 'z') {
+ out->append((char)(*p & 0xdf));
+ } else {
+ out->append(*p);
+ }
+ }
+ return out;
+}
+
+// Remove trailing "Identity-H"/"Identity-V" from the name.
+// Split the name into tokens at space/comma/dash/underscore.
+// Remove trailing "MT" or "BT" from tokens.
+// Remove trailing "PS" and "WGL4" from tokens.
+// Uppercase each token.
+// Concatenate tokens (dropping the space/comma/dash chars).
+GString *SysFontInfo::mungeName2(GString *in) {
+ GString *out = new GString();
+ char *p0 = in->getCString();
+ while (*p0) {
+ if (!strcmp(p0, "Identity-H") || !strcmp(p0, "Identity-V")) {
+ break;
+ }
+ char *p1;
+ for (p1 = p0 + 1;
+ *p1 && *p1 != ' ' && *p1 != ',' && *p1 != '-' && *p1 != '_';
+ ++p1) ;
+ char *p2 = p1;
+ if (p2 - p0 >= 2 && (p2[-2] == 'B' || p2[-2] == 'M') && p2[-1] == 'T') {
+ p2 -= 2;
+ }
+ if (p2 - p0 >= 2 && p2[-2] == 'P' && p2[-1] == 'S') {
+ p2 -= 2;
+ }
+ if (p2 - p0 >= 4 &&
+ p2[-4] == 'W' && p2[-3] == 'G' && p2[-2] == 'L' && p2[-1] == '4') {
+ p2 -= 4;
+ }
+ for (; p0 < p2; ++p0) {
+ if (*p0 >= 'a' && *p0 <= 'z') {
+ out->append((char)(*p0 & 0xdf));
+ } else {
+ out->append(*p0);
+ }
+ }
+ for (p0 = p1; *p0 == ' ' || *p0 == ',' || *p0 == '-' || *p0 == '_'; ++p0) ;
+ }
+ return out;
+}
+
+// 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
+//------------------------------------------------------------------------
+
+class SysFontList {
+public:
+
+ SysFontList();
+ ~SysFontList();
+ SysFontInfo *find(GString *name);
+
+#ifdef _WIN32
+ void scanWindowsFonts(char *winFontDir);
+#endif
+
+#if HAVE_FONTCONFIG
+ void scanFontconfigFonts();
+#endif
+
+private:
+
+#ifdef _WIN32
+ SysFontInfo *makeWindowsFont(char *name, int fontNum,
+ char *path);
+#endif
+
+ GList *fonts; // [SysFontInfo]
+};
+
+SysFontList::SysFontList() {
+ fonts = new GList();
+}
+
+SysFontList::~SysFontList() {
+ deleteGList(fonts, SysFontInfo);
+}
+
+SysFontInfo *SysFontList::find(GString *name) {
+ 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;
+ }
+ }
+ return match;
+}
+
+#ifdef _WIN32
+void SysFontList::scanWindowsFonts(char *winFontDir) {
+ OSVERSIONINFO version;
+ const char *path;
+ DWORD idx, valNameLen, dataLen, type;
+ HKEY regKey;
+ char valName[1024], data[1024];
+ int n, fontNum;
+ char *p0, *p1;
+ GString *fontPath;
+
+ version.dwOSVersionInfoSize = sizeof(version);
+ GetVersionEx(&version);
+ if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ path = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\";
+ } else {
+ path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts\\";
+ }
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0,
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
+ &regKey) == ERROR_SUCCESS) {
+ idx = 0;
+ while (1) {
+ valNameLen = sizeof(valName) - 1;
+ dataLen = sizeof(data) - 1;
+ if (RegEnumValueA(regKey, idx, valName, &valNameLen, NULL,
+ &type, (LPBYTE)data, &dataLen) != ERROR_SUCCESS) {
+ break;
+ }
+ if (type == REG_SZ &&
+ valNameLen > 0 && valNameLen < sizeof(valName) &&
+ dataLen > 0 && dataLen < sizeof(data)) {
+ valName[valNameLen] = '\0';
+ data[dataLen] = '\0';
+ n = (int)strlen(data);
+ if (!strcasecmp(data + n - 4, ".ttf") ||
+ !strcasecmp(data + n - 4, ".ttc") ||
+ !strcasecmp(data + n - 4, ".otf")) {
+ fontPath = new GString(data);
+ if (!(dataLen >= 3 && data[1] == ':' && data[2] == '\\')) {
+ fontPath->insert(0, '\\');
+ fontPath->insert(0, winFontDir);
+ }
+ p0 = valName;
+ fontNum = 0;
+ while (*p0) {
+ p1 = strstr(p0, " & ");
+ if (p1) {
+ *p1 = '\0';
+ p1 = p1 + 3;
+ } else {
+ p1 = p0 + strlen(p0);
+ }
+ fonts->append(makeWindowsFont(p0, fontNum,
+ fontPath->getCString()));
+ p0 = p1;
+ ++fontNum;
+ }
+ delete fontPath;
+ }
+ }
+ ++idx;
+ }
+ RegCloseKey(regKey);
+ }
+}
+
+SysFontInfo *SysFontList::makeWindowsFont(char *name, int fontNum,
+ char *path) {
+ int n = (int)strlen(name);
+
+ // remove trailing ' (TrueType)' or ' (OpenType)'
+ if (n > 11 && (!strncmp(name + n - 11, " (TrueType)", 11) ||
+ !strncmp(name + n - 11, " (OpenType)", 11))) {
+ n -= 11;
+ }
+
+ SysFontType type;
+ if (!strcasecmp(path + strlen(path) - 4, ".ttc")) {
+ type = sysFontTTC;
+ } else if (!strcasecmp(path + strlen(path) - 4, ".otf")) {
+ type = sysFontOTF;
+ } else {
+ type = sysFontTTF;
+ }
+
+ return new SysFontInfo(new GString(name, n), new GString(path),
+ type, fontNum);
+}
+#endif // _WIN32
+
+#if HAVE_FONTCONFIG
+void SysFontList::scanFontconfigFonts() {
+ FcConfig *cfg;
+ FcPattern *pattern;
+ FcObjectSet *objSet;
+ FcFontSet *fontSet;
+ char *name, *file;
+ SysFontType type;
+ int fontNum, i, n;
+
+ if (!(cfg = FcInitLoadConfigAndFonts())) {
+ return;
+ }
+
+ pattern = FcPatternBuild(NULL,
+ FC_OUTLINE, FcTypeBool, FcTrue,
+ FC_SCALABLE, FcTypeBool, FcTrue,
+ NULL);
+ objSet = FcObjectSetBuild(FC_FULLNAME, FC_FILE, FC_INDEX, NULL);
+ fontSet = FcFontList(cfg, pattern, objSet);
+ FcPatternDestroy(pattern);
+ FcObjectSetDestroy(objSet);
+
+ if (fontSet) {
+ for (i = 0; i < fontSet->nfont; ++i) {
+
+ //--- font file, font type
+ if (FcPatternGetString(fontSet->fonts[i], FC_FILE, 0,
+ (FcChar8 **)&file)
+ != FcResultMatch) {
+ continue;
+ }
+ n = (int)strlen(file);
+ if (n > 4 && !strcasecmp(file + n - 4, ".pfa")) {
+ type = sysFontPFA;
+ } else if (n > 4 && !strcasecmp(file + n - 4, ".pfb")) {
+ type = sysFontPFB;
+ } else if (n > 4 && !strcasecmp(file + n - 4, ".ttf")) {
+ type = sysFontTTF;
+ } else if (n > 4 && !strcasecmp(file + n - 4, ".otf")) {
+ type = sysFontOTF;
+ } else {
+ continue;
+ }
+
+ //--- font number
+ if (FcPatternGetInteger(fontSet->fonts[i], FC_INDEX, 0, &fontNum)
+ != FcResultMatch) {
+ fontNum = 0;
+ }
+
+ //--- font name
+ if (FcPatternGetString(fontSet->fonts[i], FC_FULLNAME, 0,
+ (FcChar8 **)&name)
+ != FcResultMatch) {
+ continue;
+ }
+
+ fonts->append(new SysFontInfo(new GString(name), new GString(file),
+ type, fontNum));
+ }
+
+ FcFontSetDestroy(fontSet);
+ }
+
+ FcConfigDestroy(cfg);
+}
+#endif // HAVE_FONTCONFIG
+
+//------------------------------------------------------------------------
+// KeyBinding
+//------------------------------------------------------------------------
+
+KeyBinding::KeyBinding(int codeA, int modsA, int contextA, const char *cmd0) {
+ code = codeA;
+ mods = modsA;
+ context = contextA;
+ cmds = new GList();
+ cmds->append(new GString(cmd0));
+}
+
+KeyBinding::KeyBinding(int codeA, int modsA, int contextA,
+ const char *cmd0, const char *cmd1) {
+ code = codeA;
+ mods = modsA;
+ context = contextA;
+ cmds = new GList();
+ cmds->append(new GString(cmd0));
+ cmds->append(new GString(cmd1));
+}
+
+KeyBinding::KeyBinding(int codeA, int modsA, int contextA, GList *cmdsA) {
+ code = codeA;
+ mods = modsA;
+ context = contextA;
+ cmds = cmdsA;
+}
+
+KeyBinding::~KeyBinding() {
+ deleteGList(cmds, GString);
+}
+
+//------------------------------------------------------------------------
+// PopupMenuCmd
+//------------------------------------------------------------------------
+
+PopupMenuCmd::PopupMenuCmd(GString *labelA, GList *cmdsA) {
+ label = labelA;
+ cmds = cmdsA;
+}
+
+PopupMenuCmd::~PopupMenuCmd() {
+ delete label;
+ deleteGList(cmds, GString);
+}
+
+//------------------------------------------------------------------------
+// parsing
+//------------------------------------------------------------------------
+
+GlobalParams::GlobalParams(const char *cfgFileName) {
+ UnicodeMap *map;
+ GString *fileName;
+ FILE *f;
+ int i;
+
+#if MULTITHREADED
+ gInitMutex(&mutex);
+ gInitMutex(&unicodeMapCacheMutex);
+ gInitMutex(&cMapCacheMutex);
+#endif
+
+#ifdef _WIN32
+ tlsWin32ErrorInfo = TlsAlloc();
+#endif
+
+ initBuiltinFontTables();
+
+ // scan the encoding in reverse because we want the lowest-numbered
+ // index for each char name ('space' is encoded twice)
+ macRomanReverseMap = new NameToCharCode();
+ for (i = 255; i >= 0; --i) {
+ if (macRomanEncoding[i]) {
+ macRomanReverseMap->add(macRomanEncoding[i], (CharCode)i);
+ }
+ }
+
+#ifdef _WIN32
+ // baseDir will be set by a call to setBaseDir
+ baseDir = new GString();
+#else
+ baseDir = appendToPath(getHomeDir(), ".xpdf");
+#endif
+ configFileVars = new GHash(gTrue);
+ setDataDirVar();
+ nameToUnicode = new NameToCharCode();
+ cidToUnicodes = new GHash(gTrue);
+ unicodeToUnicodes = new GHash(gTrue);
+ residentUnicodeMaps = new GHash();
+ unicodeMaps = new GHash(gTrue);
+ cMapDirs = new GHash(gTrue);
+ toUnicodeDirs = new GList();
+ unicodeRemapping = new UnicodeRemapping();
+ fontFiles = new GHash(gTrue);
+ fontDirs = new GList();
+ ccFontFiles = new GHash(gTrue);
+ base14SysFonts = new GHash(gTrue);
+ sysFonts = new SysFontList();
+#if HAVE_PAPER_H
+ char *paperName;
+ const struct paper *paperType;
+ paperinit();
+ if ((paperName = systempapername())) {
+ paperType = paperinfo(paperName);
+ psPaperWidth = (int)paperpswidth(paperType);
+ psPaperHeight = (int)paperpsheight(paperType);
+ } else {
+ error(errConfig, -1, "No paper information available - using defaults");
+ psPaperWidth = defPaperWidth;
+ psPaperHeight = defPaperHeight;
+ }
+ paperdone();
+#else
+ psPaperWidth = defPaperWidth;
+ psPaperHeight = defPaperHeight;
+#endif
+ psImageableLLX = psImageableLLY = 0;
+ psImageableURX = psPaperWidth;
+ psImageableURY = psPaperHeight;
+ psCrop = gTrue;
+ psUseCropBoxAsPage = gFalse;
+ psExpandSmaller = gFalse;
+ psShrinkLarger = gTrue;
+ psCenter = gTrue;
+ psDuplex = gFalse;
+ psLevel = psLevel2;
+ psResidentFonts = new GHash(gTrue);
+ psResidentFonts16 = new GList();
+ psResidentFontsCC = new GList();
+ psEmbedType1 = gTrue;
+ psEmbedTrueType = gTrue;
+ psEmbedCIDPostScript = gTrue;
+ psEmbedCIDTrueType = gTrue;
+ psFontPassthrough = gFalse;
+ psPreload = gFalse;
+ psOPI = gFalse;
+ psASCIIHex = gFalse;
+ psLZW = gTrue;
+ psUncompressPreloadedImages = gFalse;
+ psMinLineWidth = 0;
+ psRasterResolution = 300;
+ psRasterMono = gFalse;
+ psRasterSliceSize = 20000000;
+ psAlwaysRasterize = gFalse;
+ psNeverRasterize = gFalse;
+ textEncoding = new GString(defaultTextEncoding);
+#if defined(_WIN32)
+ textEOL = eolDOS;
+#else
+ textEOL = eolUnix;
+#endif
+ textPageBreaks = gTrue;
+ textKeepTinyChars = gTrue;
+ initialZoom = new GString("125");
+ defaultFitZoom = 0;
+ initialDisplayMode = new GString("continuous");
+ initialToolbarState = gTrue;
+ initialSidebarState = gTrue;
+ initialSidebarWidth = 0;
+ initialSelectMode = new GString("linear");
+ maxTileWidth = 1500;
+ maxTileHeight = 1500;
+ tileCacheSize = 10;
+ workerThreads = 1;
+ enableFreeType = gTrue;
+ disableFreeTypeHinting = gFalse;
+ antialias = gTrue;
+ vectorAntialias = gTrue;
+ antialiasPrinting = gFalse;
+ strokeAdjust = strokeAdjustNormal;
+ screenType = screenUnset;
+ screenSize = -1;
+ screenDotRadius = -1;
+ screenGamma = 1.0;
+ screenBlackThreshold = 0.0;
+ screenWhiteThreshold = 1.0;
+ minLineWidth = 0.0;
+ enablePathSimplification = gFalse;
+ drawAnnotations = gTrue;
+ drawFormFields = gTrue;
+ overprintPreview = gFalse;
+ paperColor = new GString("#ffffff");
+ matteColor = new GString("#808080");
+ fullScreenMatteColor = new GString("#000000");
+ selectionColor = new GString("#8080ff");
+ reverseVideoInvertImages = gFalse;
+ launchCommand = NULL;
+ movieCommand = NULL;
+ defaultPrinter = NULL;
+ mapNumericCharNames = gTrue;
+ mapUnknownCharNames = gFalse;
+ mapExtTrueTypeFontsViaUnicode = gTrue;
+ droppedFonts = new GHash(gTrue);
+ createDefaultKeyBindings();
+ popupMenuCmds = new GList();
+ tabStateFile = appendToPath(getHomeDir(), ".xpdf.tab-state");
+ printCommands = gFalse;
+ errQuiet = gFalse;
+ debugLogFile = NULL;
+
+ cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize);
+ unicodeToUnicodeCache =
+ new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize);
+ unicodeMapCache = new UnicodeMapCache();
+ cMapCache = new CMapCache();
+
+ // set up the initial nameToUnicode table
+ for (i = 0; nameToUnicodeTab[i].name; ++i) {
+ nameToUnicode->add(nameToUnicodeTab[i].name, nameToUnicodeTab[i].u);
+ }
+
+ // set up the residentUnicodeMaps table
+ map = new UnicodeMap("Latin1", gFalse,
+ latin1UnicodeMapRanges, latin1UnicodeMapLen);
+ residentUnicodeMaps->add(map->getEncodingName(), map);
+ map = new UnicodeMap("ASCII7", gFalse,
+ ascii7UnicodeMapRanges, ascii7UnicodeMapLen);
+ residentUnicodeMaps->add(map->getEncodingName(), map);
+ map = new UnicodeMap("Symbol", gFalse,
+ symbolUnicodeMapRanges, symbolUnicodeMapLen);
+ residentUnicodeMaps->add(map->getEncodingName(), map);
+ map = new UnicodeMap("ZapfDingbats", gFalse, zapfDingbatsUnicodeMapRanges,
+ zapfDingbatsUnicodeMapLen);
+ residentUnicodeMaps->add(map->getEncodingName(), map);
+ map = new UnicodeMap("UTF-8", gTrue, &mapUTF8);
+ residentUnicodeMaps->add(map->getEncodingName(), map);
+ map = new UnicodeMap("UCS-2", gTrue, &mapUCS2);
+ residentUnicodeMaps->add(map->getEncodingName(), map);
+
+ // look for a user config file, then a system-wide config file
+ f = NULL;
+ fileName = NULL;
+ if (cfgFileName && cfgFileName[0]) {
+#ifndef PDF_PARSER_ONLY
+ fileName = new GString(cfgFileName);
+ if (!(f = fopen(fileName->getCString(), "r"))) {
+ delete fileName;
+ }
+ }
+ if (!f) {
+ fileName = appendToPath(getHomeDir(), xpdfUserConfigFile);
+ if (!(f = fopen(fileName->getCString(), "r"))) {
+ delete fileName;
+ }
+ }
+ if (!f) {
+#ifdef _WIN32
+ char buf[512];
+ i = GetModuleFileNameA(NULL, buf, sizeof(buf));
+ if (i <= 0 || i >= sizeof(buf)) {
+ // error or path too long for buffer - just use the current dir
+ buf[0] = '\0';
+ }
+ fileName = grabPath(buf);
+ appendToPath(fileName, xpdfSysConfigFile);
+#else
+ fileName = new GString(xpdfSysConfigFile);
+#endif
+ if (!(f = fopen(fileName->getCString(), "r"))) {
+ delete fileName;
+ }
+ }
+ if (f) {
+ parseFile(fileName, f);
+ delete fileName;
+ fclose(f);
+#endif /* !PDF_PARSER_ONLY */
+ }
+}
+
+void GlobalParams::setDataDirVar() {
+ GString *dir;
+
+#if defined(XPDFRC_DATADIR)
+ dir = new GString(XPDFRC_DATADIR);
+#elif defined(_WIN32)
+ wchar_t buf[512];
+ DWORD n = GetModuleFileNameW(NULL, buf, sizeof(buf) / sizeof(wchar_t));
+ if (n <= 0 || n >= sizeof(buf)) {
+ // error or path too long for buffer - just use the current dir
+ buf[0] = L'\0';
+ }
+ GString *path = fileNameToUTF8(buf);
+ dir = grabPath(path->getCString());
+ delete path;
+ appendToPath(dir, "data");
+#else
+ //~ may be useful to allow the options of using the install dir
+ //~ and/or the user's home dir (?)
+ dir = new GString("./data");
+#endif
+
+ configFileVars->add(new GString("DATADIR"), dir);
+}
+
+void GlobalParams::createDefaultKeyBindings() {
+ keyBindings = new GList();
+
+ //----- mouse buttons
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress1, xpdfKeyModNone,
+ xpdfKeyContextAny, "startSelection"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress1, xpdfKeyModShift,
+ xpdfKeyContextAny,
+ "startExtendedSelection"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease1, xpdfKeyModNone,
+ xpdfKeyContextAny, "endSelection"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease1, xpdfKeyModShift,
+ xpdfKeyContextAny,
+ "endSelection"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMouseDoubleClick1,
+ xpdfKeyModNone, xpdfKeyContextAny,
+ "selectWord"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMouseTripleClick1,
+ xpdfKeyModNone, xpdfKeyContextAny,
+ "selectLine"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMouseClick1, xpdfKeyModNone,
+ xpdfKeyContextAny, "followLinkNoSel"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMouseClick2, xpdfKeyModNone,
+ xpdfKeyContextOverLink,
+ "followLinkInNewTab"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress2, xpdfKeyModNone,
+ xpdfKeyContextAny, "startPan"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease2, xpdfKeyModNone,
+ xpdfKeyContextAny, "endPan"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress3, xpdfKeyModNone,
+ xpdfKeyContextAny, "postPopupMenu"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress4, xpdfKeyModNone,
+ xpdfKeyContextAny,
+ "scrollUpPrevPage(16)"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress5, xpdfKeyModNone,
+ xpdfKeyContextAny,
+ "scrollDownNextPage(16)"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress6, xpdfKeyModNone,
+ 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,
+ xpdfKeyContextAny, "open"));
+ keyBindings->append(new KeyBinding('r', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "reload"));
+ keyBindings->append(new KeyBinding('f', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "find"));
+ keyBindings->append(new KeyBinding('g', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "findNext"));
+ keyBindings->append(new KeyBinding('c', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "copy"));
+ keyBindings->append(new KeyBinding('p', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "print"));
+ keyBindings->append(new KeyBinding('0', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "zoomPercent(125)"));
+ keyBindings->append(new KeyBinding('+', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "zoomIn"));
+ keyBindings->append(new KeyBinding('=', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "zoomIn"));
+ keyBindings->append(new KeyBinding('-', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "zoomOut"));
+ keyBindings->append(new KeyBinding('s', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "saveAs"));
+ keyBindings->append(new KeyBinding('t', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "newTab"));
+ keyBindings->append(new KeyBinding('n', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "newWindow"));
+ keyBindings->append(new KeyBinding('w', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "closeTabOrQuit"));
+ keyBindings->append(new KeyBinding('l', xpdfKeyModCtrl,
+ xpdfKeyContextAny,
+ "toggleFullScreenMode"));
+ keyBindings->append(new KeyBinding('q', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "quit"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeTab, xpdfKeyModCtrl,
+ xpdfKeyContextAny, "nextTab"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeTab,
+ xpdfKeyModShift | xpdfKeyModCtrl,
+ xpdfKeyContextAny, "prevTab"));
+ keyBindings->append(new KeyBinding('?', xpdfKeyModCtrl,
+ xpdfKeyContextAny, "help"));
+
+ //----- alt keys
+ keyBindings->append(new KeyBinding(xpdfKeyCodeLeft, xpdfKeyModAlt,
+ xpdfKeyContextAny, "goBackward"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeRight, xpdfKeyModAlt,
+ xpdfKeyContextAny, "goForward"));
+
+ //----- home/end keys
+ keyBindings->append(new KeyBinding(xpdfKeyCodeHome, xpdfKeyModCtrl,
+ xpdfKeyContextAny, "gotoPage(1)"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeHome, xpdfKeyModNone,
+ xpdfKeyContextAny, "scrollToTopLeft"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeEnd, xpdfKeyModCtrl,
+ xpdfKeyContextAny, "gotoLastPage"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeEnd, xpdfKeyModNone,
+ xpdfKeyContextAny,
+ "scrollToBottomRight"));
+
+ //----- pgup/pgdn keys
+ keyBindings->append(new KeyBinding(xpdfKeyCodePgUp, xpdfKeyModNone,
+ xpdfKeyContextAny, "pageUp"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodePgDn, xpdfKeyModNone,
+ xpdfKeyContextAny, "pageDown"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodePgUp, xpdfKeyModCtrl,
+ xpdfKeyContextAny, "prevPage"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodePgDn, xpdfKeyModCtrl,
+ xpdfKeyContextAny, "nextPage"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodePgUp, xpdfKeyModCtrl,
+ xpdfKeyContextScrLockOn,
+ "prevPageNoScroll"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodePgDn, xpdfKeyModCtrl,
+ xpdfKeyContextScrLockOn,
+ "nextPageNoScroll"));
+
+ //----- esc key
+ keyBindings->append(new KeyBinding(xpdfKeyCodeEsc, xpdfKeyModNone,
+ xpdfKeyContextFullScreen,
+ "windowMode"));
+
+ //----- arrow keys
+ keyBindings->append(new KeyBinding(xpdfKeyCodeLeft, xpdfKeyModNone,
+ xpdfKeyContextAny, "scrollLeft(16)"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeRight, xpdfKeyModNone,
+ xpdfKeyContextAny, "scrollRight(16)"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeUp, xpdfKeyModNone,
+ xpdfKeyContextAny, "scrollUp(16)"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeDown, xpdfKeyModNone,
+ xpdfKeyContextAny, "scrollDown(16)"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeUp, xpdfKeyModCtrl,
+ xpdfKeyContextAny, "prevPage"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeDown, xpdfKeyModCtrl,
+ xpdfKeyContextAny, "nextPage"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeUp, xpdfKeyModCtrl,
+ xpdfKeyContextScrLockOn,
+ "prevPageNoScroll"));
+ keyBindings->append(new KeyBinding(xpdfKeyCodeDown, xpdfKeyModCtrl,
+ xpdfKeyContextScrLockOn,
+ "nextPageNoScroll"));
+
+ //----- letter keys
+ keyBindings->append(new KeyBinding(' ', xpdfKeyModNone,
+ xpdfKeyContextAny, "pageDown"));
+ keyBindings->append(new KeyBinding('g', xpdfKeyModNone,
+ xpdfKeyContextAny, "focusToPageNum"));
+ keyBindings->append(new KeyBinding('z', xpdfKeyModNone,
+ xpdfKeyContextAny, "zoomFitPage"));
+ keyBindings->append(new KeyBinding('w', xpdfKeyModNone,
+ xpdfKeyContextAny, "zoomFitWidth"));
+}
+
+void GlobalParams::parseFile(GString *fileName, FILE *f) {
+ int line;
+ char buf[512];
+
+ line = 1;
+ while (getLine(buf, sizeof(buf) - 1, f)) {
+ parseLine(buf, fileName, line);
+ ++line;
+ }
+}
+
+void GlobalParams::parseLine(char *buf, GString *fileName, int line) {
+ GList *tokens;
+ GString *cmd, *incFile;
+ FILE *f2;
+
+ // break the line into tokens
+ tokens = parseLineTokens(buf, fileName, line);
+
+ // parse the line
+ if (tokens->getLength() > 0 &&
+ ((GString *)tokens->get(0))->getChar(0) != '#') {
+ cmd = (GString *)tokens->get(0);
+ if (!cmd->cmp("include")) {
+ if (tokens->getLength() == 2) {
+ incFile = (GString *)tokens->get(1);
+ if ((f2 = openFile(incFile->getCString(), "r"))) {
+ parseFile(incFile, f2);
+ fclose(f2);
+ } else {
+ error(errConfig, -1,
+ "Couldn't find included config file: '{0:t}' ({1:t}:{2:d})",
+ incFile, fileName, line);
+ }
+ } else {
+ error(errConfig, -1, "Bad 'include' config file command ({0:t}:{1:d})",
+ fileName, line);
+ }
+ } else if (!cmd->cmp("nameToUnicode")) {
+ parseNameToUnicode(tokens, fileName, line);
+ } else if (!cmd->cmp("cidToUnicode")) {
+ parseCIDToUnicode(tokens, fileName, line);
+ } else if (!cmd->cmp("unicodeToUnicode")) {
+ parseUnicodeToUnicode(tokens, fileName, line);
+ } else if (!cmd->cmp("unicodeMap")) {
+ parseUnicodeMap(tokens, fileName, line);
+ } else if (!cmd->cmp("cMapDir")) {
+ parseCMapDir(tokens, fileName, line);
+ } else if (!cmd->cmp("toUnicodeDir")) {
+ parseToUnicodeDir(tokens, fileName, line);
+ } else if (!cmd->cmp("unicodeRemapping")) {
+ parseUnicodeRemapping(tokens, fileName, line);
+ } else if (!cmd->cmp("fontFile")) {
+ parseFontFile(tokens, fileName, line);
+ } else if (!cmd->cmp("fontDir")) {
+ parseFontDir(tokens, fileName, line);
+ } else if (!cmd->cmp("fontFileCC")) {
+ parseFontFileCC(tokens, fileName, line);
+ } else if (!cmd->cmp("psPaperSize")) {
+ parsePSPaperSize(tokens, fileName, line);
+ } else if (!cmd->cmp("psImageableArea")) {
+ parsePSImageableArea(tokens, fileName, line);
+ } else if (!cmd->cmp("psCrop")) {
+ parseYesNo("psCrop", &psCrop, tokens, fileName, line);
+ } else if (!cmd->cmp("psUseCropBoxAsPage")) {
+ parseYesNo("psUseCropBoxAsPage", &psUseCropBoxAsPage,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psExpandSmaller")) {
+ parseYesNo("psExpandSmaller", &psExpandSmaller,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psShrinkLarger")) {
+ parseYesNo("psShrinkLarger", &psShrinkLarger, tokens, fileName, line);
+ } else if (!cmd->cmp("psCenter")) {
+ parseYesNo("psCenter", &psCenter, tokens, fileName, line);
+ } else if (!cmd->cmp("psDuplex")) {
+ parseYesNo("psDuplex", &psDuplex, tokens, fileName, line);
+ } else if (!cmd->cmp("psLevel")) {
+ parsePSLevel(tokens, fileName, line);
+ } else if (!cmd->cmp("psResidentFont")) {
+ parsePSResidentFont(tokens, fileName, line);
+ } else if (!cmd->cmp("psResidentFont16")) {
+ parsePSResidentFont16(tokens, fileName, line);
+ } else if (!cmd->cmp("psResidentFontCC")) {
+ parsePSResidentFontCC(tokens, fileName, line);
+ } else if (!cmd->cmp("psEmbedType1Fonts")) {
+ parseYesNo("psEmbedType1", &psEmbedType1, tokens, fileName, line);
+ } else if (!cmd->cmp("psEmbedTrueTypeFonts")) {
+ parseYesNo("psEmbedTrueType", &psEmbedTrueType,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psEmbedCIDPostScriptFonts")) {
+ parseYesNo("psEmbedCIDPostScript", &psEmbedCIDPostScript,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psEmbedCIDTrueTypeFonts")) {
+ parseYesNo("psEmbedCIDTrueType", &psEmbedCIDTrueType,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psFontPassthrough")) {
+ parseYesNo("psFontPassthrough", &psFontPassthrough,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psPreload")) {
+ parseYesNo("psPreload", &psPreload, tokens, fileName, line);
+ } else if (!cmd->cmp("psOPI")) {
+ parseYesNo("psOPI", &psOPI, tokens, fileName, line);
+ } else if (!cmd->cmp("psASCIIHex")) {
+ parseYesNo("psASCIIHex", &psASCIIHex, tokens, fileName, line);
+ } else if (!cmd->cmp("psLZW")) {
+ parseYesNo("psLZW", &psLZW, tokens, fileName, line);
+ } else if (!cmd->cmp("psUncompressPreloadedImages")) {
+ parseYesNo("psUncompressPreloadedImages", &psUncompressPreloadedImages,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psMinLineWidth")) {
+ parseFloat("psMinLineWidth", &psMinLineWidth,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psRasterResolution")) {
+ parseFloat("psRasterResolution", &psRasterResolution,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psRasterMono")) {
+ parseYesNo("psRasterMono", &psRasterMono, tokens, fileName, line);
+ } else if (!cmd->cmp("psRasterSliceSize")) {
+ parseInteger("psRasterSliceSize", &psRasterSliceSize,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psAlwaysRasterize")) {
+ parseYesNo("psAlwaysRasterize", &psAlwaysRasterize,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psNeverRasterize")) {
+ parseYesNo("psNeverRasterize", &psNeverRasterize,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("textEncoding")) {
+ parseString("textEncoding", &textEncoding, tokens, fileName, line);
+ } else if (!cmd->cmp("textEOL")) {
+ parseTextEOL(tokens, fileName, line);
+ } else if (!cmd->cmp("textPageBreaks")) {
+ parseYesNo("textPageBreaks", &textPageBreaks,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("textKeepTinyChars")) {
+ parseYesNo("textKeepTinyChars", &textKeepTinyChars,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("initialZoom")) {
+ parseString("initialZoom", &initialZoom, tokens, fileName, line);
+ } else if (!cmd->cmp("defaultFitZoom")) {
+ parseInteger("defaultFitZoom", &defaultFitZoom, tokens, fileName, line);
+ } else if (!cmd->cmp("initialDisplayMode")) {
+ parseString("initialDisplayMode", &initialDisplayMode,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("initialToolbarState")) {
+ parseYesNo("initialToolbarState", &initialToolbarState,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("initialSidebarState")) {
+ parseYesNo("initialSidebarState", &initialSidebarState,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("initialSidebarWidth")) {
+ parseInteger("initialSidebarWidth", &initialSidebarWidth,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("initialSelectMode")) {
+ parseString("initialSelectMode", &initialSelectMode,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("maxTileWidth")) {
+ parseInteger("maxTileWidth", &maxTileWidth, tokens, fileName, line);
+ } else if (!cmd->cmp("maxTileHeight")) {
+ parseInteger("maxTileHeight", &maxTileHeight, tokens, fileName, line);
+ } else if (!cmd->cmp("tileCacheSize")) {
+ parseInteger("tileCacheSize", &tileCacheSize, tokens, fileName, line);
+ } else if (!cmd->cmp("workerThreads")) {
+ parseInteger("workerThreads", &workerThreads, tokens, fileName, line);
+ } else if (!cmd->cmp("enableFreeType")) {
+ parseYesNo("enableFreeType", &enableFreeType, tokens, fileName, line);
+ } else if (!cmd->cmp("disableFreeTypeHinting")) {
+ parseYesNo("disableFreeTypeHinting", &disableFreeTypeHinting,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("antialias")) {
+ parseYesNo("antialias", &antialias, tokens, fileName, line);
+ } else if (!cmd->cmp("vectorAntialias")) {
+ parseYesNo("vectorAntialias", &vectorAntialias,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("antialiasPrinting")) {
+ parseYesNo("antialiasPrinting", &antialiasPrinting,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("strokeAdjust")) {
+ parseStrokeAdjust(tokens, fileName, line);
+ } else if (!cmd->cmp("screenType")) {
+ parseScreenType(tokens, fileName, line);
+ } else if (!cmd->cmp("screenSize")) {
+ parseInteger("screenSize", &screenSize, tokens, fileName, line);
+ } else if (!cmd->cmp("screenDotRadius")) {
+ parseInteger("screenDotRadius", &screenDotRadius,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("screenGamma")) {
+ parseFloat("screenGamma", &screenGamma,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("screenBlackThreshold")) {
+ parseFloat("screenBlackThreshold", &screenBlackThreshold,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("screenWhiteThreshold")) {
+ parseFloat("screenWhiteThreshold", &screenWhiteThreshold,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("minLineWidth")) {
+ parseFloat("minLineWidth", &minLineWidth,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("enablePathSimplification")) {
+ parseYesNo("enablePathSimplification", &enablePathSimplification,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("drawAnnotations")) {
+ parseYesNo("drawAnnotations", &drawAnnotations,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("drawFormFields")) {
+ parseYesNo("drawFormFields", &drawFormFields,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("overprintPreview")) {
+ parseYesNo("overprintPreview", &overprintPreview,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("paperColor")) {
+ parseString("paperColor", &paperColor, tokens, fileName, line);
+ } else if (!cmd->cmp("matteColor")) {
+ parseString("matteColor", &matteColor, tokens, fileName, line);
+ } else if (!cmd->cmp("fullScreenMatteColor")) {
+ parseString("fullScreenMatteColor", &fullScreenMatteColor,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("selectionColor")) {
+ parseString("selectionColor", &selectionColor, tokens, fileName, line);
+ } else if (!cmd->cmp("reverseVideoInvertImages")) {
+ parseYesNo("reverseVideoInvertImages", &reverseVideoInvertImages,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("launchCommand")) {
+ parseString("launchCommand", &launchCommand, tokens, fileName, line);
+ } else if (!cmd->cmp("movieCommand")) {
+ parseString("movieCommand", &movieCommand, tokens, fileName, line);
+ } else if (!cmd->cmp("defaultPrinter")) {
+ parseString("defaultPrinter", &defaultPrinter, tokens, fileName, line);
+ } else if (!cmd->cmp("mapNumericCharNames")) {
+ parseYesNo("mapNumericCharNames", &mapNumericCharNames,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("mapUnknownCharNames")) {
+ parseYesNo("mapUnknownCharNames", &mapUnknownCharNames,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("mapExtTrueTypeFontsViaUnicode")) {
+ parseYesNo("mapExtTrueTypeFontsViaUnicode",
+ &mapExtTrueTypeFontsViaUnicode,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("dropFont")) {
+ parseDropFont(tokens, fileName, line);
+ } else if (!cmd->cmp("bind")) {
+ parseBind(tokens, fileName, line);
+ } else if (!cmd->cmp("unbind")) {
+ parseUnbind(tokens, fileName, line);
+ } else if (!cmd->cmp("popupMenuCmd")) {
+ parsePopupMenuCmd(tokens, fileName, line);
+ } else if (!cmd->cmp("tabStateFile")) {
+ parseString("tabStateFile", &tabStateFile, tokens, fileName, line);
+ } else if (!cmd->cmp("printCommands")) {
+ parseYesNo("printCommands", &printCommands, tokens, fileName, line);
+ } else if (!cmd->cmp("errQuiet")) {
+ parseYesNo("errQuiet", &errQuiet, tokens, fileName, line);
+ } else if (!cmd->cmp("debugLogFile")) {
+ parseString("debugLogFile", &debugLogFile, tokens, fileName, line);
+ } else {
+ error(errConfig, -1, "Unknown config file command '{0:t}' ({1:t}:{2:d})",
+ cmd, fileName, line);
+ if (!cmd->cmp("displayFontX") ||
+ !cmd->cmp("displayNamedCIDFontX") ||
+ !cmd->cmp("displayCIDFontX")) {
+ error(errConfig, -1, "Xpdf no longer supports X fonts");
+ } else if (!cmd->cmp("enableT1lib")) {
+ error(errConfig, -1, "Xpdf no longer uses t1lib");
+ } else if (!cmd->cmp("t1libControl") || !cmd->cmp("freetypeControl")) {
+ error(errConfig, -1,
+ "The t1libControl and freetypeControl options have been replaced by the enableT1lib, enableFreeType, and antialias options");
+ } 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");
+ }
+ }
+ }
+
+ 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;
+ char *tok1, *tok2;
+ FILE *f;
+ char buf[256];
+ int line2;
+ Unicode u;
+
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1,
+ "Bad 'nameToUnicode' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ name = (GString *)tokens->get(1);
+ if (!(f = openFile(name->getCString(), "r"))) {
+ error(errConfig, -1, "Couldn't open 'nameToUnicode' file '{0:t}'", name);
+ return;
+ }
+ line2 = 1;
+ while (getLine(buf, sizeof(buf), f)) {
+ tok1 = strtok(buf, " \t\r\n");
+ tok2 = strtok(NULL, " \t\r\n");
+ if (tok1 && tok2) {
+ sscanf(tok1, "%x", &u);
+ nameToUnicode->add(tok2, u);
+ } else {
+ error(errConfig, -1, "Bad line in 'nameToUnicode' file ({0:t}:{1:d})",
+ name, line2);
+ }
+ ++line2;
+ }
+ fclose(f);
+}
+
+void GlobalParams::parseCIDToUnicode(GList *tokens, GString *fileName,
+ int line) {
+ GString *collection, *name, *old;
+
+ if (tokens->getLength() != 3) {
+ error(errConfig, -1,
+ "Bad 'cidToUnicode' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ collection = (GString *)tokens->get(1);
+ name = (GString *)tokens->get(2);
+ if ((old = (GString *)cidToUnicodes->remove(collection))) {
+ delete old;
+ }
+ cidToUnicodes->add(collection->copy(), name->copy());
+}
+
+void GlobalParams::parseUnicodeToUnicode(GList *tokens, GString *fileName,
+ int line) {
+ GString *font, *file, *old;
+
+ if (tokens->getLength() != 3) {
+ error(errConfig, -1,
+ "Bad 'unicodeToUnicode' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ font = (GString *)tokens->get(1);
+ file = (GString *)tokens->get(2);
+ if ((old = (GString *)unicodeToUnicodes->remove(font))) {
+ delete old;
+ }
+ unicodeToUnicodes->add(font->copy(), file->copy());
+}
+
+void GlobalParams::parseUnicodeMap(GList *tokens, GString *fileName,
+ int line) {
+ GString *encodingName, *name, *old;
+
+ if (tokens->getLength() != 3) {
+ error(errConfig, -1, "Bad 'unicodeMap' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ encodingName = (GString *)tokens->get(1);
+ name = (GString *)tokens->get(2);
+ if ((old = (GString *)unicodeMaps->remove(encodingName))) {
+ delete old;
+ }
+ unicodeMaps->add(encodingName->copy(), name->copy());
+}
+
+void GlobalParams::parseCMapDir(GList *tokens, GString *fileName, int line) {
+ GString *collection, *dir;
+ GList *list;
+
+ if (tokens->getLength() != 3) {
+ error(errConfig, -1, "Bad 'cMapDir' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ collection = (GString *)tokens->get(1);
+ dir = (GString *)tokens->get(2);
+ if (!(list = (GList *)cMapDirs->lookup(collection))) {
+ list = new GList();
+ cMapDirs->add(collection->copy(), list);
+ }
+ list->append(dir->copy());
+}
+
+void GlobalParams::parseToUnicodeDir(GList *tokens, GString *fileName,
+ int line) {
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1,
+ "Bad 'toUnicodeDir' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ toUnicodeDirs->append(((GString *)tokens->get(1))->copy());
+}
+
+void GlobalParams::parseUnicodeRemapping(GList *tokens, GString *fileName,
+ int line) {
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1,
+ "Bad 'unicodeRemapping' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ unicodeRemapping->parseFile((GString *)tokens->get(1));
+}
+
+void GlobalParams::parseFontFile(GList *tokens, GString *fileName, int line) {
+ if (tokens->getLength() != 3) {
+ error(errConfig, -1, "Bad 'fontFile' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ fontFiles->add(((GString *)tokens->get(1))->copy(),
+ ((GString *)tokens->get(2))->copy());
+}
+
+void GlobalParams::parseFontDir(GList *tokens, GString *fileName, int line) {
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1, "Bad 'fontDir' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ fontDirs->append(((GString *)tokens->get(1))->copy());
+}
+
+void GlobalParams::parseFontFileCC(GList *tokens, GString *fileName,
+ int line) {
+ if (tokens->getLength() != 3) {
+ error(errConfig, -1, "Bad 'fontFileCC' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ ccFontFiles->add(((GString *)tokens->get(1))->copy(),
+ ((GString *)tokens->get(2))->copy());
+}
+
+void GlobalParams::parsePSPaperSize(GList *tokens, GString *fileName,
+ int line) {
+ GString *tok;
+
+ if (tokens->getLength() == 2) {
+ tok = (GString *)tokens->get(1);
+ if (!setPSPaperSize(tok->getCString())) {
+ error(errConfig, -1,
+ "Bad 'psPaperSize' config file command ({0:s}:{1:d})",
+ fileName, line);
+ }
+ } else if (tokens->getLength() == 3) {
+ tok = (GString *)tokens->get(1);
+ psPaperWidth = atoi(tok->getCString());
+ tok = (GString *)tokens->get(2);
+ psPaperHeight = atoi(tok->getCString());
+ psImageableLLX = psImageableLLY = 0;
+ psImageableURX = psPaperWidth;
+ psImageableURY = psPaperHeight;
+ } else {
+ error(errConfig, -1, "Bad 'psPaperSize' config file command ({0:t}:{1:d})",
+ fileName, line);
+ }
+}
+
+void GlobalParams::parsePSImageableArea(GList *tokens, GString *fileName,
+ int line) {
+ if (tokens->getLength() != 5) {
+ error(errConfig, -1,
+ "Bad 'psImageableArea' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ psImageableLLX = atoi(((GString *)tokens->get(1))->getCString());
+ psImageableLLY = atoi(((GString *)tokens->get(2))->getCString());
+ psImageableURX = atoi(((GString *)tokens->get(3))->getCString());
+ psImageableURY = atoi(((GString *)tokens->get(4))->getCString());
+}
+
+void GlobalParams::parsePSLevel(GList *tokens, GString *fileName, int line) {
+ GString *tok;
+
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1, "Bad 'psLevel' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ tok = (GString *)tokens->get(1);
+ if (!tok->cmp("level1")) {
+ psLevel = psLevel1;
+ } else if (!tok->cmp("level1sep")) {
+ psLevel = psLevel1Sep;
+ } else if (!tok->cmp("level2")) {
+ psLevel = psLevel2;
+ } else if (!tok->cmp("level2gray")) {
+ psLevel = psLevel2Gray;
+ } else if (!tok->cmp("level2sep")) {
+ psLevel = psLevel2Sep;
+ } else if (!tok->cmp("level3")) {
+ psLevel = psLevel3;
+ } else if (!tok->cmp("level3gray")) {
+ psLevel = psLevel3Gray;
+ } else if (!tok->cmp("level3Sep")) {
+ psLevel = psLevel3Sep;
+ } else {
+ error(errConfig, -1, "Bad 'psLevel' config file command ({0:t}:{1:d})",
+ fileName, line);
+ }
+}
+
+void GlobalParams::parsePSResidentFont(GList *tokens, GString *fileName,
+ int line) {
+ if (tokens->getLength() != 3) {
+ error(errConfig, -1, "Bad 'psResidentFont' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ psResidentFonts->add(((GString *)tokens->get(1))->copy(),
+ ((GString *)tokens->get(2))->copy());
+}
+
+void GlobalParams::parsePSResidentFont16(GList *tokens, GString *fileName,
+ int line) {
+ PSFontParam16 *param;
+ int wMode;
+ GString *tok;
+
+ if (tokens->getLength() != 5) {
+ error(errConfig, -1, "Bad 'psResidentFont16' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ tok = (GString *)tokens->get(2);
+ if (!tok->cmp("H")) {
+ wMode = 0;
+ } else if (!tok->cmp("V")) {
+ wMode = 1;
+ } else {
+ error(errConfig, -1, "Bad wMode in psResidentFont16 config file command ({1:t}:{2:d})",
+ fileName, line);
+ return;
+ }
+ param = new PSFontParam16(((GString *)tokens->get(1))->copy(),
+ wMode,
+ ((GString *)tokens->get(3))->copy(),
+ ((GString *)tokens->get(4))->copy());
+ psResidentFonts16->append(param);
+}
+
+void GlobalParams::parsePSResidentFontCC(GList *tokens, GString *fileName,
+ int line) {
+ PSFontParam16 *param;
+ int wMode;
+ GString *tok;
+
+ if (tokens->getLength() != 5) {
+ error(errConfig, -1, "Bad 'psResidentFontCC' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ tok = (GString *)tokens->get(2);
+ if (!tok->cmp("H")) {
+ wMode = 0;
+ } else if (!tok->cmp("V")) {
+ wMode = 1;
+ } else {
+ error(errConfig, -1, "Bad wMode in psResidentFontCC config file command ({1:t}:{2:d})",
+ fileName, line);
+ return;
+ }
+ param = new PSFontParam16(((GString *)tokens->get(1))->copy(),
+ wMode,
+ ((GString *)tokens->get(3))->copy(),
+ ((GString *)tokens->get(4))->copy());
+ psResidentFontsCC->append(param);
+}
+
+void GlobalParams::parseTextEOL(GList *tokens, GString *fileName, int line) {
+ GString *tok;
+
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1, "Bad 'textEOL' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ tok = (GString *)tokens->get(1);
+ if (!tok->cmp("unix")) {
+ textEOL = eolUnix;
+ } else if (!tok->cmp("dos")) {
+ textEOL = eolDOS;
+ } else if (!tok->cmp("mac")) {
+ textEOL = eolMac;
+ } else {
+ error(errConfig, -1, "Bad 'textEOL' config file command ({0:t}:{1:d})",
+ fileName, line);
+ }
+}
+
+void GlobalParams::parseStrokeAdjust(GList *tokens, GString *fileName,
+ int line) {
+ GString *tok;
+
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1,
+ "Bad 'strokeAdjust' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ tok = (GString *)tokens->get(1);
+ if (!tok->cmp("no")) {
+ strokeAdjust = strokeAdjustOff;
+ } else if (!tok->cmp("yes")) {
+ strokeAdjust = strokeAdjustNormal;
+ } else if (!tok->cmp("cad")) {
+ strokeAdjust = strokeAdjustCAD;
+ } else {
+ error(errConfig, -1,
+ "Bad 'strokeAdjust' config file command ({0:t}:{1:d})",
+ fileName, line);
+ }
+}
+
+void GlobalParams::parseScreenType(GList *tokens, GString *fileName,
+ int line) {
+ GString *tok;
+
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1, "Bad 'screenType' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ tok = (GString *)tokens->get(1);
+ if (!tok->cmp("dispersed")) {
+ screenType = screenDispersed;
+ } else if (!tok->cmp("clustered")) {
+ screenType = screenClustered;
+ } else if (!tok->cmp("stochasticClustered")) {
+ screenType = screenStochasticClustered;
+ } else {
+ error(errConfig, -1, "Bad 'screenType' config file command ({0:t}:{1:d})",
+ fileName, line);
+ }
+}
+
+void GlobalParams::parseDropFont(GList *tokens, GString *fileName, int line) {
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1, "Bad 'dropFont' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ droppedFonts->add(((GString *)tokens->get(1))->copy(), 1);
+}
+
+void GlobalParams::parseBind(GList *tokens, GString *fileName, int line) {
+ KeyBinding *binding;
+ GList *cmds;
+ int code, mods, context, i;
+
+ if (tokens->getLength() < 4) {
+ error(errConfig, -1, "Bad 'bind' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ if (!parseKey((GString *)tokens->get(1), (GString *)tokens->get(2),
+ &code, &mods, &context,
+ "bind", tokens, fileName, line)) {
+ return;
+ }
+ for (i = 0; i < keyBindings->getLength(); ++i) {
+ binding = (KeyBinding *)keyBindings->get(i);
+ if (binding->code == code &&
+ binding->mods == mods &&
+ binding->context == context) {
+ delete (KeyBinding *)keyBindings->del(i);
+ break;
+ }
+ }
+ cmds = new GList();
+ for (i = 3; i < tokens->getLength(); ++i) {
+ cmds->append(((GString *)tokens->get(i))->copy());
+ }
+ keyBindings->append(new KeyBinding(code, mods, context, cmds));
+}
+
+void GlobalParams::parseUnbind(GList *tokens, GString *fileName, int line) {
+ KeyBinding *binding;
+ int code, mods, context, i;
+
+ if (tokens->getLength() != 3) {
+ error(errConfig, -1, "Bad 'unbind' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ if (!parseKey((GString *)tokens->get(1), (GString *)tokens->get(2),
+ &code, &mods, &context,
+ "unbind", tokens, fileName, line)) {
+ return;
+ }
+ for (i = 0; i < keyBindings->getLength(); ++i) {
+ binding = (KeyBinding *)keyBindings->get(i);
+ if (binding->code == code &&
+ binding->mods == mods &&
+ binding->context == context) {
+ delete (KeyBinding *)keyBindings->del(i);
+ break;
+ }
+ }
+}
+
+GBool GlobalParams::parseKey(GString *modKeyStr, GString *contextStr,
+ int *code, int *mods, int *context,
+ const char *cmdName,
+ GList *tokens, GString *fileName, int line) {
+ char *p0;
+ int btn;
+
+ *mods = xpdfKeyModNone;
+ p0 = modKeyStr->getCString();
+ while (1) {
+ if (!strncmp(p0, "shift-", 6)) {
+ *mods |= xpdfKeyModShift;
+ p0 += 6;
+ } else if (!strncmp(p0, "ctrl-", 5)) {
+ *mods |= xpdfKeyModCtrl;
+ p0 += 5;
+ } else if (!strncmp(p0, "alt-", 4)) {
+ *mods |= xpdfKeyModAlt;
+ p0 += 4;
+ } else {
+ break;
+ }
+ }
+
+ if (!strcmp(p0, "space")) {
+ *code = ' ';
+ } else if (!strcmp(p0, "tab")) {
+ *code = xpdfKeyCodeTab;
+ } else if (!strcmp(p0, "return")) {
+ *code = xpdfKeyCodeReturn;
+ } else if (!strcmp(p0, "enter")) {
+ *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")) {
+ *code = xpdfKeyCodeDelete;
+ } else if (!strcmp(p0, "home")) {
+ *code = xpdfKeyCodeHome;
+ } else if (!strcmp(p0, "end")) {
+ *code = xpdfKeyCodeEnd;
+ } else if (!strcmp(p0, "pgup")) {
+ *code = xpdfKeyCodePgUp;
+ } else if (!strcmp(p0, "pgdn")) {
+ *code = xpdfKeyCodePgDn;
+ } else if (!strcmp(p0, "left")) {
+ *code = xpdfKeyCodeLeft;
+ } else if (!strcmp(p0, "right")) {
+ *code = xpdfKeyCodeRight;
+ } else if (!strcmp(p0, "up")) {
+ *code = xpdfKeyCodeUp;
+ } else if (!strcmp(p0, "down")) {
+ *code = xpdfKeyCodeDown;
+ } else if (p0[0] == 'f' && p0[1] >= '1' && p0[1] <= '9' && !p0[2]) {
+ *code = xpdfKeyCodeF1 + (p0[1] - '1');
+ } else if (p0[0] == 'f' &&
+ ((p0[1] >= '1' && p0[1] <= '2' && p0[2] >= '0' && p0[2] <= '9') ||
+ (p0[1] == '3' && p0[2] >= '0' && p0[2] <= '5')) &&
+ !p0[3]) {
+ *code = xpdfKeyCodeF1 + 10 * (p0[1] - '0') + (p0[2] - '0') - 1;
+ } else if (!strncmp(p0, "mousePress", 10) &&
+ p0[10] >= '0' && p0[10] <= '9' &&
+ (!p0[11] || (p0[11] >= '0' && p0[11] <= '9' && !p0[12])) &&
+ (btn = atoi(p0 + 10)) >= 1 && btn <= 32) {
+ *code = xpdfKeyCodeMousePress1 + btn - 1;
+ } else if (!strncmp(p0, "mouseRelease", 12) &&
+ p0[12] >= '0' && p0[12] <= '9' &&
+ (!p0[13] || (p0[13] >= '0' && p0[13] <= '9' && !p0[14])) &&
+ (btn = atoi(p0 + 12)) >= 1 && btn <= 32) {
+ *code = xpdfKeyCodeMouseRelease1 + btn - 1;
+ } else if (!strncmp(p0, "mouseClick", 10) &&
+ p0[10] >= '0' && p0[10] <= '9' &&
+ (!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 {
+ error(errConfig, -1,
+ "Bad key/modifier in '{0:s}' config file command ({1:t}:{2:d})",
+ cmdName, fileName, line);
+ return gFalse;
+ }
+
+ p0 = contextStr->getCString();
+ if (!strcmp(p0, "any")) {
+ *context = xpdfKeyContextAny;
+ } else {
+ *context = xpdfKeyContextAny;
+ while (1) {
+ if (!strncmp(p0, "fullScreen", 10)) {
+ *context |= xpdfKeyContextFullScreen;
+ p0 += 10;
+ } else if (!strncmp(p0, "window", 6)) {
+ *context |= xpdfKeyContextWindow;
+ p0 += 6;
+ } else if (!strncmp(p0, "continuous", 10)) {
+ *context |= xpdfKeyContextContinuous;
+ p0 += 10;
+ } else if (!strncmp(p0, "singlePage", 10)) {
+ *context |= xpdfKeyContextSinglePage;
+ p0 += 10;
+ } else if (!strncmp(p0, "overLink", 8)) {
+ *context |= xpdfKeyContextOverLink;
+ p0 += 8;
+ } else if (!strncmp(p0, "offLink", 7)) {
+ *context |= xpdfKeyContextOffLink;
+ p0 += 7;
+ } else if (!strncmp(p0, "outline", 7)) {
+ *context |= xpdfKeyContextOutline;
+ p0 += 7;
+ } else if (!strncmp(p0, "mainWin", 7)) {
+ *context |= xpdfKeyContextMainWin;
+ p0 += 7;
+ } else if (!strncmp(p0, "scrLockOn", 9)) {
+ *context |= xpdfKeyContextScrLockOn;
+ p0 += 9;
+ } else if (!strncmp(p0, "scrLockOff", 10)) {
+ *context |= xpdfKeyContextScrLockOff;
+ p0 += 10;
+ } else {
+ error(errConfig, -1,
+ "Bad context in '{0:s}' config file command ({1:t}:{2:d})",
+ cmdName, fileName, line);
+ return gFalse;
+ }
+ if (!*p0) {
+ break;
+ }
+ if (*p0 != ',') {
+ error(errConfig, -1,
+ "Bad context in '{0:s}' config file command ({1:t}:{2:d})",
+ cmdName, fileName, line);
+ return gFalse;
+ }
+ ++p0;
+ }
+ }
+
+ return gTrue;
+}
+
+void GlobalParams::parsePopupMenuCmd(GList *tokens,
+ GString *fileName, int line) {
+ GList *cmds;
+ int i;
+
+ if (tokens->getLength() < 3) {
+ error(errConfig, -1,
+ "Bad 'popupMenuCmd' config file command ({0:t}:{1:d})",
+ fileName, line);
+ return;
+ }
+ cmds = new GList();
+ for (i = 2; i < tokens->getLength(); ++i) {
+ cmds->append(((GString *)tokens->get(i))->copy());
+ }
+ popupMenuCmds->append(new PopupMenuCmd(((GString *)tokens->get(1))->copy(),
+ cmds));
+}
+
+void GlobalParams::parseYesNo(const char *cmdName, GBool *flag,
+ GList *tokens, GString *fileName, int line) {
+ GString *tok;
+
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})",
+ cmdName, fileName, line);
+ return;
+ }
+ tok = (GString *)tokens->get(1);
+ if (!parseYesNo2(tok->getCString(), flag)) {
+ error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})",
+ cmdName, fileName, line);
+ }
+}
+
+GBool GlobalParams::parseYesNo2(char *token, GBool *flag) {
+ if (!strcmp(token, "yes")) {
+ *flag = gTrue;
+ } else if (!strcmp(token, "no")) {
+ *flag = gFalse;
+ } else {
+ return gFalse;
+ }
+ return gTrue;
+}
+
+void GlobalParams::parseString(const char *cmdName, GString **s,
+ GList *tokens, GString *fileName, int line) {
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})",
+ cmdName, fileName, line);
+ return;
+ }
+ if (*s) {
+ delete *s;
+ }
+ *s = ((GString *)tokens->get(1))->copy();
+}
+
+void GlobalParams::parseInteger(const char *cmdName, int *val,
+ GList *tokens, GString *fileName, int line) {
+ GString *tok;
+ int i;
+
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})",
+ cmdName, fileName, line);
+ return;
+ }
+ tok = (GString *)tokens->get(1);
+ if (tok->getLength() == 0) {
+ error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})",
+ cmdName, fileName, line);
+ return;
+ }
+ if (tok->getChar(0) == '-') {
+ i = 1;
+ } else {
+ i = 0;
+ }
+ for (; i < tok->getLength(); ++i) {
+ if (tok->getChar(i) < '0' || tok->getChar(i) > '9') {
+ error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})",
+ cmdName, fileName, line);
+ return;
+ }
+ }
+ *val = atoi(tok->getCString());
+}
+
+void GlobalParams::parseFloat(const char *cmdName, double *val,
+ GList *tokens, GString *fileName, int line) {
+ GString *tok;
+ int i;
+
+ if (tokens->getLength() != 2) {
+ error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})",
+ cmdName, fileName, line);
+ return;
+ }
+ tok = (GString *)tokens->get(1);
+ if (tok->getLength() == 0) {
+ error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})",
+ cmdName, fileName, line);
+ return;
+ }
+ if (tok->getChar(0) == '-') {
+ i = 1;
+ } else {
+ i = 0;
+ }
+ for (; i < tok->getLength(); ++i) {
+ if (!((tok->getChar(i) >= '0' && tok->getChar(i) <= '9') ||
+ tok->getChar(i) == '.')) {
+ error(errConfig, -1, "Bad '{0:s}' config file command ({1:t}:{2:d})",
+ cmdName, fileName, line);
+ return;
+ }
+ }
+ *val = atof(tok->getCString());
+}
+
+GlobalParams::~GlobalParams() {
+ GHashIter *iter;
+ GString *key;
+ GList *list;
+
+ freeBuiltinFontTables();
+
+ delete macRomanReverseMap;
+
+ delete baseDir;
+ deleteGHash(configFileVars, GString);
+ delete nameToUnicode;
+ deleteGHash(cidToUnicodes, GString);
+ deleteGHash(unicodeToUnicodes, GString);
+ deleteGHash(residentUnicodeMaps, UnicodeMap);
+ deleteGHash(unicodeMaps, GString);
+ deleteGList(toUnicodeDirs, GString);
+ delete unicodeRemapping;
+ deleteGHash(fontFiles, GString);
+ deleteGList(fontDirs, GString);
+ deleteGHash(ccFontFiles, GString);
+ deleteGHash(base14SysFonts, Base14FontInfo);
+ delete sysFonts;
+ deleteGHash(psResidentFonts, GString);
+ deleteGList(psResidentFonts16, PSFontParam16);
+ deleteGList(psResidentFontsCC, PSFontParam16);
+ delete textEncoding;
+ delete initialZoom;
+ delete initialDisplayMode;
+ delete initialSelectMode;
+ if (paperColor) {
+ delete paperColor;
+ }
+ if (matteColor) {
+ delete matteColor;
+ }
+ if (fullScreenMatteColor) {
+ delete fullScreenMatteColor;
+ }
+ if (selectionColor) {
+ delete selectionColor;
+ }
+ if (launchCommand) {
+ delete launchCommand;
+ }
+ if (movieCommand) {
+ delete movieCommand;
+ }
+ if (defaultPrinter) {
+ delete defaultPrinter;
+ }
+ delete droppedFonts;
+ deleteGList(keyBindings, KeyBinding);
+ deleteGList(popupMenuCmds, PopupMenuCmd);
+ delete tabStateFile;
+
+ cMapDirs->startIter(&iter);
+ while (cMapDirs->getNext(&iter, &key, (void **)&list)) {
+ deleteGList(list, GString);
+ }
+ delete cMapDirs;
+
+ delete cidToUnicodeCache;
+ delete unicodeToUnicodeCache;
+ delete unicodeMapCache;
+ delete cMapCache;
+
+#if MULTITHREADED
+ gDestroyMutex(&mutex);
+ gDestroyMutex(&unicodeMapCacheMutex);
+ gDestroyMutex(&cMapCacheMutex);
+#endif
+}
+
+//------------------------------------------------------------------------
+
+void GlobalParams::setBaseDir(const char *dir) {
+ delete baseDir;
+ baseDir = new GString(dir);
+}
+
+#ifdef _WIN32
+static void getWinFontDir(char *winFontDir) {
+ HMODULE shell32Lib;
+ BOOL (__stdcall *SHGetSpecialFolderPathFunc)(HWND hwndOwner,
+ LPSTR lpszPath,
+ int nFolder,
+ BOOL fCreate);
+ char *p;
+ int i;
+
+ // SHGetSpecialFolderPath isn't available in older versions of
+ // shell32.dll (Win95 and WinNT4), so do a dynamic load
+ winFontDir[0] = '\0';
+ if ((shell32Lib = LoadLibraryA("shell32.dll"))) {
+ if ((SHGetSpecialFolderPathFunc =
+ (BOOL (__stdcall *)(HWND hwndOwner, LPSTR lpszPath,
+ int nFolder, BOOL fCreate))
+ GetProcAddress(shell32Lib, "SHGetSpecialFolderPathA"))) {
+ if (!(*SHGetSpecialFolderPathFunc)(NULL, winFontDir,
+ CSIDL_FONTS, FALSE)) {
+ winFontDir[0] = '\0';
+ }
+ // kludge: Terminal Server changes CSIDL_FONTS to something like
+ // "C:\Users\whatever\Windows\Fonts", which doesn't actually
+ // contain any fonts -- kill that, so we hit the fallback code
+ // below.
+ for (p = winFontDir; *p; ++p) {
+ if (!strncasecmp(p, "\\Users\\", 7)) {
+ winFontDir[0] = '\0';
+ break;
+ }
+ }
+ }
+ FreeLibrary(shell32Lib);
+ }
+ // if something went wrong, or we're on a Terminal Server, try using
+ // %SYSTEMROOT%\Fonts
+ if (!winFontDir[0]) {
+ GetSystemWindowsDirectoryA(winFontDir, MAX_PATH - 6);
+ winFontDir[MAX_PATH - 7] = '\0';
+ i = (int)strlen(winFontDir);
+ if (winFontDir[i-1] != '\\') {
+ winFontDir[i++] = '\\';
+ }
+ strcpy(winFontDir + i, "Fonts");
+ }
+}
+#endif
+
+#ifdef __APPLE__
+// Apple dfonts and ttc fonts seem to randomly and interchangeably use
+// space and hyphen, and sometimes drop the separator entirely.
+static GBool macFontNameMatches(GString *name1, const char *name2) {
+ const char *p1, *p2;
+ char c1, c2;
+
+ p1 = name1->getCString();
+ p2 = name2;
+ while (*p1 && *p2) {
+ c1 = *p1;
+ c2 = *p2;
+ if (c2 == ' ') {
+ // * space or hyphen matches a space in the pattern
+ // * separators can also be dropped, in which case we move to
+ // the next char in the pattern
+ if (c1 == ' ' || c1 == '-') {
+ ++p1;
+ }
+ } else {
+ if (c1 != c2) {
+ return gFalse;
+ }
+ ++p1;
+ }
+ ++p2;
+ }
+ if (*p1 || *p2) {
+ return gFalse;
+ }
+ return gTrue;
+}
+#endif
+
+void GlobalParams::setupBaseFonts(const char *dir) {
+ GString *fontName;
+ GString *fileName;
+ int fontNum;
+ const char *s;
+ Base14FontInfo *base14;
+#ifdef _WIN32
+ char winFontDir[MAX_PATH];
+#endif
+#ifdef __APPLE__
+ static const char *macFontExts[3] = { "dfont", "ttc", "ttf" };
+ GList *dfontFontNames;
+ GBool found;
+ int k;
+#endif
+ FILE *f;
+ int i, j;
+
+#ifdef _WIN32
+ getWinFontDir(winFontDir);
+#endif
+#ifdef __APPLE__
+ dfontFontNames = NULL;
+#endif
+ for (i = 0; displayFontTab[i].name; ++i) {
+ if (fontFiles->lookup(displayFontTab[i].name)) {
+ continue;
+ }
+ fontName = new GString(displayFontTab[i].name);
+ fileName = NULL;
+ fontNum = 0;
+ if (dir) {
+ fileName = appendToPath(new GString(dir), displayFontTab[i].t1FileName);
+ if ((f = fopen(fileName->getCString(), "rb"))) {
+ fclose(f);
+ } else {
+ delete fileName;
+ fileName = NULL;
+ }
+ }
+#ifdef _WIN32
+ if (!fileName && winFontDir[0] && displayFontTab[i].ttFileName) {
+ fileName = appendToPath(new GString(winFontDir),
+ displayFontTab[i].ttFileName);
+ if ((f = fopen(fileName->getCString(), "rb"))) {
+ fclose(f);
+ } else {
+ delete fileName;
+ fileName = NULL;
+ }
+ }
+#endif
+#ifdef __APPLE__
+ // Check for Mac OS X system fonts.
+ s = displayFontTab[i].macFileName;
+ if (dfontFontNames && i > 0 &&
+ (!s || strcmp(s, displayFontTab[i-1].macFileName))) {
+ deleteGList(dfontFontNames, GString);
+ dfontFontNames = NULL;
+ }
+ if (!fileName && s) {
+ for (j = 0; j < 3; ++j) {
+ fileName = GString::format("{0:s}/{1:s}.{2:s}",
+ macSystemFontPath, s, macFontExts[j]);
+ if (!(f = fopen(fileName->getCString(), "rb"))) {
+ delete fileName;
+ fileName = NULL;
+ } else {
+ fclose(f);
+ found = gFalse;
+ // for .dfont or .ttc, we need to scan the font list
+ if (j < 2) {
+ if (!dfontFontNames) {
+ dfontFontNames =
+ FoFiIdentifier::getFontList(fileName->getCString());
+ }
+ if (dfontFontNames) {
+ for (k = 0; k < dfontFontNames->getLength(); ++k) {
+ if (macFontNameMatches((GString *)dfontFontNames->get(k),
+ displayFontTab[i].macFontName)) {
+ fontNum = k;
+ found = gTrue;
+ break;
+ }
+ }
+ }
+ // for .ttf, we just use the font
+ } else {
+ found = gTrue;
+ }
+ if (!found) {
+ delete fileName;
+ fileName = NULL;
+ }
+ break;
+ }
+ }
+ }
+#endif // __APPLE__
+ // On Linux, this checks the "standard" ghostscript font
+ // directories. On Windows, it checks the "standard" system font
+ // directories (because SHGetSpecialFolderPath(CSIDL_FONTS)
+ // doesn't work on Win 2k Server or Win2003 Server, or with older
+ // versions of shell32.dll).
+#ifdef _WIN32
+ s = displayFontTab[i].ttFileName;
+#else
+ s = displayFontTab[i].t1FileName;
+#endif
+ if (!fileName && s) {
+ for (j = 0; !fileName && displayFontDirs[j]; ++j) {
+ fileName = appendToPath(new GString(displayFontDirs[j]), s);
+ if ((f = fopen(fileName->getCString(), "rb"))) {
+ fclose(f);
+ } else {
+ delete fileName;
+ fileName = NULL;
+ }
+ }
+ }
+ if (!fileName) {
+ delete fontName;
+ continue;
+ }
+ base14SysFonts->add(fontName, new Base14FontInfo(fileName, fontNum, 0));
+ }
+#ifdef __APPLE__
+ if (dfontFontNames) {
+ deleteGList(dfontFontNames, GString);
+ }
+#endif
+ for (i = 0; displayFontTab[i].name; ++i) {
+ if (!base14SysFonts->lookup(displayFontTab[i].name) &&
+ !fontFiles->lookup(displayFontTab[i].name)) {
+ if (displayFontTab[i].obliqueFont &&
+ ((base14 = (Base14FontInfo *)base14SysFonts
+ ->lookup(displayFontTab[i].obliqueFont)))) {
+ base14SysFonts->add(
+ new GString(displayFontTab[i].name),
+ new Base14FontInfo(base14->fileName->copy(),
+ base14->fontNum,
+ displayFontTab[i].obliqueFactor));
+ } else {
+// Do not display unnecessary looking message on W32
+#ifndef _WIN32
+ error(errConfig, -1, "No display font for '{0:s}'",
+ displayFontTab[i].name);
+#endif
+ }
+ }
+ }
+#ifdef _WIN32
+ if (winFontDir[0]) {
+ sysFonts->scanWindowsFonts(winFontDir);
+ }
+#endif
+#if HAVE_FONTCONFIG
+ sysFonts->scanFontconfigFonts();
+#endif
+}
+
+//------------------------------------------------------------------------
+// accessors
+//------------------------------------------------------------------------
+
+CharCode GlobalParams::getMacRomanCharCode(char *charName) {
+ // no need to lock - macRomanReverseMap is constant
+ return macRomanReverseMap->lookup(charName);
+}
+
+GString *GlobalParams::getBaseDir() {
+ GString *s;
+
+ lockGlobalParams;
+ s = baseDir->copy();
+ unlockGlobalParams;
+ return s;
+}
+
+Unicode GlobalParams::mapNameToUnicode(const char *charName) {
+ // no need to lock - nameToUnicode is constant
+ return nameToUnicode->lookup(charName);
+}
+
+UnicodeMap *GlobalParams::getResidentUnicodeMap(GString *encodingName) {
+ UnicodeMap *map;
+
+ lockGlobalParams;
+ map = (UnicodeMap *)residentUnicodeMaps->lookup(encodingName);
+ unlockGlobalParams;
+ if (map) {
+ map->incRefCnt();
+ }
+ return map;
+}
+
+FILE *GlobalParams::getUnicodeMapFile(GString *encodingName) {
+ GString *fileName;
+ FILE *f;
+
+ lockGlobalParams;
+ if ((fileName = (GString *)unicodeMaps->lookup(encodingName))) {
+ f = openFile(fileName->getCString(), "r");
+ } else {
+ f = NULL;
+ }
+ unlockGlobalParams;
+ return f;
+}
+
+FILE *GlobalParams::findCMapFile(GString *collection, GString *cMapName) {
+ GList *list;
+ GString *dir;
+ GString *fileName;
+ FILE *f;
+ int i;
+
+ lockGlobalParams;
+ if (!(list = (GList *)cMapDirs->lookup(collection))) {
+ unlockGlobalParams;
+ return NULL;
+ }
+ for (i = 0; i < list->getLength(); ++i) {
+ dir = (GString *)list->get(i);
+ fileName = appendToPath(dir->copy(), cMapName->getCString());
+ f = openFile(fileName->getCString(), "r");
+ delete fileName;
+ if (f) {
+ unlockGlobalParams;
+ return f;
+ }
+ }
+ unlockGlobalParams;
+ return NULL;
+}
+
+FILE *GlobalParams::findToUnicodeFile(GString *name) {
+ GString *dir, *fileName;
+ FILE *f;
+ int i;
+
+ lockGlobalParams;
+ for (i = 0; i < toUnicodeDirs->getLength(); ++i) {
+ dir = (GString *)toUnicodeDirs->get(i);
+ fileName = appendToPath(dir->copy(), name->getCString());
+ f = openFile(fileName->getCString(), "r");
+ delete fileName;
+ if (f) {
+ unlockGlobalParams;
+ return f;
+ }
+ }
+ unlockGlobalParams;
+ return NULL;
+}
+
+UnicodeRemapping *GlobalParams::getUnicodeRemapping() {
+ return unicodeRemapping;
+}
+
+GString *GlobalParams::findFontFile(GString *fontName) {
+ static const char *exts[] = { ".pfa", ".pfb", ".ttf", ".ttc", ".otf" };
+ GString *path, *dir;
+#ifdef _WIN32
+ GString *fontNameU;
+#endif
+ const char *ext;
+ FILE *f;
+ int i, j;
+
+ lockGlobalParams;
+ if ((path = (GString *)fontFiles->lookup(fontName))) {
+ path = path->copy();
+ unlockGlobalParams;
+ return path;
+ }
+ for (i = 0; i < fontDirs->getLength(); ++i) {
+ dir = (GString *)fontDirs->get(i);
+ for (j = 0; j < (int)(sizeof(exts) / sizeof(exts[0])); ++j) {
+ ext = exts[j];
+#ifdef _WIN32
+ fontNameU = fileNameToUTF8(fontName->getCString());
+ path = appendToPath(dir->copy(), fontNameU->getCString());
+ delete fontNameU;
+#else
+ path = appendToPath(dir->copy(), fontName->getCString());
+#endif
+ path->append(ext);
+ if ((f = openFile(path->getCString(), "rb"))) {
+ fclose(f);
+ unlockGlobalParams;
+ return path;
+ }
+ delete path;
+ }
+ }
+ unlockGlobalParams;
+ return NULL;
+}
+
+GString *GlobalParams::findBase14FontFile(GString *fontName, int *fontNum,
+ double *oblique) {
+ Base14FontInfo *fi;
+ GString *path;
+
+ lockGlobalParams;
+ if ((fi = (Base14FontInfo *)base14SysFonts->lookup(fontName))) {
+ path = fi->fileName->copy();
+ *fontNum = fi->fontNum;
+ *oblique = fi->oblique;
+ unlockGlobalParams;
+ return path;
+ }
+ unlockGlobalParams;
+ *fontNum = 0;
+ *oblique = 0;
+ return findFontFile(fontName);
+}
+
+GString *GlobalParams::findSystemFontFile(GString *fontName,
+ SysFontType *type,
+ int *fontNum) {
+ SysFontInfo *fi;
+ GString *path;
+
+ path = NULL;
+ lockGlobalParams;
+ if ((fi = sysFonts->find(fontName))) {
+ path = fi->path->copy();
+ *type = fi->type;
+ *fontNum = fi->fontNum;
+ }
+ unlockGlobalParams;
+ return path;
+}
+
+GString *GlobalParams::findCCFontFile(GString *collection) {
+ GString *path;
+
+ lockGlobalParams;
+ if ((path = (GString *)ccFontFiles->lookup(collection))) {
+ path = path->copy();
+ }
+ unlockGlobalParams;
+ return path;
+}
+
+int GlobalParams::getPSPaperWidth() {
+ int w;
+
+ lockGlobalParams;
+ w = psPaperWidth;
+ unlockGlobalParams;
+ return w;
+}
+
+int GlobalParams::getPSPaperHeight() {
+ int h;
+
+ lockGlobalParams;
+ h = psPaperHeight;
+ unlockGlobalParams;
+ return h;
+}
+
+void GlobalParams::getPSImageableArea(int *llx, int *lly, int *urx, int *ury) {
+ lockGlobalParams;
+ *llx = psImageableLLX;
+ *lly = psImageableLLY;
+ *urx = psImageableURX;
+ *ury = psImageableURY;
+ unlockGlobalParams;
+}
+
+GBool GlobalParams::getPSCrop() {
+ GBool f;
+
+ lockGlobalParams;
+ f = psCrop;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getPSUseCropBoxAsPage() {
+ GBool f;
+
+ lockGlobalParams;
+ f = psUseCropBoxAsPage;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getPSExpandSmaller() {
+ GBool f;
+
+ lockGlobalParams;
+ f = psExpandSmaller;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getPSShrinkLarger() {
+ GBool f;
+
+ lockGlobalParams;
+ f = psShrinkLarger;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getPSCenter() {
+ GBool f;
+
+ lockGlobalParams;
+ f = psCenter;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getPSDuplex() {
+ GBool d;
+
+ lockGlobalParams;
+ d = psDuplex;
+ unlockGlobalParams;
+ return d;
+}
+
+PSLevel GlobalParams::getPSLevel() {
+ PSLevel level;
+
+ lockGlobalParams;
+ level = psLevel;
+ unlockGlobalParams;
+ return level;
+}
+
+GString *GlobalParams::getPSResidentFont(GString *fontName) {
+ GString *psName;
+
+ lockGlobalParams;
+ if ((psName = (GString *)psResidentFonts->lookup(fontName))) {
+ psName = psName->copy();
+ }
+ unlockGlobalParams;
+ return psName;
+}
+
+GList *GlobalParams::getPSResidentFonts() {
+ GList *names;
+ GHashIter *iter;
+ GString *name;
+ GString *psName;
+
+ names = new GList();
+ lockGlobalParams;
+ psResidentFonts->startIter(&iter);
+ while (psResidentFonts->getNext(&iter, &name, (void **)&psName)) {
+ names->append(psName->copy());
+ }
+ unlockGlobalParams;
+ return names;
+}
+
+PSFontParam16 *GlobalParams::getPSResidentFont16(GString *fontName,
+ int wMode) {
+ PSFontParam16 *p;
+ int i;
+
+ lockGlobalParams;
+ p = NULL;
+ for (i = 0; i < psResidentFonts16->getLength(); ++i) {
+ p = (PSFontParam16 *)psResidentFonts16->get(i);
+ if (!(p->name->cmp(fontName)) && p->wMode == wMode) {
+ break;
+ }
+ p = NULL;
+ }
+ unlockGlobalParams;
+ return p;
+}
+
+PSFontParam16 *GlobalParams::getPSResidentFontCC(GString *collection,
+ int wMode) {
+ PSFontParam16 *p;
+ int i;
+
+ lockGlobalParams;
+ p = NULL;
+ for (i = 0; i < psResidentFontsCC->getLength(); ++i) {
+ p = (PSFontParam16 *)psResidentFontsCC->get(i);
+ if (!(p->name->cmp(collection)) && p->wMode == wMode) {
+ break;
+ }
+ p = NULL;
+ }
+ unlockGlobalParams;
+ return p;
+}
+
+GBool GlobalParams::getPSEmbedType1() {
+ GBool e;
+
+ lockGlobalParams;
+ e = psEmbedType1;
+ unlockGlobalParams;
+ return e;
+}
+
+GBool GlobalParams::getPSEmbedTrueType() {
+ GBool e;
+
+ lockGlobalParams;
+ e = psEmbedTrueType;
+ unlockGlobalParams;
+ return e;
+}
+
+GBool GlobalParams::getPSEmbedCIDPostScript() {
+ GBool e;
+
+ lockGlobalParams;
+ e = psEmbedCIDPostScript;
+ unlockGlobalParams;
+ return e;
+}
+
+GBool GlobalParams::getPSEmbedCIDTrueType() {
+ GBool e;
+
+ lockGlobalParams;
+ e = psEmbedCIDTrueType;
+ unlockGlobalParams;
+ return e;
+}
+
+GBool GlobalParams::getPSFontPassthrough() {
+ GBool e;
+
+ lockGlobalParams;
+ e = psFontPassthrough;
+ unlockGlobalParams;
+ return e;
+}
+
+GBool GlobalParams::getPSPreload() {
+ GBool preload;
+
+ lockGlobalParams;
+ preload = psPreload;
+ unlockGlobalParams;
+ return preload;
+}
+
+GBool GlobalParams::getPSOPI() {
+ GBool opi;
+
+ lockGlobalParams;
+ opi = psOPI;
+ unlockGlobalParams;
+ return opi;
+}
+
+GBool GlobalParams::getPSASCIIHex() {
+ GBool ah;
+
+ lockGlobalParams;
+ ah = psASCIIHex;
+ unlockGlobalParams;
+ return ah;
+}
+
+GBool GlobalParams::getPSLZW() {
+ GBool ah;
+
+ lockGlobalParams;
+ ah = psLZW;
+ unlockGlobalParams;
+ return ah;
+}
+
+GBool GlobalParams::getPSUncompressPreloadedImages() {
+ GBool ah;
+
+ lockGlobalParams;
+ ah = psUncompressPreloadedImages;
+ unlockGlobalParams;
+ return ah;
+}
+
+double GlobalParams::getPSMinLineWidth() {
+ double w;
+
+ lockGlobalParams;
+ w = psMinLineWidth;
+ unlockGlobalParams;
+ return w;
+}
+
+double GlobalParams::getPSRasterResolution() {
+ double res;
+
+ lockGlobalParams;
+ res = psRasterResolution;
+ unlockGlobalParams;
+ return res;
+}
+
+GBool GlobalParams::getPSRasterMono() {
+ GBool mono;
+
+ lockGlobalParams;
+ mono = psRasterMono;
+ unlockGlobalParams;
+ return mono;
+}
+
+int GlobalParams::getPSRasterSliceSize() {
+ int slice;
+
+ lockGlobalParams;
+ slice = psRasterSliceSize;
+ unlockGlobalParams;
+ return slice;
+}
+
+GBool GlobalParams::getPSAlwaysRasterize() {
+ GBool rast;
+
+ lockGlobalParams;
+ rast = psAlwaysRasterize;
+ unlockGlobalParams;
+ return rast;
+}
+
+GBool GlobalParams::getPSNeverRasterize() {
+ GBool rast;
+
+ lockGlobalParams;
+ rast = psNeverRasterize;
+ unlockGlobalParams;
+ return rast;
+}
+
+GString *GlobalParams::getTextEncodingName() {
+ GString *s;
+
+ lockGlobalParams;
+ s = textEncoding->copy();
+ unlockGlobalParams;
+ 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;
+
+ lockGlobalParams;
+ eol = textEOL;
+ unlockGlobalParams;
+ return eol;
+}
+
+GBool GlobalParams::getTextPageBreaks() {
+ GBool pageBreaks;
+
+ lockGlobalParams;
+ pageBreaks = textPageBreaks;
+ unlockGlobalParams;
+ return pageBreaks;
+}
+
+GBool GlobalParams::getTextKeepTinyChars() {
+ GBool tiny;
+
+ lockGlobalParams;
+ tiny = textKeepTinyChars;
+ unlockGlobalParams;
+ return tiny;
+}
+
+GString *GlobalParams::getInitialZoom() {
+ GString *s;
+
+ lockGlobalParams;
+ s = initialZoom->copy();
+ unlockGlobalParams;
+ return s;
+}
+
+int GlobalParams::getDefaultFitZoom() {
+ int z;
+
+ lockGlobalParams;
+ z = defaultFitZoom;
+ unlockGlobalParams;
+ return z;
+}
+
+GString *GlobalParams::getInitialDisplayMode() {
+ GString *s;
+
+ lockGlobalParams;
+ s = initialDisplayMode->copy();
+ unlockGlobalParams;
+ return s;
+}
+
+GBool GlobalParams::getInitialToolbarState() {
+ GBool state;
+
+ lockGlobalParams;
+ state = initialToolbarState;
+ unlockGlobalParams;
+ return state;
+}
+
+GBool GlobalParams::getInitialSidebarState() {
+ GBool state;
+
+ lockGlobalParams;
+ state = initialSidebarState;
+ unlockGlobalParams;
+ return state;
+}
+
+int GlobalParams::getInitialSidebarWidth() {
+ int w;
+
+ lockGlobalParams;
+ w = initialSidebarWidth;
+ unlockGlobalParams;
+ return w;
+}
+
+GString *GlobalParams::getInitialSelectMode() {
+ GString *s;
+
+ lockGlobalParams;
+ s = initialSelectMode->copy();
+ unlockGlobalParams;
+ return s;
+}
+
+int GlobalParams::getMaxTileWidth() {
+ int w;
+
+ lockGlobalParams;
+ w = maxTileWidth;
+ unlockGlobalParams;
+ return w;
+}
+
+int GlobalParams::getMaxTileHeight() {
+ int h;
+
+ lockGlobalParams;
+ h = maxTileHeight;
+ unlockGlobalParams;
+ return h;
+}
+
+int GlobalParams::getTileCacheSize() {
+ int n;
+
+ lockGlobalParams;
+ n = tileCacheSize;
+ unlockGlobalParams;
+ return n;
+}
+
+int GlobalParams::getWorkerThreads() {
+ int n;
+
+ lockGlobalParams;
+ n = workerThreads;
+ unlockGlobalParams;
+ return n;
+}
+
+GBool GlobalParams::getEnableFreeType() {
+ GBool f;
+
+ lockGlobalParams;
+ f = enableFreeType;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getDisableFreeTypeHinting() {
+ GBool f;
+
+ lockGlobalParams;
+ f = disableFreeTypeHinting;
+ unlockGlobalParams;
+ return f;
+}
+
+
+GBool GlobalParams::getAntialias() {
+ GBool f;
+
+ lockGlobalParams;
+ f = antialias;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getVectorAntialias() {
+ GBool f;
+
+ lockGlobalParams;
+ f = vectorAntialias;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getAntialiasPrinting() {
+ GBool f;
+
+ lockGlobalParams;
+ f = antialiasPrinting;
+ unlockGlobalParams;
+ return f;
+}
+
+StrokeAdjustMode GlobalParams::getStrokeAdjust() {
+ StrokeAdjustMode mode;
+
+ lockGlobalParams;
+ mode = strokeAdjust;
+ unlockGlobalParams;
+ return mode;
+}
+
+ScreenType GlobalParams::getScreenType() {
+ ScreenType t;
+
+ lockGlobalParams;
+ t = screenType;
+ unlockGlobalParams;
+ return t;
+}
+
+int GlobalParams::getScreenSize() {
+ int size;
+
+ lockGlobalParams;
+ size = screenSize;
+ unlockGlobalParams;
+ return size;
+}
+
+int GlobalParams::getScreenDotRadius() {
+ int r;
+
+ lockGlobalParams;
+ r = screenDotRadius;
+ unlockGlobalParams;
+ return r;
+}
+
+double GlobalParams::getScreenGamma() {
+ double gamma;
+
+ lockGlobalParams;
+ gamma = screenGamma;
+ unlockGlobalParams;
+ return gamma;
+}
+
+double GlobalParams::getScreenBlackThreshold() {
+ double thresh;
+
+ lockGlobalParams;
+ thresh = screenBlackThreshold;
+ unlockGlobalParams;
+ return thresh;
+}
+
+double GlobalParams::getScreenWhiteThreshold() {
+ double thresh;
+
+ lockGlobalParams;
+ thresh = screenWhiteThreshold;
+ unlockGlobalParams;
+ return thresh;
+}
+
+double GlobalParams::getMinLineWidth() {
+ double w;
+
+ lockGlobalParams;
+ w = minLineWidth;
+ unlockGlobalParams;
+ return w;
+}
+
+GBool GlobalParams::getEnablePathSimplification() {
+ GBool en;
+
+ lockGlobalParams;
+ en = enablePathSimplification;
+ unlockGlobalParams;
+ return en;
+}
+
+GBool GlobalParams::getDrawAnnotations() {
+ GBool draw;
+
+ lockGlobalParams;
+ draw = drawAnnotations;
+ unlockGlobalParams;
+ return draw;
+}
+
+GBool GlobalParams::getDrawFormFields() {
+ GBool draw;
+
+ lockGlobalParams;
+ draw = drawFormFields;
+ unlockGlobalParams;
+ return draw;
+}
+
+
+
+GString *GlobalParams::getPaperColor() {
+ GString *s;
+
+ lockGlobalParams;
+ s = paperColor->copy();
+ unlockGlobalParams;
+ return s;
+}
+
+GString *GlobalParams::getMatteColor() {
+ GString *s;
+
+ lockGlobalParams;
+ s = matteColor->copy();
+ unlockGlobalParams;
+ return s;
+}
+
+GString *GlobalParams::getFullScreenMatteColor() {
+ GString *s;
+
+ lockGlobalParams;
+ s = fullScreenMatteColor->copy();
+ unlockGlobalParams;
+ return s;
+}
+
+GString *GlobalParams::getSelectionColor() {
+ GString *s;
+
+ lockGlobalParams;
+ s = selectionColor->copy();
+ unlockGlobalParams;
+ return s;
+}
+
+GBool GlobalParams::getReverseVideoInvertImages() {
+ GBool invert;
+
+ lockGlobalParams;
+ invert = reverseVideoInvertImages;
+ unlockGlobalParams;
+ return invert;
+}
+
+GString *GlobalParams::getDefaultPrinter() {
+ GString *s;
+
+ lockGlobalParams;
+ s = defaultPrinter ? defaultPrinter->copy() : (GString *)NULL;
+ unlockGlobalParams;
+ return s;
+}
+
+GBool GlobalParams::getMapNumericCharNames() {
+ GBool map;
+
+ lockGlobalParams;
+ map = mapNumericCharNames;
+ unlockGlobalParams;
+ return map;
+}
+
+GBool GlobalParams::getMapUnknownCharNames() {
+ GBool map;
+
+ lockGlobalParams;
+ map = mapUnknownCharNames;
+ unlockGlobalParams;
+ return map;
+}
+
+GBool GlobalParams::getMapExtTrueTypeFontsViaUnicode() {
+ GBool map;
+
+ lockGlobalParams;
+ map = mapExtTrueTypeFontsViaUnicode;
+ unlockGlobalParams;
+ return map;
+}
+
+GBool GlobalParams::isDroppedFont(const char *fontName) {
+ GBool isDropped;
+
+ lockGlobalParams;
+ isDropped = droppedFonts->lookupInt(fontName) != 0;
+ unlockGlobalParams;
+ return isDropped;
+}
+
+GList *GlobalParams::getKeyBinding(int code, int mods, int context) {
+ KeyBinding *binding;
+ GList *cmds;
+ int modMask;
+ int i, j;
+
+ lockGlobalParams;
+ cmds = NULL;
+ // for ASCII chars, ignore the shift modifier
+ modMask = (code >= 0x21 && code <= 0xff) ? ~xpdfKeyModShift : ~0;
+ for (i = 0; i < keyBindings->getLength(); ++i) {
+ binding = (KeyBinding *)keyBindings->get(i);
+ if (binding->code == code &&
+ (binding->mods & modMask) == (mods & modMask) &&
+ (~binding->context | context) == ~0) {
+ cmds = new GList();
+ for (j = 0; j < binding->cmds->getLength(); ++j) {
+ cmds->append(((GString *)binding->cmds->get(j))->copy());
+ }
+ break;
+ }
+ }
+ unlockGlobalParams;
+ return cmds;
+}
+
+GList *GlobalParams::getAllKeyBindings() {
+ return keyBindings;
+}
+
+int GlobalParams::getNumPopupMenuCmds() {
+ int n;
+
+ lockGlobalParams;
+ n = popupMenuCmds->getLength();
+ unlockGlobalParams;
+ return n;
+}
+
+PopupMenuCmd *GlobalParams::getPopupMenuCmd(int idx) {
+ PopupMenuCmd *cmd;
+
+ lockGlobalParams;
+ if (idx < popupMenuCmds->getLength()) {
+ cmd = (PopupMenuCmd *)popupMenuCmds->get(idx);
+ } else {
+ cmd = NULL;
+ }
+ unlockGlobalParams;
+ return cmd;
+}
+
+GString *GlobalParams::getTabStateFile() {
+ GString *s;
+
+ lockGlobalParams;
+ s = tabStateFile->copy();
+ unlockGlobalParams;
+ return s;
+}
+
+GBool GlobalParams::getPrintCommands() {
+ GBool p;
+
+ lockGlobalParams;
+ p = printCommands;
+ unlockGlobalParams;
+ return p;
+}
+
+GBool GlobalParams::getErrQuiet() {
+ // no locking -- this function may get called from inside a locked
+ // section
+ return errQuiet;
+}
+
+GString *GlobalParams::getDebugLogFile() {
+ return debugLogFile;
+}
+
+void GlobalParams::debugLogPrintf(const char *fmt, ...) {
+ GString *path;
+ FILE *f;
+ GBool needClose;
+ time_t t;
+ struct tm tm;
+ va_list args;
+
+ if (!(path = getDebugLogFile())) {
+ return;
+ }
+ needClose = gFalse;
+ if (!path->cmp("-")) {
+ f = stdout;
+ } else if (!path->cmp("+")) {
+ f = stderr;
+ } else {
+ f = fopen(path->getCString(), "a");
+ needClose = gTrue;
+ }
+ if (!f) {
+ return;
+ }
+ t = time(NULL);
+#ifdef _WIN32
+ localtime_s(&tm, &t);
+#else
+ localtime_r(&t, &tm);
+#endif
+ fprintf(f, "[%04d-%02d-%02d %02d:%02d:%02d] ",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ va_start(args, fmt);
+ vfprintf(f, fmt, args);
+ va_end(args);
+ fflush(f);
+ if (needClose) {
+ fclose(f);
+ }
+}
+
+CharCodeToUnicode *GlobalParams::getCIDToUnicode(GString *collection) {
+ GString *fileName;
+ CharCodeToUnicode *ctu;
+
+ lockGlobalParams;
+ if (!(ctu = cidToUnicodeCache->getCharCodeToUnicode(collection))) {
+ if ((fileName = (GString *)cidToUnicodes->lookup(collection)) &&
+ (ctu = CharCodeToUnicode::parseCIDToUnicode(fileName, collection))) {
+ cidToUnicodeCache->add(ctu);
+ }
+ }
+ unlockGlobalParams;
+ return ctu;
+}
+
+CharCodeToUnicode *GlobalParams::getUnicodeToUnicode(GString *fontName) {
+ CharCodeToUnicode *ctu;
+ GHashIter *iter;
+ GString *fontPattern, *fileName;
+
+ lockGlobalParams;
+ fileName = NULL;
+ unicodeToUnicodes->startIter(&iter);
+ while (unicodeToUnicodes->getNext(&iter, &fontPattern, (void **)&fileName)) {
+ if (strstr(fontName->getCString(), fontPattern->getCString())) {
+ unicodeToUnicodes->killIter(&iter);
+ break;
+ }
+ fileName = NULL;
+ }
+ if (fileName) {
+ if (!(ctu = unicodeToUnicodeCache->getCharCodeToUnicode(fileName))) {
+ if ((ctu = CharCodeToUnicode::parseUnicodeToUnicode(fileName))) {
+ unicodeToUnicodeCache->add(ctu);
+ }
+ }
+ } else {
+ ctu = NULL;
+ }
+ unlockGlobalParams;
+ return ctu;
+}
+
+UnicodeMap *GlobalParams::getUnicodeMap(GString *encodingName) {
+ return getUnicodeMap2(encodingName);
+}
+
+UnicodeMap *GlobalParams::getUnicodeMap2(GString *encodingName) {
+ UnicodeMap *map;
+
+ if (!(map = getResidentUnicodeMap(encodingName))) {
+ lockUnicodeMapCache;
+ map = unicodeMapCache->getUnicodeMap(encodingName);
+ unlockUnicodeMapCache;
+ }
+ return map;
+}
+
+CMap *GlobalParams::getCMap(GString *collection, GString *cMapName) {
+ CMap *cMap;
+
+ lockCMapCache;
+ cMap = cMapCache->getCMap(collection, cMapName);
+ unlockCMapCache;
+ return cMap;
+}
+
+UnicodeMap *GlobalParams::getTextEncoding() {
+ return getUnicodeMap2(textEncoding);
+}
+
+//------------------------------------------------------------------------
+// functions to set parameters
+//------------------------------------------------------------------------
+
+void GlobalParams::addUnicodeRemapping(Unicode in, Unicode *out, int len) {
+ unicodeRemapping->addRemapping(in, out, len);
+}
+
+void GlobalParams::addFontFile(GString *fontName, GString *path) {
+ lockGlobalParams;
+ fontFiles->add(fontName, path);
+ unlockGlobalParams;
+}
+
+GBool GlobalParams::setPSPaperSize(char *size) {
+ lockGlobalParams;
+ if (!strcmp(size, "match")) {
+ psPaperWidth = psPaperHeight = -1;
+ } else if (!strcmp(size, "letter")) {
+ psPaperWidth = 612;
+ psPaperHeight = 792;
+ } else if (!strcmp(size, "legal")) {
+ psPaperWidth = 612;
+ psPaperHeight = 1008;
+ } else if (!strcmp(size, "A4")) {
+ psPaperWidth = 595;
+ psPaperHeight = 842;
+ } else if (!strcmp(size, "A3")) {
+ psPaperWidth = 842;
+ psPaperHeight = 1190;
+ } else {
+ unlockGlobalParams;
+ return gFalse;
+ }
+ psImageableLLX = psImageableLLY = 0;
+ psImageableURX = psPaperWidth;
+ psImageableURY = psPaperHeight;
+ unlockGlobalParams;
+ return gTrue;
+}
+
+void GlobalParams::setPSPaperWidth(int width) {
+ lockGlobalParams;
+ psPaperWidth = width;
+ psImageableLLX = 0;
+ psImageableURX = psPaperWidth;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSPaperHeight(int height) {
+ lockGlobalParams;
+ psPaperHeight = height;
+ psImageableLLY = 0;
+ psImageableURY = psPaperHeight;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSImageableArea(int llx, int lly, int urx, int ury) {
+ lockGlobalParams;
+ psImageableLLX = llx;
+ psImageableLLY = lly;
+ psImageableURX = urx;
+ psImageableURY = ury;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSCrop(GBool crop) {
+ lockGlobalParams;
+ psCrop = crop;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSUseCropBoxAsPage(GBool crop) {
+ lockGlobalParams;
+ psUseCropBoxAsPage = crop;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSExpandSmaller(GBool expand) {
+ lockGlobalParams;
+ psExpandSmaller = expand;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSShrinkLarger(GBool shrink) {
+ lockGlobalParams;
+ psShrinkLarger = shrink;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSCenter(GBool center) {
+ lockGlobalParams;
+ psCenter = center;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSDuplex(GBool duplex) {
+ lockGlobalParams;
+ psDuplex = duplex;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSLevel(PSLevel level) {
+ lockGlobalParams;
+ psLevel = level;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSEmbedType1(GBool embed) {
+ lockGlobalParams;
+ psEmbedType1 = embed;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSEmbedTrueType(GBool embed) {
+ lockGlobalParams;
+ psEmbedTrueType = embed;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSEmbedCIDPostScript(GBool embed) {
+ lockGlobalParams;
+ psEmbedCIDPostScript = embed;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSEmbedCIDTrueType(GBool embed) {
+ lockGlobalParams;
+ psEmbedCIDTrueType = embed;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSFontPassthrough(GBool passthrough) {
+ lockGlobalParams;
+ psFontPassthrough = passthrough;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSPreload(GBool preload) {
+ lockGlobalParams;
+ psPreload = preload;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSOPI(GBool opi) {
+ lockGlobalParams;
+ psOPI = opi;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSASCIIHex(GBool hex) {
+ lockGlobalParams;
+ psASCIIHex = hex;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setTextEncoding(const char *encodingName) {
+ lockGlobalParams;
+ delete textEncoding;
+ textEncoding = new GString(encodingName);
+ unlockGlobalParams;
+}
+
+GBool GlobalParams::setTextEOL(char *s) {
+ lockGlobalParams;
+ if (!strcmp(s, "unix")) {
+ textEOL = eolUnix;
+ } else if (!strcmp(s, "dos")) {
+ textEOL = eolDOS;
+ } else if (!strcmp(s, "mac")) {
+ textEOL = eolMac;
+ } else {
+ unlockGlobalParams;
+ return gFalse;
+ }
+ unlockGlobalParams;
+ return gTrue;
+}
+
+void GlobalParams::setTextPageBreaks(GBool pageBreaks) {
+ lockGlobalParams;
+ textPageBreaks = pageBreaks;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setTextKeepTinyChars(GBool keep) {
+ lockGlobalParams;
+ textKeepTinyChars = keep;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setInitialZoom(char *s) {
+ lockGlobalParams;
+ delete initialZoom;
+ initialZoom = new GString(s);
+ unlockGlobalParams;
+}
+
+GBool GlobalParams::setEnableFreeType(char *s) {
+ GBool ok;
+
+ lockGlobalParams;
+ ok = parseYesNo2(s, &enableFreeType);
+ unlockGlobalParams;
+ return ok;
+}
+
+
+GBool GlobalParams::setAntialias(char *s) {
+ GBool ok;
+
+ lockGlobalParams;
+ ok = parseYesNo2(s, &antialias);
+ unlockGlobalParams;
+ return ok;
+}
+
+GBool GlobalParams::setVectorAntialias(char *s) {
+ GBool ok;
+
+ lockGlobalParams;
+ ok = parseYesNo2(s, &vectorAntialias);
+ unlockGlobalParams;
+ return ok;
+}
+
+void GlobalParams::setScreenType(ScreenType t) {
+ lockGlobalParams;
+ screenType = t;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setScreenSize(int size) {
+ lockGlobalParams;
+ screenSize = size;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setScreenDotRadius(int r) {
+ lockGlobalParams;
+ screenDotRadius = r;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setScreenGamma(double gamma) {
+ lockGlobalParams;
+ screenGamma = gamma;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setScreenBlackThreshold(double thresh) {
+ lockGlobalParams;
+ screenBlackThreshold = thresh;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setScreenWhiteThreshold(double thresh) {
+ lockGlobalParams;
+ screenWhiteThreshold = thresh;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setDrawFormFields(GBool draw) {
+ lockGlobalParams;
+ drawFormFields = draw;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setOverprintPreview(GBool preview) {
+ lockGlobalParams;
+ overprintPreview = preview;
+ unlockGlobalParams;
+}
+
+
+
+void GlobalParams::setMapNumericCharNames(GBool map) {
+ lockGlobalParams;
+ mapNumericCharNames = map;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setMapUnknownCharNames(GBool map) {
+ lockGlobalParams;
+ mapUnknownCharNames = map;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setMapExtTrueTypeFontsViaUnicode(GBool map) {
+ lockGlobalParams;
+ mapExtTrueTypeFontsViaUnicode = map;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setTabStateFile(char *tabStateFileA) {
+ lockGlobalParams;
+ delete tabStateFile;
+ tabStateFile = new GString(tabStateFileA);
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPrintCommands(GBool printCommandsA) {
+ lockGlobalParams;
+ printCommands = printCommandsA;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setErrQuiet(GBool errQuietA) {
+ lockGlobalParams;
+ errQuiet = errQuietA;
+ unlockGlobalParams;
+}
+
+#ifdef _WIN32
+void GlobalParams::setWin32ErrorInfo(const char *func, DWORD code) {
+ XpdfWin32ErrorInfo *errorInfo;
+
+ if (tlsWin32ErrorInfo == TLS_OUT_OF_INDEXES) {
+ return;
+ }
+ errorInfo = (XpdfWin32ErrorInfo *)TlsGetValue(tlsWin32ErrorInfo);
+ if (!errorInfo) {
+ errorInfo = new XpdfWin32ErrorInfo();
+ TlsSetValue(tlsWin32ErrorInfo, errorInfo);
+ }
+ errorInfo->func = func;
+ errorInfo->code = code;
+}
+
+XpdfWin32ErrorInfo *GlobalParams::getWin32ErrorInfo() {
+ XpdfWin32ErrorInfo *errorInfo;
+
+ if (tlsWin32ErrorInfo == TLS_OUT_OF_INDEXES) {
+ return NULL;
+ }
+ errorInfo = (XpdfWin32ErrorInfo *)TlsGetValue(tlsWin32ErrorInfo);
+ if (!errorInfo) {
+ errorInfo = new XpdfWin32ErrorInfo();
+ TlsSetValue(tlsWin32ErrorInfo, errorInfo);
+ errorInfo->func = NULL;
+ errorInfo->code = 0;
+ }
+ return errorInfo;
+}
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h
new file mode 100644
index 00000000000..2823ea59421
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/GlobalParams.h
@@ -0,0 +1,612 @@
+//========================================================================
+//
+// GlobalParams.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+// Modified for TeX Live by Peter Breitenlohner <tex-live@tug.org>
+// See top-level ChangeLog for a list of all modifications
+//========================================================================
+
+#ifndef GLOBALPARAMS_H
+#define GLOBALPARAMS_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#ifdef _WIN32
+# include <windows.h>
+#endif
+#include "gtypes.h"
+#include "CharTypes.h"
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+class GString;
+class GList;
+class GHash;
+class NameToCharCode;
+class CharCodeToUnicode;
+class CharCodeToUnicodeCache;
+class UnicodeMap;
+class UnicodeMapCache;
+class UnicodeRemapping;
+class CMap;
+class CMapCache;
+struct XpdfSecurityHandler;
+class GlobalParams;
+class SysFontList;
+
+//------------------------------------------------------------------------
+
+// The global parameters object.
+extern GlobalParams *globalParams;
+
+//------------------------------------------------------------------------
+
+enum SysFontType {
+ sysFontPFA,
+ sysFontPFB,
+ sysFontTTF,
+ sysFontTTC,
+ sysFontOTF
+};
+
+//------------------------------------------------------------------------
+
+class PSFontParam16 {
+public:
+
+ GString *name; // PDF font name for psResidentFont16;
+ // char collection name for psResidentFontCC
+ int wMode; // writing mode (0=horiz, 1=vert)
+ GString *psFontName; // PostScript font name
+ GString *encoding; // encoding
+
+ PSFontParam16(GString *nameA, int wModeA,
+ GString *psFontNameA, GString *encodingA);
+ ~PSFontParam16();
+};
+
+//------------------------------------------------------------------------
+
+enum PSLevel {
+ psLevel1,
+ psLevel1Sep,
+ psLevel2,
+ psLevel2Gray,
+ psLevel2Sep,
+ psLevel3,
+ psLevel3Gray,
+ psLevel3Sep,
+};
+
+//------------------------------------------------------------------------
+
+enum EndOfLineKind {
+ eolUnix, // LF
+ eolDOS, // CR+LF
+ eolMac // CR
+};
+
+//------------------------------------------------------------------------
+
+enum StrokeAdjustMode {
+ strokeAdjustOff,
+ strokeAdjustNormal,
+ strokeAdjustCAD
+};
+
+//------------------------------------------------------------------------
+
+enum ScreenType {
+ screenUnset,
+ screenDispersed,
+ screenClustered,
+ screenStochasticClustered
+};
+
+//------------------------------------------------------------------------
+
+class KeyBinding {
+public:
+
+ int code; // 0x20 .. 0xfe = ASCII,
+ // >=0x10000 = special keys, mouse buttons,
+ // etc. (xpdfKeyCode* symbols)
+ int mods; // modifiers (xpdfKeyMod* symbols, or-ed
+ // together)
+ int context; // context (xpdfKeyContext* symbols, or-ed
+ // together)
+ GList *cmds; // list of commands [GString]
+
+ KeyBinding(int codeA, int modsA, int contextA, const char *cmd0);
+ KeyBinding(int codeA, int modsA, int contextA,
+ const char *cmd0, const char *cmd1);
+ KeyBinding(int codeA, int modsA, int contextA, GList *cmdsA);
+ ~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 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 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)
+#define xpdfKeyModAlt (1 << 2)
+#define xpdfKeyContextAny 0
+#define xpdfKeyContextFullScreen (1 << 0)
+#define xpdfKeyContextWindow (2 << 0)
+#define xpdfKeyContextContinuous (1 << 2)
+#define xpdfKeyContextSinglePage (2 << 2)
+#define xpdfKeyContextOverLink (1 << 4)
+#define xpdfKeyContextOffLink (2 << 4)
+#define xpdfKeyContextOutline (1 << 6)
+#define xpdfKeyContextMainWin (2 << 6)
+#define xpdfKeyContextScrLockOn (1 << 8)
+#define xpdfKeyContextScrLockOff (2 << 8)
+
+//------------------------------------------------------------------------
+
+class PopupMenuCmd {
+public:
+
+ GString *label; // label for display in the menu
+ GList *cmds; // list of commands [GString]
+
+ PopupMenuCmd(GString *labelA, GList *cmdsA);
+ ~PopupMenuCmd();
+};
+
+//------------------------------------------------------------------------
+
+#ifdef _WIN32
+struct XpdfWin32ErrorInfo {
+ const char *func; // last Win32 API function call to fail
+ DWORD code; // error code returned by that function
+};
+#endif
+
+//------------------------------------------------------------------------
+
+class GlobalParams {
+public:
+
+ // Initialize the global parameters by attempting to read a config
+ // file.
+ GlobalParams(const char *cfgFileName = NULL);
+
+ ~GlobalParams();
+
+ void setBaseDir(const char *dir);
+ void setupBaseFonts(const char *dir);
+
+ void parseLine(char *buf, GString *fileName, int line);
+
+ //----- accessors
+
+ CharCode getMacRomanCharCode(char *charName);
+
+ GString *getBaseDir();
+ Unicode mapNameToUnicode(const char *charName);
+ UnicodeMap *getResidentUnicodeMap(GString *encodingName);
+ FILE *getUnicodeMapFile(GString *encodingName);
+ FILE *findCMapFile(GString *collection, GString *cMapName);
+ FILE *findToUnicodeFile(GString *name);
+ UnicodeRemapping *getUnicodeRemapping();
+ GString *findFontFile(GString *fontName);
+ GString *findBase14FontFile(GString *fontName, int *fontNum,
+ double *oblique);
+ GString *findSystemFontFile(GString *fontName, SysFontType *type,
+ int *fontNum);
+ GString *findCCFontFile(GString *collection);
+ int getPSPaperWidth();
+ int getPSPaperHeight();
+ void getPSImageableArea(int *llx, int *lly, int *urx, int *ury);
+ GBool getPSDuplex();
+ GBool getPSCrop();
+ GBool getPSUseCropBoxAsPage();
+ GBool getPSExpandSmaller();
+ GBool getPSShrinkLarger();
+ GBool getPSCenter();
+ PSLevel getPSLevel();
+ GString *getPSResidentFont(GString *fontName);
+ GList *getPSResidentFonts();
+ PSFontParam16 *getPSResidentFont16(GString *fontName, int wMode);
+ PSFontParam16 *getPSResidentFontCC(GString *collection, int wMode);
+ GBool getPSEmbedType1();
+ GBool getPSEmbedTrueType();
+ GBool getPSEmbedCIDPostScript();
+ GBool getPSEmbedCIDTrueType();
+ GBool getPSFontPassthrough();
+ GBool getPSPreload();
+ GBool getPSOPI();
+ GBool getPSASCIIHex();
+ GBool getPSLZW();
+ GBool getPSUncompressPreloadedImages();
+ double getPSMinLineWidth();
+ double getPSRasterResolution();
+ GBool getPSRasterMono();
+ int getPSRasterSliceSize();
+ GBool getPSAlwaysRasterize();
+ GBool getPSNeverRasterize();
+ GString *getTextEncodingName();
+ GList *getAvailableTextEncodings();
+ EndOfLineKind getTextEOL();
+ GBool getTextPageBreaks();
+ GBool getTextKeepTinyChars();
+ GString *getInitialZoom();
+ int getDefaultFitZoom();
+ GString *getInitialDisplayMode();
+ GBool getInitialToolbarState();
+ GBool getInitialSidebarState();
+ int getInitialSidebarWidth();
+ GString *getInitialSelectMode();
+ int getMaxTileWidth();
+ int getMaxTileHeight();
+ int getTileCacheSize();
+ int getWorkerThreads();
+ GBool getEnableFreeType();
+ GBool getDisableFreeTypeHinting();
+ GBool getAntialias();
+ GBool getVectorAntialias();
+ GBool getAntialiasPrinting();
+ StrokeAdjustMode getStrokeAdjust();
+ ScreenType getScreenType();
+ int getScreenSize();
+ int getScreenDotRadius();
+ double getScreenGamma();
+ double getScreenBlackThreshold();
+ double getScreenWhiteThreshold();
+ double getMinLineWidth();
+ GBool getEnablePathSimplification();
+ GBool getDrawAnnotations();
+ GBool getDrawFormFields();
+ GBool getOverprintPreview() { return overprintPreview; }
+ GString *getPaperColor();
+ GString *getMatteColor();
+ GString *getFullScreenMatteColor();
+ GString *getSelectionColor();
+ GBool getReverseVideoInvertImages();
+ GString *getLaunchCommand() { return launchCommand; }
+ GString *getMovieCommand() { return movieCommand; }
+ GString *getDefaultPrinter();
+ GBool getMapNumericCharNames();
+ GBool getMapUnknownCharNames();
+ GBool getMapExtTrueTypeFontsViaUnicode();
+ GBool isDroppedFont(const char *fontName);
+ GList *getKeyBinding(int code, int mods, int context);
+ GList *getAllKeyBindings();
+ int getNumPopupMenuCmds();
+ PopupMenuCmd *getPopupMenuCmd(int idx);
+ GString *getTabStateFile();
+ GBool getPrintCommands();
+ GBool getErrQuiet();
+ GString *getDebugLogFile();
+ void debugLogPrintf(const char *fmt, ...);
+
+ CharCodeToUnicode *getCIDToUnicode(GString *collection);
+ CharCodeToUnicode *getUnicodeToUnicode(GString *fontName);
+ UnicodeMap *getUnicodeMap(GString *encodingName);
+ CMap *getCMap(GString *collection, GString *cMapName);
+ UnicodeMap *getTextEncoding();
+
+ //----- functions to set parameters
+
+ void addUnicodeRemapping(Unicode in, Unicode *out, int len);
+ void addFontFile(GString *fontName, GString *path);
+ GBool setPSPaperSize(char *size);
+ void setPSPaperWidth(int width);
+ void setPSPaperHeight(int height);
+ void setPSImageableArea(int llx, int lly, int urx, int ury);
+ void setPSDuplex(GBool duplex);
+ void setPSCrop(GBool crop);
+ void setPSUseCropBoxAsPage(GBool crop);
+ void setPSExpandSmaller(GBool expand);
+ void setPSShrinkLarger(GBool shrink);
+ void setPSCenter(GBool center);
+ void setPSLevel(PSLevel level);
+ void setPSEmbedType1(GBool embed);
+ void setPSEmbedTrueType(GBool embed);
+ void setPSEmbedCIDPostScript(GBool embed);
+ void setPSEmbedCIDTrueType(GBool embed);
+ void setPSFontPassthrough(GBool passthrough);
+ void setPSPreload(GBool preload);
+ void setPSOPI(GBool opi);
+ void setPSASCIIHex(GBool hex);
+ void setTextEncoding(const char *encodingName);
+ GBool setTextEOL(char *s);
+ void setTextPageBreaks(GBool pageBreaks);
+ void setTextKeepTinyChars(GBool keep);
+ void setInitialZoom(char *s);
+ GBool setEnableFreeType(char *s);
+ GBool setAntialias(char *s);
+ GBool setVectorAntialias(char *s);
+ void setScreenType(ScreenType t);
+ void setScreenSize(int size);
+ void setScreenDotRadius(int r);
+ void setScreenGamma(double gamma);
+ void setScreenBlackThreshold(double thresh);
+ void setScreenWhiteThreshold(double thresh);
+ void setDrawFormFields(GBool draw);
+ void setOverprintPreview(GBool preview);
+ void setMapNumericCharNames(GBool map);
+ void setMapUnknownCharNames(GBool map);
+ void setMapExtTrueTypeFontsViaUnicode(GBool map);
+ void setTabStateFile(char *tabStateFileA);
+ void setPrintCommands(GBool printCommandsA);
+ void setErrQuiet(GBool errQuietA);
+
+#ifdef _WIN32
+ void setWin32ErrorInfo(const char *func, DWORD code);
+ XpdfWin32ErrorInfo *getWin32ErrorInfo();
+#endif
+
+ static const char *defaultTextEncoding;
+
+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);
+ void parseUnicodeMap(GList *tokens, GString *fileName, int line);
+ void parseCMapDir(GList *tokens, GString *fileName, int line);
+ void parseToUnicodeDir(GList *tokens, GString *fileName, int line);
+ void parseUnicodeRemapping(GList *tokens, GString *fileName, int line);
+ void parseFontFile(GList *tokens, GString *fileName, int line);
+ void parseFontDir(GList *tokens, GString *fileName, int line);
+ void parseFontFileCC(GList *tokens, GString *fileName,
+ int line);
+ void parsePSPaperSize(GList *tokens, GString *fileName, int line);
+ void parsePSImageableArea(GList *tokens, GString *fileName, int line);
+ void parsePSLevel(GList *tokens, GString *fileName, int line);
+ void parsePSResidentFont(GList *tokens, GString *fileName, int line);
+ void parsePSResidentFont16(GList *tokens, GString *fileName, int line);
+ void parsePSResidentFontCC(GList *tokens, GString *fileName, int line);
+ void parseTextEOL(GList *tokens, GString *fileName, int line);
+ void parseStrokeAdjust(GList *tokens, GString *fileName, int line);
+ void parseScreenType(GList *tokens, GString *fileName, int line);
+ void parseDropFont(GList *tokens, GString *fileName, int line);
+ void parseBind(GList *tokens, GString *fileName, int line);
+ void parseUnbind(GList *tokens, GString *fileName, int line);
+ GBool parseKey(GString *modKeyStr, GString *contextStr,
+ int *code, int *mods, int *context,
+ const char *cmdName,
+ GList *tokens, GString *fileName, int line);
+ void parsePopupMenuCmd(GList *tokens, GString *fileName, int line);
+ void parseYesNo(const char *cmdName, GBool *flag,
+ GList *tokens, GString *fileName, int line);
+ GBool parseYesNo2(char *token, GBool *flag);
+ void parseString(const char *cmdName, GString **s,
+ GList *tokens, GString *fileName, int line);
+ void parseInteger(const char *cmdName, int *val,
+ GList *tokens, GString *fileName, int line);
+ void parseFloat(const char *cmdName, double *val,
+ GList *tokens, GString *fileName, int line);
+ UnicodeMap *getUnicodeMap2(GString *encodingName);
+
+ //----- static tables
+
+ 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
+
+ NameToCharCode * // mapping from char name to Unicode
+ nameToUnicode;
+ GHash *cidToUnicodes; // files for mappings from char collections
+ // to Unicode, indexed by collection name
+ // [GString]
+ GHash *unicodeToUnicodes; // files for Unicode-to-Unicode mappings,
+ // indexed by font name pattern [GString]
+ GHash *residentUnicodeMaps; // mappings from Unicode to char codes,
+ // indexed by encoding name [UnicodeMap]
+ GHash *unicodeMaps; // files for mappings from Unicode to char
+ // codes, indexed by encoding name [GString]
+ GHash *cMapDirs; // list of CMap dirs, indexed by collection
+ // name [GList[GString]]
+ GList *toUnicodeDirs; // list of ToUnicode CMap dirs [GString]
+ UnicodeRemapping * // Unicode remapping for text output
+ unicodeRemapping;
+ GHash *fontFiles; // font files: font name mapped to path
+ // [GString]
+ GList *fontDirs; // list of font dirs [GString]
+ GHash *ccFontFiles; // character collection font files:
+ // collection name mapped to path [GString]
+ GHash *base14SysFonts; // Base-14 system font files: font name
+ // mapped to path [Base14FontInfo]
+ SysFontList *sysFonts; // system fonts
+ int psPaperWidth; // paper size, in PostScript points, for
+ int psPaperHeight; // PostScript output
+ int psImageableLLX, // imageable area, in PostScript points,
+ psImageableLLY, // for PostScript output
+ psImageableURX,
+ psImageableURY;
+ GBool psCrop; // crop PS output to CropBox
+ GBool psUseCropBoxAsPage; // use CropBox as page size
+ GBool psExpandSmaller; // expand smaller pages to fill paper
+ GBool psShrinkLarger; // shrink larger pages to fit paper
+ GBool psCenter; // center pages on the paper
+ GBool psDuplex; // enable duplexing in PostScript?
+ PSLevel psLevel; // PostScript level to generate
+ GHash *psResidentFonts; // 8-bit fonts resident in printer:
+ // PDF font name mapped to PS font name
+ // [GString]
+ GList *psResidentFonts16; // 16-bit fonts resident in printer:
+ // PDF font name mapped to font info
+ // [PSFontParam16]
+ GList *psResidentFontsCC; // 16-bit character collection fonts
+ // resident in printer: collection name
+ // mapped to font info [PSFontParam16]
+ GBool psEmbedType1; // embed Type 1 fonts?
+ GBool psEmbedTrueType; // embed TrueType fonts?
+ GBool psEmbedCIDPostScript; // embed CID PostScript fonts?
+ GBool psEmbedCIDTrueType; // embed CID TrueType fonts?
+ GBool psFontPassthrough; // pass all fonts through as-is?
+ GBool psPreload; // preload PostScript images and forms into
+ // memory
+ GBool psOPI; // generate PostScript OPI comments?
+ GBool psASCIIHex; // use ASCIIHex instead of ASCII85?
+ GBool psLZW; // false to use RLE instead of LZW
+ GBool psUncompressPreloadedImages; // uncompress all preloaded images
+ double psMinLineWidth; // minimum line width for PostScript output
+ double psRasterResolution; // PostScript rasterization resolution (dpi)
+ GBool psRasterMono; // true to do PostScript rasterization
+ // in monochrome (gray); false to do it
+ // in color (RGB/CMYK)
+ int psRasterSliceSize; // maximum size (pixels) of PostScript
+ // rasterization slice
+ GBool psAlwaysRasterize; // force PostScript rasterization
+ GBool psNeverRasterize; // prevent PostScript rasterization
+ GString *textEncoding; // encoding (unicodeMap) to use for text
+ // output
+ EndOfLineKind textEOL; // type of EOL marker to use for text
+ // output
+ GBool textPageBreaks; // insert end-of-page markers?
+ GBool textKeepTinyChars; // keep all characters in text output
+ GString *initialZoom; // initial zoom level
+ int defaultFitZoom; // default zoom factor if initialZoom is
+ // 'page' or 'width'.
+ GString *initialDisplayMode; // initial display mode (single,
+ // continuous, etc.)
+ GBool initialToolbarState; // initial toolbar state - open (true)
+ // 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
+ int tileCacheSize; // number of rasterization tiles in cache
+ int workerThreads; // number of rasterization worker threads
+ GBool enableFreeType; // FreeType enable flag
+ GBool disableFreeTypeHinting; // FreeType hinting disable flag
+ GBool antialias; // font anti-aliasing enable flag
+ GBool vectorAntialias; // vector anti-aliasing enable flag
+ GBool antialiasPrinting; // allow anti-aliasing when printing
+ StrokeAdjustMode strokeAdjust; // stroke adjustment mode
+ ScreenType screenType; // halftone screen type
+ int screenSize; // screen matrix size
+ int screenDotRadius; // screen dot radius
+ double screenGamma; // screen gamma correction
+ double screenBlackThreshold; // screen black clamping threshold
+ double screenWhiteThreshold; // screen white clamping threshold
+ double minLineWidth; // minimum line width
+ GBool // enable path simplification
+ enablePathSimplification;
+ GBool drawAnnotations; // draw annotations or not
+ GBool drawFormFields; // draw form fields or not
+ GBool overprintPreview; // enable overprint preview
+ 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
+ GString *defaultPrinter; // default printer (for interactive printing
+ // from the viewer)
+ GBool mapNumericCharNames; // map numeric char names (from font subsets)?
+ GBool mapUnknownCharNames; // map unknown char names?
+ GBool mapExtTrueTypeFontsViaUnicode; // map char codes to GID via Unicode
+ // for external TrueType fonts?
+ GHash *droppedFonts; // dropped fonts [int]
+ GList *keyBindings; // key & mouse button bindings [KeyBinding]
+ GList *popupMenuCmds; // popup menu commands [PopupMenuCmd]
+ GString *tabStateFile; // path for the tab state save file
+ GBool printCommands; // print the drawing commands
+ GBool errQuiet; // suppress error messages?
+ GString *debugLogFile; // path for debug log file
+
+ CharCodeToUnicodeCache *cidToUnicodeCache;
+ CharCodeToUnicodeCache *unicodeToUnicodeCache;
+ UnicodeMapCache *unicodeMapCache;
+ CMapCache *cMapCache;
+
+#if MULTITHREADED
+ GMutex mutex;
+ GMutex unicodeMapCacheMutex;
+ GMutex cMapCacheMutex;
+#endif
+#ifdef _WIN32
+ DWORD tlsWin32ErrorInfo; // TLS index for error info
+#endif
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc
new file mode 100644
index 00000000000..6f51ad85190
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.cc
@@ -0,0 +1,826 @@
+//========================================================================
+//
+// HTMLGen.cc
+//
+// Copyright 2010 Glyph & Cog, LLC
+//
+//========================================================================
+
+//~ to do:
+//~ - fonts
+//~ - underlined? (underlines are present in the background image)
+//~ - include the original font name in the CSS entry (before the
+//~ generic serif/sans-serif/monospace name)
+//~ - check that htmlDir exists and is a directory
+//~ - links:
+//~ - links to pages
+//~ - links to named destinations
+//~ - links to URLs
+//~ - rotated text should go in the background image
+//~ - metadata
+//~ - PDF outline
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <png.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "SplashBitmap.h"
+#include "PDFDoc.h"
+#include "GfxFont.h"
+#include "TextOutputDev.h"
+#include "SplashOutputDev.h"
+#include "ErrorCodes.h"
+#include "WebFont.h"
+#include "HTMLGen.h"
+
+#ifdef _WIN32
+# define strcasecmp stricmp
+# define strncasecmp strnicmp
+#endif
+
+//------------------------------------------------------------------------
+
+struct FontStyleTagInfo {
+ const char *tag;
+ int tagLen;
+ GBool bold;
+ GBool italic;
+};
+
+// NB: these are compared, in order, against the tail of the font
+// name, so "BoldItalic" must come before "Italic", etc.
+static FontStyleTagInfo fontStyleTags[] = {
+ {"Roman", 5, gFalse, gFalse},
+ {"Regular", 7, gFalse, gFalse},
+ {"Condensed", 9, gFalse, gFalse},
+ {"CondensedBold", 13, gTrue, gFalse},
+ {"CondensedLight", 14, gFalse, gFalse},
+ {"SemiBold", 8, gTrue, gFalse},
+ {"BoldItalicMT", 12, gTrue, gTrue},
+ {"BoldItalic", 10, gTrue, gTrue},
+ {"Bold_Italic", 11, gTrue, gTrue},
+ {"BoldOblique", 11, gTrue, gTrue},
+ {"Bold_Oblique", 12, gTrue, gTrue},
+ {"BoldMT", 6, gTrue, gFalse},
+ {"Bold", 4, gTrue, gFalse},
+ {"ItalicMT", 8, gFalse, gTrue},
+ {"Italic", 6, gFalse, gTrue},
+ {"Oblique", 7, gFalse, gTrue},
+ {"Light", 5, gFalse, gFalse},
+ {NULL, 0, gFalse, gFalse}
+};
+
+struct StandardFontInfo {
+ const char *name;
+ GBool fixedWidth;
+ GBool serif;
+};
+
+static StandardFontInfo standardFonts[] = {
+ {"Arial", gFalse, gFalse},
+ {"Courier", gTrue, gFalse},
+ {"Futura", gFalse, gFalse},
+ {"Helvetica", gFalse, gFalse},
+ {"Minion", gFalse, gTrue},
+ {"NewCenturySchlbk", gFalse, gTrue},
+ {"Times", gFalse, gTrue},
+ {"TimesNew", gFalse, gTrue},
+ {"Times_New", gFalse, gTrue},
+ {"Verdana", gFalse, gFalse},
+ {"LucidaSans", gFalse, gFalse},
+ {NULL, gFalse, gFalse}
+};
+
+struct SubstFontInfo {
+ double mWidth;
+};
+
+// index: {fixed:8, serif:4, sans-serif:0} + bold*2 + italic
+static SubstFontInfo substFonts[16] = {
+ {0.833},
+ {0.833},
+ {0.889},
+ {0.889},
+ {0.788},
+ {0.722},
+ {0.833},
+ {0.778},
+ {0.600},
+ {0.600},
+ {0.600},
+ {0.600}
+};
+
+// Map Unicode indexes from the private use area, following the Adobe
+// Glyph list.
+#define privateUnicodeMapStart 0xf6f9
+#define privateUnicodeMapEnd 0xf7ff
+static int
+privateUnicodeMap[privateUnicodeMapEnd - privateUnicodeMapStart + 1] = {
+ 0x0141, 0x0152, 0, 0, 0x0160, 0, 0x017d, // f6f9
+ 0, 0, 0, 0, 0, 0, 0, 0, // f700
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f710
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x0021, 0, 0, 0x0024, 0, 0x0026, 0, // f720
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // f730
+ 0x0038, 0x0039, 0, 0, 0, 0, 0, 0x003f,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f740
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f750
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // f760
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // f770
+ 0x0058, 0x0059, 0x005a, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f780
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f790
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x00a1, 0x00a2, 0, 0, 0, 0, 0, // f7a0
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f7b0
+ 0, 0, 0, 0, 0, 0, 0, 0x00bf,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f7c0
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // f7d0
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // f7e0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0, // f7f0
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178
+};
+
+enum VerticalAlignment {
+ vertAlignBaseline,
+ vertAlignSub,
+ vertAlignSuper,
+ vertAlignTop
+};
+
+static const char *vertAlignNames[] = {
+ "baseline",
+ "sub",
+ "super",
+ "top"
+};
+
+//------------------------------------------------------------------------
+
+class HTMLGenFontDefn {
+public:
+
+ HTMLGenFontDefn(Ref fontIDA, GString *fontFaceA, GString *fontSpecA,
+ double scaleA)
+ : fontID(fontIDA), fontFace(fontFaceA), fontSpec(fontSpecA)
+ , scale(scaleA), used(gFalse) {}
+ ~HTMLGenFontDefn() { delete fontFace; delete fontSpec; }
+ GBool match(Ref fontIDA)
+ { return fontIDA.num == fontID.num && fontIDA.gen == fontID.gen; }
+
+ Ref fontID;
+ GString *fontFace; // NULL for substituted fonts
+ GString *fontSpec;
+ double scale;
+ GBool used; // set when used (per page)
+};
+
+//------------------------------------------------------------------------
+
+
+//------------------------------------------------------------------------
+
+HTMLGen::HTMLGen(double backgroundResolutionA) {
+ TextOutputControl textOutControl;
+ SplashColor paperColor;
+
+ ok = gTrue;
+
+ backgroundResolution = backgroundResolutionA;
+ zoom = 1.0;
+ drawInvisibleText = gTrue;
+ allTextInvisible = gFalse;
+ extractFontFiles = gFalse;
+
+ // set up the TextOutputDev
+ textOutControl.mode = textOutReadingOrder;
+ textOutControl.html = gTrue;
+ textOutControl.splitRotatedWords = gTrue;
+ textOut = new TextOutputDev(NULL, &textOutControl, gFalse);
+ if (!textOut->isOk()) {
+ ok = gFalse;
+ }
+
+ // set up the SplashOutputDev
+ paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
+ splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor);
+
+ fontDefns = NULL;
+}
+
+HTMLGen::~HTMLGen() {
+ delete textOut;
+ delete splashOut;
+ if (fontDefns) {
+ deleteGList(fontDefns, HTMLGenFontDefn);
+ }
+}
+
+void HTMLGen::startDoc(PDFDoc *docA) {
+ doc = docA;
+ splashOut->startDoc(doc->getXRef());
+
+ if (fontDefns) {
+ deleteGList(fontDefns, HTMLGenFontDefn);
+ }
+ fontDefns = new GList();
+ nextFontFaceIdx = 0;
+}
+
+static inline int pr(int (*writeFunc)(void *stream, const char *data, int size),
+ void *stream, const char *data) {
+ return writeFunc(stream, data, (int)strlen(data));
+}
+
+static int pf(int (*writeFunc)(void *stream, const char *data, int size),
+ void *stream, const char *fmt, ...) {
+ va_list args;
+ GString *s;
+ int ret;
+
+ va_start(args, fmt);
+ s = GString::formatv(fmt, args);
+ va_end(args);
+ ret = writeFunc(stream, s->getCString(), s->getLength());
+ delete s;
+ return ret;
+}
+
+struct PNGWriteInfo {
+ int (*writePNG)(void *stream, const char *data, int size);
+ void *pngStream;
+};
+
+static void pngWriteFunc(png_structp png, png_bytep data, png_size_t size) {
+ PNGWriteInfo *info;
+
+ info = (PNGWriteInfo *)png_get_progressive_ptr(png);
+ info->writePNG(info->pngStream, (char *)data, (int)size);
+}
+
+int HTMLGen::convertPage(
+ int pg, const char *pngURL, const char *htmlDir,
+ int (*writeHTML)(void *stream, const char *data, int size),
+ void *htmlStream,
+ int (*writePNG)(void *stream, const char *data, int size),
+ void *pngStream) {
+ png_structp png;
+ png_infop pngInfo;
+ PNGWriteInfo writeInfo;
+ SplashBitmap *bitmap;
+ Guchar *p;
+ double pageW, pageH;
+ TextPage *text;
+ GList *cols, *pars, *lines, *words;
+ TextFontInfo *font;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ HTMLGenFontDefn *fontDefn;
+ GString *s;
+ double base;
+ int primaryDir, spanDir;
+ int colIdx, parIdx, lineIdx, firstWordIdx, lastWordIdx;
+ int y, i;
+
+ // generate the background bitmap
+ splashOut->setSkipText(!allTextInvisible, gFalse);
+ doc->displayPage(splashOut, pg, backgroundResolution, backgroundResolution,
+ 0, gFalse, gTrue, gFalse);
+ bitmap = splashOut->getBitmap();
+ if (!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL)) ||
+ !(pngInfo = png_create_info_struct(png))) {
+ return errFileIO;
+ }
+ if (setjmp(png_jmpbuf(png))) {
+ return errFileIO;
+ }
+ writeInfo.writePNG = writePNG;
+ writeInfo.pngStream = pngStream;
+ png_set_write_fn(png, &writeInfo, pngWriteFunc, NULL);
+ png_set_IHDR(png, pngInfo, bitmap->getWidth(), bitmap->getHeight(),
+ 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ png_write_info(png, pngInfo);
+ p = bitmap->getDataPtr();
+ for (y = 0; y < bitmap->getHeight(); ++y) {
+ png_write_row(png, (png_bytep)p);
+ p += bitmap->getRowSize();
+ }
+ png_write_end(png, pngInfo);
+ png_destroy_write_struct(&png, &pngInfo);
+
+ // page size
+ if (doc->getPageRotate(pg) == 90 || doc->getPageRotate(pg) == 270) {
+ pageW = doc->getPageCropHeight(pg);
+ pageH = doc->getPageCropWidth(pg);
+ } else {
+ pageW = doc->getPageCropWidth(pg);
+ pageH = doc->getPageCropHeight(pg);
+ }
+
+ // get the PDF text
+ doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
+ doc->processLinks(textOut, pg);
+ text = textOut->takeText();
+ primaryDir = text->primaryDirectionIsLR() ? 1 : -1;
+
+ // HTML header
+ pr(writeHTML, htmlStream, "<html>\n");
+ pr(writeHTML, htmlStream, "<head>\n");
+ pr(writeHTML, htmlStream, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
+ pr(writeHTML, htmlStream, "<style type=\"text/css\">\n");
+ pr(writeHTML, htmlStream, ".txt { white-space:nowrap; }\n");
+ fonts = text->getFonts();
+ fontScales = (double *)gmallocn(fonts->getLength(), sizeof(double));
+ for (i = 0; i < fontDefns->getLength(); ++i) {
+ fontDefn = (HTMLGenFontDefn *)fontDefns->get(i);
+ fontDefn->used = gFalse;
+ }
+ for (i = 0; i < fonts->getLength(); ++i) {
+ font = (TextFontInfo *)fonts->get(i);
+ fontDefn = getFontDefn(font, htmlDir);
+ if (!fontDefn->used && fontDefn->fontFace) {
+ pr(writeHTML, htmlStream, fontDefn->fontFace->getCString());
+ }
+ pf(writeHTML, htmlStream, "#f{0:d} {{ {1:t} }}\n", i, fontDefn->fontSpec);
+ fontScales[i] = fontDefn->scale;
+ fontDefn->used = gTrue;
+ }
+ pr(writeHTML, htmlStream, "</style>\n");
+ pr(writeHTML, htmlStream, "</head>\n");
+ if (primaryDir >= 0) {
+ pr(writeHTML, htmlStream, "<body>\n");
+ } else {
+ pr(writeHTML, htmlStream, "<body dir=\"rtl\">\n");
+ }
+ if (primaryDir >= 0) {
+ pf(writeHTML, htmlStream, "<img id=\"background\" style=\"position:absolute; left:0px; top:0px;\" width=\"{0:d}\" height=\"{1:d}\" src=\"{2:s}\">\n",
+ (int)(pageW * zoom), (int)(pageH * zoom), pngURL);
+ } else {
+ pf(writeHTML, htmlStream, "<img id=\"background\" style=\"position:absolute; right:0px; top:0px;\" width=\"{0:d}\" height=\"{1:d}\" src=\"{2:s}\">\n",
+ (int)(pageW * zoom), (int)(pageH * zoom), pngURL);
+ }
+
+ // generate the HTML text
+ cols = text->makeColumns();
+ for (colIdx = 0; colIdx < cols->getLength(); ++colIdx) {
+ col = (TextColumn *)cols->get(colIdx);
+ pars = col->getParagraphs();
+ for (parIdx = 0; parIdx < pars->getLength(); ++parIdx) {
+ par = (TextParagraph *)pars->get(parIdx);
+ lines = par->getLines();
+ for (lineIdx = 0; lineIdx < lines->getLength(); ++lineIdx) {
+ line = (TextLine *)lines->get(lineIdx);
+ if (line->getRotation() != 0) {
+ continue;
+ }
+ words = line->getWords();
+ if (lineIdx == 0 && par->hasDropCap() && words->getLength() >= 2) {
+ base = ((TextWord *)words->get(1))->getBaseline();
+ } else {
+ base = line->getBaseline();
+ }
+ s = new GString();
+ for (firstWordIdx = (primaryDir >= 0) ? 0 : words->getLength() - 1;
+ (primaryDir >= 0) ? firstWordIdx < words->getLength()
+ : firstWordIdx >= 0;
+ firstWordIdx = lastWordIdx + primaryDir) {
+ lastWordIdx = findDirSpan(words, firstWordIdx,
+ primaryDir, &spanDir);
+ appendSpans(words, firstWordIdx, lastWordIdx,
+ primaryDir, spanDir,
+ base, lineIdx == 0 && par->hasDropCap(),
+ s);
+ }
+ if (primaryDir >= 0) {
+ pf(writeHTML, htmlStream, "<div class=\"txt\" style=\"position:absolute; left:{0:d}px; top:{1:d}px;\">{2:t}</div>\n",
+ (int)(line->getXMin() * zoom),
+ (int)(line->getYMin() * zoom), s);
+ } else {
+ pf(writeHTML, htmlStream, "<div class=\"txt\" style=\"position:absolute; right:{0:d}px; top:{1:d}px;\">{2:t}</div>\n",
+ (int)((pageW - line->getXMax()) * zoom),
+ (int)(line->getYMin() * zoom), s);
+ }
+ delete s;
+ }
+ }
+ }
+ gfree(fontScales);
+ delete text;
+ deleteGList(cols, TextColumn);
+
+ // HTML trailer
+ pr(writeHTML, htmlStream, "</body>\n");
+ pr(writeHTML, htmlStream, "</html>\n");
+
+ return errNone;
+}
+
+// Find a sequence of words, starting at <firstWordIdx>, that have the
+// same writing direction. Returns the index of the last word, and
+// sets *<spanDir> to the span direction.
+int HTMLGen::findDirSpan(GList *words, int firstWordIdx, int primaryDir,
+ int *spanDir) {
+ int dir0, dir1, nextWordIdx;
+
+ dir0 = ((TextWord *)words->get(firstWordIdx))->getDirection();
+ for (nextWordIdx = firstWordIdx + primaryDir;
+ (primaryDir >= 0) ? nextWordIdx < words->getLength()
+ : nextWordIdx >= 0;
+ nextWordIdx += primaryDir) {
+ dir1 = ((TextWord *)words->get(nextWordIdx))->getDirection();
+ if (dir0 == 0) {
+ dir0 = dir1;
+ } else if (dir1 != 0 && dir1 != dir0) {
+ break;
+ }
+ }
+
+ if (dir0 == 0) {
+ *spanDir = primaryDir;
+ } else {
+ *spanDir = dir0;
+ }
+
+ return nextWordIdx - primaryDir;
+}
+
+// Create HTML spans for words <firstWordIdx> .. <lastWordIdx>, and
+// append them to <s>.
+void HTMLGen::appendSpans(GList *words, int firstWordIdx, int lastWordIdx,
+ int primaryDir, int spanDir,
+ double base, GBool dropCapLine, GString *s) {
+ TextWord *word0, *word1;
+ VerticalAlignment vertAlign0, vertAlign1;
+ const char *dirTag;
+ Unicode u;
+ GBool invisible, sp;
+ double r0, g0, b0, r1, g1, b1;
+ double base1;
+ int wordIdx, t, i;
+
+ if (spanDir != primaryDir) {
+ t = firstWordIdx;
+ firstWordIdx = lastWordIdx;
+ lastWordIdx = t;
+ }
+
+ word0 = NULL;
+ vertAlign0 = vertAlignBaseline; // make gcc happy
+ r0 = g0 = b0 = 0; // make gcc happy
+ for (wordIdx = firstWordIdx;
+ (spanDir >= 0) ? wordIdx <= lastWordIdx : wordIdx >= lastWordIdx;
+ wordIdx += spanDir) {
+ word1 = (TextWord *)words->get(wordIdx);
+ invisible = allTextInvisible || word1->isInvisible() || word1->isRotated();
+ if (!drawInvisibleText && invisible) {
+ continue;
+ }
+ word1->getColor(&r1, &g1, &b1);
+ base1 = word1->getBaseline();
+ if (dropCapLine) {
+ //~ this will fail if there are subscripts or superscripts in
+ //~ the first line of a paragraph with a drop cap
+ vertAlign1 = vertAlignTop;
+ } else if (base1 - base < -1) {
+ vertAlign1 = vertAlignSuper;
+ } else if (base1 - base > 1) {
+ vertAlign1 = vertAlignSub;
+ } else {
+ vertAlign1 = vertAlignBaseline;
+ }
+ if (!word0 ||
+ 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) {
+ s->append("</span>");
+ }
+ for (i = 0; i < fonts->getLength(); ++i) {
+ if (word1->getFontInfo() == (TextFontInfo *)fonts->get(i)) {
+ break;
+ }
+ }
+ // we force spans to be LTR or RTL; this is a kludge, but it's
+ // far easier than implementing the full Unicode bidi algorithm
+ if (spanDir == primaryDir) {
+ dirTag = "";
+ } else if (spanDir < 0) {
+ dirTag = " dir=\"rtl\"";
+ } else {
+ dirTag = " dir=\"ltr\"";
+ }
+ s->appendf("<span id=\"f{0:d}\"{1:s} style=\"font-size:{2:d}px;vertical-align:{3:s};{4:s}color:rgba({5:d},{6:d},{7:d},{8:d});\">",
+ i,
+ dirTag,
+ (int)(fontScales[i] * word1->getFontSize() * zoom),
+ vertAlignNames[vertAlign1],
+ (dropCapLine && wordIdx == 0) ? "line-height:75%;" : "",
+ (int)(r1 * 255), (int)(g1 * 255), (int)(b1 * 255),
+ invisible ? 0 : 1);
+ }
+
+ // add a space before the word, if needed
+ // -- this only happens with the first word in a reverse section
+ if (spanDir != primaryDir && wordIdx == firstWordIdx) {
+ if (spanDir >= 0) {
+ if (wordIdx > 0) {
+ sp = ((TextWord *)words->get(wordIdx - 1))->getSpaceAfter();
+ } else {
+ sp = gFalse;
+ }
+ } else {
+ sp = word1->getSpaceAfter();
+ }
+ if (sp) {
+ s->append(' ');
+ }
+ }
+
+ for (i = (spanDir >= 0) ? 0 : word1->getLength() - 1;
+ (spanDir >= 0) ? i < word1->getLength() : i >= 0;
+ i += spanDir) {
+ u = word1->getChar(i);
+ if (u >= privateUnicodeMapStart &&
+ u <= privateUnicodeMapEnd &&
+ privateUnicodeMap[u - privateUnicodeMapStart]) {
+ u = privateUnicodeMap[u - privateUnicodeMapStart];
+ }
+ appendUTF8(u, s);
+ }
+
+ // add a space after the word, if needed
+ // -- there is never a space after the last word in a reverse
+ // section (this will be handled as a space after the last word
+ // in the previous primary-direction section)
+ if (spanDir != primaryDir && wordIdx == lastWordIdx) {
+ sp = gFalse;
+ } else if (spanDir >= 0) {
+ sp = word1->getSpaceAfter();
+ } else {
+ if (wordIdx > 0) {
+ sp = ((TextWord *)words->get(wordIdx - 1))->getSpaceAfter();
+ } else {
+ sp = gFalse;
+ }
+ }
+ if (sp) {
+ s->append(' ');
+ }
+
+ word0 = word1;
+ vertAlign0 = vertAlign1;
+ r0 = r1;
+ g0 = g1;
+ b0 = b1;
+ }
+ s->append("</span>");
+}
+
+void HTMLGen::appendUTF8(Unicode u, GString *s) {
+ if (u <= 0x7f) {
+ if (u == '&') {
+ s->append("&amp;");
+ } else if (u == '<') {
+ s->append("&lt;");
+ } else if (u == '>') {
+ s->append("&gt;");
+ } else {
+ s->append((char)u);
+ }
+ } else if (u <= 0x7ff) {
+ s->append((char)(0xc0 + (u >> 6)));
+ s->append((char)(0x80 + (u & 0x3f)));
+ } else if (u <= 0xffff) {
+ s->append((char)(0xe0 + (u >> 12)));
+ s->append((char)(0x80 + ((u >> 6) & 0x3f)));
+ s->append((char)(0x80 + (u & 0x3f)));
+ } else if (u <= 0x1fffff) {
+ s->append((char)(0xf0 + (u >> 18)));
+ s->append((char)(0x80 + ((u >> 12) & 0x3f)));
+ s->append((char)(0x80 + ((u >> 6) & 0x3f)));
+ s->append((char)(0x80 + (u & 0x3f)));
+ } else if (u <= 0x3ffffff) {
+ s->append((char)(0xf8 + (u >> 24)));
+ s->append((char)(0x80 + ((u >> 18) & 0x3f)));
+ s->append((char)(0x80 + ((u >> 12) & 0x3f)));
+ s->append((char)(0x80 + ((u >> 6) & 0x3f)));
+ s->append((char)(0x80 + (u & 0x3f)));
+ } else if (u <= 0x7fffffff) {
+ s->append((char)(0xfc + (u >> 30)));
+ s->append((char)(0x80 + ((u >> 24) & 0x3f)));
+ s->append((char)(0x80 + ((u >> 18) & 0x3f)));
+ s->append((char)(0x80 + ((u >> 12) & 0x3f)));
+ s->append((char)(0x80 + ((u >> 6) & 0x3f)));
+ s->append((char)(0x80 + (u & 0x3f)));
+ }
+}
+
+HTMLGenFontDefn *HTMLGen::getFontDefn(TextFontInfo *font,
+ const char *htmlDir) {
+ Ref id;
+ HTMLGenFontDefn *fontDefn;
+ int i;
+
+ // check the existing font defns
+ id = font->getFontID();
+ if (id.num >= 0) {
+ for (i = 0; i < fontDefns->getLength(); ++i) {
+ fontDefn = (HTMLGenFontDefn *)fontDefns->get(i);
+ if (fontDefn->match(id)) {
+ return fontDefn;
+ }
+ }
+ }
+
+ // try to extract a font file
+ if (!extractFontFiles ||
+ !(fontDefn = getFontFile(font, htmlDir))) {
+
+ // get a substitute font
+ fontDefn = getSubstituteFont(font);
+ }
+
+ fontDefns->append(fontDefn);
+ return fontDefn;
+}
+
+HTMLGenFontDefn *HTMLGen::getFontFile(TextFontInfo *font,
+ const char *htmlDir) {
+ Ref id;
+ HTMLGenFontDefn *fontDefn;
+ Object fontObj;
+ GfxFont *gfxFont;
+ WebFont *webFont;
+ GString *fontFile, *fontPath, *fontFace, *fontSpec;
+ const char *family, *weight, *style;
+ double scale;
+
+ id = font->getFontID();
+ if (id.num < 0) {
+ return NULL;
+ }
+
+ doc->getXRef()->fetch(id.num, id.gen, &fontObj);
+ if (!fontObj.isDict()) {
+ fontObj.free();
+ return NULL;
+ }
+
+ gfxFont = GfxFont::makeFont(doc->getXRef(), "F", id, fontObj.getDict());
+ webFont = new WebFont(gfxFont, doc->getXRef());
+ fontDefn = NULL;
+
+ if (webFont->canWriteTTF()) {
+ fontFile = GString::format("{0:d}.ttf", nextFontFaceIdx);
+ fontPath = GString::format("{0:s}/{1:t}", htmlDir, fontFile);
+ if (webFont->writeTTF(fontPath->getCString())) {
+ fontFace = GString::format("@font-face {{ font-family: ff{0:d}; src: url(\"{1:t}\"); }}\n",
+ nextFontFaceIdx, fontFile);
+ 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;
+ delete fontFile;
+
+ } else if (webFont->canWriteOTF()) {
+ fontFile = GString::format("{0:d}.otf", nextFontFaceIdx);
+ fontPath = GString::format("{0:s}/{1:t}", htmlDir, fontFile);
+ if (webFont->writeOTF(fontPath->getCString())) {
+ fontFace = GString::format("@font-face {{ font-family: ff{0:d}; src: url(\"{1:t}\"); }}\n",
+ nextFontFaceIdx, fontFile);
+ 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;
+ delete fontFile;
+ }
+
+ delete webFont;
+ delete gfxFont;
+ fontObj.free();
+
+ return fontDefn;
+}
+
+HTMLGenFontDefn *HTMLGen::getSubstituteFont(TextFontInfo *font) {
+ const char *family, *weight, *style;
+ double scale;
+ GString *fontSpec;
+
+ getFontDetails(font, &family, &weight, &style, &scale);
+ fontSpec = GString::format("font-family:{0:s}; font-weight:{1:s}; font-style:{2:s};",
+ family, weight, style);
+ return new HTMLGenFontDefn(font->getFontID(), NULL, fontSpec, scale);
+}
+
+void HTMLGen::getFontDetails(TextFontInfo *font, const char **family,
+ const char **weight, const char **style,
+ double *scale) {
+ GString *fontName;
+ char *fontName2;
+ FontStyleTagInfo *fst;
+ StandardFontInfo *sf;
+ GBool fixedWidth, serif, bold, italic;
+ double s;
+ int n, i;
+
+ // get the font name, remove any subset tag
+ fontName = font->getFontName();
+ if (fontName) {
+ fontName2 = fontName->getCString();
+ n = fontName->getLength();
+ for (i = 0; i < n && i < 7; ++i) {
+ if (fontName2[i] < 'A' || fontName2[i] > 'Z') {
+ break;
+ }
+ }
+ if (i == 6 && n > 7 && fontName2[6] == '+') {
+ fontName2 += 7;
+ n -= 7;
+ }
+ } else {
+ fontName2 = NULL;
+ n = 0;
+ }
+
+ // get the style info from the font descriptor flags
+ fixedWidth = font->isFixedWidth();
+ serif = font->isSerif();
+ bold = font->isBold();
+ italic = font->isItalic();
+
+ if (fontName2) {
+
+ // look for a style tag at the end of the font name -- this
+ // overrides the font descriptor bold/italic flags
+ for (fst = fontStyleTags; fst->tag; ++fst) {
+ if (n > fst->tagLen &&
+ !strcasecmp(fontName2 + n - fst->tagLen, fst->tag)) {
+ bold = fst->bold;
+ italic = fst->italic;
+ n -= fst->tagLen;
+ if (n > 1 && (fontName2[n-1] == '-' ||
+ fontName2[n-1] == ',' ||
+ fontName2[n-1] == '.' ||
+ fontName2[n-1] == '_')) {
+ --n;
+ }
+ break;
+ }
+ }
+
+ // look for a known font name -- this overrides the font descriptor
+ // fixedWidth/serif flags
+ for (sf = standardFonts; sf->name; ++sf) {
+ if (!strncasecmp(fontName2, sf->name, n)) {
+ fixedWidth = sf->fixedWidth;
+ serif = sf->serif;
+ break;
+ }
+ }
+ }
+
+ // compute the scaling factor
+ *scale = 1;
+ if ((s = font->getMWidth())) {
+ i = (fixedWidth ? 8 : serif ? 4 : 0) + (bold ? 2 : 0) + (italic ? 1 : 0);
+ if (s < substFonts[i].mWidth) {
+ *scale = s / substFonts[i].mWidth;
+ }
+ }
+
+ *family = fixedWidth ? "monospace" : serif ? "serif" : "sans-serif";
+ *weight = bold ? "bold" : "normal";
+ *style = italic ? "italic" : "normal";
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.h b/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.h
new file mode 100644
index 00000000000..d09eea3f266
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/HTMLGen.h
@@ -0,0 +1,94 @@
+//========================================================================
+//
+// HTMLGen.h
+//
+// Copyright 2010 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef HTMLGEN_H
+#define HTMLGEN_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class GString;
+class PDFDoc;
+class TextOutputDev;
+class TextFontInfo;
+class SplashOutputDev;
+class HTMLGenFontDefn;
+
+//------------------------------------------------------------------------
+
+class HTMLGen {
+public:
+
+ HTMLGen(double backgroundResolutionA);
+ ~HTMLGen();
+
+ GBool isOk() { return ok; }
+
+ double getBackgroundResolution() { return backgroundResolution; }
+ void setBackgroundResolution(double backgroundResolutionA)
+ { backgroundResolution = backgroundResolutionA; }
+
+ double getZoom() { return zoom; }
+ void setZoom(double zoomA) { zoom = zoomA; }
+
+ GBool getDrawInvisibleText() { return drawInvisibleText; }
+ void setDrawInvisibleText(GBool drawInvisibleTextA)
+ { drawInvisibleText = drawInvisibleTextA; }
+
+ GBool getAllTextInvisible() { return allTextInvisible; }
+ void setAllTextInvisible(GBool allTextInvisibleA)
+ { allTextInvisible = allTextInvisibleA; }
+
+ void setExtractFontFiles(GBool extractFontFilesA)
+ { extractFontFiles = extractFontFilesA; }
+
+ void startDoc(PDFDoc *docA);
+ int convertPage(int pg, const char *pngURL, const char *htmlDir,
+ int (*writeHTML)(void *stream, const char *data, int size),
+ void *htmlStream,
+ int (*writePNG)(void *stream, const char *data, int size),
+ void *pngStream);
+
+private:
+
+ int findDirSpan(GList *words, int firstWordIdx, int primaryDir,
+ int *spanDir);
+ void appendSpans(GList *words, int firstWordIdx, int lastWordIdx,
+ int primaryDir, int spanDir,
+ double base, GBool dropCapLine, GString *s);
+ void appendUTF8(Unicode u, GString *s);
+ HTMLGenFontDefn *getFontDefn(TextFontInfo *font, const char *htmlDir);
+ HTMLGenFontDefn *getFontFile(TextFontInfo *font, const char *htmlDir);
+ HTMLGenFontDefn *getSubstituteFont(TextFontInfo *font);
+ void getFontDetails(TextFontInfo *font, const char **family,
+ const char **weight, const char **style,
+ double *scale);
+
+ double backgroundResolution;
+ double zoom;
+ GBool drawInvisibleText;
+ GBool allTextInvisible;
+ GBool extractFontFiles;
+
+ PDFDoc *doc;
+ TextOutputDev *textOut;
+ SplashOutputDev *splashOut;
+
+ GList *fonts; // [TextFontInfo]
+ double *fontScales;
+
+ GList *fontDefns; // [HTMLGenFontDefn]
+ int nextFontFaceIdx;
+
+ GBool ok;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc
new file mode 100644
index 00000000000..5a694d7b665
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.cc
@@ -0,0 +1,453 @@
+//========================================================================
+//
+// ImageOutputDev.cc
+//
+// Copyright 1998-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <math.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "config.h"
+#include "Error.h"
+#include "GfxState.h"
+#include "Object.h"
+#include "Stream.h"
+#include "ImageOutputDev.h"
+
+ImageOutputDev::ImageOutputDev(char *fileRootA, GBool dumpJPEGA,
+ GBool dumpRawA, GBool listA) {
+ fileRoot = copyString(fileRootA);
+ dumpJPEG = dumpJPEGA;
+ dumpRaw = dumpRawA;
+ list = listA;
+ imgNum = 0;
+ curPageNum = 0;
+ ok = gTrue;
+}
+
+ImageOutputDev::~ImageOutputDev() {
+ gfree(fileRoot);
+}
+
+void ImageOutputDev::startPage(int pageNum, GfxState *state) {
+ curPageNum = pageNum;
+}
+
+void ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
+ Object *strRef,
+ int paintType, int tilingType,
+ Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep) {
+ // do nothing -- this avoids the potentially slow loop in Gfx.cc
+}
+
+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;
+
+ // dump raw file
+ if (dumpRaw && !inlineImg) {
+
+ // open the image file
+ fileName = GString::format("{0:s}-{1:04d}.{2:s}",
+ fileRoot, imgNum, getRawFileExtension(str));
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+
+ // initialize stream
+ str = getRawStream(str);
+ str->reset();
+
+ // copy the stream
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
+
+ str->close();
+ fclose(f);
+
+ // dump JPEG file
+ } else if (dumpJPEG && str->getKind() == strDCT && !inlineImg) {
+
+ // open the image file
+ fileName = GString::format("{0:s}-{1:04d}.jpg", fileRoot, imgNum);
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+
+ // initialize stream
+ str = ((DCTStream *)str)->getRawStream();
+ str->reset();
+
+ // copy the stream
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
+
+ str->close();
+ fclose(f);
+
+ // dump PBM file
+ } else {
+
+ // open the image file and write the PBM header
+ fileName = GString::format("{0:s}-{1:04d}.pbm", fileRoot, imgNum);
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+ fprintf(f, "P4\n");
+ fprintf(f, "%d %d\n", width, height);
+
+ // initialize stream
+ str->reset();
+
+ // copy the stream
+ size = height * ((width + 7) / 8);
+ while (size > 0) {
+ i = size < (int)sizeof(buf) ? size : (int)sizeof(buf);
+ n = str->getBlock(buf, i);
+ fwrite(buf, 1, n, f);
+ if (n < i) {
+ break;
+ }
+ size -= n;
+ }
+
+ str->close();
+ fclose(f);
+ }
+
+ if (list) {
+ writeImageInfo(fileName, width, height, state, NULL);
+ }
+
+ delete fileName;
+}
+
+void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
+ GfxColorSpaceMode csMode;
+ GString *fileName;
+ FILE *f;
+ ImageStream *imgStr;
+ Guchar *p;
+ GfxRGB rgb;
+ GfxGray gray;
+ int x, y;
+ char buf[4096];
+ int size, n, i, j;
+
+ csMode = colorMap->getColorSpace()->getMode();
+ if (csMode == csIndexed) {
+ csMode = ((GfxIndexedColorSpace *)colorMap->getColorSpace())
+ ->getBase()->getMode();
+ }
+
+ // dump raw file
+ if (dumpRaw && !inlineImg) {
+
+ // open the image file
+ fileName = GString::format("{0:s}-{1:04d}.{2:s}",
+ fileRoot, imgNum, getRawFileExtension(str));
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+
+ // initialize stream
+ str = getRawStream(str);
+ str->reset();
+
+ // copy the stream
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
+
+ str->close();
+ fclose(f);
+
+ // dump JPEG file
+ } else if (dumpJPEG && str->getKind() == strDCT &&
+ (colorMap->getNumPixelComps() == 1 ||
+ colorMap->getNumPixelComps() == 3) &&
+ !inlineImg) {
+
+ // open the image file
+ fileName = GString::format("{0:s}-{1:04d}.jpg", fileRoot, imgNum);
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+
+ // initialize stream
+ str = ((DCTStream *)str)->getRawStream();
+ str->reset();
+
+ // copy the stream
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
+
+ str->close();
+ fclose(f);
+
+ // dump PBM file
+ } else if (colorMap->getNumPixelComps() == 1 &&
+ colorMap->getBits() == 1) {
+
+ // open the image file and write the PBM header
+ fileName = GString::format("{0:s}-{1:04d}.pbm", fileRoot, imgNum);
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+ fprintf(f, "P4\n");
+ fprintf(f, "%d %d\n", width, height);
+
+ // initialize stream
+ str->reset();
+
+ // copy the stream
+ size = height * ((width + 7) / 8);
+ while (size > 0) {
+ i = size < (int)sizeof(buf) ? size : (int)sizeof(buf);
+ n = str->getBlock(buf, i);
+ for (j = 0; j < n; ++j) {
+ buf[j] = (char)(buf[j] ^ 0xff);
+ }
+ fwrite(buf, 1, n, f);
+ if (n < i) {
+ break;
+ }
+ size -= n;
+ }
+
+ str->close();
+ fclose(f);
+
+ // dump PGM file
+ } else if (colorMap->getNumPixelComps() == 1 &&
+ (csMode == csDeviceGray || csMode == csCalGray)) {
+
+ // open the image file and write the PGM header
+ fileName = GString::format("{0:s}-{1:04d}.pgm", fileRoot, imgNum);
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+ fprintf(f, "P5\n");
+ fprintf(f, "%d %d\n", width, height);
+ fprintf(f, "255\n");
+
+ // initialize stream
+ imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgStr->reset();
+
+ // for each line...
+ for (y = 0; y < height; ++y) {
+
+ // write the line
+ if ((p = imgStr->getLine())) {
+ for (x = 0; x < width; ++x) {
+ colorMap->getGray(p, &gray, state->getRenderingIntent());
+ fputc(colToByte(gray), f);
+ ++p;
+ }
+ } else {
+ for (x = 0; x < width; ++x) {
+ fputc(0, f);
+ }
+ }
+ }
+
+ imgStr->close();
+ delete imgStr;
+
+ fclose(f);
+
+ // dump PPM file
+ } else {
+
+ // open the image file and write the PPM header
+ fileName = GString::format("{0:s}-{1:04d}.ppm", fileRoot, imgNum);
+ ++imgNum;
+ if (!(f = openFile(fileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open image file '{0:t}'", fileName);
+ delete fileName;
+ return;
+ }
+ fprintf(f, "P6\n");
+ fprintf(f, "%d %d\n", width, height);
+ fprintf(f, "255\n");
+
+ // initialize stream
+ imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgStr->reset();
+
+ // for each line...
+ for (y = 0; y < height; ++y) {
+
+ // write the line
+ if ((p = imgStr->getLine())) {
+ for (x = 0; x < width; ++x) {
+ colorMap->getRGB(p, &rgb, state->getRenderingIntent());
+ fputc(colToByte(rgb.r), f);
+ fputc(colToByte(rgb.g), f);
+ fputc(colToByte(rgb.b), f);
+ p += colorMap->getNumPixelComps();
+ }
+ } else {
+ for (x = 0; x < width; ++x) {
+ fputc(0, f);
+ fputc(0, f);
+ fputc(0, f);
+ }
+ }
+ }
+
+ imgStr->close();
+ delete imgStr;
+
+ fclose(f);
+ }
+
+ if (list) {
+ writeImageInfo(fileName, width, height, state, colorMap);
+ }
+
+ delete fileName;
+}
+
+void ImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GBool maskInvert, GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap,
+ NULL, gFalse, interpolate);
+ drawImageMask(state, maskRef, maskStr, maskWidth, maskHeight, maskInvert,
+ gFalse, interpolate);
+}
+
+void ImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
+ Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ 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, maskRef, maskStr, maskWidth, maskHeight, maskColorMap,
+ NULL, gFalse, interpolate);
+}
+
+Stream *ImageOutputDev::getRawStream(Stream *str) {
+ switch (str->getKind()) {
+ case strLZW:
+ case strRunLength:
+ case strCCITTFax:
+ case strDCT:
+ case strFlate:
+ case strJBIG2:
+ case strJPX:
+ return ((FilterStream *)str)->getNextStream();
+ default:
+ return str;
+ }
+}
+
+const char *ImageOutputDev::getRawFileExtension(Stream *str) {
+ switch (str->getKind()) {
+ case strLZW: return "lzw";
+ case strRunLength: return "rle";
+ case strCCITTFax: return "fax";
+ case strDCT: return "jpg";
+ case strFlate: return "flate";
+ case strJBIG2: return "jb2";
+ case strJPX: return "jpx";
+ default: return "unknown";
+ }
+}
+
+void ImageOutputDev::writeImageInfo(GString *fileName,
+ int width, int height, GfxState *state,
+ GfxImageColorMap *colorMap) {
+ const char *mode;
+ double hdpi, vdpi, x0, y0, x1, y1;
+ int bpc;
+
+ // this works for 0/90/180/270-degree rotations, along with
+ // horizontal/vertical flips
+ state->transformDelta(1, 0, &x0, &y0);
+ state->transformDelta(0, 1, &x1, &y1);
+ x0 = fabs(x0);
+ y0 = fabs(y0);
+ x1 = fabs(x1);
+ y1 = fabs(y1);
+ if (x0 > y0) {
+ hdpi = (72 * width) / x0;
+ vdpi = (72 * height) / y1;
+ } else {
+ hdpi = (72 * height) / x1;
+ vdpi = (72 * width) / y0;
+ }
+
+ if (colorMap) {
+ mode = GfxColorSpace::getColorSpaceModeName(
+ colorMap->getColorSpace()->getMode());
+ bpc = colorMap->getBits();
+ } else {
+ mode = NULL;
+ bpc = 1;
+ }
+
+ printf("%s: page=%d width=%d height=%d hdpi=%.2f vdpi=%.2f %s%s bpc=%d\n",
+ fileName->getCString(), curPageNum, width, height, hdpi, vdpi,
+ mode ? "colorspace=" : "mask",
+ mode ? mode : "",
+ bpc);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h
new file mode 100644
index 00000000000..5a72925aee9
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ImageOutputDev.h
@@ -0,0 +1,116 @@
+//========================================================================
+//
+// ImageOutputDev.h
+//
+// Copyright 1998-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef IMAGEOUTPUTDEV_H
+#define IMAGEOUTPUTDEV_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include "gtypes.h"
+#include "OutputDev.h"
+
+class GfxImageColorMap;
+class GfxState;
+
+//------------------------------------------------------------------------
+// ImageOutputDev
+//------------------------------------------------------------------------
+
+class ImageOutputDev: public OutputDev {
+public:
+
+ // Create an OutputDev which will write images to files named
+ // <fileRoot>-NNN.<type>. Normally, all images are written as PBM
+ // (.pbm) or PPM (.ppm) files. If <dumpJPEG> is set, JPEG images
+ // are written as JPEG (.jpg) files. If <dumpRaw> is set, all
+ // images are written in PDF-native formats. If <list> is set, a
+ // one-line summary will be written to stdout for each image.
+ ImageOutputDev(char *fileRootA, GBool dumpJPEGA, GBool dumpRawA,
+ GBool listA);
+
+ // Destructor.
+ virtual ~ImageOutputDev();
+
+ // Check if file was successfully created.
+ virtual GBool isOk() { return ok; }
+
+ // Does this device use tilingPatternFill()? If this returns false,
+ // tiling pattern fills will be reduced to a series of other drawing
+ // operations.
+ virtual GBool useTilingPatternFill() { return gTrue; }
+
+ // Does this device use beginType3Char/endType3Char? Otherwise,
+ // text in Type 3 fonts will be drawn with drawChar/drawString.
+ virtual GBool interpretType3Chars() { return gFalse; }
+
+ // Does this device need non-text content?
+ virtual GBool needNonText() { return gTrue; }
+
+ //---- get info about output device
+
+ // Does this device use upside-down coordinates?
+ // (Upside-down means (0,0) is the top left corner of the page.)
+ virtual GBool upsideDown() { return gTrue; }
+
+ // Does this device use drawChar() or drawString()?
+ virtual GBool useDrawChar() { return gFalse; }
+
+ //----- initialization and control
+ virtual void startPage(int pageNum, GfxState *state);
+
+ //----- path painting
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
+ int paintType, int tilingType, Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep);
+
+ //----- image drawing
+ virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate);
+ virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg, GBool interpolate);
+ virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ 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,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ double *matte, GBool interpolate);
+
+private:
+
+ Stream *getRawStream(Stream *str);
+ const char *getRawFileExtension(Stream *str);
+ void writeImageInfo(GString *fileName,
+ int width, int height, GfxState *state,
+ GfxImageColorMap *colorMap);
+
+ char *fileRoot; // root of 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
+ int imgNum; // current image number
+ int curPageNum; // current page number
+ GBool ok; // set up ok?
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JArithmeticDecoder.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/JArithmeticDecoder.cc
new file mode 100644
index 00000000000..3d10cb4acad
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JArithmeticDecoder.cc
@@ -0,0 +1,370 @@
+//========================================================================
+//
+// JArithmeticDecoder.cc
+//
+// Copyright 2002-2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmempp.h"
+#include "Object.h"
+#include "Stream.h"
+#include "JArithmeticDecoder.h"
+
+//------------------------------------------------------------------------
+// JArithmeticDecoderStates
+//------------------------------------------------------------------------
+
+JArithmeticDecoderStats::JArithmeticDecoderStats(int contextSizeA) {
+ contextSize = contextSizeA;
+ cxTab = (Guchar *)gmallocn(contextSize, sizeof(Guchar));
+ reset();
+}
+
+JArithmeticDecoderStats::~JArithmeticDecoderStats() {
+ gfree(cxTab);
+}
+
+JArithmeticDecoderStats *JArithmeticDecoderStats::copy() {
+ JArithmeticDecoderStats *stats;
+
+ stats = new JArithmeticDecoderStats(contextSize);
+ memcpy(stats->cxTab, cxTab, contextSize);
+ return stats;
+}
+
+void JArithmeticDecoderStats::reset() {
+ memset(cxTab, 0, contextSize);
+}
+
+void JArithmeticDecoderStats::copyFrom(JArithmeticDecoderStats *stats) {
+ memcpy(cxTab, stats->cxTab, contextSize);
+}
+
+void JArithmeticDecoderStats::setEntry(Guint cx, int i, int mps) {
+ cxTab[cx] = (Guchar)((i << 1) + mps);
+}
+
+//------------------------------------------------------------------------
+// JArithmeticDecoder
+//------------------------------------------------------------------------
+
+Guint JArithmeticDecoder::qeTab[47] = {
+ 0x56010000, 0x34010000, 0x18010000, 0x0AC10000,
+ 0x05210000, 0x02210000, 0x56010000, 0x54010000,
+ 0x48010000, 0x38010000, 0x30010000, 0x24010000,
+ 0x1C010000, 0x16010000, 0x56010000, 0x54010000,
+ 0x51010000, 0x48010000, 0x38010000, 0x34010000,
+ 0x30010000, 0x28010000, 0x24010000, 0x22010000,
+ 0x1C010000, 0x18010000, 0x16010000, 0x14010000,
+ 0x12010000, 0x11010000, 0x0AC10000, 0x09C10000,
+ 0x08A10000, 0x05210000, 0x04410000, 0x02A10000,
+ 0x02210000, 0x01410000, 0x01110000, 0x00850000,
+ 0x00490000, 0x00250000, 0x00150000, 0x00090000,
+ 0x00050000, 0x00010000, 0x56010000
+};
+
+int JArithmeticDecoder::nmpsTab[47] = {
+ 1, 2, 3, 4, 5, 38, 7, 8, 9, 10, 11, 12, 13, 29, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46
+};
+
+int JArithmeticDecoder::nlpsTab[47] = {
+ 1, 6, 9, 12, 29, 33, 6, 14, 14, 14, 17, 18, 20, 21, 14, 14,
+ 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 46
+};
+
+int JArithmeticDecoder::switchTab[47] = {
+ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+JArithmeticDecoder::JArithmeticDecoder() {
+ str = NULL;
+ dataLen = 0;
+ limitStream = gFalse;
+ nBytesRead = 0;
+ readBuf = -1;
+}
+
+inline Guint JArithmeticDecoder::readByte() {
+ Guint x;
+
+ if (limitStream) {
+ if (readBuf >= 0) {
+ x = (Guint)readBuf;
+ readBuf = -1;
+ return x;
+ }
+ --dataLen;
+ if (dataLen < 0) {
+ return 0xff;
+ }
+ }
+ ++nBytesRead;
+ return (Guint)str->getChar() & 0xff;
+}
+
+JArithmeticDecoder::~JArithmeticDecoder() {
+ cleanup();
+}
+
+void JArithmeticDecoder::start() {
+ buf0 = readByte();
+ buf1 = readByte();
+
+ // INITDEC
+ c = (buf0 ^ 0xff) << 16;
+ byteIn();
+ c <<= 7;
+ ct -= 7;
+ a = 0x80000000;
+}
+
+void JArithmeticDecoder::restart(int dataLenA) {
+ Guint cAdd;
+ GBool prevFF;
+ int k, nBits;
+
+ if (dataLen >= 0) {
+ dataLen = dataLenA;
+ } else if (dataLen == -1) {
+ dataLen = dataLenA;
+ buf1 = readByte();
+ } else {
+ k = (-dataLen - 1) * 8 - ct;
+ dataLen = dataLenA;
+ cAdd = 0;
+ prevFF = gFalse;
+ while (k > 0) {
+ buf0 = readByte();
+ if (prevFF) {
+ cAdd += 0xfe00 - (buf0 << 9);
+ nBits = 7;
+ } else {
+ cAdd += 0xff00 - (buf0 << 8);
+ nBits = 8;
+ }
+ prevFF = buf0 == 0xff;
+ if (k > nBits) {
+ cAdd <<= nBits;
+ k -= nBits;
+ } else {
+ cAdd <<= k;
+ ct = nBits - k;
+ k = 0;
+ }
+ }
+ c += cAdd;
+ buf1 = readByte();
+ }
+}
+
+void JArithmeticDecoder::cleanup() {
+ if (limitStream) {
+ // This saves one extra byte of data from the end of packet i, to
+ // be used in packet i+1. It's not clear from the JPEG 2000 spec
+ // exactly how this should work, but this kludge does seem to fix
+ // decode of some problematic JPEG 2000 streams. It may actually
+ // be necessary to buffer an arbitrary number of bytes (not just
+ // one byte), but I haven't run into that case yet.
+ while (dataLen > 0) {
+ readBuf = -1;
+ readBuf = readByte();
+ }
+ }
+}
+
+int JArithmeticDecoder::decodeBit(Guint context,
+ JArithmeticDecoderStats *stats) {
+ int bit;
+ Guint qe;
+ int iCX, mpsCX;
+
+ iCX = stats->cxTab[context] >> 1;
+ mpsCX = stats->cxTab[context] & 1;
+ qe = qeTab[iCX];
+ a -= qe;
+ if (c < a) {
+ if (a & 0x80000000) {
+ bit = mpsCX;
+ } else {
+ // MPS_EXCHANGE
+ if (a < qe) {
+ bit = 1 - mpsCX;
+ if (switchTab[iCX]) {
+ stats->cxTab[context] = (Guchar)((nlpsTab[iCX] << 1) | (1 - mpsCX));
+ } else {
+ stats->cxTab[context] = (Guchar)((nlpsTab[iCX] << 1) | mpsCX);
+ }
+ } else {
+ bit = mpsCX;
+ stats->cxTab[context] = (Guchar)((nmpsTab[iCX] << 1) | mpsCX);
+ }
+ // RENORMD
+ do {
+ if (ct == 0) {
+ byteIn();
+ }
+ a <<= 1;
+ c <<= 1;
+ --ct;
+ } while (!(a & 0x80000000));
+ }
+ } else {
+ c -= a;
+ // LPS_EXCHANGE
+ if (a < qe) {
+ bit = mpsCX;
+ stats->cxTab[context] = (Guchar)((nmpsTab[iCX] << 1) | mpsCX);
+ } else {
+ bit = 1 - mpsCX;
+ if (switchTab[iCX]) {
+ stats->cxTab[context] = (Guchar)((nlpsTab[iCX] << 1) | (1 - mpsCX));
+ } else {
+ stats->cxTab[context] = (Guchar)((nlpsTab[iCX] << 1) | mpsCX);
+ }
+ }
+ a = qe;
+ // RENORMD
+ do {
+ if (ct == 0) {
+ byteIn();
+ }
+ a <<= 1;
+ c <<= 1;
+ --ct;
+ } while (!(a & 0x80000000));
+ }
+ return bit;
+}
+
+int JArithmeticDecoder::decodeByte(Guint context,
+ JArithmeticDecoderStats *stats) {
+ int byte;
+ int i;
+
+ byte = 0;
+ for (i = 0; i < 8; ++i) {
+ byte = (byte << 1) | decodeBit(context, stats);
+ }
+ return byte;
+}
+
+GBool JArithmeticDecoder::decodeInt(int *x, JArithmeticDecoderStats *stats) {
+ int s;
+ Guint v;
+ int i;
+
+ prev = 1;
+ s = decodeIntBit(stats);
+ if (decodeIntBit(stats)) {
+ if (decodeIntBit(stats)) {
+ if (decodeIntBit(stats)) {
+ if (decodeIntBit(stats)) {
+ if (decodeIntBit(stats)) {
+ v = 0;
+ for (i = 0; i < 32; ++i) {
+ v = (v << 1) | decodeIntBit(stats);
+ }
+ v += 4436;
+ } else {
+ v = 0;
+ for (i = 0; i < 12; ++i) {
+ v = (v << 1) | decodeIntBit(stats);
+ }
+ v += 340;
+ }
+ } else {
+ v = 0;
+ for (i = 0; i < 8; ++i) {
+ v = (v << 1) | decodeIntBit(stats);
+ }
+ v += 84;
+ }
+ } else {
+ v = 0;
+ for (i = 0; i < 6; ++i) {
+ v = (v << 1) | decodeIntBit(stats);
+ }
+ v += 20;
+ }
+ } else {
+ v = decodeIntBit(stats);
+ v = (v << 1) | decodeIntBit(stats);
+ v = (v << 1) | decodeIntBit(stats);
+ v = (v << 1) | decodeIntBit(stats);
+ v += 4;
+ }
+ } else {
+ v = decodeIntBit(stats);
+ v = (v << 1) | decodeIntBit(stats);
+ }
+
+ if (s) {
+ if (v == 0) {
+ return gFalse;
+ }
+ *x = -(int)v;
+ } else {
+ *x = (int)v;
+ }
+ return gTrue;
+}
+
+int JArithmeticDecoder::decodeIntBit(JArithmeticDecoderStats *stats) {
+ int bit;
+
+ bit = decodeBit(prev, stats);
+ if (prev < 0x100) {
+ prev = (prev << 1) | bit;
+ } else {
+ prev = (((prev << 1) | bit) & 0x1ff) | 0x100;
+ }
+ return bit;
+}
+
+Guint JArithmeticDecoder::decodeIAID(Guint codeLen,
+ JArithmeticDecoderStats *stats) {
+ Guint i;
+ int bit;
+
+ prev = 1;
+ for (i = 0; i < codeLen; ++i) {
+ bit = decodeBit(prev, stats);
+ prev = (prev << 1) | bit;
+ }
+ return prev - (1 << codeLen);
+}
+
+void JArithmeticDecoder::byteIn() {
+ if (buf0 == 0xff) {
+ if (buf1 > 0x8f) {
+ if (limitStream) {
+ buf0 = buf1;
+ buf1 = readByte();
+ c = c + 0xff00 - (buf0 << 8);
+ }
+ ct = 8;
+ } else {
+ buf0 = buf1;
+ buf1 = readByte();
+ c = c + 0xfe00 - (buf0 << 9);
+ ct = 7;
+ }
+ } else {
+ buf0 = buf1;
+ buf1 = readByte();
+ c = c + 0xff00 - (buf0 << 8);
+ ct = 8;
+ }
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JArithmeticDecoder.h b/Build/source/libs/xpdf/xpdf-src/xpdf/JArithmeticDecoder.h
new file mode 100644
index 00000000000..c0f773fb107
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JArithmeticDecoder.h
@@ -0,0 +1,114 @@
+//========================================================================
+//
+// JArithmeticDecoder.h
+//
+// Arithmetic decoder used by the JBIG2 and JPEG2000 decoders.
+//
+// Copyright 2002-2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef JARITHMETICDECODER_H
+#define JARITHMETICDECODER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class Stream;
+
+//------------------------------------------------------------------------
+// JArithmeticDecoderStats
+//------------------------------------------------------------------------
+
+class JArithmeticDecoderStats {
+public:
+
+ JArithmeticDecoderStats(int contextSizeA);
+ ~JArithmeticDecoderStats();
+ JArithmeticDecoderStats *copy();
+ void reset();
+ int getContextSize() { return contextSize; }
+ void copyFrom(JArithmeticDecoderStats *stats);
+ void setEntry(Guint cx, int i, int mps);
+
+private:
+
+ Guchar *cxTab; // cxTab[cx] = (i[cx] << 1) + mps[cx]
+ int contextSize;
+
+ friend class JArithmeticDecoder;
+};
+
+//------------------------------------------------------------------------
+// JArithmeticDecoder
+//------------------------------------------------------------------------
+
+class JArithmeticDecoder {
+public:
+
+ JArithmeticDecoder();
+ ~JArithmeticDecoder();
+
+ void setStream(Stream *strA)
+ { str = strA; dataLen = 0; limitStream = gFalse; }
+ void setStream(Stream *strA, int dataLenA)
+ { str = strA; dataLen = dataLenA; limitStream = gTrue; }
+
+ // Start decoding on a new stream. This fills the byte buffers and
+ // runs INITDEC.
+ void start();
+
+ // Restart decoding on an interrupted stream. This refills the
+ // buffers if needed, but does not run INITDEC. (This is used in
+ // JPEG 2000 streams when codeblock data is split across multiple
+ // packets/layers.)
+ void restart(int dataLenA);
+
+ // Read any leftover data in the stream.
+ void cleanup();
+
+ // Decode one bit.
+ int decodeBit(Guint context, JArithmeticDecoderStats *stats);
+
+ // Decode eight bits.
+ int decodeByte(Guint context, JArithmeticDecoderStats *stats);
+
+ // Returns false for OOB, otherwise sets *<x> and returns true.
+ GBool decodeInt(int *x, JArithmeticDecoderStats *stats);
+
+ Guint decodeIAID(Guint codeLen,
+ JArithmeticDecoderStats *stats);
+
+ void resetByteCounter() { nBytesRead = 0; }
+ Guint getByteCounter() { return nBytesRead; }
+
+private:
+
+ Guint readByte();
+ int decodeIntBit(JArithmeticDecoderStats *stats);
+ void byteIn();
+
+ static Guint qeTab[47];
+ static int nmpsTab[47];
+ static int nlpsTab[47];
+ static int switchTab[47];
+
+ Guint buf0, buf1;
+ Guint c, a;
+ int ct;
+
+ Guint prev; // for the integer decoder
+
+ Stream *str;
+ Guint nBytesRead;
+ int dataLen;
+ GBool limitStream;
+ int readBuf;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc
new file mode 100644
index 00000000000..4c4e5183026
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.cc
@@ -0,0 +1,4207 @@
+//========================================================================
+//
+// JBIG2Stream.cc
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <limits.h>
+#include "gmempp.h"
+#include "GList.h"
+#include "Error.h"
+#include "JArithmeticDecoder.h"
+#include "JBIG2Stream.h"
+
+//~ share these tables
+#include "Stream-CCITT.h"
+
+//------------------------------------------------------------------------
+
+static int contextSize[4] = { 16, 13, 10, 10 };
+static int refContextSize[2] = { 13, 10 };
+
+//------------------------------------------------------------------------
+// JBIG2HuffmanTable
+//------------------------------------------------------------------------
+
+#define jbig2HuffmanLOW 0xfffffffd
+#define jbig2HuffmanOOB 0xfffffffe
+#define jbig2HuffmanEOT 0xffffffff
+
+struct JBIG2HuffmanTable {
+ int val;
+ Guint prefixLen;
+ Guint rangeLen; // can also be LOW, OOB, or EOT
+ Guint prefix;
+};
+
+JBIG2HuffmanTable huffTableA[] = {
+ { 0, 1, 4, 0x000 },
+ { 16, 2, 8, 0x002 },
+ { 272, 3, 16, 0x006 },
+ { 65808, 3, 32, 0x007 },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableB[] = {
+ { 0, 1, 0, 0x000 },
+ { 1, 2, 0, 0x002 },
+ { 2, 3, 0, 0x006 },
+ { 3, 4, 3, 0x00e },
+ { 11, 5, 6, 0x01e },
+ { 75, 6, 32, 0x03e },
+ { 0, 6, jbig2HuffmanOOB, 0x03f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableC[] = {
+ { 0, 1, 0, 0x000 },
+ { 1, 2, 0, 0x002 },
+ { 2, 3, 0, 0x006 },
+ { 3, 4, 3, 0x00e },
+ { 11, 5, 6, 0x01e },
+ { 0, 6, jbig2HuffmanOOB, 0x03e },
+ { 75, 7, 32, 0x0fe },
+ { -256, 8, 8, 0x0fe },
+ { -257, 8, jbig2HuffmanLOW, 0x0ff },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableD[] = {
+ { 1, 1, 0, 0x000 },
+ { 2, 2, 0, 0x002 },
+ { 3, 3, 0, 0x006 },
+ { 4, 4, 3, 0x00e },
+ { 12, 5, 6, 0x01e },
+ { 76, 5, 32, 0x01f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableE[] = {
+ { 1, 1, 0, 0x000 },
+ { 2, 2, 0, 0x002 },
+ { 3, 3, 0, 0x006 },
+ { 4, 4, 3, 0x00e },
+ { 12, 5, 6, 0x01e },
+ { 76, 6, 32, 0x03e },
+ { -255, 7, 8, 0x07e },
+ { -256, 7, jbig2HuffmanLOW, 0x07f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableF[] = {
+ { 0, 2, 7, 0x000 },
+ { 128, 3, 7, 0x002 },
+ { 256, 3, 8, 0x003 },
+ { -1024, 4, 9, 0x008 },
+ { -512, 4, 8, 0x009 },
+ { -256, 4, 7, 0x00a },
+ { -32, 4, 5, 0x00b },
+ { 512, 4, 9, 0x00c },
+ { 1024, 4, 10, 0x00d },
+ { -2048, 5, 10, 0x01c },
+ { -128, 5, 6, 0x01d },
+ { -64, 5, 5, 0x01e },
+ { -2049, 6, jbig2HuffmanLOW, 0x03e },
+ { 2048, 6, 32, 0x03f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableG[] = {
+ { -512, 3, 8, 0x000 },
+ { 256, 3, 8, 0x001 },
+ { 512, 3, 9, 0x002 },
+ { 1024, 3, 10, 0x003 },
+ { -1024, 4, 9, 0x008 },
+ { -256, 4, 7, 0x009 },
+ { -32, 4, 5, 0x00a },
+ { 0, 4, 5, 0x00b },
+ { 128, 4, 7, 0x00c },
+ { -128, 5, 6, 0x01a },
+ { -64, 5, 5, 0x01b },
+ { 32, 5, 5, 0x01c },
+ { 64, 5, 6, 0x01d },
+ { -1025, 5, jbig2HuffmanLOW, 0x01e },
+ { 2048, 5, 32, 0x01f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableH[] = {
+ { 0, 2, 1, 0x000 },
+ { 0, 2, jbig2HuffmanOOB, 0x001 },
+ { 4, 3, 4, 0x004 },
+ { -1, 4, 0, 0x00a },
+ { 22, 4, 4, 0x00b },
+ { 38, 4, 5, 0x00c },
+ { 2, 5, 0, 0x01a },
+ { 70, 5, 6, 0x01b },
+ { 134, 5, 7, 0x01c },
+ { 3, 6, 0, 0x03a },
+ { 20, 6, 1, 0x03b },
+ { 262, 6, 7, 0x03c },
+ { 646, 6, 10, 0x03d },
+ { -2, 7, 0, 0x07c },
+ { 390, 7, 8, 0x07d },
+ { -15, 8, 3, 0x0fc },
+ { -5, 8, 1, 0x0fd },
+ { -7, 9, 1, 0x1fc },
+ { -3, 9, 0, 0x1fd },
+ { -16, 9, jbig2HuffmanLOW, 0x1fe },
+ { 1670, 9, 32, 0x1ff },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableI[] = {
+ { 0, 2, jbig2HuffmanOOB, 0x000 },
+ { -1, 3, 1, 0x002 },
+ { 1, 3, 1, 0x003 },
+ { 7, 3, 5, 0x004 },
+ { -3, 4, 1, 0x00a },
+ { 43, 4, 5, 0x00b },
+ { 75, 4, 6, 0x00c },
+ { 3, 5, 1, 0x01a },
+ { 139, 5, 7, 0x01b },
+ { 267, 5, 8, 0x01c },
+ { 5, 6, 1, 0x03a },
+ { 39, 6, 2, 0x03b },
+ { 523, 6, 8, 0x03c },
+ { 1291, 6, 11, 0x03d },
+ { -5, 7, 1, 0x07c },
+ { 779, 7, 9, 0x07d },
+ { -31, 8, 4, 0x0fc },
+ { -11, 8, 2, 0x0fd },
+ { -15, 9, 2, 0x1fc },
+ { -7, 9, 1, 0x1fd },
+ { -32, 9, jbig2HuffmanLOW, 0x1fe },
+ { 3339, 9, 32, 0x1ff },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableJ[] = {
+ { -2, 2, 2, 0x000 },
+ { 6, 2, 6, 0x001 },
+ { 0, 2, jbig2HuffmanOOB, 0x002 },
+ { -3, 5, 0, 0x018 },
+ { 2, 5, 0, 0x019 },
+ { 70, 5, 5, 0x01a },
+ { 3, 6, 0, 0x036 },
+ { 102, 6, 5, 0x037 },
+ { 134, 6, 6, 0x038 },
+ { 198, 6, 7, 0x039 },
+ { 326, 6, 8, 0x03a },
+ { 582, 6, 9, 0x03b },
+ { 1094, 6, 10, 0x03c },
+ { -21, 7, 4, 0x07a },
+ { -4, 7, 0, 0x07b },
+ { 4, 7, 0, 0x07c },
+ { 2118, 7, 11, 0x07d },
+ { -5, 8, 0, 0x0fc },
+ { 5, 8, 0, 0x0fd },
+ { -22, 8, jbig2HuffmanLOW, 0x0fe },
+ { 4166, 8, 32, 0x0ff },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableK[] = {
+ { 1, 1, 0, 0x000 },
+ { 2, 2, 1, 0x002 },
+ { 4, 4, 0, 0x00c },
+ { 5, 4, 1, 0x00d },
+ { 7, 5, 1, 0x01c },
+ { 9, 5, 2, 0x01d },
+ { 13, 6, 2, 0x03c },
+ { 17, 7, 2, 0x07a },
+ { 21, 7, 3, 0x07b },
+ { 29, 7, 4, 0x07c },
+ { 45, 7, 5, 0x07d },
+ { 77, 7, 6, 0x07e },
+ { 141, 7, 32, 0x07f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableL[] = {
+ { 1, 1, 0, 0x000 },
+ { 2, 2, 0, 0x002 },
+ { 3, 3, 1, 0x006 },
+ { 5, 5, 0, 0x01c },
+ { 6, 5, 1, 0x01d },
+ { 8, 6, 1, 0x03c },
+ { 10, 7, 0, 0x07a },
+ { 11, 7, 1, 0x07b },
+ { 13, 7, 2, 0x07c },
+ { 17, 7, 3, 0x07d },
+ { 25, 7, 4, 0x07e },
+ { 41, 8, 5, 0x0fe },
+ { 73, 8, 32, 0x0ff },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableM[] = {
+ { 1, 1, 0, 0x000 },
+ { 2, 3, 0, 0x004 },
+ { 7, 3, 3, 0x005 },
+ { 3, 4, 0, 0x00c },
+ { 5, 4, 1, 0x00d },
+ { 4, 5, 0, 0x01c },
+ { 15, 6, 1, 0x03a },
+ { 17, 6, 2, 0x03b },
+ { 21, 6, 3, 0x03c },
+ { 29, 6, 4, 0x03d },
+ { 45, 6, 5, 0x03e },
+ { 77, 7, 6, 0x07e },
+ { 141, 7, 32, 0x07f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableN[] = {
+ { 0, 1, 0, 0x000 },
+ { -2, 3, 0, 0x004 },
+ { -1, 3, 0, 0x005 },
+ { 1, 3, 0, 0x006 },
+ { 2, 3, 0, 0x007 },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableO[] = {
+ { 0, 1, 0, 0x000 },
+ { -1, 3, 0, 0x004 },
+ { 1, 3, 0, 0x005 },
+ { -2, 4, 0, 0x00c },
+ { 2, 4, 0, 0x00d },
+ { -4, 5, 1, 0x01c },
+ { 3, 5, 1, 0x01d },
+ { -8, 6, 2, 0x03c },
+ { 5, 6, 2, 0x03d },
+ { -24, 7, 4, 0x07c },
+ { 9, 7, 4, 0x07d },
+ { -25, 7, jbig2HuffmanLOW, 0x07e },
+ { 25, 7, 32, 0x07f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+//------------------------------------------------------------------------
+// JBIG2HuffmanDecoder
+//------------------------------------------------------------------------
+
+class JBIG2HuffmanDecoder {
+public:
+
+ JBIG2HuffmanDecoder();
+ ~JBIG2HuffmanDecoder();
+ void setStream(Stream *strA) { str = strA; }
+
+ void reset();
+
+ // Returns false for OOB, otherwise sets *<x> and returns true.
+ GBool decodeInt(int *x, JBIG2HuffmanTable *table);
+
+ Guint readBits(Guint n);
+ Guint readBit();
+
+ // Sort the table by prefix length and assign prefix values.
+ void buildTable(JBIG2HuffmanTable *table, Guint len);
+
+ void resetByteCounter() { byteCounter = 0; }
+ Guint getByteCounter() { return byteCounter; }
+
+private:
+
+ Stream *str;
+ Guint buf;
+ Guint bufLen;
+ Guint byteCounter;
+};
+
+JBIG2HuffmanDecoder::JBIG2HuffmanDecoder() {
+ str = NULL;
+ byteCounter = 0;
+ reset();
+}
+
+JBIG2HuffmanDecoder::~JBIG2HuffmanDecoder() {
+}
+
+void JBIG2HuffmanDecoder::reset() {
+ buf = 0;
+ bufLen = 0;
+}
+
+//~ optimize this
+GBool JBIG2HuffmanDecoder::decodeInt(int *x, JBIG2HuffmanTable *table) {
+ Guint i, len, prefix;
+
+ i = 0;
+ len = 0;
+ prefix = 0;
+ while (table[i].rangeLen != jbig2HuffmanEOT) {
+ while (len < table[i].prefixLen) {
+ prefix = (prefix << 1) | readBit();
+ ++len;
+ }
+ if (prefix == table[i].prefix) {
+ if (table[i].rangeLen == jbig2HuffmanOOB) {
+ return gFalse;
+ }
+ if (table[i].rangeLen == jbig2HuffmanLOW) {
+ *x = table[i].val - readBits(32);
+ } else if (table[i].rangeLen > 0) {
+ *x = table[i].val + readBits(table[i].rangeLen);
+ } else {
+ *x = table[i].val;
+ }
+ return gTrue;
+ }
+ ++i;
+ }
+ return gFalse;
+}
+
+Guint JBIG2HuffmanDecoder::readBits(Guint n) {
+ Guint x, mask, nLeft;
+
+ mask = (n == 32) ? 0xffffffff : ((1 << n) - 1);
+ if (bufLen >= n) {
+ x = (buf >> (bufLen - n)) & mask;
+ bufLen -= n;
+ } else {
+ x = buf & ((1 << bufLen) - 1);
+ nLeft = n - bufLen;
+ bufLen = 0;
+ while (nLeft >= 8) {
+ x = (x << 8) | (str->getChar() & 0xff);
+ ++byteCounter;
+ nLeft -= 8;
+ }
+ if (nLeft > 0) {
+ buf = str->getChar();
+ ++byteCounter;
+ bufLen = 8 - nLeft;
+ x = (x << nLeft) | ((buf >> bufLen) & ((1 << nLeft) - 1));
+ }
+ }
+ return x;
+}
+
+Guint JBIG2HuffmanDecoder::readBit() {
+ if (bufLen == 0) {
+ buf = str->getChar();
+ ++byteCounter;
+ bufLen = 8;
+ }
+ --bufLen;
+ return (buf >> bufLen) & 1;
+}
+
+void JBIG2HuffmanDecoder::buildTable(JBIG2HuffmanTable *table, Guint len) {
+ Guint i, j, k, prefix;
+ JBIG2HuffmanTable tab;
+
+ // stable selection sort:
+ // - entries with prefixLen > 0, in ascending prefixLen order
+ // - entry with prefixLen = 0, rangeLen = EOT
+ // - all other entries with prefixLen = 0
+ // (on entry, table[len] has prefixLen = 0, rangeLen = EOT)
+ for (i = 0; i < len; ++i) {
+ for (j = i; j < len && table[j].prefixLen == 0; ++j) ;
+ if (j == len) {
+ break;
+ }
+ for (k = j + 1; k < len; ++k) {
+ if (table[k].prefixLen > 0 &&
+ table[k].prefixLen < table[j].prefixLen) {
+ j = k;
+ }
+ }
+ if (j != i) {
+ tab = table[j];
+ for (k = j; k > i; --k) {
+ table[k] = table[k - 1];
+ }
+ table[i] = tab;
+ }
+ }
+ table[i] = table[len];
+
+ // assign prefixes
+ if (table[0].rangeLen != jbig2HuffmanEOT) {
+ i = 0;
+ prefix = 0;
+ table[i++].prefix = prefix++;
+ for (; table[i].rangeLen != jbig2HuffmanEOT; ++i) {
+ prefix <<= table[i].prefixLen - table[i-1].prefixLen;
+ table[i].prefix = prefix++;
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// JBIG2MMRDecoder
+//------------------------------------------------------------------------
+
+class JBIG2MMRDecoder {
+public:
+
+ JBIG2MMRDecoder();
+ ~JBIG2MMRDecoder();
+ void setStream(Stream *strA) { str = strA; }
+ void reset();
+ int get2DCode();
+ int getBlackCode();
+ int getWhiteCode();
+ Guint get24Bits();
+ void resetByteCounter() { byteCounter = 0; }
+ Guint getByteCounter() { return byteCounter; }
+ void skipTo(Guint length);
+
+private:
+
+ Stream *str;
+ Guint buf;
+ Guint bufLen;
+ Guint nBytesRead;
+ Guint byteCounter;
+};
+
+JBIG2MMRDecoder::JBIG2MMRDecoder() {
+ str = NULL;
+ byteCounter = 0;
+ reset();
+}
+
+JBIG2MMRDecoder::~JBIG2MMRDecoder() {
+}
+
+void JBIG2MMRDecoder::reset() {
+ buf = 0;
+ bufLen = 0;
+ nBytesRead = 0;
+}
+
+int JBIG2MMRDecoder::get2DCode() {
+ CCITTCode *p;
+
+ if (bufLen == 0) {
+ buf = str->getChar() & 0xff;
+ bufLen = 8;
+ ++nBytesRead;
+ ++byteCounter;
+ p = &twoDimTab1[(buf >> 1) & 0x7f];
+ } else if (bufLen >= 7) {
+ p = &twoDimTab1[(buf >> (bufLen - 7)) & 0x7f];
+ } else {
+ p = &twoDimTab1[(buf << (7 - bufLen)) & 0x7f];
+ if (p->bits < 0 || p->bits > (int)bufLen) {
+ buf = (buf << 8) | (str->getChar() & 0xff);
+ bufLen += 8;
+ ++nBytesRead;
+ ++byteCounter;
+ p = &twoDimTab1[(buf >> (bufLen - 7)) & 0x7f];
+ }
+ }
+ if (p->bits < 0) {
+ error(errSyntaxError, str->getPos(),
+ "Bad two dim code in JBIG2 MMR stream");
+ return EOF;
+ }
+ bufLen -= p->bits;
+ return p->n;
+}
+
+int JBIG2MMRDecoder::getWhiteCode() {
+ CCITTCode *p;
+ Guint code;
+
+ if (bufLen == 0) {
+ buf = str->getChar() & 0xff;
+ bufLen = 8;
+ ++nBytesRead;
+ ++byteCounter;
+ }
+ while (1) {
+ if (bufLen >= 11 && ((buf >> (bufLen - 7)) & 0x7f) == 0) {
+ if (bufLen <= 12) {
+ code = buf << (12 - bufLen);
+ } else {
+ code = buf >> (bufLen - 12);
+ }
+ p = &whiteTab1[code & 0x1f];
+ } else {
+ if (bufLen <= 9) {
+ code = buf << (9 - bufLen);
+ } else {
+ code = buf >> (bufLen - 9);
+ }
+ p = &whiteTab2[code & 0x1ff];
+ }
+ if (p->bits > 0 && p->bits <= (int)bufLen) {
+ bufLen -= p->bits;
+ return p->n;
+ }
+ if (bufLen >= 12) {
+ break;
+ }
+ buf = (buf << 8) | (str->getChar() & 0xff);
+ bufLen += 8;
+ ++nBytesRead;
+ ++byteCounter;
+ }
+ error(errSyntaxError, str->getPos(), "Bad white code in JBIG2 MMR stream");
+ // eat a bit and return a positive number so that the caller doesn't
+ // go into an infinite loop
+ --bufLen;
+ return 1;
+}
+
+int JBIG2MMRDecoder::getBlackCode() {
+ CCITTCode *p;
+ Guint code;
+
+ if (bufLen == 0) {
+ buf = str->getChar() & 0xff;
+ bufLen = 8;
+ ++nBytesRead;
+ ++byteCounter;
+ }
+ while (1) {
+ if (bufLen >= 10 && ((buf >> (bufLen - 6)) & 0x3f) == 0) {
+ if (bufLen <= 13) {
+ code = buf << (13 - bufLen);
+ } else {
+ code = buf >> (bufLen - 13);
+ }
+ p = &blackTab1[code & 0x7f];
+ } else if (bufLen >= 7 && ((buf >> (bufLen - 4)) & 0x0f) == 0 &&
+ ((buf >> (bufLen - 6)) & 0x03) != 0) {
+ if (bufLen <= 12) {
+ code = buf << (12 - bufLen);
+ } else {
+ code = buf >> (bufLen - 12);
+ }
+ p = &blackTab2[(code & 0xff) - 64];
+ } else {
+ if (bufLen <= 6) {
+ code = buf << (6 - bufLen);
+ } else {
+ code = buf >> (bufLen - 6);
+ }
+ p = &blackTab3[code & 0x3f];
+ }
+ if (p->bits > 0 && p->bits <= (int)bufLen) {
+ bufLen -= p->bits;
+ return p->n;
+ }
+ if (bufLen >= 13) {
+ break;
+ }
+ buf = (buf << 8) | (str->getChar() & 0xff);
+ bufLen += 8;
+ ++nBytesRead;
+ ++byteCounter;
+ }
+ error(errSyntaxError, str->getPos(), "Bad black code in JBIG2 MMR stream");
+ // eat a bit and return a positive number so that the caller doesn't
+ // go into an infinite loop
+ --bufLen;
+ return 1;
+}
+
+Guint JBIG2MMRDecoder::get24Bits() {
+ while (bufLen < 24) {
+ buf = (buf << 8) | (str->getChar() & 0xff);
+ bufLen += 8;
+ ++nBytesRead;
+ ++byteCounter;
+ }
+ return (buf >> (bufLen - 24)) & 0xffffff;
+}
+
+void JBIG2MMRDecoder::skipTo(Guint length) {
+ int n;
+
+ n = str->discardChars(length - nBytesRead);
+ nBytesRead += n;
+ byteCounter += n;
+}
+
+//------------------------------------------------------------------------
+// JBIG2Segment
+//------------------------------------------------------------------------
+
+enum JBIG2SegmentType {
+ jbig2SegBitmap,
+ jbig2SegSymbolDict,
+ jbig2SegPatternDict,
+ jbig2SegCodeTable
+};
+
+class JBIG2Segment {
+public:
+
+ JBIG2Segment(Guint segNumA) { segNum = segNumA; }
+ virtual ~JBIG2Segment() {}
+ void setSegNum(Guint segNumA) { segNum = segNumA; }
+ Guint getSegNum() { return segNum; }
+ virtual JBIG2SegmentType getType() = 0;
+
+private:
+
+ Guint segNum;
+};
+
+//------------------------------------------------------------------------
+// JBIG2Bitmap
+//------------------------------------------------------------------------
+
+struct JBIG2BitmapPtr {
+ Guchar *p;
+ int shift;
+ int x;
+};
+
+class JBIG2Bitmap: public JBIG2Segment {
+public:
+
+ JBIG2Bitmap(Guint segNumA, int wA, int hA);
+ virtual ~JBIG2Bitmap();
+ virtual JBIG2SegmentType getType() { return jbig2SegBitmap; }
+ JBIG2Bitmap *copy() { return new JBIG2Bitmap(0, this); }
+ JBIG2Bitmap *getSlice(Guint x, Guint y, Guint wA, Guint hA);
+ void expand(int newH, Guint pixel);
+ void clearToZero();
+ void clearToOne();
+ int getWidth() { return w; }
+ int getHeight() { return h; }
+ int getLineSize() { return line; }
+ int getPixel(int x, int y)
+ { return (x < 0 || x >= w || y < 0 || y >= h) ? 0 :
+ (data[y * line + (x >> 3)] >> (7 - (x & 7))) & 1; }
+ void setPixel(int x, int y)
+ { data[y * line + (x >> 3)] |= (Guchar)(1 << (7 - (x & 7))); }
+ void clearPixel(int x, int y)
+ { data[y * line + (x >> 3)] &= (Guchar)(0x7f7f >> (x & 7)); }
+ void getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr);
+ int nextPixel(JBIG2BitmapPtr *ptr);
+ void duplicateRow(int yDest, int ySrc);
+ void combine(JBIG2Bitmap *bitmap, int x, int y, Guint combOp);
+ Guchar *getDataPtr() { return data; }
+ int getDataSize() { return h * line; }
+
+private:
+
+ JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap);
+
+ int w, h, line;
+ Guchar *data;
+};
+
+JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, int wA, int hA):
+ JBIG2Segment(segNumA)
+{
+ w = wA;
+ h = hA;
+ line = (wA + 7) >> 3;
+ if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) {
+ // force a call to gmalloc(-1), which will throw an exception
+ h = -1;
+ line = 2;
+ }
+ // need to allocate one extra guard byte for use in combine()
+ data = (Guchar *)gmalloc(h * line + 1);
+ data[h * line] = 0;
+}
+
+JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap):
+ JBIG2Segment(segNumA)
+{
+ w = bitmap->w;
+ h = bitmap->h;
+ line = bitmap->line;
+ if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) {
+ // force a call to gmalloc(-1), which will throw an exception
+ h = -1;
+ line = 2;
+ }
+ // need to allocate one extra guard byte for use in combine()
+ data = (Guchar *)gmalloc(h * line + 1);
+ memcpy(data, bitmap->data, h * line);
+ data[h * line] = 0;
+}
+
+JBIG2Bitmap::~JBIG2Bitmap() {
+ gfree(data);
+}
+
+//~ optimize this
+JBIG2Bitmap *JBIG2Bitmap::getSlice(Guint x, Guint y, Guint wA, Guint hA) {
+ JBIG2Bitmap *slice;
+ Guint xx, yy;
+
+ slice = new JBIG2Bitmap(0, wA, hA);
+ slice->clearToZero();
+ for (yy = 0; yy < hA; ++yy) {
+ for (xx = 0; xx < wA; ++xx) {
+ if (getPixel(x + xx, y + yy)) {
+ slice->setPixel(xx, yy);
+ }
+ }
+ }
+ return slice;
+}
+
+void JBIG2Bitmap::expand(int newH, Guint pixel) {
+ if (newH <= h || line <= 0 || newH >= (INT_MAX - 1) / line) {
+ return;
+ }
+ // need to allocate one extra guard byte for use in combine()
+ data = (Guchar *)grealloc(data, newH * line + 1);
+ if (pixel) {
+ memset(data + h * line, 0xff, (newH - h) * line);
+ } else {
+ memset(data + h * line, 0x00, (newH - h) * line);
+ }
+ h = newH;
+ data[h * line] = 0;
+}
+
+void JBIG2Bitmap::clearToZero() {
+ memset(data, 0, h * line);
+}
+
+void JBIG2Bitmap::clearToOne() {
+ memset(data, 0xff, h * line);
+}
+
+inline void JBIG2Bitmap::getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr) {
+ if (y < 0 || y >= h || x >= w) {
+ ptr->p = NULL;
+ ptr->shift = 0; // make gcc happy
+ ptr->x = 0; // make gcc happy
+ } else if (x < 0) {
+ ptr->p = &data[y * line];
+ ptr->shift = 7;
+ ptr->x = x;
+ } else {
+ ptr->p = &data[y * line + (x >> 3)];
+ ptr->shift = 7 - (x & 7);
+ ptr->x = x;
+ }
+}
+
+inline int JBIG2Bitmap::nextPixel(JBIG2BitmapPtr *ptr) {
+ int pix;
+
+ if (!ptr->p) {
+ pix = 0;
+ } else if (ptr->x < 0) {
+ ++ptr->x;
+ pix = 0;
+ } else {
+ pix = (*ptr->p >> ptr->shift) & 1;
+ if (++ptr->x == w) {
+ ptr->p = NULL;
+ } else if (ptr->shift == 0) {
+ ++ptr->p;
+ ptr->shift = 7;
+ } else {
+ --ptr->shift;
+ }
+ }
+ return pix;
+}
+
+void JBIG2Bitmap::duplicateRow(int yDest, int ySrc) {
+ memcpy(data + yDest * line, data + ySrc * line, line);
+}
+
+void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y,
+ Guint combOp) {
+ int x0, x1, y0, y1, xx, yy;
+ Guchar *srcPtr, *destPtr;
+ Guchar dest, src0, src1, src, m1, m2, m3;
+ Guint s1, s2;
+ GBool oneByte;
+
+ // check for the pathological case where y = -2^31
+ if (y < -0x7fffffff) {
+ return;
+ }
+ if (y < 0) {
+ y0 = -y;
+ } else {
+ y0 = 0;
+ }
+ if (y > INT_MAX - bitmap->h) {
+ return;
+ }
+ if (y + bitmap->h > h) {
+ y1 = h - y;
+ } else {
+ y1 = bitmap->h;
+ }
+ if (y0 >= y1) {
+ return;
+ }
+
+ if (x >= 0) {
+ x0 = x & ~7;
+ } else {
+ x0 = 0;
+ }
+ x1 = x + bitmap->w;
+ if (x1 > w) {
+ x1 = w;
+ }
+ if (x0 >= x1) {
+ return;
+ }
+
+ s1 = x & 7;
+ s2 = 8 - s1;
+ m1 = (Guchar)(0xff >> (x1 & 7));
+ m2 = (Guchar)(0xff << (((x1 & 7) == 0) ? 0 : 8 - (x1 & 7)));
+ m3 = (Guchar)((0xff >> s1) & m2);
+
+ oneByte = x0 == ((x1 - 1) & ~7);
+
+ for (yy = y0; yy < y1; ++yy) {
+
+ // one byte per line -- need to mask both left and right side
+ if (oneByte) {
+ if (x >= 0) {
+ destPtr = data + (y + yy) * line + (x >> 3);
+ srcPtr = bitmap->data + yy * bitmap->line;
+ dest = *destPtr;
+ src1 = *srcPtr;
+ switch (combOp) {
+ case 0: // or
+ dest |= (Guchar)((src1 >> s1) & m2);
+ break;
+ case 1: // and
+ dest &= (Guchar)(((0xff00 | src1) >> s1) | m1);
+ break;
+ case 2: // xor
+ dest ^= (Guchar)((src1 >> s1) & m2);
+ break;
+ case 3: // xnor
+ dest ^= (Guchar)(((src1 ^ 0xff) >> s1) & m2);
+ break;
+ case 4: // replace
+ dest = (Guchar)((dest & ~m3) | ((src1 >> s1) & m3));
+ break;
+ }
+ *destPtr = dest;
+ } else {
+ destPtr = data + (y + yy) * line;
+ srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);
+ dest = *destPtr;
+ src1 = *srcPtr;
+ switch (combOp) {
+ case 0: // or
+ dest |= src1 & m2;
+ break;
+ case 1: // and
+ dest &= src1 | m1;
+ break;
+ case 2: // xor
+ dest ^= src1 & m2;
+ break;
+ case 3: // xnor
+ dest ^= (src1 ^ 0xff) & m2;
+ break;
+ case 4: // replace
+ dest = (src1 & m2) | (dest & m1);
+ break;
+ }
+ *destPtr = dest;
+ }
+
+ // multiple bytes per line -- need to mask left side of left-most
+ // byte and right side of right-most byte
+ } else {
+
+ // left-most byte
+ if (x >= 0) {
+ destPtr = data + (y + yy) * line + (x >> 3);
+ srcPtr = bitmap->data + yy * bitmap->line;
+ src1 = *srcPtr++;
+ dest = *destPtr;
+ switch (combOp) {
+ case 0: // or
+ dest |= (Guchar)(src1 >> s1);
+ break;
+ case 1: // and
+ dest &= (Guchar)((0xff00 | src1) >> s1);
+ break;
+ case 2: // xor
+ dest ^= (Guchar)(src1 >> s1);
+ break;
+ case 3: // xnor
+ dest ^= (Guchar)((src1 ^ 0xff) >> s1);
+ break;
+ case 4: // replace
+ dest = (Guchar)((dest & (0xff << s2)) | (src1 >> s1));
+ break;
+ }
+ *destPtr++ = dest;
+ xx = x0 + 8;
+ } else {
+ destPtr = data + (y + yy) * line;
+ srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);
+ src1 = *srcPtr++;
+ xx = x0;
+ }
+
+ // middle bytes
+ for (; xx < x1 - 8; xx += 8) {
+ dest = *destPtr;
+ src0 = src1;
+ src1 = *srcPtr++;
+ src = (Guchar)(((src0 << 8) | src1) >> s1);
+ switch (combOp) {
+ case 0: // or
+ dest |= src;
+ break;
+ case 1: // and
+ dest &= src;
+ break;
+ case 2: // xor
+ dest ^= src;
+ break;
+ case 3: // xnor
+ dest ^= src ^ 0xff;
+ break;
+ case 4: // replace
+ dest = src;
+ break;
+ }
+ *destPtr++ = dest;
+ }
+
+ // right-most byte
+ // note: this last byte (src1) may not actually be used, depending
+ // on the values of s1, m1, and m2 - and in fact, it may be off
+ // the edge of the source bitmap, which means we need to allocate
+ // one extra guard byte at the end of each bitmap
+ dest = *destPtr;
+ src0 = src1;
+ src1 = *srcPtr++;
+ src = (Guchar)(((src0 << 8) | src1) >> s1);
+ switch (combOp) {
+ case 0: // or
+ dest |= src & m2;
+ break;
+ case 1: // and
+ dest &= src | m1;
+ break;
+ case 2: // xor
+ dest ^= src & m2;
+ break;
+ case 3: // xnor
+ dest ^= (src ^ 0xff) & m2;
+ break;
+ case 4: // replace
+ dest = (src & m2) | (dest & m1);
+ break;
+ }
+ *destPtr = dest;
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// JBIG2SymbolDict
+//------------------------------------------------------------------------
+
+class JBIG2SymbolDict: public JBIG2Segment {
+public:
+
+ JBIG2SymbolDict(Guint segNumA, Guint sizeA);
+ virtual ~JBIG2SymbolDict();
+ virtual JBIG2SegmentType getType() { return jbig2SegSymbolDict; }
+ Guint getSize() { return size; }
+ void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
+ JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }
+ void setGenericRegionStats(JArithmeticDecoderStats *stats)
+ { genericRegionStats = stats; }
+ void setRefinementRegionStats(JArithmeticDecoderStats *stats)
+ { refinementRegionStats = stats; }
+ JArithmeticDecoderStats *getGenericRegionStats()
+ { return genericRegionStats; }
+ JArithmeticDecoderStats *getRefinementRegionStats()
+ { return refinementRegionStats; }
+
+private:
+
+ Guint size;
+ JBIG2Bitmap **bitmaps;
+ JArithmeticDecoderStats *genericRegionStats;
+ JArithmeticDecoderStats *refinementRegionStats;
+};
+
+JBIG2SymbolDict::JBIG2SymbolDict(Guint segNumA, Guint sizeA):
+ JBIG2Segment(segNumA)
+{
+ Guint i;
+
+ size = sizeA;
+ bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
+ for (i = 0; i < size; ++i) {
+ bitmaps[i] = NULL;
+ }
+ genericRegionStats = NULL;
+ refinementRegionStats = NULL;
+}
+
+JBIG2SymbolDict::~JBIG2SymbolDict() {
+ Guint i;
+
+ for (i = 0; i < size; ++i) {
+ if (bitmaps[i]) {
+ delete bitmaps[i];
+ }
+ }
+ gfree(bitmaps);
+ if (genericRegionStats) {
+ delete genericRegionStats;
+ }
+ if (refinementRegionStats) {
+ delete refinementRegionStats;
+ }
+}
+
+//------------------------------------------------------------------------
+// JBIG2PatternDict
+//------------------------------------------------------------------------
+
+class JBIG2PatternDict: public JBIG2Segment {
+public:
+
+ JBIG2PatternDict(Guint segNumA, Guint sizeA);
+ virtual ~JBIG2PatternDict();
+ virtual JBIG2SegmentType getType() { return jbig2SegPatternDict; }
+ Guint getSize() { return size; }
+ void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
+ JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }
+
+private:
+
+ Guint size;
+ JBIG2Bitmap **bitmaps;
+};
+
+JBIG2PatternDict::JBIG2PatternDict(Guint segNumA, Guint sizeA):
+ JBIG2Segment(segNumA)
+{
+ size = sizeA;
+ bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
+}
+
+JBIG2PatternDict::~JBIG2PatternDict() {
+ Guint i;
+
+ for (i = 0; i < size; ++i) {
+ delete bitmaps[i];
+ }
+ gfree(bitmaps);
+}
+
+//------------------------------------------------------------------------
+// JBIG2CodeTable
+//------------------------------------------------------------------------
+
+class JBIG2CodeTable: public JBIG2Segment {
+public:
+
+ JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA);
+ virtual ~JBIG2CodeTable();
+ virtual JBIG2SegmentType getType() { return jbig2SegCodeTable; }
+ JBIG2HuffmanTable *getHuffTable() { return table; }
+
+private:
+
+ JBIG2HuffmanTable *table;
+};
+
+JBIG2CodeTable::JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA):
+ JBIG2Segment(segNumA)
+{
+ table = tableA;
+}
+
+JBIG2CodeTable::~JBIG2CodeTable() {
+ gfree(table);
+}
+
+//------------------------------------------------------------------------
+// JBIG2Stream
+//------------------------------------------------------------------------
+
+JBIG2Stream::JBIG2Stream(Stream *strA, Object *globalsStreamA):
+ FilterStream(strA)
+{
+ pageBitmap = NULL;
+
+ arithDecoder = new JArithmeticDecoder();
+ genericRegionStats = new JArithmeticDecoderStats(1 << 1);
+ refinementRegionStats = new JArithmeticDecoderStats(1 << 1);
+ iadhStats = new JArithmeticDecoderStats(1 << 9);
+ iadwStats = new JArithmeticDecoderStats(1 << 9);
+ iaexStats = new JArithmeticDecoderStats(1 << 9);
+ iaaiStats = new JArithmeticDecoderStats(1 << 9);
+ iadtStats = new JArithmeticDecoderStats(1 << 9);
+ iaitStats = new JArithmeticDecoderStats(1 << 9);
+ iafsStats = new JArithmeticDecoderStats(1 << 9);
+ iadsStats = new JArithmeticDecoderStats(1 << 9);
+ iardxStats = new JArithmeticDecoderStats(1 << 9);
+ iardyStats = new JArithmeticDecoderStats(1 << 9);
+ iardwStats = new JArithmeticDecoderStats(1 << 9);
+ iardhStats = new JArithmeticDecoderStats(1 << 9);
+ iariStats = new JArithmeticDecoderStats(1 << 9);
+ iaidStats = new JArithmeticDecoderStats(1 << 1);
+ huffDecoder = new JBIG2HuffmanDecoder();
+ mmrDecoder = new JBIG2MMRDecoder();
+
+ globalsStreamA->copy(&globalsStream);
+ segments = globalSegments = NULL;
+ curStr = NULL;
+ dataPtr = dataEnd = NULL;
+}
+
+JBIG2Stream::~JBIG2Stream() {
+ close();
+ globalsStream.free();
+ delete arithDecoder;
+ delete genericRegionStats;
+ delete refinementRegionStats;
+ delete iadhStats;
+ delete iadwStats;
+ delete iaexStats;
+ delete iaaiStats;
+ delete iadtStats;
+ delete iaitStats;
+ delete iafsStats;
+ delete iadsStats;
+ delete iardxStats;
+ delete iardyStats;
+ delete iardwStats;
+ delete iardhStats;
+ delete iariStats;
+ delete iaidStats;
+ delete huffDecoder;
+ delete mmrDecoder;
+ delete str;
+}
+
+Stream *JBIG2Stream::copy() {
+ return new JBIG2Stream(str->copy(), &globalsStream);
+}
+
+void JBIG2Stream::reset() {
+ GList *t;
+
+ segments = new GList();
+ globalSegments = new GList();
+
+ // read the globals stream
+ if (globalsStream.isStream()) {
+ curStr = globalsStream.getStream();
+ curStr->reset();
+ arithDecoder->setStream(curStr);
+ huffDecoder->setStream(curStr);
+ mmrDecoder->setStream(curStr);
+ readSegments();
+ curStr->close();
+ // swap the newly read segments list into globalSegments
+ t = segments;
+ segments = globalSegments;
+ globalSegments = t;
+ }
+
+ // read the main stream
+ curStr = str;
+ curStr->reset();
+ arithDecoder->setStream(curStr);
+ huffDecoder->setStream(curStr);
+ mmrDecoder->setStream(curStr);
+ readSegments();
+
+ if (pageBitmap) {
+ dataPtr = pageBitmap->getDataPtr();
+ dataEnd = dataPtr + pageBitmap->getDataSize();
+ } else {
+ dataPtr = dataEnd = NULL;
+ }
+}
+
+void JBIG2Stream::close() {
+ if (pageBitmap) {
+ delete pageBitmap;
+ pageBitmap = NULL;
+ }
+ if (segments) {
+ deleteGList(segments, JBIG2Segment);
+ segments = NULL;
+ }
+ if (globalSegments) {
+ deleteGList(globalSegments, JBIG2Segment);
+ globalSegments = NULL;
+ }
+ dataPtr = dataEnd = NULL;
+ FilterStream::close();
+}
+
+int JBIG2Stream::getChar() {
+ if (dataPtr && dataPtr < dataEnd) {
+ return (*dataPtr++ ^ 0xff) & 0xff;
+ }
+ return EOF;
+}
+
+int JBIG2Stream::lookChar() {
+ if (dataPtr && dataPtr < dataEnd) {
+ return (*dataPtr ^ 0xff) & 0xff;
+ }
+ return EOF;
+}
+
+int JBIG2Stream::getBlock(char *blk, int size) {
+ int n, i;
+
+ if (size <= 0) {
+ return 0;
+ }
+ if (dataEnd - dataPtr < size) {
+ n = (int)(dataEnd - dataPtr);
+ } else {
+ n = size;
+ }
+ for (i = 0; i < n; ++i) {
+ blk[i] = *dataPtr++ ^ 0xff;
+ }
+ return n;
+}
+
+GString *JBIG2Stream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ return NULL;
+}
+
+GBool JBIG2Stream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+void JBIG2Stream::readSegments() {
+ Guint segNum, segFlags, segType, page, segLength;
+ Guint refFlags, nRefSegs;
+ Guint *refSegs;
+ int c1, c2, c3;
+ Guint i;
+
+ done = gFalse;
+ while (!done && readULong(&segNum)) {
+
+ // segment header flags
+ if (!readUByte(&segFlags)) {
+ goto eofError1;
+ }
+ segType = segFlags & 0x3f;
+
+ // referred-to segment count and retention flags
+ if (!readUByte(&refFlags)) {
+ goto eofError1;
+ }
+ nRefSegs = refFlags >> 5;
+ if (nRefSegs == 7) {
+ if ((c1 = curStr->getChar()) == EOF ||
+ (c2 = curStr->getChar()) == EOF ||
+ (c3 = curStr->getChar()) == EOF) {
+ goto eofError1;
+ }
+ refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3;
+ nRefSegs = refFlags & 0x1fffffff;
+ i = (nRefSegs + 9) >> 3;
+ if (curStr->discardChars(i) != i) {
+ goto eofError1;
+ }
+ }
+
+ // referred-to segment numbers
+ refSegs = (Guint *)gmallocn(nRefSegs, sizeof(Guint));
+ if (segNum <= 256) {
+ for (i = 0; i < nRefSegs; ++i) {
+ if (!readUByte(&refSegs[i])) {
+ goto eofError2;
+ }
+ }
+ } else if (segNum <= 65536) {
+ for (i = 0; i < nRefSegs; ++i) {
+ if (!readUWord(&refSegs[i])) {
+ goto eofError2;
+ }
+ }
+ } else {
+ for (i = 0; i < nRefSegs; ++i) {
+ if (!readULong(&refSegs[i])) {
+ goto eofError2;
+ }
+ }
+ }
+
+ // segment page association
+ if (segFlags & 0x40) {
+ if (!readULong(&page)) {
+ goto eofError2;
+ }
+ } else {
+ if (!readUByte(&page)) {
+ goto eofError2;
+ }
+ }
+
+ // segment data length
+ if (!readULong(&segLength)) {
+ goto eofError2;
+ }
+
+ // check for missing page information segment
+ if (!pageBitmap && ((segType >= 4 && segType <= 7) ||
+ (segType >= 20 && segType <= 43))) {
+ error(errSyntaxError, getPos(),
+ "First JBIG2 segment associated with a page must be a page information segment");
+ goto syntaxError;
+ }
+
+ // read the segment data
+ arithDecoder->resetByteCounter();
+ huffDecoder->resetByteCounter();
+ mmrDecoder->resetByteCounter();
+ byteCounter = 0;
+ switch (segType) {
+ case 0:
+ if (!readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs)) {
+ goto syntaxError;
+ }
+ break;
+ case 4:
+ readTextRegionSeg(segNum, gFalse, gFalse, segLength, refSegs, nRefSegs);
+ break;
+ case 6:
+ readTextRegionSeg(segNum, gTrue, gFalse, segLength, refSegs, nRefSegs);
+ break;
+ case 7:
+ readTextRegionSeg(segNum, gTrue, gTrue, segLength, refSegs, nRefSegs);
+ break;
+ case 16:
+ readPatternDictSeg(segNum, segLength);
+ break;
+ case 20:
+ readHalftoneRegionSeg(segNum, gFalse, gFalse, segLength,
+ refSegs, nRefSegs);
+ break;
+ case 22:
+ readHalftoneRegionSeg(segNum, gTrue, gFalse, segLength,
+ refSegs, nRefSegs);
+ break;
+ case 23:
+ readHalftoneRegionSeg(segNum, gTrue, gTrue, segLength,
+ refSegs, nRefSegs);
+ break;
+ case 36:
+ readGenericRegionSeg(segNum, gFalse, gFalse, segLength);
+ break;
+ case 38:
+ readGenericRegionSeg(segNum, gTrue, gFalse, segLength);
+ break;
+ case 39:
+ readGenericRegionSeg(segNum, gTrue, gTrue, segLength);
+ break;
+ case 40:
+ readGenericRefinementRegionSeg(segNum, gFalse, gFalse, segLength,
+ refSegs, nRefSegs);
+ break;
+ case 42:
+ readGenericRefinementRegionSeg(segNum, gTrue, gFalse, segLength,
+ refSegs, nRefSegs);
+ break;
+ case 43:
+ readGenericRefinementRegionSeg(segNum, gTrue, gTrue, segLength,
+ refSegs, nRefSegs);
+ break;
+ case 48:
+ readPageInfoSeg(segLength);
+ break;
+ case 50:
+ readEndOfStripeSeg(segLength);
+ break;
+ case 51:
+ // end of file segment
+ done = gTrue;
+ break;
+ case 52:
+ readProfilesSeg(segLength);
+ break;
+ case 53:
+ readCodeTableSeg(segNum, segLength);
+ break;
+ case 62:
+ readExtensionSeg(segLength);
+ break;
+ default:
+ error(errSyntaxError, getPos(), "Unknown segment type in JBIG2 stream");
+ if (curStr->discardChars(segLength) != segLength) {
+ goto eofError2;
+ }
+ break;
+ }
+
+ // skip any unused data at the end of the segment
+ // (except for immediate generic region segments which have
+ // 0xffffffff = unspecified length)
+ if (!(segType == 38 && segLength == 0xffffffff)) {
+ byteCounter += arithDecoder->getByteCounter();
+ byteCounter += huffDecoder->getByteCounter();
+ byteCounter += mmrDecoder->getByteCounter();
+ // do a sanity check on byteCounter vs segLength -- if there is
+ // a problem, abort the decode
+ if (byteCounter > segLength ||
+ segLength - byteCounter > 65536) {
+ error(errSyntaxError, getPos(),
+ "Invalid segment length in JBIG2 stream");
+ gfree(refSegs);
+ break;
+ }
+ byteCounter += curStr->discardChars(segLength - byteCounter);
+ }
+
+ gfree(refSegs);
+ }
+
+ return;
+
+ syntaxError:
+ gfree(refSegs);
+ return;
+
+ eofError2:
+ gfree(refSegs);
+ eofError1:
+ error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
+ Guint *refSegs, Guint nRefSegs) {
+ JBIG2SymbolDict *symbolDict;
+ JBIG2HuffmanTable *huffDHTable, *huffDWTable;
+ JBIG2HuffmanTable *huffBMSizeTable, *huffAggInstTable;
+ JBIG2Segment *seg;
+ GList *codeTables;
+ JBIG2SymbolDict *inputSymbolDict;
+ Guint flags, sdTemplate, sdrTemplate, huff, refAgg;
+ Guint huffDH, huffDW, huffBMSize, huffAggInst;
+ Guint contextUsed, contextRetained;
+ int sdATX[4], sdATY[4], sdrATX[2], sdrATY[2];
+ Guint numExSyms, numNewSyms, numInputSyms, symCodeLen;
+ JBIG2Bitmap **bitmaps;
+ JBIG2Bitmap *collBitmap, *refBitmap;
+ Guint *symWidths;
+ Guint symHeight, symWidth, totalWidth, x, symID;
+ int dh, dw, refAggNum, refDX, refDY, bmSize;
+ GBool ex;
+ int run, prevRun, cnt;
+ Guint i, j, k;
+
+ symWidths = NULL;
+
+ // symbol dictionary flags
+ if (!readUWord(&flags)) {
+ goto eofError;
+ }
+ sdTemplate = (flags >> 10) & 3;
+ sdrTemplate = (flags >> 12) & 1;
+ huff = flags & 1;
+ refAgg = (flags >> 1) & 1;
+ huffDH = (flags >> 2) & 3;
+ huffDW = (flags >> 4) & 3;
+ huffBMSize = (flags >> 6) & 1;
+ huffAggInst = (flags >> 7) & 1;
+ contextUsed = (flags >> 8) & 1;
+ contextRetained = (flags >> 9) & 1;
+
+ // symbol dictionary AT flags
+ if (!huff) {
+ if (sdTemplate == 0) {
+ if (!readByte(&sdATX[0]) ||
+ !readByte(&sdATY[0]) ||
+ !readByte(&sdATX[1]) ||
+ !readByte(&sdATY[1]) ||
+ !readByte(&sdATX[2]) ||
+ !readByte(&sdATY[2]) ||
+ !readByte(&sdATX[3]) ||
+ !readByte(&sdATY[3])) {
+ goto eofError;
+ }
+ } else {
+ if (!readByte(&sdATX[0]) ||
+ !readByte(&sdATY[0])) {
+ goto eofError;
+ }
+ }
+ }
+
+ // symbol dictionary refinement AT flags
+ if (refAgg && !sdrTemplate) {
+ if (!readByte(&sdrATX[0]) ||
+ !readByte(&sdrATY[0]) ||
+ !readByte(&sdrATX[1]) ||
+ !readByte(&sdrATY[1])) {
+ goto eofError;
+ }
+ }
+
+ // SDNUMEXSYMS and SDNUMNEWSYMS
+ if (!readULong(&numExSyms) || !readULong(&numNewSyms)) {
+ goto eofError;
+ }
+
+ // get referenced segments: input symbol dictionaries and code tables
+ codeTables = new GList();
+ numInputSyms = 0;
+ for (i = 0; i < nRefSegs; ++i) {
+ if ((seg = findSegment(refSegs[i]))) {
+ if (seg->getType() == jbig2SegSymbolDict) {
+ j = ((JBIG2SymbolDict *)seg)->getSize();
+ if (j > INT_MAX || numInputSyms > INT_MAX - j) {
+ error(errSyntaxError, getPos(),
+ "Too many input symbols in JBIG2 symbol dictionary");
+ delete codeTables;
+ goto eofError;
+ }
+ numInputSyms += j;
+ } else if (seg->getType() == jbig2SegCodeTable) {
+ codeTables->append(seg);
+ }
+ }
+ }
+ if (numNewSyms > INT_MAX || numInputSyms > INT_MAX - numNewSyms) {
+ error(errSyntaxError, getPos(),
+ "Too many input symbols in JBIG2 symbol dictionary");
+ delete codeTables;
+ goto eofError;
+ }
+
+ // compute symbol code length
+ i = numInputSyms + numNewSyms;
+ if (i <= 1) {
+ symCodeLen = huff ? 1 : 0;
+ } else {
+ --i;
+ symCodeLen = 0;
+ // i = floor((numSyms-1) / 2^symCodeLen)
+ while (i > 0) {
+ ++symCodeLen;
+ i >>= 1;
+ }
+ }
+
+ // get the input symbol bitmaps
+ bitmaps = (JBIG2Bitmap **)gmallocn(numInputSyms + numNewSyms,
+ sizeof(JBIG2Bitmap *));
+ for (i = 0; i < numInputSyms + numNewSyms; ++i) {
+ bitmaps[i] = NULL;
+ }
+ k = 0;
+ inputSymbolDict = NULL;
+ for (i = 0; i < nRefSegs; ++i) {
+ if ((seg = findSegment(refSegs[i]))) {
+ if (seg->getType() == jbig2SegSymbolDict) {
+ inputSymbolDict = (JBIG2SymbolDict *)seg;
+ for (j = 0; j < inputSymbolDict->getSize(); ++j) {
+ bitmaps[k++] = inputSymbolDict->getBitmap(j);
+ }
+ }
+ }
+ }
+
+ // get the Huffman tables
+ huffDHTable = huffDWTable = NULL; // make gcc happy
+ huffBMSizeTable = huffAggInstTable = NULL; // make gcc happy
+ i = 0;
+ if (huff) {
+ if (huffDH == 0) {
+ huffDHTable = huffTableD;
+ } else if (huffDH == 1) {
+ huffDHTable = huffTableE;
+ } else {
+ if (i >= (Guint)codeTables->getLength()) {
+ goto codeTableError;
+ }
+ huffDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffDW == 0) {
+ huffDWTable = huffTableB;
+ } else if (huffDW == 1) {
+ huffDWTable = huffTableC;
+ } else {
+ if (i >= (Guint)codeTables->getLength()) {
+ goto codeTableError;
+ }
+ huffDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffBMSize == 0) {
+ huffBMSizeTable = huffTableA;
+ } else {
+ if (i >= (Guint)codeTables->getLength()) {
+ goto codeTableError;
+ }
+ huffBMSizeTable =
+ ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffAggInst == 0) {
+ huffAggInstTable = huffTableA;
+ } else {
+ if (i >= (Guint)codeTables->getLength()) {
+ goto codeTableError;
+ }
+ huffAggInstTable =
+ ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ }
+ delete codeTables;
+
+ // set up the Huffman decoder
+ if (huff) {
+ huffDecoder->reset();
+
+ // set up the arithmetic decoder
+ } else {
+ if (contextUsed && inputSymbolDict) {
+ resetGenericStats(sdTemplate, inputSymbolDict->getGenericRegionStats());
+ } else {
+ resetGenericStats(sdTemplate, NULL);
+ }
+ resetIntStats(symCodeLen);
+ arithDecoder->start();
+ }
+
+ // set up the arithmetic decoder for refinement/aggregation
+ if (refAgg) {
+ if (contextUsed && inputSymbolDict) {
+ resetRefinementStats(sdrTemplate,
+ inputSymbolDict->getRefinementRegionStats());
+ } else {
+ resetRefinementStats(sdrTemplate, NULL);
+ }
+ }
+
+ // allocate symbol widths storage
+ if (huff && !refAgg) {
+ symWidths = (Guint *)gmallocn(numNewSyms, sizeof(Guint));
+ }
+
+ symHeight = 0;
+ i = 0;
+ while (i < numNewSyms) {
+
+ // read the height class delta height
+ if (huff) {
+ huffDecoder->decodeInt(&dh, huffDHTable);
+ } else {
+ arithDecoder->decodeInt(&dh, iadhStats);
+ }
+ if ((dh <= 0 && (Guint)-dh >= symHeight) ||
+ (dh > 0 && (Guint)dh > UINT_MAX - symHeight)) {
+ error(errSyntaxError, getPos(),
+ "Bad 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) {
+
+ // read the delta width
+ if (huff) {
+ if (!huffDecoder->decodeInt(&dw, huffDWTable)) {
+ break;
+ }
+ } else {
+ if (!arithDecoder->decodeInt(&dw, iadwStats)) {
+ break;
+ }
+ }
+ if ((dw <= 0 && (Guint)-dw >= symWidth) ||
+ (dw > 0 && (Guint)dw > UINT_MAX - symWidth)) {
+ error(errSyntaxError, getPos(),
+ "Bad delta-height value in JBIG2 symbol dictionary");
+ goto syntaxError;
+ }
+ symWidth += dw;
+ if (i >= numNewSyms) {
+ error(errSyntaxError, getPos(),
+ "Too many symbols in JBIG2 symbol dictionary");
+ 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;
+ totalWidth += symWidth;
+
+ // refinement/aggregate coding
+ } else if (refAgg) {
+ if (huff) {
+ if (!huffDecoder->decodeInt(&refAggNum, huffAggInstTable)) {
+ break;
+ }
+ } else {
+ if (!arithDecoder->decodeInt(&refAggNum, iaaiStats)) {
+ break;
+ }
+ }
+#if 0 //~ This special case was added about a year before the final draft
+ //~ of the JBIG2 spec was released. I have encountered some old
+ //~ JBIG2 images that predate it.
+ if (0) {
+#else
+ if (refAggNum == 1) {
+#endif
+ if (huff) {
+ symID = huffDecoder->readBits(symCodeLen);
+ huffDecoder->decodeInt(&refDX, huffTableO);
+ huffDecoder->decodeInt(&refDY, huffTableO);
+ huffDecoder->decodeInt(&bmSize, huffTableA);
+ huffDecoder->reset();
+ arithDecoder->start();
+ } else {
+ symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
+ arithDecoder->decodeInt(&refDX, iardxStats);
+ arithDecoder->decodeInt(&refDY, iardyStats);
+ }
+ if (symID >= numInputSyms + i) {
+ error(errSyntaxError, getPos(),
+ "Invalid symbol ID in JBIG2 symbol dictionary");
+ goto syntaxError;
+ }
+ refBitmap = bitmaps[symID];
+ bitmaps[numInputSyms + i] =
+ readGenericRefinementRegion(symWidth, symHeight,
+ sdrTemplate, gFalse,
+ refBitmap, refDX, refDY,
+ sdrATX, sdrATY);
+ //~ do we need to use the bmSize value here (in Huffman mode)?
+ } else {
+ bitmaps[numInputSyms + i] =
+ readTextRegion(huff, gTrue, symWidth, symHeight,
+ refAggNum, 0, numInputSyms + i, NULL,
+ symCodeLen, bitmaps, 0, 0, 0, 1, 0,
+ huffTableF, huffTableH, huffTableK, huffTableO,
+ huffTableO, huffTableO, huffTableO, huffTableA,
+ sdrTemplate, sdrATX, sdrATY);
+ }
+
+ // non-ref/agg coding
+ } else {
+ bitmaps[numInputSyms + i] =
+ readGenericBitmap(gFalse, symWidth, symHeight,
+ sdTemplate, gFalse, gFalse, NULL,
+ sdATX, sdATY, 0);
+ }
+
+ ++i;
+ }
+
+ // read the collective bitmap
+ if (huff && !refAgg) {
+ if (totalWidth == 0) {
+ error(errSyntaxError, getPos(),
+ "Invalid height class width in JBIG2 symbol dictionary");
+ goto syntaxError;
+ }
+ huffDecoder->decodeInt(&bmSize, huffBMSizeTable);
+ huffDecoder->reset();
+ if (bmSize == 0) {
+ collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight);
+ bmSize = symHeight * ((totalWidth + 7) >> 3);
+ byteCounter += curStr->getBlock((char *)collBitmap->getDataPtr(),
+ bmSize);
+ } else {
+ collBitmap = readGenericBitmap(gTrue, totalWidth, symHeight,
+ 0, gFalse, gFalse, NULL, NULL, NULL,
+ bmSize);
+ }
+ x = 0;
+ for (; j < i; ++j) {
+ bitmaps[numInputSyms + j] =
+ collBitmap->getSlice(x, 0, symWidths[j], symHeight);
+ x += symWidths[j];
+ }
+ delete collBitmap;
+ }
+ }
+
+ // create the symbol dict object
+ symbolDict = new JBIG2SymbolDict(segNum, numExSyms);
+
+ // exported symbol list
+ i = j = 0;
+ ex = gFalse;
+ prevRun = 1;
+ while (i < numInputSyms + numNewSyms) {
+ if (huff) {
+ huffDecoder->decodeInt(&run, huffTableA);
+ } else {
+ arithDecoder->decodeInt(&run, iaexStats);
+ }
+ if (run == 0 && prevRun == 0) {
+ // this avoids infinite loops with damaged files (consecutive
+ // zero runs are never useful)
+ error(errSyntaxError, getPos(),
+ "Invalid exported symbol list in JBIG2 symbol dictionary");
+ delete symbolDict;
+ goto syntaxError;
+ }
+ if (i + run > numInputSyms + numNewSyms ||
+ (ex && j + run > numExSyms)) {
+ error(errSyntaxError, getPos(),
+ "Too many exported symbols in JBIG2 symbol dictionary");
+ delete symbolDict;
+ goto syntaxError;
+ }
+ if (ex) {
+ for (cnt = 0; cnt < run; ++cnt) {
+ symbolDict->setBitmap(j++, bitmaps[i++]->copy());
+ }
+ } else {
+ i += run;
+ }
+ ex = !ex;
+ prevRun = run;
+ }
+ if (j != numExSyms) {
+ error(errSyntaxError, getPos(), "Too few symbols in JBIG2 symbol dictionary");
+ delete symbolDict;
+ goto syntaxError;
+ }
+
+ for (i = 0; i < numNewSyms; ++i) {
+ delete bitmaps[numInputSyms + i];
+ }
+ gfree(bitmaps);
+ if (symWidths) {
+ gfree(symWidths);
+ }
+
+ // save the arithmetic decoder stats
+ if (!huff && contextRetained) {
+ symbolDict->setGenericRegionStats(genericRegionStats->copy());
+ if (refAgg) {
+ symbolDict->setRefinementRegionStats(refinementRegionStats->copy());
+ }
+ }
+
+ // store the new symbol dict
+ segments->append(symbolDict);
+
+ return gTrue;
+
+ codeTableError:
+ error(errSyntaxError, getPos(), "Missing code table in JBIG2 symbol dictionary");
+ delete codeTables;
+
+ syntaxError:
+ for (i = 0; i < numNewSyms; ++i) {
+ if (bitmaps[numInputSyms + i]) {
+ delete bitmaps[numInputSyms + i];
+ }
+ }
+ gfree(bitmaps);
+ if (symWidths) {
+ gfree(symWidths);
+ }
+ return gFalse;
+
+ eofError:
+ error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream");
+ return gFalse;
+}
+
+void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length,
+ Guint *refSegs, Guint nRefSegs) {
+ JBIG2Bitmap *bitmap;
+ JBIG2HuffmanTable runLengthTab[36];
+ JBIG2HuffmanTable *symCodeTab;
+ JBIG2HuffmanTable *huffFSTable, *huffDSTable, *huffDTTable;
+ JBIG2HuffmanTable *huffRDWTable, *huffRDHTable;
+ JBIG2HuffmanTable *huffRDXTable, *huffRDYTable, *huffRSizeTable;
+ JBIG2Segment *seg;
+ GList *codeTables;
+ JBIG2SymbolDict *symbolDict;
+ JBIG2Bitmap **syms;
+ Guint w, h, x, y, segInfoFlags, extCombOp;
+ Guint flags, huff, refine, logStrips, refCorner, transposed;
+ Guint combOp, defPixel, templ;
+ int sOffset;
+ Guint huffFlags, huffFS, huffDS, huffDT;
+ Guint huffRDW, huffRDH, huffRDX, huffRDY, huffRSize;
+ Guint numInstances, numSyms, symCodeLen;
+ int atx[2], aty[2];
+ Guint i, k, kk;
+ int j;
+
+ // region segment info field
+ if (!readULong(&w) || !readULong(&h) ||
+ !readULong(&x) || !readULong(&y) ||
+ !readUByte(&segInfoFlags)) {
+ goto eofError;
+ }
+ if (w == 0 || h == 0) {
+ 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
+ if (!readUWord(&flags)) {
+ goto eofError;
+ }
+ huff = flags & 1;
+ refine = (flags >> 1) & 1;
+ logStrips = (flags >> 2) & 3;
+ refCorner = (flags >> 4) & 3;
+ transposed = (flags >> 6) & 1;
+ combOp = (flags >> 7) & 3;
+ defPixel = (flags >> 9) & 1;
+ sOffset = (flags >> 10) & 0x1f;
+ if (sOffset & 0x10) {
+ sOffset |= -1 - 0x0f;
+ }
+ templ = (flags >> 15) & 1;
+ huffFS = huffDS = huffDT = 0; // make gcc happy
+ huffRDW = huffRDH = huffRDX = huffRDY = huffRSize = 0; // make gcc happy
+ if (huff) {
+ if (!readUWord(&huffFlags)) {
+ goto eofError;
+ }
+ huffFS = huffFlags & 3;
+ huffDS = (huffFlags >> 2) & 3;
+ huffDT = (huffFlags >> 4) & 3;
+ huffRDW = (huffFlags >> 6) & 3;
+ huffRDH = (huffFlags >> 8) & 3;
+ huffRDX = (huffFlags >> 10) & 3;
+ huffRDY = (huffFlags >> 12) & 3;
+ huffRSize = (huffFlags >> 14) & 1;
+ }
+ if (refine && templ == 0) {
+ if (!readByte(&atx[0]) || !readByte(&aty[0]) ||
+ !readByte(&atx[1]) || !readByte(&aty[1])) {
+ goto eofError;
+ }
+ }
+ if (!readULong(&numInstances)) {
+ goto eofError;
+ }
+
+ // get symbol dictionaries and tables
+ codeTables = new GList();
+ numSyms = 0;
+ for (i = 0; i < nRefSegs; ++i) {
+ if ((seg = findSegment(refSegs[i]))) {
+ if (seg->getType() == jbig2SegSymbolDict) {
+ numSyms += ((JBIG2SymbolDict *)seg)->getSize();
+ } else if (seg->getType() == jbig2SegCodeTable) {
+ codeTables->append(seg);
+ }
+ } else {
+ error(errSyntaxError, getPos(),
+ "Invalid segment reference in JBIG2 text region");
+ delete codeTables;
+ return;
+ }
+ }
+ i = numSyms;
+ if (i <= 1) {
+ symCodeLen = huff ? 1 : 0;
+ } else {
+ --i;
+ symCodeLen = 0;
+ // i = floor((numSyms-1) / 2^symCodeLen)
+ while (i > 0) {
+ ++symCodeLen;
+ i >>= 1;
+ }
+ }
+
+ // get the symbol bitmaps
+ syms = (JBIG2Bitmap **)gmallocn(numSyms, sizeof(JBIG2Bitmap *));
+ kk = 0;
+ for (i = 0; i < nRefSegs; ++i) {
+ if ((seg = findSegment(refSegs[i]))) {
+ if (seg->getType() == jbig2SegSymbolDict) {
+ symbolDict = (JBIG2SymbolDict *)seg;
+ for (k = 0; k < symbolDict->getSize(); ++k) {
+ syms[kk++] = symbolDict->getBitmap(k);
+ }
+ }
+ }
+ }
+
+ // get the Huffman tables
+ huffFSTable = huffDSTable = huffDTTable = NULL; // make gcc happy
+ huffRDWTable = huffRDHTable = NULL; // make gcc happy
+ huffRDXTable = huffRDYTable = huffRSizeTable = NULL; // make gcc happy
+ i = 0;
+ if (huff) {
+ if (huffFS == 0) {
+ huffFSTable = huffTableF;
+ } else if (huffFS == 1) {
+ huffFSTable = huffTableG;
+ } else {
+ if (i >= (Guint)codeTables->getLength()) {
+ goto codeTableError;
+ }
+ huffFSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffDS == 0) {
+ huffDSTable = huffTableH;
+ } else if (huffDS == 1) {
+ huffDSTable = huffTableI;
+ } else if (huffDS == 2) {
+ huffDSTable = huffTableJ;
+ } else {
+ if (i >= (Guint)codeTables->getLength()) {
+ goto codeTableError;
+ }
+ huffDSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffDT == 0) {
+ huffDTTable = huffTableK;
+ } else if (huffDT == 1) {
+ huffDTTable = huffTableL;
+ } else if (huffDT == 2) {
+ huffDTTable = huffTableM;
+ } else {
+ if (i >= (Guint)codeTables->getLength()) {
+ goto codeTableError;
+ }
+ huffDTTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffRDW == 0) {
+ huffRDWTable = huffTableN;
+ } else if (huffRDW == 1) {
+ huffRDWTable = huffTableO;
+ } else {
+ if (i >= (Guint)codeTables->getLength()) {
+ goto codeTableError;
+ }
+ huffRDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffRDH == 0) {
+ huffRDHTable = huffTableN;
+ } else if (huffRDH == 1) {
+ huffRDHTable = huffTableO;
+ } else {
+ if (i >= (Guint)codeTables->getLength()) {
+ goto codeTableError;
+ }
+ huffRDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffRDX == 0) {
+ huffRDXTable = huffTableN;
+ } else if (huffRDX == 1) {
+ huffRDXTable = huffTableO;
+ } else {
+ if (i >= (Guint)codeTables->getLength()) {
+ goto codeTableError;
+ }
+ huffRDXTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffRDY == 0) {
+ huffRDYTable = huffTableN;
+ } else if (huffRDY == 1) {
+ huffRDYTable = huffTableO;
+ } else {
+ if (i >= (Guint)codeTables->getLength()) {
+ goto codeTableError;
+ }
+ huffRDYTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffRSize == 0) {
+ huffRSizeTable = huffTableA;
+ } else {
+ if (i >= (Guint)codeTables->getLength()) {
+ goto codeTableError;
+ }
+ huffRSizeTable =
+ ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ }
+ delete codeTables;
+
+ // symbol ID Huffman decoding table
+ if (huff) {
+ huffDecoder->reset();
+ for (i = 0; i < 32; ++i) {
+ runLengthTab[i].val = i;
+ runLengthTab[i].prefixLen = huffDecoder->readBits(4);
+ runLengthTab[i].rangeLen = 0;
+ runLengthTab[i].prefix = 0;
+ }
+ runLengthTab[32].val = 0x103;
+ runLengthTab[32].prefixLen = huffDecoder->readBits(4);
+ runLengthTab[32].rangeLen = 2;
+ runLengthTab[32].prefix = 0;
+ runLengthTab[33].val = 0x203;
+ runLengthTab[33].prefixLen = huffDecoder->readBits(4);
+ runLengthTab[33].rangeLen = 3;
+ runLengthTab[33].prefix = 0;
+ runLengthTab[34].val = 0x20b;
+ runLengthTab[34].prefixLen = huffDecoder->readBits(4);
+ runLengthTab[34].rangeLen = 7;
+ runLengthTab[34].prefix = 0;
+ runLengthTab[35].prefixLen = 0;
+ runLengthTab[35].rangeLen = jbig2HuffmanEOT;
+ runLengthTab[35].prefix = 0;
+ huffDecoder->buildTable(runLengthTab, 35);
+ symCodeTab = (JBIG2HuffmanTable *)gmallocn(numSyms + 1,
+ sizeof(JBIG2HuffmanTable));
+ for (i = 0; i < numSyms; ++i) {
+ symCodeTab[i].val = i;
+ symCodeTab[i].rangeLen = 0;
+ }
+ i = 0;
+ while (i < numSyms) {
+ huffDecoder->decodeInt(&j, runLengthTab);
+ if (j > 0x200) {
+ for (j -= 0x200; j && i < numSyms; --j) {
+ symCodeTab[i++].prefixLen = 0;
+ }
+ } else if (j > 0x100) {
+ if (i == 0) {
+ error(errSyntaxError, getPos(), "Invalid code in JBIG2 text region");
+ gfree(syms);
+ gfree(symCodeTab);
+ return;
+ }
+ for (j -= 0x100; j && i < numSyms; --j) {
+ symCodeTab[i].prefixLen = symCodeTab[i-1].prefixLen;
+ ++i;
+ }
+ } else {
+ symCodeTab[i++].prefixLen = j;
+ }
+ }
+ symCodeTab[numSyms].prefixLen = 0;
+ symCodeTab[numSyms].rangeLen = jbig2HuffmanEOT;
+ huffDecoder->buildTable(symCodeTab, numSyms);
+ huffDecoder->reset();
+
+ // set up the arithmetic decoder
+ } else {
+ symCodeTab = NULL;
+ resetIntStats(symCodeLen);
+ arithDecoder->start();
+ }
+ if (refine) {
+ resetRefinementStats(templ, NULL);
+ }
+
+ bitmap = readTextRegion(huff, refine, w, h, numInstances,
+ logStrips, numSyms, symCodeTab, symCodeLen, syms,
+ defPixel, combOp, transposed, refCorner, sOffset,
+ huffFSTable, huffDSTable, huffDTTable,
+ huffRDWTable, huffRDHTable,
+ huffRDXTable, huffRDYTable, huffRSizeTable,
+ templ, atx, aty);
+
+ gfree(syms);
+
+ // combine the region bitmap into the page bitmap
+ if (imm) {
+ if (pageH == 0xffffffff && y + h > curPageH) {
+ pageBitmap->expand(y + h, pageDefPixel);
+ }
+ pageBitmap->combine(bitmap, x, y, extCombOp);
+ delete bitmap;
+
+ // store the region bitmap
+ } else {
+ bitmap->setSegNum(segNum);
+ segments->append(bitmap);
+ }
+
+ // clean up the Huffman decoder
+ if (huff) {
+ gfree(symCodeTab);
+ }
+
+ return;
+
+ codeTableError:
+ error(errSyntaxError, getPos(), "Missing code table in JBIG2 text region");
+ delete codeTables;
+ gfree(syms);
+ return;
+
+ eofError:
+ error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream");
+ return;
+}
+
+JBIG2Bitmap *JBIG2Stream::readTextRegion(GBool huff, GBool refine,
+ int w, int h,
+ Guint numInstances,
+ Guint logStrips,
+ int numSyms,
+ JBIG2HuffmanTable *symCodeTab,
+ Guint symCodeLen,
+ JBIG2Bitmap **syms,
+ Guint defPixel, Guint combOp,
+ Guint transposed, Guint refCorner,
+ int sOffset,
+ JBIG2HuffmanTable *huffFSTable,
+ JBIG2HuffmanTable *huffDSTable,
+ JBIG2HuffmanTable *huffDTTable,
+ JBIG2HuffmanTable *huffRDWTable,
+ JBIG2HuffmanTable *huffRDHTable,
+ JBIG2HuffmanTable *huffRDXTable,
+ JBIG2HuffmanTable *huffRDYTable,
+ JBIG2HuffmanTable *huffRSizeTable,
+ Guint templ,
+ int *atx, int *aty) {
+ JBIG2Bitmap *bitmap;
+ JBIG2Bitmap *symbolBitmap;
+ Guint strips;
+ int t, dt, tt, s, ds, sFirst, j;
+ int rdw, rdh, rdx, rdy, ri, refDX, refDY, bmSize;
+ Guint symID, inst, bw, bh;
+
+ strips = 1 << logStrips;
+
+ // allocate the bitmap
+ bitmap = new JBIG2Bitmap(0, w, h);
+ if (defPixel) {
+ bitmap->clearToOne();
+ } else {
+ bitmap->clearToZero();
+ }
+
+ // decode initial T value
+ if (huff) {
+ huffDecoder->decodeInt(&t, huffDTTable);
+ } else {
+ arithDecoder->decodeInt(&t, iadtStats);
+ }
+ t *= -(int)strips;
+
+ inst = 0;
+ sFirst = 0;
+ while (inst < numInstances) {
+
+ // decode delta-T
+ if (huff) {
+ if (!huffDecoder->decodeInt(&dt, huffDTTable)) {
+ break;
+ }
+ } else {
+ if (!arithDecoder->decodeInt(&dt, iadtStats)) {
+ break;
+ }
+ }
+ t += dt * strips;
+
+ // first S value
+ if (huff) {
+ if (!huffDecoder->decodeInt(&ds, huffFSTable)) {
+ break;
+ }
+ } else {
+ if (!arithDecoder->decodeInt(&ds, iafsStats)) {
+ break;
+ }
+ }
+ sFirst += ds;
+ s = sFirst;
+
+ // read the instances
+ // (this loop test is here to avoid an infinite loop with damaged
+ // JBIG2 streams where the normal loop exit doesn't get triggered)
+ while (inst < numInstances) {
+
+ // T value
+ if (strips == 1) {
+ dt = 0;
+ } else if (huff) {
+ dt = huffDecoder->readBits(logStrips);
+ } else {
+ arithDecoder->decodeInt(&dt, iaitStats);
+ }
+ tt = t + dt;
+
+ // symbol ID
+ if (huff) {
+ if (symCodeTab) {
+ huffDecoder->decodeInt(&j, symCodeTab);
+ symID = (Guint)j;
+ } else {
+ symID = huffDecoder->readBits(symCodeLen);
+ }
+ } else {
+ symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
+ }
+
+ if (symID >= (Guint)numSyms) {
+ error(errSyntaxError, getPos(),
+ "Invalid symbol number in JBIG2 text region");
+ } else {
+
+ // get the symbol bitmap
+ symbolBitmap = NULL;
+ if (refine) {
+ if (huff) {
+ ri = (int)huffDecoder->readBit();
+ } else {
+ arithDecoder->decodeInt(&ri, iariStats);
+ }
+ } else {
+ ri = 0;
+ }
+ if (ri) {
+ if (huff) {
+ huffDecoder->decodeInt(&rdw, huffRDWTable);
+ huffDecoder->decodeInt(&rdh, huffRDHTable);
+ huffDecoder->decodeInt(&rdx, huffRDXTable);
+ huffDecoder->decodeInt(&rdy, huffRDYTable);
+ huffDecoder->decodeInt(&bmSize, huffRSizeTable);
+ huffDecoder->reset();
+ arithDecoder->start();
+ } else {
+ arithDecoder->decodeInt(&rdw, iardwStats);
+ arithDecoder->decodeInt(&rdh, iardhStats);
+ arithDecoder->decodeInt(&rdx, iardxStats);
+ arithDecoder->decodeInt(&rdy, iardyStats);
+ }
+ refDX = ((rdw >= 0) ? rdw : rdw - 1) / 2 + rdx;
+ refDY = ((rdh >= 0) ? rdh : rdh - 1) / 2 + rdy;
+
+ symbolBitmap =
+ readGenericRefinementRegion(rdw + syms[symID]->getWidth(),
+ rdh + syms[symID]->getHeight(),
+ templ, gFalse, syms[symID],
+ refDX, refDY, atx, aty);
+ //~ do we need to use the bmSize value here (in Huffman mode)?
+ } else {
+ symbolBitmap = syms[symID];
+ }
+
+ // combine the symbol bitmap into the region bitmap
+ //~ something is wrong here - refCorner shouldn't degenerate into
+ //~ two cases
+ bw = symbolBitmap->getWidth() - 1;
+ bh = symbolBitmap->getHeight() - 1;
+ if (transposed) {
+ switch (refCorner) {
+ case 0: // bottom left
+ bitmap->combine(symbolBitmap, tt, s, combOp);
+ break;
+ case 1: // top left
+ bitmap->combine(symbolBitmap, tt, s, combOp);
+ break;
+ case 2: // bottom right
+ bitmap->combine(symbolBitmap, tt - bw, s, combOp);
+ break;
+ case 3: // top right
+ bitmap->combine(symbolBitmap, tt - bw, s, combOp);
+ break;
+ }
+ s += bh;
+ } else {
+ switch (refCorner) {
+ case 0: // bottom left
+ bitmap->combine(symbolBitmap, s, tt - bh, combOp);
+ break;
+ case 1: // top left
+ bitmap->combine(symbolBitmap, s, tt, combOp);
+ break;
+ case 2: // bottom right
+ bitmap->combine(symbolBitmap, s, tt - bh, combOp);
+ break;
+ case 3: // top right
+ bitmap->combine(symbolBitmap, s, tt, combOp);
+ break;
+ }
+ s += bw;
+ }
+ if (ri) {
+ delete symbolBitmap;
+ }
+ }
+
+ // next instance
+ ++inst;
+
+ // next S value
+ if (huff) {
+ if (!huffDecoder->decodeInt(&ds, huffDSTable)) {
+ break;
+ }
+ } else {
+ if (!arithDecoder->decodeInt(&ds, iadsStats)) {
+ break;
+ }
+ }
+ s += sOffset + ds;
+ }
+ }
+
+ return bitmap;
+}
+
+void JBIG2Stream::readPatternDictSeg(Guint segNum, Guint length) {
+ JBIG2PatternDict *patternDict;
+ JBIG2Bitmap *bitmap;
+ Guint flags, patternW, patternH, grayMax, templ, mmr;
+ int atx[4], aty[4];
+ Guint i, x;
+
+ // halftone dictionary flags, pattern width and height, max gray value
+ if (!readUByte(&flags) ||
+ !readUByte(&patternW) ||
+ !readUByte(&patternH) ||
+ !readULong(&grayMax)) {
+ goto eofError;
+ }
+ if (patternW == 0 || patternH == 0) {
+ error(errSyntaxError, getPos(),
+ "Bad size in JBIG2 pattern dictionary segment");
+ return;
+ }
+ templ = (flags >> 1) & 3;
+ mmr = flags & 1;
+
+ // set up the arithmetic decoder
+ if (!mmr) {
+ resetGenericStats(templ, NULL);
+ arithDecoder->start();
+ }
+
+ // read the bitmap
+ atx[0] = -(int)patternW; aty[0] = 0;
+ atx[1] = -3; aty[1] = -1;
+ atx[2] = 2; aty[2] = -2;
+ atx[3] = -2; aty[3] = -2;
+ bitmap = readGenericBitmap(mmr, (grayMax + 1) * patternW, patternH,
+ templ, gFalse, gFalse, NULL,
+ atx, aty, length - 7);
+
+ // create the pattern dict object
+ patternDict = new JBIG2PatternDict(segNum, grayMax + 1);
+
+ // split up the bitmap
+ x = 0;
+ for (i = 0; i <= grayMax; ++i) {
+ patternDict->setBitmap(i, bitmap->getSlice(x, 0, patternW, patternH));
+ x += patternW;
+ }
+
+ // free memory
+ delete bitmap;
+
+ // store the new pattern dict
+ segments->append(patternDict);
+
+ return;
+
+ eofError:
+ error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readHalftoneRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length,
+ Guint *refSegs, Guint nRefSegs) {
+ JBIG2Bitmap *bitmap;
+ JBIG2Segment *seg;
+ JBIG2PatternDict *patternDict;
+ JBIG2Bitmap *skipBitmap;
+ Guint *grayImg;
+ JBIG2Bitmap *grayBitmap;
+ JBIG2Bitmap *patternBitmap;
+ Guint w, h, x, y, segInfoFlags, extCombOp;
+ Guint flags, mmr, templ, enableSkip, combOp;
+ Guint gridW, gridH, stepX, stepY, patW, patH;
+ int atx[4], aty[4];
+ int gridX, gridY, xx, yy, bit, j;
+ Guint bpp, m, n, i;
+
+ // region segment info field
+ if (!readULong(&w) || !readULong(&h) ||
+ !readULong(&x) || !readULong(&y) ||
+ !readUByte(&segInfoFlags)) {
+ goto eofError;
+ }
+ // 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
+ if (!readUByte(&flags)) {
+ goto eofError;
+ }
+ mmr = flags & 1;
+ templ = (flags >> 1) & 3;
+ enableSkip = (flags >> 3) & 1;
+ combOp = (flags >> 4) & 7;
+ if (!readULong(&gridW) || !readULong(&gridH) ||
+ !readLong(&gridX) || !readLong(&gridY) ||
+ !readUWord(&stepX) || !readUWord(&stepY)) {
+ goto eofError;
+ }
+ if (w == 0 || h == 0 || w >= INT_MAX / h) {
+ error(errSyntaxError, getPos(),
+ "Bad bitmap size in JBIG2 halftone segment");
+ return;
+ }
+ if (gridW == 0 || gridH == 0 || gridW >= INT_MAX / gridH) {
+ error(errSyntaxError, getPos(), "Bad grid size in JBIG2 halftone segment");
+ return;
+ }
+
+ // get pattern dictionary
+ if (nRefSegs != 1) {
+ error(errSyntaxError, getPos(),
+ "Bad symbol dictionary reference in JBIG2 halftone segment");
+ return;
+ }
+ if (!(seg = findSegment(refSegs[0])) ||
+ seg->getType() != jbig2SegPatternDict) {
+ error(errSyntaxError, getPos(),
+ "Bad symbol dictionary reference in JBIG2 halftone segment");
+ return;
+ }
+ patternDict = (JBIG2PatternDict *)seg;
+ i = patternDict->getSize();
+ if (i <= 1) {
+ bpp = 0;
+ } else {
+ --i;
+ bpp = 0;
+ // i = floor((size-1) / 2^bpp)
+ while (i > 0) {
+ ++bpp;
+ i >>= 1;
+ }
+ }
+ patW = patternDict->getBitmap(0)->getWidth();
+ patH = patternDict->getBitmap(0)->getHeight();
+
+ // set up the arithmetic decoder
+ if (!mmr) {
+ resetGenericStats(templ, NULL);
+ arithDecoder->start();
+ }
+
+ // allocate the bitmap
+ bitmap = new JBIG2Bitmap(segNum, w, h);
+ if (flags & 0x80) { // HDEFPIXEL
+ bitmap->clearToOne();
+ } else {
+ bitmap->clearToZero();
+ }
+
+ // compute the skip bitmap
+ skipBitmap = NULL;
+ if (enableSkip) {
+ skipBitmap = new JBIG2Bitmap(0, gridW, gridH);
+ skipBitmap->clearToZero();
+ for (m = 0; m < gridH; ++m) {
+ for (n = 0; n < gridW; ++n) {
+ xx = gridX + m * stepY + n * stepX;
+ yy = gridY + m * stepX - n * stepY;
+ if (((xx + (int)patW) >> 8) <= 0 || (xx >> 8) >= (int)w ||
+ ((yy + (int)patH) >> 8) <= 0 || (yy >> 8) >= (int)h) {
+ skipBitmap->setPixel(n, m);
+ }
+ }
+ }
+ }
+
+ // read the gray-scale image
+ grayImg = (Guint *)gmallocn(gridW * gridH, sizeof(Guint));
+ memset(grayImg, 0, gridW * gridH * sizeof(Guint));
+ atx[0] = templ <= 1 ? 3 : 2; aty[0] = -1;
+ atx[1] = -3; aty[1] = -1;
+ atx[2] = 2; aty[2] = -2;
+ atx[3] = -2; aty[3] = -2;
+ for (j = bpp - 1; j >= 0; --j) {
+ grayBitmap = readGenericBitmap(mmr, gridW, gridH, templ, gFalse,
+ enableSkip, skipBitmap, atx, aty, -1);
+ i = 0;
+ for (m = 0; m < gridH; ++m) {
+ for (n = 0; n < gridW; ++n) {
+ bit = grayBitmap->getPixel(n, m) ^ (grayImg[i] & 1);
+ grayImg[i] = (grayImg[i] << 1) | bit;
+ ++i;
+ }
+ }
+ delete grayBitmap;
+ }
+
+ // decode the image
+ i = 0;
+ for (m = 0; m < gridH; ++m) {
+ xx = gridX + m * stepY;
+ yy = gridY + m * stepX;
+ for (n = 0; n < gridW; ++n) {
+ if (!(enableSkip && skipBitmap->getPixel(n, m))) {
+ patternBitmap = patternDict->getBitmap(grayImg[i]);
+ bitmap->combine(patternBitmap, xx >> 8, yy >> 8, combOp);
+ }
+ xx += stepX;
+ yy -= stepY;
+ ++i;
+ }
+ }
+
+ gfree(grayImg);
+ if (skipBitmap) {
+ delete skipBitmap;
+ }
+
+ // combine the region bitmap into the page bitmap
+ if (imm) {
+ if (pageH == 0xffffffff && y + h > curPageH) {
+ pageBitmap->expand(y + h, pageDefPixel);
+ }
+ pageBitmap->combine(bitmap, x, y, extCombOp);
+ delete bitmap;
+
+ // store the region bitmap
+ } else {
+ segments->append(bitmap);
+ }
+
+ return;
+
+ eofError:
+ error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readGenericRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length) {
+ JBIG2Bitmap *bitmap;
+ Guint w, h, x, y, segInfoFlags, extCombOp, rowCount;
+ Guint flags, mmr, templ, tpgdOn;
+ int atx[4], aty[4];
+
+ // region segment info field
+ if (!readULong(&w) || !readULong(&h) ||
+ !readULong(&x) || !readULong(&y) ||
+ !readUByte(&segInfoFlags)) {
+ goto eofError;
+ }
+ if (w == 0 || h == 0) {
+ error(errSyntaxError, getPos(),
+ "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
+ if (!readUByte(&flags)) {
+ goto eofError;
+ }
+ mmr = flags & 1;
+ templ = (flags >> 1) & 3;
+ tpgdOn = (flags >> 3) & 1;
+
+ // AT flags
+ if (!mmr) {
+ if (templ == 0) {
+ if (!readByte(&atx[0]) ||
+ !readByte(&aty[0]) ||
+ !readByte(&atx[1]) ||
+ !readByte(&aty[1]) ||
+ !readByte(&atx[2]) ||
+ !readByte(&aty[2]) ||
+ !readByte(&atx[3]) ||
+ !readByte(&aty[3])) {
+ goto eofError;
+ }
+ } else {
+ if (!readByte(&atx[0]) ||
+ !readByte(&aty[0])) {
+ goto eofError;
+ }
+ }
+ }
+
+ // set up the arithmetic decoder
+ if (!mmr) {
+ resetGenericStats(templ, NULL);
+ arithDecoder->start();
+ }
+
+ // read the bitmap
+ bitmap = readGenericBitmap(mmr, w, h, templ, tpgdOn, gFalse,
+ NULL, atx, aty, mmr ? length - 18 : 0);
+
+ // combine the region bitmap into the page bitmap
+ if (imm) {
+ if (pageH == 0xffffffff && y + h > curPageH) {
+ pageBitmap->expand(y + h, pageDefPixel);
+ }
+ pageBitmap->combine(bitmap, x, y, extCombOp);
+ delete bitmap;
+
+ // store the region bitmap
+ } else {
+ bitmap->setSegNum(segNum);
+ segments->append(bitmap);
+ }
+
+ // immediate generic segments can have an unspecified length, in
+ // which case, a row count is stored at the end of the segment
+ if (imm && length == 0xffffffff) {
+ readULong(&rowCount);
+ }
+
+ return;
+
+ eofError:
+ error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+inline void JBIG2Stream::mmrAddPixels(int a1, int blackPixels,
+ int *codingLine, int *a0i, int w) {
+ if (a1 > codingLine[*a0i]) {
+ if (a1 > w) {
+ error(errSyntaxError, getPos(),
+ "JBIG2 MMR row is wrong length ({0:d})", a1);
+ a1 = w;
+ }
+ if ((*a0i & 1) ^ blackPixels) {
+ ++*a0i;
+ }
+ codingLine[*a0i] = a1;
+ }
+}
+
+inline void JBIG2Stream::mmrAddPixelsNeg(int a1, int blackPixels,
+ int *codingLine, int *a0i, int w) {
+ if (a1 > codingLine[*a0i]) {
+ if (a1 > w) {
+ error(errSyntaxError, getPos(),
+ "JBIG2 MMR row is wrong length ({0:d})", a1);
+ a1 = w;
+ }
+ if ((*a0i & 1) ^ blackPixels) {
+ ++*a0i;
+ }
+ codingLine[*a0i] = a1;
+ } else if (a1 < codingLine[*a0i]) {
+ if (a1 < 0) {
+ error(errSyntaxError, getPos(), "Invalid JBIG2 MMR code");
+ a1 = 0;
+ }
+ while (*a0i > 0 && a1 <= codingLine[*a0i - 1]) {
+ --*a0i;
+ }
+ codingLine[*a0i] = a1;
+ }
+}
+
+JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,
+ int templ, GBool tpgdOn,
+ GBool useSkip, JBIG2Bitmap *skip,
+ int *atx, int *aty,
+ int mmrDataLength) {
+ JBIG2Bitmap *bitmap;
+ GBool ltp;
+ Guint ltpCX, cx, cx0, cx1, cx2;
+ int *refLine, *codingLine;
+ int code1, code2, code3;
+ Guchar *p0, *p1, *p2, *pp;
+ Guchar *atP0, *atP1, *atP2, *atP3;
+ Guint buf0, buf1, buf2;
+ Guint atBuf0, atBuf1, atBuf2, atBuf3;
+ int atShift0, atShift1, atShift2, atShift3;
+ Guchar mask;
+ int x, y, x0, x1, a0i, b1i, blackPixels, pix, i;
+
+ bitmap = new JBIG2Bitmap(0, w, h);
+ bitmap->clearToZero();
+
+ //----- MMR decode
+
+ if (mmr) {
+
+ mmrDecoder->reset();
+ if (w > INT_MAX - 3) {
+ error(errSyntaxError, getPos(), "Bad width in JBIG2 generic bitmap");
+ // force a call to gmalloc(-1), which will throw an exception
+ w = -4;
+ }
+ // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = w
+ // ---> max codingLine size = w + 1
+ // refLine has two extra guard entries at the end
+ // ---> max refLine size = w + 3
+ codingLine = (int *)gmallocn(w + 1, sizeof(int));
+ refLine = (int *)gmallocn(w + 3, sizeof(int));
+ codingLine[0] = w;
+
+ for (y = 0; y < h; ++y) {
+
+ // copy coding line to ref line
+ for (i = 0; codingLine[i] < w; ++i) {
+ refLine[i] = codingLine[i];
+ }
+ refLine[i++] = w;
+ refLine[i++] = w;
+ refLine[i] = w;
+
+ // decode a line
+ codingLine[0] = 0;
+ a0i = 0;
+ b1i = 0;
+ blackPixels = 0;
+ // invariant:
+ // refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1] <= w
+ // exception at left edge:
+ // codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible
+ // exception at right edge:
+ // refLine[b1i] = refLine[b1i+1] = w is possible
+ while (codingLine[a0i] < w) {
+ code1 = mmrDecoder->get2DCode();
+ switch (code1) {
+ case twoDimPass:
+ mmrAddPixels(refLine[b1i + 1], blackPixels, codingLine, &a0i, w);
+ if (refLine[b1i + 1] < w) {
+ b1i += 2;
+ }
+ break;
+ case twoDimHoriz:
+ code1 = code2 = 0;
+ if (blackPixels) {
+ do {
+ code1 += code3 = mmrDecoder->getBlackCode();
+ } while (code3 >= 64);
+ do {
+ code2 += code3 = mmrDecoder->getWhiteCode();
+ } while (code3 >= 64);
+ } else {
+ do {
+ code1 += code3 = mmrDecoder->getWhiteCode();
+ } while (code3 >= 64);
+ do {
+ code2 += code3 = mmrDecoder->getBlackCode();
+ } while (code3 >= 64);
+ }
+ mmrAddPixels(codingLine[a0i] + code1, blackPixels,
+ codingLine, &a0i, w);
+ if (codingLine[a0i] < w) {
+ mmrAddPixels(codingLine[a0i] + code2, blackPixels ^ 1,
+ codingLine, &a0i, w);
+ }
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
+ b1i += 2;
+ }
+ break;
+ case twoDimVertR3:
+ mmrAddPixels(refLine[b1i] + 3, blackPixels, codingLine, &a0i, w);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < w) {
+ ++b1i;
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertR2:
+ mmrAddPixels(refLine[b1i] + 2, blackPixels, codingLine, &a0i, w);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < w) {
+ ++b1i;
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertR1:
+ mmrAddPixels(refLine[b1i] + 1, blackPixels, codingLine, &a0i, w);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < w) {
+ ++b1i;
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVert0:
+ mmrAddPixels(refLine[b1i], blackPixels, codingLine, &a0i, w);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < w) {
+ ++b1i;
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertL3:
+ mmrAddPixelsNeg(refLine[b1i] - 3, blackPixels, codingLine, &a0i, w);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < w) {
+ if (b1i > 0) {
+ --b1i;
+ } else {
+ ++b1i;
+ }
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertL2:
+ mmrAddPixelsNeg(refLine[b1i] - 2, blackPixels, codingLine, &a0i, w);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < w) {
+ if (b1i > 0) {
+ --b1i;
+ } else {
+ ++b1i;
+ }
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertL1:
+ mmrAddPixelsNeg(refLine[b1i] - 1, blackPixels, codingLine, &a0i, w);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < w) {
+ if (b1i > 0) {
+ --b1i;
+ } else {
+ ++b1i;
+ }
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < w) {
+ b1i += 2;
+ }
+ }
+ break;
+ case EOF:
+ mmrAddPixels(w, 0, codingLine, &a0i, w);
+ break;
+ default:
+ error(errSyntaxError, getPos(),
+ "Illegal code in JBIG2 MMR bitmap data");
+ mmrAddPixels(w, 0, codingLine, &a0i, w);
+ break;
+ }
+ }
+
+ // convert the run lengths to a bitmap line
+ i = 0;
+ while (1) {
+ if (codingLine[i] >= w) {
+ break;
+ }
+ for (x = codingLine[i]; x < codingLine[i+1]; ++x) {
+ bitmap->setPixel(x, y);
+ }
+ if (codingLine[i+1] >= w) {
+ break;
+ }
+ i += 2;
+ }
+ }
+
+ if (mmrDataLength >= 0) {
+ mmrDecoder->skipTo(mmrDataLength);
+ } else {
+ if (mmrDecoder->get24Bits() != 0x001001) {
+ error(errSyntaxError, getPos(),
+ "Missing EOFB in JBIG2 MMR bitmap data");
+ }
+ }
+
+ gfree(refLine);
+ gfree(codingLine);
+
+ //----- arithmetic decode
+
+ } else {
+ // set up the typical row context
+ ltpCX = 0; // make gcc happy
+ if (tpgdOn) {
+ switch (templ) {
+ case 0:
+ ltpCX = 0x3953; // 001 11001 0101 0011
+ break;
+ case 1:
+ ltpCX = 0x079a; // 0011 11001 101 0
+ break;
+ case 2:
+ ltpCX = 0x0e3; // 001 1100 01 1
+ break;
+ case 3:
+ ltpCX = 0x18b; // 01100 0101 1
+ break;
+ }
+ }
+
+ ltp = 0;
+ cx = cx0 = cx1 = cx2 = 0; // make gcc happy
+ for (y = 0; y < h; ++y) {
+
+ // check for a "typical" (duplicate) row
+ if (tpgdOn) {
+ if (arithDecoder->decodeBit(ltpCX, genericRegionStats)) {
+ ltp = !ltp;
+ }
+ if (ltp) {
+ if (y > 0) {
+ bitmap->duplicateRow(y, y-1);
+ }
+ continue;
+ }
+ }
+
+ switch (templ) {
+ case 0:
+
+ // set up the context
+ p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize();
+ buf2 = *p2++ << 8;
+ if (y >= 1) {
+ p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize();
+ buf1 = *p1++ << 8;
+ if (y >= 2) {
+ p0 = bitmap->getDataPtr() + (y - 2) * bitmap->getLineSize();
+ buf0 = *p0++ << 8;
+ } else {
+ p0 = NULL;
+ buf0 = 0;
+ }
+ } else {
+ p1 = p0 = NULL;
+ buf1 = buf0 = 0;
+ }
+
+ if (atx[0] >= -8 && atx[0] <= 8 &&
+ atx[1] >= -8 && atx[1] <= 8 &&
+ atx[2] >= -8 && atx[2] <= 8 &&
+ atx[3] >= -8 && atx[3] <= 8) {
+ // set up the adaptive context
+ if (aty[0] <= 0 && y + aty[0] >= 0) {
+ atP0 = bitmap->getDataPtr() + (y + aty[0]) * bitmap->getLineSize();
+ atBuf0 = *atP0++ << 8;
+ } else {
+ atP0 = NULL;
+ atBuf0 = 0;
+ }
+ atShift0 = 15 - atx[0];
+ if (aty[1] <= 0 && y + aty[1] >= 0) {
+ atP1 = bitmap->getDataPtr() + (y + aty[1]) * bitmap->getLineSize();
+ atBuf1 = *atP1++ << 8;
+ } else {
+ atP1 = NULL;
+ atBuf1 = 0;
+ }
+ atShift1 = 15 - atx[1];
+ if (aty[2] <= 0 && y + aty[2] >= 0) {
+ atP2 = bitmap->getDataPtr() + (y + aty[2]) * bitmap->getLineSize();
+ atBuf2 = *atP2++ << 8;
+ } else {
+ atP2 = NULL;
+ atBuf2 = 0;
+ }
+ atShift2 = 15 - atx[2];
+ if (aty[3] <= 0 && y + aty[3] >= 0) {
+ atP3 = bitmap->getDataPtr() + (y + aty[3]) * bitmap->getLineSize();
+ atBuf3 = *atP3++ << 8;
+ } else {
+ atP3 = NULL;
+ atBuf3 = 0;
+ }
+ atShift3 = 15 - atx[3];
+
+ // decode the row
+ for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
+ if (x0 + 8 < w) {
+ if (p0) {
+ buf0 |= *p0++;
+ }
+ if (p1) {
+ buf1 |= *p1++;
+ }
+ buf2 |= *p2++;
+ if (atP0) {
+ atBuf0 |= *atP0++;
+ }
+ if (atP1) {
+ atBuf1 |= *atP1++;
+ }
+ if (atP2) {
+ atBuf2 |= *atP2++;
+ }
+ if (atP3) {
+ atBuf3 |= *atP3++;
+ }
+ }
+ for (x1 = 0, mask = 0x80;
+ x1 < 8 && x < w;
+ ++x1, ++x, mask = (Guchar)(mask >> 1)) {
+
+ // build the context
+ cx0 = (buf0 >> 14) & 0x07;
+ cx1 = (buf1 >> 13) & 0x1f;
+ cx2 = (buf2 >> 16) & 0x0f;
+ cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) |
+ (((atBuf0 >> atShift0) & 1) << 3) |
+ (((atBuf1 >> atShift1) & 1) << 2) |
+ (((atBuf2 >> atShift2) & 1) << 1) |
+ ((atBuf3 >> atShift3) & 1);
+
+ // check for a skipped pixel
+ if (!(useSkip && skip->getPixel(x, y))) {
+
+ // decode the pixel
+ if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+ *pp |= mask;
+ buf2 |= 0x8000;
+ if (aty[0] == 0) {
+ atBuf0 |= 0x8000;
+ }
+ if (aty[1] == 0) {
+ atBuf1 |= 0x8000;
+ }
+ if (aty[2] == 0) {
+ atBuf2 |= 0x8000;
+ }
+ if (aty[3] == 0) {
+ atBuf3 |= 0x8000;
+ }
+ }
+ }
+
+ // update the context
+ buf0 <<= 1;
+ buf1 <<= 1;
+ buf2 <<= 1;
+ atBuf0 <<= 1;
+ atBuf1 <<= 1;
+ atBuf2 <<= 1;
+ atBuf3 <<= 1;
+ }
+ }
+
+ } else {
+ // decode the row
+ for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
+ if (x0 + 8 < w) {
+ if (p0) {
+ buf0 |= *p0++;
+ }
+ if (p1) {
+ buf1 |= *p1++;
+ }
+ buf2 |= *p2++;
+ }
+ for (x1 = 0, mask = 0x80;
+ x1 < 8 && x < w;
+ ++x1, ++x, mask = (Guchar)(mask >> 1)) {
+
+ // build the context
+ cx0 = (buf0 >> 14) & 0x07;
+ cx1 = (buf1 >> 13) & 0x1f;
+ cx2 = (buf2 >> 16) & 0x0f;
+ cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) |
+ (bitmap->getPixel(x + atx[0], y + aty[0]) << 3) |
+ (bitmap->getPixel(x + atx[1], y + aty[1]) << 2) |
+ (bitmap->getPixel(x + atx[2], y + aty[2]) << 1) |
+ bitmap->getPixel(x + atx[3], y + aty[3]);
+
+ // check for a skipped pixel
+ if (!(useSkip && skip->getPixel(x, y))) {
+
+ // decode the pixel
+ if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+ *pp |= mask;
+ buf2 |= 0x8000;
+ }
+ }
+
+ // update the context
+ buf0 <<= 1;
+ buf1 <<= 1;
+ buf2 <<= 1;
+ }
+ }
+ }
+ break;
+
+ case 1:
+
+ // set up the context
+ p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize();
+ buf2 = *p2++ << 8;
+ if (y >= 1) {
+ p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize();
+ buf1 = *p1++ << 8;
+ if (y >= 2) {
+ p0 = bitmap->getDataPtr() + (y - 2) * bitmap->getLineSize();
+ buf0 = *p0++ << 8;
+ } else {
+ p0 = NULL;
+ buf0 = 0;
+ }
+ } else {
+ p1 = p0 = NULL;
+ buf1 = buf0 = 0;
+ }
+
+ if (atx[0] >= -8 && atx[0] <= 8) {
+ // set up the adaptive context
+ if (aty[0] <= 0 && y + aty[0] >= 0) {
+ atP0 = bitmap->getDataPtr() + (y + aty[0]) * bitmap->getLineSize();
+ atBuf0 = *atP0++ << 8;
+ } else {
+ atP0 = NULL;
+ atBuf0 = 0;
+ }
+ atShift0 = 15 - atx[0];
+
+ // decode the row
+ for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
+ if (x0 + 8 < w) {
+ if (p0) {
+ buf0 |= *p0++;
+ }
+ if (p1) {
+ buf1 |= *p1++;
+ }
+ buf2 |= *p2++;
+ if (atP0) {
+ atBuf0 |= *atP0++;
+ }
+ }
+ for (x1 = 0, mask = 0x80;
+ x1 < 8 && x < w;
+ ++x1, ++x, mask = (Guchar)(mask >> 1)) {
+
+ // build the context
+ cx0 = (buf0 >> 13) & 0x0f;
+ cx1 = (buf1 >> 13) & 0x1f;
+ cx2 = (buf2 >> 16) & 0x07;
+ cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) |
+ ((atBuf0 >> atShift0) & 1);
+
+ // check for a skipped pixel
+ if (!(useSkip && skip->getPixel(x, y))) {
+
+ // decode the pixel
+ if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+ *pp |= mask;
+ buf2 |= 0x8000;
+ if (aty[0] == 0) {
+ atBuf0 |= 0x8000;
+ }
+ }
+ }
+
+ // update the context
+ buf0 <<= 1;
+ buf1 <<= 1;
+ buf2 <<= 1;
+ atBuf0 <<= 1;
+ }
+ }
+
+ } else {
+ // decode the row
+ for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
+ if (x0 + 8 < w) {
+ if (p0) {
+ buf0 |= *p0++;
+ }
+ if (p1) {
+ buf1 |= *p1++;
+ }
+ buf2 |= *p2++;
+ }
+ for (x1 = 0, mask = 0x80;
+ x1 < 8 && x < w;
+ ++x1, ++x, mask = (Guchar)(mask >> 1)) {
+
+ // build the context
+ cx0 = (buf0 >> 13) & 0x0f;
+ cx1 = (buf1 >> 13) & 0x1f;
+ cx2 = (buf2 >> 16) & 0x07;
+ cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) |
+ bitmap->getPixel(x + atx[0], y + aty[0]);
+
+ // check for a skipped pixel
+ if (!(useSkip && skip->getPixel(x, y))) {
+
+ // decode the pixel
+ if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+ *pp |= mask;
+ buf2 |= 0x8000;
+ }
+ }
+
+ // update the context
+ buf0 <<= 1;
+ buf1 <<= 1;
+ buf2 <<= 1;
+ }
+ }
+ }
+ break;
+
+ case 2:
+
+ // set up the context
+ p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize();
+ buf2 = *p2++ << 8;
+ if (y >= 1) {
+ p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize();
+ buf1 = *p1++ << 8;
+ if (y >= 2) {
+ p0 = bitmap->getDataPtr() + (y - 2) * bitmap->getLineSize();
+ buf0 = *p0++ << 8;
+ } else {
+ p0 = NULL;
+ buf0 = 0;
+ }
+ } else {
+ p1 = p0 = NULL;
+ buf1 = buf0 = 0;
+ }
+
+ if (atx[0] >= -8 && atx[0] <= 8) {
+ // set up the adaptive context
+ if (aty[0] <= 0 && y + aty[0] >= 0) {
+ atP0 = bitmap->getDataPtr() + (y + aty[0]) * bitmap->getLineSize();
+ atBuf0 = *atP0++ << 8;
+ } else {
+ atP0 = NULL;
+ atBuf0 = 0;
+ }
+ atShift0 = 15 - atx[0];
+
+ // decode the row
+ for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
+ if (x0 + 8 < w) {
+ if (p0) {
+ buf0 |= *p0++;
+ }
+ if (p1) {
+ buf1 |= *p1++;
+ }
+ buf2 |= *p2++;
+ if (atP0) {
+ atBuf0 |= *atP0++;
+ }
+ }
+ for (x1 = 0, mask = 0x80;
+ x1 < 8 && x < w;
+ ++x1, ++x, mask = (Guchar)(mask >> 1)) {
+
+ // build the context
+ cx0 = (buf0 >> 14) & 0x07;
+ cx1 = (buf1 >> 14) & 0x0f;
+ cx2 = (buf2 >> 16) & 0x03;
+ cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) |
+ ((atBuf0 >> atShift0) & 1);
+
+ // check for a skipped pixel
+ if (!(useSkip && skip->getPixel(x, y))) {
+
+ // decode the pixel
+ if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+ *pp |= mask;
+ buf2 |= 0x8000;
+ if (aty[0] == 0) {
+ atBuf0 |= 0x8000;
+ }
+ }
+ }
+
+ // update the context
+ buf0 <<= 1;
+ buf1 <<= 1;
+ buf2 <<= 1;
+ atBuf0 <<= 1;
+ }
+ }
+
+ } else {
+ // decode the row
+ for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
+ if (x0 + 8 < w) {
+ if (p0) {
+ buf0 |= *p0++;
+ }
+ if (p1) {
+ buf1 |= *p1++;
+ }
+ buf2 |= *p2++;
+ }
+ for (x1 = 0, mask = 0x80;
+ x1 < 8 && x < w;
+ ++x1, ++x, mask = (Guchar)(mask >> 1)) {
+
+ // build the context
+ cx0 = (buf0 >> 14) & 0x07;
+ cx1 = (buf1 >> 14) & 0x0f;
+ cx2 = (buf2 >> 16) & 0x03;
+ cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) |
+ bitmap->getPixel(x + atx[0], y + aty[0]);
+
+ // check for a skipped pixel
+ if (!(useSkip && skip->getPixel(x, y))) {
+
+ // decode the pixel
+ if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+ *pp |= mask;
+ buf2 |= 0x8000;
+ }
+ }
+
+ // update the context
+ buf0 <<= 1;
+ buf1 <<= 1;
+ buf2 <<= 1;
+ }
+ }
+ }
+ break;
+
+ case 3:
+
+ // set up the context
+ p2 = pp = bitmap->getDataPtr() + y * bitmap->getLineSize();
+ buf2 = *p2++ << 8;
+ if (y >= 1) {
+ p1 = bitmap->getDataPtr() + (y - 1) * bitmap->getLineSize();
+ buf1 = *p1++ << 8;
+ } else {
+ p1 = NULL;
+ buf1 = 0;
+ }
+
+ if (atx[0] >= -8 && atx[0] <= 8) {
+ // set up the adaptive context
+ if (aty[0] <= 0 && y + aty[0] >= 0) {
+ atP0 = bitmap->getDataPtr() + (y + aty[0]) * bitmap->getLineSize();
+ atBuf0 = *atP0++ << 8;
+ } else {
+ atP0 = NULL;
+ atBuf0 = 0;
+ }
+ atShift0 = 15 - atx[0];
+
+ // decode the row
+ for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
+ if (x0 + 8 < w) {
+ if (p1) {
+ buf1 |= *p1++;
+ }
+ buf2 |= *p2++;
+ if (atP0) {
+ atBuf0 |= *atP0++;
+ }
+ }
+ for (x1 = 0, mask = 0x80;
+ x1 < 8 && x < w;
+ ++x1, ++x, mask = (Guchar)(mask >> 1)) {
+
+ // build the context
+ cx1 = (buf1 >> 14) & 0x1f;
+ cx2 = (buf2 >> 16) & 0x0f;
+ cx = (cx1 << 5) | (cx2 << 1) |
+ ((atBuf0 >> atShift0) & 1);
+
+ // check for a skipped pixel
+ if (!(useSkip && skip->getPixel(x, y))) {
+
+ // decode the pixel
+ if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+ *pp |= mask;
+ buf2 |= 0x8000;
+ if (aty[0] == 0) {
+ atBuf0 |= 0x8000;
+ }
+ }
+ }
+
+ // update the context
+ buf1 <<= 1;
+ buf2 <<= 1;
+ atBuf0 <<= 1;
+ }
+ }
+
+ } else {
+ // decode the row
+ for (x0 = 0, x = 0; x0 < w; x0 += 8, ++pp) {
+ if (x0 + 8 < w) {
+ if (p1) {
+ buf1 |= *p1++;
+ }
+ buf2 |= *p2++;
+ }
+ for (x1 = 0, mask = 0x80;
+ x1 < 8 && x < w;
+ ++x1, ++x, mask = (Guchar)(mask >> 1)) {
+
+ // build the context
+ cx1 = (buf1 >> 14) & 0x1f;
+ cx2 = (buf2 >> 16) & 0x0f;
+ cx = (cx1 << 5) | (cx2 << 1) |
+ bitmap->getPixel(x + atx[0], y + aty[0]);
+
+ // check for a skipped pixel
+ if (!(useSkip && skip->getPixel(x, y))) {
+
+ // decode the pixel
+ if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+ *pp |= mask;
+ buf2 |= 0x8000;
+ }
+ }
+
+ // update the context
+ buf1 <<= 1;
+ buf2 <<= 1;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return bitmap;
+}
+
+void JBIG2Stream::readGenericRefinementRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length,
+ Guint *refSegs,
+ Guint nRefSegs) {
+ JBIG2Bitmap *bitmap, *refBitmap;
+ Guint w, h, x, y, segInfoFlags, extCombOp;
+ Guint flags, templ, tpgrOn;
+ int atx[2], aty[2];
+ JBIG2Segment *seg;
+
+ // region segment info field
+ if (!readULong(&w) || !readULong(&h) ||
+ !readULong(&x) || !readULong(&y) ||
+ !readUByte(&segInfoFlags)) {
+ goto eofError;
+ }
+ if (w == 0 || h == 0) {
+ error(errSyntaxError, getPos(),
+ "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
+ if (!readUByte(&flags)) {
+ goto eofError;
+ }
+ templ = flags & 1;
+ tpgrOn = (flags >> 1) & 1;
+
+ // AT flags
+ if (!templ) {
+ if (!readByte(&atx[0]) || !readByte(&aty[0]) ||
+ !readByte(&atx[1]) || !readByte(&aty[1])) {
+ goto eofError;
+ }
+ }
+
+ // resize the page bitmap if needed
+ if (nRefSegs == 0 || imm) {
+ if (pageH == 0xffffffff && y + h > curPageH) {
+ pageBitmap->expand(y + h, pageDefPixel);
+ }
+ }
+
+ // get referenced bitmap
+ if (nRefSegs > 1) {
+ error(errSyntaxError, getPos(),
+ "Bad reference in JBIG2 generic refinement segment");
+ return;
+ }
+ if (nRefSegs == 1) {
+ if (!(seg = findSegment(refSegs[0])) ||
+ seg->getType() != jbig2SegBitmap) {
+ error(errSyntaxError, getPos(),
+ "Bad bitmap reference in JBIG2 generic refinement segment");
+ return;
+ }
+ refBitmap = (JBIG2Bitmap *)seg;
+ } else {
+ refBitmap = pageBitmap->getSlice(x, y, w, h);
+ }
+
+ // set up the arithmetic decoder
+ resetRefinementStats(templ, NULL);
+ arithDecoder->start();
+
+ // read
+ bitmap = readGenericRefinementRegion(w, h, templ, tpgrOn,
+ refBitmap, 0, 0, atx, aty);
+
+ // combine the region bitmap into the page bitmap
+ if (imm) {
+ pageBitmap->combine(bitmap, x, y, extCombOp);
+ delete bitmap;
+
+ // store the region bitmap
+ } else {
+ bitmap->setSegNum(segNum);
+ segments->append(bitmap);
+ }
+
+ // delete the referenced bitmap
+ if (nRefSegs == 1) {
+ discardSegment(refSegs[0]);
+ } else {
+ delete refBitmap;
+ }
+
+ return;
+
+ eofError:
+ error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+JBIG2Bitmap *JBIG2Stream::readGenericRefinementRegion(int w, int h,
+ int templ, GBool tpgrOn,
+ JBIG2Bitmap *refBitmap,
+ int refDX, int refDY,
+ int *atx, int *aty) {
+ JBIG2Bitmap *bitmap;
+ GBool ltp;
+ Guint ltpCX, cx, cx0, cx2, cx3, cx4, tpgrCX0, tpgrCX1, tpgrCX2;
+ JBIG2BitmapPtr cxPtr0, cxPtr1, cxPtr2, cxPtr3, cxPtr4, cxPtr5, cxPtr6;
+ JBIG2BitmapPtr tpgrCXPtr0, tpgrCXPtr1, tpgrCXPtr2;
+ int x, y, pix;
+
+ bitmap = new JBIG2Bitmap(0, w, h);
+ bitmap->clearToZero();
+
+ // set up the typical row context
+ if (templ) {
+ ltpCX = 0x008;
+ } else {
+ ltpCX = 0x0010;
+ }
+
+ ltp = 0;
+ for (y = 0; y < h; ++y) {
+
+ if (templ) {
+
+ // set up the context
+ bitmap->getPixelPtr(0, y-1, &cxPtr0);
+ cx0 = bitmap->nextPixel(&cxPtr0);
+ bitmap->getPixelPtr(-1, y, &cxPtr1);
+ refBitmap->getPixelPtr(-refDX, y-1-refDY, &cxPtr2);
+ refBitmap->getPixelPtr(-1-refDX, y-refDY, &cxPtr3);
+ cx3 = refBitmap->nextPixel(&cxPtr3);
+ cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3);
+ refBitmap->getPixelPtr(-refDX, y+1-refDY, &cxPtr4);
+ cx4 = refBitmap->nextPixel(&cxPtr4);
+
+ // set up the typical prediction context
+ tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy
+ if (tpgrOn) {
+ refBitmap->getPixelPtr(-1-refDX, y-1-refDY, &tpgrCXPtr0);
+ tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0);
+ tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
+ tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
+ refBitmap->getPixelPtr(-1-refDX, y-refDY, &tpgrCXPtr1);
+ tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1);
+ tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
+ tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
+ refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &tpgrCXPtr2);
+ tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2);
+ tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+ tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+ } else {
+ tpgrCXPtr0.p = tpgrCXPtr1.p = tpgrCXPtr2.p = NULL; // make gcc happy
+ tpgrCXPtr0.shift = tpgrCXPtr1.shift = tpgrCXPtr2.shift = 0;
+ tpgrCXPtr0.x = tpgrCXPtr1.x = tpgrCXPtr2.x = 0;
+ }
+
+ for (x = 0; x < w; ++x) {
+
+ // update the context
+ cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 7;
+ cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7;
+ cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 3;
+
+ if (tpgrOn) {
+ // update the typical predictor context
+ tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7;
+ tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7;
+ tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7;
+
+ // check for a "typical" pixel
+ if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) {
+ ltp = !ltp;
+ }
+ if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) {
+ bitmap->clearPixel(x, y);
+ continue;
+ } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) {
+ bitmap->setPixel(x, y);
+ continue;
+ }
+ }
+
+ // build the context
+ cx = (cx0 << 7) | (bitmap->nextPixel(&cxPtr1) << 6) |
+ (refBitmap->nextPixel(&cxPtr2) << 5) |
+ (cx3 << 2) | cx4;
+
+ // decode the pixel
+ if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) {
+ bitmap->setPixel(x, y);
+ }
+ }
+
+ } else {
+
+ // set up the context
+ bitmap->getPixelPtr(0, y-1, &cxPtr0);
+ cx0 = bitmap->nextPixel(&cxPtr0);
+ bitmap->getPixelPtr(-1, y, &cxPtr1);
+ refBitmap->getPixelPtr(-refDX, y-1-refDY, &cxPtr2);
+ cx2 = refBitmap->nextPixel(&cxPtr2);
+ refBitmap->getPixelPtr(-1-refDX, y-refDY, &cxPtr3);
+ cx3 = refBitmap->nextPixel(&cxPtr3);
+ cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3);
+ refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &cxPtr4);
+ cx4 = refBitmap->nextPixel(&cxPtr4);
+ cx4 = (cx4 << 1) | refBitmap->nextPixel(&cxPtr4);
+ bitmap->getPixelPtr(atx[0], y+aty[0], &cxPtr5);
+ refBitmap->getPixelPtr(atx[1]-refDX, y+aty[1]-refDY, &cxPtr6);
+
+ // set up the typical prediction context
+ tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy
+ if (tpgrOn) {
+ refBitmap->getPixelPtr(-1-refDX, y-1-refDY, &tpgrCXPtr0);
+ tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0);
+ tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
+ tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
+ refBitmap->getPixelPtr(-1-refDX, y-refDY, &tpgrCXPtr1);
+ tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1);
+ tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
+ tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
+ refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &tpgrCXPtr2);
+ tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2);
+ tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+ tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+ } else {
+ tpgrCXPtr0.p = tpgrCXPtr1.p = tpgrCXPtr2.p = NULL; // make gcc happy
+ tpgrCXPtr0.shift = tpgrCXPtr1.shift = tpgrCXPtr2.shift = 0;
+ tpgrCXPtr0.x = tpgrCXPtr1.x = tpgrCXPtr2.x = 0;
+ }
+
+ for (x = 0; x < w; ++x) {
+
+ // update the context
+ cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 3;
+ cx2 = ((cx2 << 1) | refBitmap->nextPixel(&cxPtr2)) & 3;
+ cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7;
+ cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 7;
+
+ if (tpgrOn) {
+ // update the typical predictor context
+ tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7;
+ tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7;
+ tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7;
+
+ // check for a "typical" pixel
+ if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) {
+ ltp = !ltp;
+ }
+ if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) {
+ bitmap->clearPixel(x, y);
+ continue;
+ } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) {
+ bitmap->setPixel(x, y);
+ continue;
+ }
+ }
+
+ // build the context
+ cx = (cx0 << 11) | (bitmap->nextPixel(&cxPtr1) << 10) |
+ (cx2 << 8) | (cx3 << 5) | (cx4 << 2) |
+ (bitmap->nextPixel(&cxPtr5) << 1) |
+ refBitmap->nextPixel(&cxPtr6);
+
+ // decode the pixel
+ if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) {
+ bitmap->setPixel(x, y);
+ }
+ }
+ }
+ }
+
+ return bitmap;
+}
+
+void JBIG2Stream::readPageInfoSeg(Guint length) {
+ Guint xRes, yRes, flags, striping;
+
+ if (!readULong(&pageW) || !readULong(&pageH) ||
+ !readULong(&xRes) || !readULong(&yRes) ||
+ !readUByte(&flags) || !readUWord(&striping)) {
+ goto eofError;
+ }
+ if (pageW == 0 || pageH == 0 || pageW > INT_MAX / pageW) {
+ error(errSyntaxError, getPos(), "Bad page size in JBIG2 stream");
+ return;
+ }
+ pageDefPixel = (flags >> 2) & 1;
+ defCombOp = (flags >> 3) & 3;
+
+ // allocate the page bitmap
+ if (pageH == 0xffffffff) {
+ curPageH = striping & 0x7fff;
+ } else {
+ curPageH = pageH;
+ }
+ pageBitmap = new JBIG2Bitmap(0, pageW, curPageH);
+
+ // default pixel value
+ if (pageDefPixel) {
+ pageBitmap->clearToOne();
+ } else {
+ pageBitmap->clearToZero();
+ }
+
+ return;
+
+ eofError:
+ error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readEndOfStripeSeg(Guint length) {
+ // skip the segment
+ byteCounter += curStr->discardChars(length);
+}
+
+void JBIG2Stream::readProfilesSeg(Guint length) {
+ // skip the segment
+ byteCounter += curStr->discardChars(length);
+}
+
+void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
+ JBIG2HuffmanTable *huffTab;
+ Guint flags, oob, prefixBits, rangeBits;
+ int lowVal, highVal, val;
+ Guint huffTabSize, i;
+
+ if (!readUByte(&flags) || !readLong(&lowVal) || !readLong(&highVal)) {
+ goto eofError;
+ }
+ oob = flags & 1;
+ prefixBits = ((flags >> 1) & 7) + 1;
+ rangeBits = ((flags >> 4) & 7) + 1;
+
+ huffDecoder->reset();
+ huffTabSize = 8;
+ huffTab = (JBIG2HuffmanTable *)
+ gmallocn(huffTabSize, sizeof(JBIG2HuffmanTable));
+ i = 0;
+ val = lowVal;
+ while (val < highVal) {
+ if (i == huffTabSize) {
+ huffTabSize *= 2;
+ huffTab = (JBIG2HuffmanTable *)
+ greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
+ }
+ huffTab[i].val = val;
+ huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+ huffTab[i].rangeLen = huffDecoder->readBits(rangeBits);
+ val += 1 << huffTab[i].rangeLen;
+ ++i;
+ }
+ if (i + oob + 3 > huffTabSize) {
+ huffTabSize = i + oob + 3;
+ huffTab = (JBIG2HuffmanTable *)
+ greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
+ }
+ huffTab[i].val = lowVal - 1;
+ huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+ huffTab[i].rangeLen = jbig2HuffmanLOW;
+ ++i;
+ huffTab[i].val = highVal;
+ huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+ huffTab[i].rangeLen = 32;
+ ++i;
+ if (oob) {
+ huffTab[i].val = 0;
+ huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+ huffTab[i].rangeLen = jbig2HuffmanOOB;
+ ++i;
+ }
+ huffTab[i].val = 0;
+ huffTab[i].prefixLen = 0;
+ huffTab[i].rangeLen = jbig2HuffmanEOT;
+ huffDecoder->buildTable(huffTab, i);
+
+ // create and store the new table segment
+ segments->append(new JBIG2CodeTable(segNum, huffTab));
+
+ return;
+
+ eofError:
+ error(errSyntaxError, getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readExtensionSeg(Guint length) {
+ // skip the segment
+ byteCounter += curStr->discardChars(length);
+}
+
+JBIG2Segment *JBIG2Stream::findSegment(Guint segNum) {
+ JBIG2Segment *seg;
+ int i;
+
+ for (i = 0; i < globalSegments->getLength(); ++i) {
+ seg = (JBIG2Segment *)globalSegments->get(i);
+ if (seg->getSegNum() == segNum) {
+ return seg;
+ }
+ }
+ for (i = 0; i < segments->getLength(); ++i) {
+ seg = (JBIG2Segment *)segments->get(i);
+ if (seg->getSegNum() == segNum) {
+ return seg;
+ }
+ }
+ return NULL;
+}
+
+void JBIG2Stream::discardSegment(Guint segNum) {
+ JBIG2Segment *seg;
+ int i;
+
+ for (i = 0; i < globalSegments->getLength(); ++i) {
+ seg = (JBIG2Segment *)globalSegments->get(i);
+ if (seg->getSegNum() == segNum) {
+ globalSegments->del(i);
+ return;
+ }
+ }
+ for (i = 0; i < segments->getLength(); ++i) {
+ seg = (JBIG2Segment *)segments->get(i);
+ if (seg->getSegNum() == segNum) {
+ segments->del(i);
+ return;
+ }
+ }
+}
+
+void JBIG2Stream::resetGenericStats(Guint templ,
+ JArithmeticDecoderStats *prevStats) {
+ int size;
+
+ size = contextSize[templ];
+ if (prevStats && prevStats->getContextSize() == size) {
+ if (genericRegionStats->getContextSize() == size) {
+ genericRegionStats->copyFrom(prevStats);
+ } else {
+ delete genericRegionStats;
+ genericRegionStats = prevStats->copy();
+ }
+ } else {
+ if (genericRegionStats->getContextSize() == size) {
+ genericRegionStats->reset();
+ } else {
+ delete genericRegionStats;
+ genericRegionStats = new JArithmeticDecoderStats(1 << size);
+ }
+ }
+}
+
+void JBIG2Stream::resetRefinementStats(Guint templ,
+ JArithmeticDecoderStats *prevStats) {
+ int size;
+
+ size = refContextSize[templ];
+ if (prevStats && prevStats->getContextSize() == size) {
+ if (refinementRegionStats->getContextSize() == size) {
+ refinementRegionStats->copyFrom(prevStats);
+ } else {
+ delete refinementRegionStats;
+ refinementRegionStats = prevStats->copy();
+ }
+ } else {
+ if (refinementRegionStats->getContextSize() == size) {
+ refinementRegionStats->reset();
+ } else {
+ delete refinementRegionStats;
+ refinementRegionStats = new JArithmeticDecoderStats(1 << size);
+ }
+ }
+}
+
+void JBIG2Stream::resetIntStats(int symCodeLen) {
+ iadhStats->reset();
+ iadwStats->reset();
+ iaexStats->reset();
+ iaaiStats->reset();
+ iadtStats->reset();
+ iaitStats->reset();
+ iafsStats->reset();
+ iadsStats->reset();
+ iardxStats->reset();
+ iardyStats->reset();
+ iardwStats->reset();
+ iardhStats->reset();
+ iariStats->reset();
+ if (iaidStats->getContextSize() == 1 << (symCodeLen + 1)) {
+ iaidStats->reset();
+ } else {
+ delete iaidStats;
+ iaidStats = new JArithmeticDecoderStats(1 << (symCodeLen + 1));
+ }
+}
+
+GBool JBIG2Stream::readUByte(Guint *x) {
+ int c0;
+
+ if ((c0 = curStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ ++byteCounter;
+ *x = (Guint)c0;
+ return gTrue;
+}
+
+GBool JBIG2Stream::readByte(int *x) {
+ int c0;
+
+ if ((c0 = curStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ ++byteCounter;
+ *x = c0;
+ if (c0 & 0x80) {
+ *x |= -1 - 0xff;
+ }
+ return gTrue;
+}
+
+GBool JBIG2Stream::readUWord(Guint *x) {
+ int c0, c1;
+
+ if ((c0 = curStr->getChar()) == EOF ||
+ (c1 = curStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ byteCounter += 2;
+ *x = (Guint)((c0 << 8) | c1);
+ return gTrue;
+}
+
+GBool JBIG2Stream::readULong(Guint *x) {
+ int c0, c1, c2, c3;
+
+ if ((c0 = curStr->getChar()) == EOF ||
+ (c1 = curStr->getChar()) == EOF ||
+ (c2 = curStr->getChar()) == EOF ||
+ (c3 = curStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ byteCounter += 4;
+ *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
+ return gTrue;
+}
+
+GBool JBIG2Stream::readLong(int *x) {
+ int c0, c1, c2, c3;
+
+ if ((c0 = curStr->getChar()) == EOF ||
+ (c1 = curStr->getChar()) == EOF ||
+ (c2 = curStr->getChar()) == EOF ||
+ (c3 = curStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ byteCounter += 4;
+ *x = ((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
+ if (c0 & 0x80) {
+ *x |= -1 - (int)0xffffffff;
+ }
+ return gTrue;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h
new file mode 100644
index 00000000000..cca722b4bfc
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JBIG2Stream.h
@@ -0,0 +1,154 @@
+//========================================================================
+//
+// JBIG2Stream.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef JBIG2STREAM_H
+#define JBIG2STREAM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+#include "Stream.h"
+
+class GList;
+class JBIG2Segment;
+class JBIG2Bitmap;
+class JArithmeticDecoder;
+class JArithmeticDecoderStats;
+class JBIG2HuffmanDecoder;
+struct JBIG2HuffmanTable;
+class JBIG2MMRDecoder;
+
+//------------------------------------------------------------------------
+
+class JBIG2Stream: public FilterStream {
+public:
+
+ JBIG2Stream(Stream *strA, Object *globalsStreamA);
+ virtual ~JBIG2Stream();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strJBIG2; }
+ virtual void reset();
+ virtual void close();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual int getBlock(char *blk, int size);
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ void readSegments();
+ GBool readSymbolDictSeg(Guint segNum, Guint length,
+ Guint *refSegs, Guint nRefSegs);
+ void readTextRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length,
+ Guint *refSegs, Guint nRefSegs);
+ JBIG2Bitmap *readTextRegion(GBool huff, GBool refine,
+ int w, int h,
+ Guint numInstances,
+ Guint logStrips,
+ int numSyms,
+ JBIG2HuffmanTable *symCodeTab,
+ Guint symCodeLen,
+ JBIG2Bitmap **syms,
+ Guint defPixel, Guint combOp,
+ Guint transposed, Guint refCorner,
+ int sOffset,
+ JBIG2HuffmanTable *huffFSTable,
+ JBIG2HuffmanTable *huffDSTable,
+ JBIG2HuffmanTable *huffDTTable,
+ JBIG2HuffmanTable *huffRDWTable,
+ JBIG2HuffmanTable *huffRDHTable,
+ JBIG2HuffmanTable *huffRDXTable,
+ JBIG2HuffmanTable *huffRDYTable,
+ JBIG2HuffmanTable *huffRSizeTable,
+ Guint templ,
+ int *atx, int *aty);
+ void readPatternDictSeg(Guint segNum, Guint length);
+ void readHalftoneRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length,
+ Guint *refSegs, Guint nRefSegs);
+ void readGenericRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length);
+ void mmrAddPixels(int a1, int blackPixels,
+ int *codingLine, int *a0i, int w);
+ void mmrAddPixelsNeg(int a1, int blackPixels,
+ int *codingLine, int *a0i, int w);
+ JBIG2Bitmap *readGenericBitmap(GBool mmr, int w, int h,
+ int templ, GBool tpgdOn,
+ GBool useSkip, JBIG2Bitmap *skip,
+ int *atx, int *aty,
+ int mmrDataLength);
+ void readGenericRefinementRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length,
+ Guint *refSegs,
+ Guint nRefSegs);
+ JBIG2Bitmap *readGenericRefinementRegion(int w, int h,
+ int templ, GBool tpgrOn,
+ JBIG2Bitmap *refBitmap,
+ int refDX, int refDY,
+ int *atx, int *aty);
+ void readPageInfoSeg(Guint length);
+ void readEndOfStripeSeg(Guint length);
+ void readProfilesSeg(Guint length);
+ void readCodeTableSeg(Guint segNum, Guint length);
+ void readExtensionSeg(Guint length);
+ JBIG2Segment *findSegment(Guint segNum);
+ void discardSegment(Guint segNum);
+ void resetGenericStats(Guint templ,
+ JArithmeticDecoderStats *prevStats);
+ void resetRefinementStats(Guint templ,
+ JArithmeticDecoderStats *prevStats);
+ void resetIntStats(int symCodeLen);
+ GBool readUByte(Guint *x);
+ GBool readByte(int *x);
+ GBool readUWord(Guint *x);
+ GBool readULong(Guint *x);
+ GBool readLong(int *x);
+
+ Object globalsStream;
+ Guint pageW, pageH, curPageH;
+ Guint pageDefPixel;
+ JBIG2Bitmap *pageBitmap;
+ Guint defCombOp;
+ GList *segments; // [JBIG2Segment]
+ GList *globalSegments; // [JBIG2Segment]
+ Stream *curStr;
+ Guchar *dataPtr;
+ Guchar *dataEnd;
+ Guint byteCounter;
+ GBool done;
+
+ JArithmeticDecoder *arithDecoder;
+ JArithmeticDecoderStats *genericRegionStats;
+ JArithmeticDecoderStats *refinementRegionStats;
+ JArithmeticDecoderStats *iadhStats;
+ JArithmeticDecoderStats *iadwStats;
+ JArithmeticDecoderStats *iaexStats;
+ JArithmeticDecoderStats *iaaiStats;
+ JArithmeticDecoderStats *iadtStats;
+ JArithmeticDecoderStats *iaitStats;
+ JArithmeticDecoderStats *iafsStats;
+ JArithmeticDecoderStats *iadsStats;
+ JArithmeticDecoderStats *iardxStats;
+ JArithmeticDecoderStats *iardyStats;
+ JArithmeticDecoderStats *iardwStats;
+ JArithmeticDecoderStats *iardhStats;
+ JArithmeticDecoderStats *iariStats;
+ JArithmeticDecoderStats *iaidStats;
+ JBIG2HuffmanDecoder *huffDecoder;
+ JBIG2MMRDecoder *mmrDecoder;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc
new file mode 100644
index 00000000000..d9c295bb12c
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.cc
@@ -0,0 +1,3540 @@
+//========================================================================
+//
+// JPXStream.cc
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <limits.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "Error.h"
+#include "JArithmeticDecoder.h"
+#include "JPXStream.h"
+
+//~ to do:
+// - ROI
+// - progression order changes
+// - packed packet headers
+// - support for palettes, channel maps, etc.
+// - 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
+// - in coefficient decoding (readCodeBlockData):
+// - selective arithmetic coding bypass
+// (this also affects reading the cb->dataLen array)
+// - coeffs longer than 31 bits (should just ignore the extra bits?)
+// - handle boxes larger than 2^32 bytes
+// - the fixed-point arithmetic won't handle 16-bit pixels
+
+//------------------------------------------------------------------------
+
+// number of contexts for the arithmetic decoder
+#define jpxNContexts 19
+
+#define jpxContextSigProp 0 // 0 - 8: significance prop and cleanup
+#define jpxContextSign 9 // 9 - 13: sign
+#define jpxContextMagRef 14 // 14 - 16: magnitude refinement
+#define jpxContextRunLength 17 // cleanup: run length
+#define jpxContextUniform 18 // cleanup: first signif coeff
+
+//------------------------------------------------------------------------
+
+#define jpxPassSigProp 0
+#define jpxPassMagRef 1
+#define jpxPassCleanup 2
+
+//------------------------------------------------------------------------
+
+// arithmetic decoder context for the significance propagation and
+// cleanup passes:
+// [horiz][vert][diag][subband]
+// where subband = 0 for HL
+// = 1 for LH and LL
+// = 2 for HH
+static Guint sigPropContext[3][3][5][3] = {
+ {{{ 0, 0, 0 }, // horiz=0, vert=0, diag=0
+ { 1, 1, 3 }, // horiz=0, vert=0, diag=1
+ { 2, 2, 6 }, // horiz=0, vert=0, diag=2
+ { 2, 2, 8 }, // horiz=0, vert=0, diag=3
+ { 2, 2, 8 }}, // horiz=0, vert=0, diag=4
+ {{ 5, 3, 1 }, // horiz=0, vert=1, diag=0
+ { 6, 3, 4 }, // horiz=0, vert=1, diag=1
+ { 6, 3, 7 }, // horiz=0, vert=1, diag=2
+ { 6, 3, 8 }, // horiz=0, vert=1, diag=3
+ { 6, 3, 8 }}, // horiz=0, vert=1, diag=4
+ {{ 8, 4, 2 }, // horiz=0, vert=2, diag=0
+ { 8, 4, 5 }, // horiz=0, vert=2, diag=1
+ { 8, 4, 7 }, // horiz=0, vert=2, diag=2
+ { 8, 4, 8 }, // horiz=0, vert=2, diag=3
+ { 8, 4, 8 }}}, // horiz=0, vert=2, diag=4
+ {{{ 3, 5, 1 }, // horiz=1, vert=0, diag=0
+ { 3, 6, 4 }, // horiz=1, vert=0, diag=1
+ { 3, 6, 7 }, // horiz=1, vert=0, diag=2
+ { 3, 6, 8 }, // horiz=1, vert=0, diag=3
+ { 3, 6, 8 }}, // horiz=1, vert=0, diag=4
+ {{ 7, 7, 2 }, // horiz=1, vert=1, diag=0
+ { 7, 7, 5 }, // horiz=1, vert=1, diag=1
+ { 7, 7, 7 }, // horiz=1, vert=1, diag=2
+ { 7, 7, 8 }, // horiz=1, vert=1, diag=3
+ { 7, 7, 8 }}, // horiz=1, vert=1, diag=4
+ {{ 8, 7, 2 }, // horiz=1, vert=2, diag=0
+ { 8, 7, 5 }, // horiz=1, vert=2, diag=1
+ { 8, 7, 7 }, // horiz=1, vert=2, diag=2
+ { 8, 7, 8 }, // horiz=1, vert=2, diag=3
+ { 8, 7, 8 }}}, // horiz=1, vert=2, diag=4
+ {{{ 4, 8, 2 }, // horiz=2, vert=0, diag=0
+ { 4, 8, 5 }, // horiz=2, vert=0, diag=1
+ { 4, 8, 7 }, // horiz=2, vert=0, diag=2
+ { 4, 8, 8 }, // horiz=2, vert=0, diag=3
+ { 4, 8, 8 }}, // horiz=2, vert=0, diag=4
+ {{ 7, 8, 2 }, // horiz=2, vert=1, diag=0
+ { 7, 8, 5 }, // horiz=2, vert=1, diag=1
+ { 7, 8, 7 }, // horiz=2, vert=1, diag=2
+ { 7, 8, 8 }, // horiz=2, vert=1, diag=3
+ { 7, 8, 8 }}, // horiz=2, vert=1, diag=4
+ {{ 8, 8, 2 }, // horiz=2, vert=2, diag=0
+ { 8, 8, 5 }, // horiz=2, vert=2, diag=1
+ { 8, 8, 7 }, // horiz=2, vert=2, diag=2
+ { 8, 8, 8 }, // horiz=2, vert=2, diag=3
+ { 8, 8, 8 }}} // horiz=2, vert=2, diag=4
+};
+
+// arithmetic decoder context and xor bit for the sign bit in the
+// significance propagation pass:
+// [horiz][vert][k]
+// where horiz/vert are offset by 2 (i.e., range is -2 .. 2)
+// and k = 0 for the context
+// = 1 for the xor bit
+static Guint signContext[5][5][2] = {
+ {{ 13, 1 }, // horiz=-2, vert=-2
+ { 13, 1 }, // horiz=-2, vert=-1
+ { 12, 1 }, // horiz=-2, vert= 0
+ { 11, 1 }, // horiz=-2, vert=+1
+ { 11, 1 }}, // horiz=-2, vert=+2
+ {{ 13, 1 }, // horiz=-1, vert=-2
+ { 13, 1 }, // horiz=-1, vert=-1
+ { 12, 1 }, // horiz=-1, vert= 0
+ { 11, 1 }, // horiz=-1, vert=+1
+ { 11, 1 }}, // horiz=-1, vert=+2
+ {{ 10, 1 }, // horiz= 0, vert=-2
+ { 10, 1 }, // horiz= 0, vert=-1
+ { 9, 0 }, // horiz= 0, vert= 0
+ { 10, 0 }, // horiz= 0, vert=+1
+ { 10, 0 }}, // horiz= 0, vert=+2
+ {{ 11, 0 }, // horiz=+1, vert=-2
+ { 11, 0 }, // horiz=+1, vert=-1
+ { 12, 0 }, // horiz=+1, vert= 0
+ { 13, 0 }, // horiz=+1, vert=+1
+ { 13, 0 }}, // horiz=+1, vert=+2
+ {{ 11, 0 }, // horiz=+2, vert=-2
+ { 11, 0 }, // horiz=+2, vert=-1
+ { 12, 0 }, // horiz=+2, vert= 0
+ { 13, 0 }, // horiz=+2, vert=+1
+ { 13, 0 }}, // horiz=+2, vert=+2
+};
+
+//------------------------------------------------------------------------
+
+// constants used in the IDWT
+#define idwtAlpha -1.586134342059924
+#define idwtBeta -0.052980118572961
+#define idwtGamma 0.882911075530934
+#define idwtDelta 0.443506852043971
+#define idwtKappa 1.230174104914001
+#define idwtIKappa (1.0 / idwtKappa)
+
+// sum of the sample size (number of bits) and the number of bits to
+// the right of the decimal point for the fixed point arithmetic used
+// in the IDWT
+#define fracBits 24
+
+//------------------------------------------------------------------------
+
+// floor(x / y)
+#define jpxFloorDiv(x, y) ((x) / (y))
+
+// floor(x / 2^y)
+#define jpxFloorDivPow2(x, y) ((x) >> (y))
+
+// ceil(x / y)
+#define jpxCeilDiv(x, y) (((x) + (y) - 1) / (y))
+
+// ceil(x / 2^y)
+#define jpxCeilDivPow2(x, y) (((x) + (1 << (y)) - 1) >> (y))
+
+//------------------------------------------------------------------------
+
+#if 1 //----- disable coverage tracking
+
+#define cover(idx)
+
+#else //----- enable coverage tracking
+
+class JPXCover {
+public:
+
+ JPXCover(int sizeA);
+ ~JPXCover();
+ void incr(int idx);
+
+private:
+
+ int size, used;
+ int *data;
+};
+
+JPXCover::JPXCover(int sizeA) {
+ size = sizeA;
+ used = -1;
+ data = (int *)gmallocn(size, sizeof(int));
+ memset(data, 0, size * sizeof(int));
+}
+
+JPXCover::~JPXCover() {
+ int i;
+
+ printf("JPX coverage:\n");
+ for (i = 0; i <= used; ++i) {
+ printf(" %4d: %8d\n", i, data[i]);
+ }
+ gfree(data);
+}
+
+void JPXCover::incr(int idx) {
+ if (idx < size) {
+ ++data[idx];
+ if (idx > used) {
+ used = idx;
+ }
+ }
+}
+
+JPXCover jpxCover(150);
+
+#define cover(idx) jpxCover.incr(idx)
+
+#endif //----- coverage tracking
+
+//------------------------------------------------------------------------
+
+JPXStream::JPXStream(Stream *strA):
+ FilterStream(strA)
+{
+ bufStr = new BufStream(str, 3);
+
+ nComps = 0;
+ bpc = NULL;
+ width = height = 0;
+ reduction = 0;
+ haveCS = gFalse;
+
+ palette.bpc = NULL;
+ palette.c = NULL;
+ havePalette = gFalse;
+
+ compMap.comp = NULL;
+ compMap.type = NULL;
+ compMap.pComp = NULL;
+ haveCompMap = gFalse;
+
+ channelDefn.idx = NULL;
+ channelDefn.type = NULL;
+ channelDefn.assoc = NULL;
+ haveChannelDefn = gFalse;
+
+ img.tiles = NULL;
+
+ bitBuf = 0;
+ bitBufLen = 0;
+ bitBufSkip = gFalse;
+ byteCount = 0;
+}
+
+JPXStream::~JPXStream() {
+ close();
+ delete bufStr;
+}
+
+Stream *JPXStream::copy() {
+ return new JPXStream(str->copy());
+}
+
+void JPXStream::reset() {
+ img.ySize = 0;
+ bufStr->reset();
+ if (readBoxes() == jpxDecodeFatalError) {
+ // readBoxes reported an error, so we go immediately to EOF
+ curY = img.ySize >> reduction;
+ } else {
+ curY = img.yOffsetR;
+ }
+ curX = img.xOffsetR;
+ curComp = 0;
+ readBufLen = 0;
+}
+
+void JPXStream::close() {
+ JPXTile *tile;
+ JPXTileComp *tileComp;
+ JPXResLevel *resLevel;
+ JPXPrecinct *precinct;
+ JPXSubband *subband;
+ JPXCodeBlock *cb;
+ Guint comp, i, k, r, pre, sb;
+
+ gfree(bpc);
+ bpc = NULL;
+ if (havePalette) {
+ gfree(palette.bpc);
+ gfree(palette.c);
+ havePalette = gFalse;
+ }
+ if (haveCompMap) {
+ gfree(compMap.comp);
+ gfree(compMap.type);
+ gfree(compMap.pComp);
+ haveCompMap = gFalse;
+ }
+ if (haveChannelDefn) {
+ gfree(channelDefn.idx);
+ gfree(channelDefn.type);
+ gfree(channelDefn.assoc);
+ haveChannelDefn = gFalse;
+ }
+
+ if (img.tiles) {
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ tile = &img.tiles[i];
+ if (tile->tileComps) {
+ for (comp = 0; comp < img.nComps; ++comp) {
+ tileComp = &tile->tileComps[comp];
+ gfree(tileComp->quantSteps);
+ gfree(tileComp->data);
+ gfree(tileComp->buf);
+ if (tileComp->resLevels) {
+ for (r = 0; r <= tileComp->nDecompLevels; ++r) {
+ resLevel = &tileComp->resLevels[r];
+ if (resLevel->precincts) {
+ for (pre = 0; pre < resLevel->nPrecincts; ++pre) {
+ precinct = &resLevel->precincts[pre];
+ if (precinct->subbands) {
+ for (sb = 0; sb < (Guint)(r == 0 ? 1 : 3); ++sb) {
+ subband = &precinct->subbands[sb];
+ gfree(subband->inclusion);
+ gfree(subband->zeroBitPlane);
+ if (subband->cbs) {
+ for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) {
+ cb = &subband->cbs[k];
+ gfree(cb->dataLen);
+ gfree(cb->touched);
+ if (cb->arithDecoder) {
+ delete cb->arithDecoder;
+ }
+ if (cb->stats) {
+ delete cb->stats;
+ }
+ }
+ gfree(subband->cbs);
+ }
+ }
+ gfree(precinct->subbands);
+ }
+ }
+ gfree(resLevel->precincts);
+ }
+ }
+ gfree(tileComp->resLevels);
+ }
+ }
+ gfree(tile->tileComps);
+ }
+ }
+ gfree(img.tiles);
+ img.tiles = NULL;
+ }
+ bufStr->close();
+}
+
+int JPXStream::getChar() {
+ int c;
+
+ if (readBufLen < 8) {
+ fillReadBuf();
+ }
+ if (readBufLen == 8) {
+ c = readBuf & 0xff;
+ readBufLen = 0;
+ } else if (readBufLen > 8) {
+ c = (readBuf >> (readBufLen - 8)) & 0xff;
+ readBufLen -= 8;
+ } else if (readBufLen == 0) {
+ c = EOF;
+ } else {
+ c = (readBuf << (8 - readBufLen)) & 0xff;
+ readBufLen = 0;
+ }
+ return c;
+}
+
+int JPXStream::lookChar() {
+ int c;
+
+ if (readBufLen < 8) {
+ fillReadBuf();
+ }
+ if (readBufLen == 8) {
+ c = readBuf & 0xff;
+ } else if (readBufLen > 8) {
+ c = (readBuf >> (readBufLen - 8)) & 0xff;
+ } else if (readBufLen == 0) {
+ c = EOF;
+ } else {
+ c = (readBuf << (8 - readBufLen)) & 0xff;
+ }
+ return c;
+}
+
+void JPXStream::fillReadBuf() {
+ JPXTileComp *tileComp;
+ Guint tileIdx, tx, ty;
+ int pix, pixBits, k;
+ GBool eol;
+
+ do {
+ if (curY >= (img.ySize >> reduction)) {
+ return;
+ }
+ tileIdx = (((curY << reduction) - img.yTileOffset) / img.yTileSize)
+ * img.nXTiles
+ + ((curX << reduction) - img.xTileOffset) / img.xTileSize;
+#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
+ tileComp = &img.tiles[tileIdx].tileComps[curComp];
+#else
+ tileComp = &img.tiles[tileIdx].tileComps[havePalette ? 0 : curComp];
+#endif
+ tx = jpxFloorDiv(curX - jpxCeilDivPow2(img.tiles[tileIdx].x0, reduction),
+ tileComp->hSep);
+ ty = jpxFloorDiv(curY - jpxCeilDivPow2(img.tiles[tileIdx].y0, reduction),
+ tileComp->vSep);
+ pix = (int)tileComp->data[ty * tileComp->w + tx];
+ pixBits = tileComp->prec;
+ eol = gFalse;
+#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
+ if (++curComp == img.nComps) {
+#else
+ if (havePalette) {
+ if (pix >= 0 && pix < palette.nEntries) {
+ pix = palette.c[pix * palette.nComps + curComp];
+ } else {
+ pix = 0;
+ }
+ pixBits = palette.bpc[curComp];
+ }
+ if (++curComp == (Guint)(havePalette ? palette.nComps : img.nComps)) {
+#endif
+ curComp = 0;
+ if (++curX == (img.xSize >> reduction)) {
+ curX = img.xOffsetR;
+ ++curY;
+ eol = gTrue;
+ }
+ }
+ if (pixBits == 8) {
+ readBuf = (readBuf << 8) | (pix & 0xff);
+ } else {
+ readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1));
+ }
+ readBufLen += pixBits;
+ if (eol && (k = readBufLen & 7)) {
+ readBuf <<= 8 - k;
+ readBufLen += 8 - k;
+ }
+ } while (readBufLen < 8);
+}
+
+GString *JPXStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ return NULL;
+}
+
+GBool JPXStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+void JPXStream::getImageParams(int *bitsPerComponent,
+ StreamColorSpaceMode *csMode) {
+ Guint boxType, boxLen, dataLen, csEnum;
+ Guint bpc1, dummy;
+ int csMeth, csPrec, csPrec1, dummy2;
+ StreamColorSpaceMode csMode1;
+ GBool haveBPC, haveCSMode;
+
+ csPrec = 0; // make gcc happy
+ haveBPC = haveCSMode = gFalse;
+ bufStr->reset();
+ if (bufStr->lookChar() == 0xff) {
+ getImageParams2(bitsPerComponent, csMode);
+ } else {
+ while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
+ if (boxType == 0x6a703268) { // JP2 header
+ cover(0);
+ // skip the superbox
+ } else if (boxType == 0x69686472) { // image header
+ cover(1);
+ if (readULong(&dummy) &&
+ readULong(&dummy) &&
+ readUWord(&dummy) &&
+ readUByte(&bpc1) &&
+ readUByte(&dummy) &&
+ readUByte(&dummy) &&
+ readUByte(&dummy)) {
+ *bitsPerComponent = bpc1 + 1;
+ haveBPC = gTrue;
+ }
+ } else if (boxType == 0x636F6C72) { // color specification
+ cover(2);
+ if (readByte(&csMeth) &&
+ readByte(&csPrec1) &&
+ readByte(&dummy2)) {
+ if (csMeth == 1) {
+ if (readULong(&csEnum)) {
+ csMode1 = streamCSNone;
+ if (csEnum == jpxCSBiLevel ||
+ csEnum == jpxCSGrayscale) {
+ csMode1 = streamCSDeviceGray;
+ } else if (csEnum == jpxCSCMYK) {
+ csMode1 = streamCSDeviceCMYK;
+ } else if (csEnum == jpxCSsRGB ||
+ csEnum == jpxCSCISesRGB ||
+ csEnum == jpxCSROMMRGB) {
+ csMode1 = streamCSDeviceRGB;
+ }
+ if (csMode1 != streamCSNone &&
+ (!haveCSMode || csPrec1 > csPrec)) {
+ *csMode = csMode1;
+ csPrec = csPrec1;
+ haveCSMode = gTrue;
+ }
+ if (dataLen > 7) {
+ bufStr->discardChars(dataLen - 7);
+ }
+ }
+ } else {
+ if (dataLen > 3) {
+ bufStr->discardChars(dataLen - 3);
+ }
+ }
+ }
+ } else if (boxType == 0x6A703263) { // codestream
+ cover(3);
+ if (!(haveBPC && haveCSMode)) {
+ getImageParams2(bitsPerComponent, csMode);
+ }
+ break;
+ } else {
+ cover(4);
+ bufStr->discardChars(dataLen);
+ }
+ }
+ }
+ bufStr->close();
+}
+
+// Get image parameters from the codestream.
+void JPXStream::getImageParams2(int *bitsPerComponent,
+ StreamColorSpaceMode *csMode) {
+ int segType;
+ Guint segLen, nComps1, bpc1, dummy;
+
+ while (readMarkerHdr(&segType, &segLen)) {
+ if (segType == 0x51) { // SIZ - image and tile size
+ cover(5);
+ if (readUWord(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readUWord(&nComps1) &&
+ readUByte(&bpc1)) {
+ *bitsPerComponent = (bpc1 & 0x7f) + 1;
+ // if there's no color space info, take a guess
+ if (nComps1 == 1) {
+ *csMode = streamCSDeviceGray;
+ } else if (nComps1 == 3) {
+ *csMode = streamCSDeviceRGB;
+ } else if (nComps1 == 4) {
+ *csMode = streamCSDeviceCMYK;
+ }
+ }
+ break;
+ } else {
+ cover(6);
+ if (segLen > 2) {
+ bufStr->discardChars(segLen - 2);
+ }
+ }
+ }
+}
+
+JPXDecodeResult JPXStream::readBoxes() {
+ JPXDecodeResult result;
+ GBool haveCodestream;
+ Guint boxType, boxLen, dataLen;
+ Guint w, h, n, bpc1, compression, unknownColorspace, ipr;
+ Guint i, j;
+
+ haveImgHdr = gFalse;
+ haveCodestream = gFalse;
+
+ // check for a naked JPEG 2000 codestream (without the JP2/JPX
+ // wrapper) -- this appears to be a violation of the PDF spec, but
+ // Acrobat allows it
+ if (bufStr->lookChar() == 0xff) {
+ cover(7);
+ error(errSyntaxWarning, getPos(),
+ "Naked JPEG 2000 codestream, missing JP2/JPX wrapper");
+ if ((result = readCodestream(0)) == jpxDecodeFatalError) {
+ return result;
+ }
+ nComps = img.nComps;
+ bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
+ for (i = 0; i < nComps; ++i) {
+ bpc[i] = img.tiles[0].tileComps[i].prec;
+ }
+ width = img.xSize - img.xOffset;
+ height = img.ySize - img.yOffset;
+ return result;
+ }
+
+ while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
+ switch (boxType) {
+ case 0x6a703268: // JP2 header
+ // this is a grouping box ('superbox') which has no real
+ // contents and doesn't appear to be used consistently, i.e.,
+ // some things which should be subboxes of the JP2 header box
+ // show up outside of it - so we simply ignore the JP2 header
+ // box
+ cover(8);
+ break;
+ case 0x69686472: // image header
+ cover(9);
+ if (!readULong(&h) ||
+ !readULong(&w) ||
+ !readUWord(&n) ||
+ !readUByte(&bpc1) ||
+ !readUByte(&compression) ||
+ !readUByte(&unknownColorspace) ||
+ !readUByte(&ipr)) {
+ error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ if (compression != 7) {
+ error(errSyntaxError, getPos(),
+ "Unknown compression type in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ height = h;
+ width = w;
+ nComps = n;
+ bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
+ for (i = 0; i < nComps; ++i) {
+ bpc[i] = bpc1;
+ }
+ haveImgHdr = gTrue;
+ break;
+ case 0x62706363: // bits per component
+ cover(10);
+ if (!haveImgHdr) {
+ error(errSyntaxError, getPos(),
+ "Found bits per component box before image header box in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ if (dataLen != nComps) {
+ error(errSyntaxError, getPos(),
+ "Invalid bits per component box in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ for (i = 0; i < nComps; ++i) {
+ if (!readUByte(&bpc[i])) {
+ error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ }
+ break;
+ case 0x636F6C72: // color specification
+ cover(11);
+ if (!readColorSpecBox(dataLen)) {
+ return jpxDecodeFatalError;
+ }
+ break;
+ case 0x70636c72: // palette
+ cover(12);
+ if (!readUWord(&palette.nEntries) ||
+ !readUByte(&palette.nComps)) {
+ error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ havePalette = gTrue;
+ palette.bpc = (Guint *)gmallocn(palette.nComps, sizeof(Guint));
+ palette.c =
+ (int *)gmallocn(palette.nEntries * palette.nComps, sizeof(int));
+ for (i = 0; i < palette.nComps; ++i) {
+ if (!readUByte(&palette.bpc[i])) {
+ error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ ++palette.bpc[i];
+ }
+ for (i = 0; i < palette.nEntries; ++i) {
+ for (j = 0; j < palette.nComps; ++j) {
+ if (!readNBytes(((palette.bpc[j] & 0x7f) + 7) >> 3,
+ (palette.bpc[j] & 0x80) ? gTrue : gFalse,
+ &palette.c[i * palette.nComps + j])) {
+ error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ }
+ }
+ break;
+ case 0x636d6170: // component mapping
+ cover(13);
+ haveCompMap = gTrue;
+ compMap.nChannels = dataLen / 4;
+ compMap.comp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
+ compMap.type = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
+ compMap.pComp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
+ for (i = 0; i < compMap.nChannels; ++i) {
+ if (!readUWord(&compMap.comp[i]) ||
+ !readUByte(&compMap.type[i]) ||
+ !readUByte(&compMap.pComp[i])) {
+ error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ }
+ break;
+ case 0x63646566: // channel definition
+ cover(14);
+ if (!readUWord(&channelDefn.nChannels)) {
+ error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ haveChannelDefn = gTrue;
+ channelDefn.idx =
+ (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
+ channelDefn.type =
+ (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
+ channelDefn.assoc =
+ (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
+ for (i = 0; i < channelDefn.nChannels; ++i) {
+ if (!readUWord(&channelDefn.idx[i]) ||
+ !readUWord(&channelDefn.type[i]) ||
+ !readUWord(&channelDefn.assoc[i])) {
+ error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ }
+ break;
+ case 0x6A703263: // contiguous codestream
+ cover(15);
+ if (!bpc) {
+ error(errSyntaxError, getPos(),
+ "JPX stream is missing the image header box");
+ }
+ if (!haveCS) {
+ error(errSyntaxError, getPos(),
+ "JPX stream has no supported color spec");
+ }
+ if ((result = readCodestream(dataLen)) != jpxDecodeOk) {
+ return result;
+ }
+ haveCodestream = gTrue;
+ break;
+ default:
+ cover(16);
+ if (bufStr->discardChars(dataLen) != dataLen) {
+ error(errSyntaxError, getPos(), "Unexpected EOF in JPX stream");
+ return haveCodestream ? jpxDecodeNonFatalError : jpxDecodeFatalError;
+ }
+ break;
+ }
+ }
+ return jpxDecodeOk;
+}
+
+GBool JPXStream::readColorSpecBox(Guint dataLen) {
+ JPXColorSpec newCS;
+ Guint csApprox, csEnum;
+ GBool ok;
+
+ ok = gFalse;
+ if (!readUByte(&newCS.meth) ||
+ !readByte(&newCS.prec) ||
+ !readUByte(&csApprox)) {
+ goto err;
+ }
+ switch (newCS.meth) {
+ case 1: // enumerated colorspace
+ cover(17);
+ if (!readULong(&csEnum)) {
+ goto err;
+ }
+ newCS.enumerated.type = (JPXColorSpaceType)csEnum;
+ switch (newCS.enumerated.type) {
+ case jpxCSBiLevel:
+ ok = gTrue;
+ break;
+ case jpxCSYCbCr1:
+ ok = gTrue;
+ break;
+ case jpxCSYCbCr2:
+ ok = gTrue;
+ break;
+ case jpxCSYCBCr3:
+ ok = gTrue;
+ break;
+ case jpxCSPhotoYCC:
+ ok = gTrue;
+ break;
+ case jpxCSCMY:
+ ok = gTrue;
+ break;
+ case jpxCSCMYK:
+ ok = gTrue;
+ break;
+ case jpxCSYCCK:
+ ok = gTrue;
+ break;
+ case jpxCSCIELab:
+ if (dataLen == 7 + 7*4) {
+ if (!readULong(&newCS.enumerated.cieLab.rl) ||
+ !readULong(&newCS.enumerated.cieLab.ol) ||
+ !readULong(&newCS.enumerated.cieLab.ra) ||
+ !readULong(&newCS.enumerated.cieLab.oa) ||
+ !readULong(&newCS.enumerated.cieLab.rb) ||
+ !readULong(&newCS.enumerated.cieLab.ob) ||
+ !readULong(&newCS.enumerated.cieLab.il)) {
+ goto err;
+ }
+ } else if (dataLen == 7) {
+ //~ this assumes the 8-bit case
+ cover(92);
+ newCS.enumerated.cieLab.rl = 100;
+ newCS.enumerated.cieLab.ol = 0;
+ newCS.enumerated.cieLab.ra = 255;
+ newCS.enumerated.cieLab.oa = 128;
+ newCS.enumerated.cieLab.rb = 255;
+ newCS.enumerated.cieLab.ob = 96;
+ newCS.enumerated.cieLab.il = 0x00443530;
+ } else {
+ goto err;
+ }
+ ok = gTrue;
+ break;
+ case jpxCSsRGB:
+ ok = gTrue;
+ break;
+ case jpxCSGrayscale:
+ ok = gTrue;
+ break;
+ case jpxCSBiLevel2:
+ ok = gTrue;
+ break;
+ case jpxCSCIEJab:
+ // not allowed in PDF
+ goto err;
+ case jpxCSCISesRGB:
+ ok = gTrue;
+ break;
+ case jpxCSROMMRGB:
+ ok = gTrue;
+ break;
+ case jpxCSsRGBYCbCr:
+ ok = gTrue;
+ break;
+ case jpxCSYPbPr1125:
+ ok = gTrue;
+ break;
+ case jpxCSYPbPr1250:
+ ok = gTrue;
+ break;
+ default:
+ goto err;
+ }
+ break;
+ case 2: // restricted ICC profile
+ case 3: // any ICC profile (JPX)
+ case 4: // vendor color (JPX)
+ cover(18);
+ if (dataLen > 3 &&
+ bufStr->discardChars(dataLen - 3) != dataLen - 3) {
+ goto err;
+ }
+ break;
+ }
+
+ if (ok && (!haveCS || newCS.prec > cs.prec)) {
+ cs = newCS;
+ haveCS = gTrue;
+ }
+
+ return gTrue;
+
+ err:
+ error(errSyntaxError, getPos(), "Error in JPX color spec");
+ return gFalse;
+}
+
+JPXDecodeResult JPXStream::readCodestream(Guint len) {
+ JPXTile *tile;
+ JPXTileComp *tileComp;
+ int segType;
+ GBool haveSIZ, haveCOD, haveQCD, haveSOT, ok;
+ Guint style, progOrder, nLayers, multiComp, nDecompLevels;
+ Guint codeBlockW, codeBlockH, codeBlockStyle, transform;
+ Guint precinctSize;
+ Guint segLen, capabilities, comp, i, j, r;
+
+ //----- main header
+ haveSIZ = haveCOD = haveQCD = haveSOT = gFalse;
+ do {
+ if (!readMarkerHdr(&segType, &segLen)) {
+ error(errSyntaxError, getPos(), "Error in JPX codestream");
+ return jpxDecodeFatalError;
+ }
+ switch (segType) {
+ case 0x4f: // SOC - start of codestream
+ // marker only
+ cover(19);
+ break;
+ case 0x51: // SIZ - image and tile size
+ cover(20);
+ if (haveSIZ) {
+ error(errSyntaxError, getPos(),
+ "Duplicate SIZ marker segment in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ if (!readUWord(&capabilities) ||
+ !readULong(&img.xSize) ||
+ !readULong(&img.ySize) ||
+ !readULong(&img.xOffset) ||
+ !readULong(&img.yOffset) ||
+ !readULong(&img.xTileSize) ||
+ !readULong(&img.yTileSize) ||
+ !readULong(&img.xTileOffset) ||
+ !readULong(&img.yTileOffset) ||
+ !readUWord(&img.nComps)) {
+ error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
+ return jpxDecodeFatalError;
+ }
+ if (haveImgHdr && img.nComps != nComps) {
+ error(errSyntaxError, getPos(),
+ "Different number of components in JPX SIZ marker segment");
+ return jpxDecodeFatalError;
+ }
+ if (img.xSize == 0 || img.ySize == 0 ||
+ img.xOffset >= img.xSize || img.yOffset >= img.ySize ||
+ img.xTileSize == 0 || img.yTileSize == 0 ||
+ img.xTileOffset > img.xOffset ||
+ img.yTileOffset > img.yOffset ||
+ img.xTileSize + img.xTileOffset <= img.xOffset ||
+ img.yTileSize + img.yTileOffset <= img.yOffset ||
+ img.nComps == 0) {
+ error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
+ return jpxDecodeFatalError;
+ }
+ img.xSizeR = jpxCeilDivPow2(img.xSize, reduction);
+ img.ySizeR = jpxCeilDivPow2(img.ySize, reduction);
+ img.xOffsetR = jpxCeilDivPow2(img.xOffset, reduction);
+ img.yOffsetR = jpxCeilDivPow2(img.yOffset, reduction);
+ img.nXTiles = (img.xSize - img.xTileOffset + img.xTileSize - 1)
+ / img.xTileSize;
+ img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1)
+ / img.yTileSize;
+ // check for overflow before allocating memory
+ if (img.nXTiles <= 0 || img.nYTiles <= 0 ||
+ img.nXTiles >= INT_MAX / img.nYTiles) {
+ error(errSyntaxError, getPos(),
+ "Bad tile count in JPX SIZ marker segment");
+ return jpxDecodeFatalError;
+ }
+ img.tiles = (JPXTile *)gmallocn(img.nXTiles * img.nYTiles,
+ sizeof(JPXTile));
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ img.tiles[i].init = gFalse;
+ img.tiles[i].nextTilePart = 0;
+ img.tiles[i].tileComps = NULL;
+ }
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ img.tiles[i].tileComps = (JPXTileComp *)gmallocn(img.nComps,
+ sizeof(JPXTileComp));
+ for (comp = 0; comp < img.nComps; ++comp) {
+ img.tiles[i].tileComps[comp].quantSteps = NULL;
+ img.tiles[i].tileComps[comp].data = NULL;
+ img.tiles[i].tileComps[comp].buf = NULL;
+ img.tiles[i].tileComps[comp].resLevels = NULL;
+ }
+ }
+ for (comp = 0; comp < img.nComps; ++comp) {
+ if (!readUByte(&img.tiles[0].tileComps[comp].prec) ||
+ !readUByte(&img.tiles[0].tileComps[comp].hSep) ||
+ !readUByte(&img.tiles[0].tileComps[comp].vSep)) {
+ error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
+ return jpxDecodeFatalError;
+ }
+ if (img.tiles[0].tileComps[comp].hSep == 0 ||
+ img.tiles[0].tileComps[comp].vSep == 0) {
+ error(errSyntaxError, getPos(), "Error in JPX SIZ marker segment");
+ return jpxDecodeFatalError;
+ }
+ img.tiles[0].tileComps[comp].sgned =
+ (img.tiles[0].tileComps[comp].prec & 0x80) ? gTrue : gFalse;
+ img.tiles[0].tileComps[comp].prec =
+ (img.tiles[0].tileComps[comp].prec & 0x7f) + 1;
+ for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
+ img.tiles[i].tileComps[comp] = img.tiles[0].tileComps[comp];
+ }
+ }
+ haveSIZ = gTrue;
+ break;
+ case 0x52: // COD - coding style default
+ cover(21);
+ if (!haveSIZ) {
+ error(errSyntaxError, getPos(),
+ "JPX COD marker segment before SIZ segment");
+ return jpxDecodeFatalError;
+ }
+ if (!readUByte(&style) ||
+ !readUByte(&progOrder) ||
+ !readUWord(&nLayers) ||
+ !readUByte(&multiComp) ||
+ !readUByte(&nDecompLevels) ||
+ !readUByte(&codeBlockW) ||
+ !readUByte(&codeBlockH) ||
+ !readUByte(&codeBlockStyle) ||
+ !readUByte(&transform)) {
+ error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
+ return jpxDecodeFatalError;
+ }
+ if (nDecompLevels < 1 ||
+ nDecompLevels > 31 ||
+ codeBlockW > 8 ||
+ codeBlockH > 8) {
+ error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
+ return jpxDecodeFatalError;
+ }
+ codeBlockW += 2;
+ codeBlockH += 2;
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ img.tiles[i].progOrder = progOrder;
+ img.tiles[i].nLayers = nLayers;
+ img.tiles[i].multiComp = multiComp;
+ for (comp = 0; comp < img.nComps; ++comp) {
+ img.tiles[i].tileComps[comp].style = style;
+ img.tiles[i].tileComps[comp].nDecompLevels = nDecompLevels;
+ img.tiles[i].tileComps[comp].codeBlockW = codeBlockW;
+ img.tiles[i].tileComps[comp].codeBlockH = codeBlockH;
+ img.tiles[i].tileComps[comp].codeBlockStyle = codeBlockStyle;
+ img.tiles[i].tileComps[comp].transform = transform;
+ img.tiles[i].tileComps[comp].resLevels =
+ (JPXResLevel *)gmallocn(nDecompLevels + 1, sizeof(JPXResLevel));
+ for (r = 0; r <= nDecompLevels; ++r) {
+ img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;
+ }
+ }
+ }
+ for (r = 0; r <= nDecompLevels; ++r) {
+ if (style & 0x01) {
+ cover(91);
+ if (!readUByte(&precinctSize)) {
+ error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
+ return jpxDecodeFatalError;
+ }
+ img.tiles[0].tileComps[0].resLevels[r].precinctWidth =
+ precinctSize & 0x0f;
+ img.tiles[0].tileComps[0].resLevels[r].precinctHeight =
+ (precinctSize >> 4) & 0x0f;
+ } else {
+ img.tiles[0].tileComps[0].resLevels[r].precinctWidth = 15;
+ img.tiles[0].tileComps[0].resLevels[r].precinctHeight = 15;
+ }
+ }
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ for (comp = 0; comp < img.nComps; ++comp) {
+ if (!(i == 0 && comp == 0)) {
+ for (r = 0; r <= nDecompLevels; ++r) {
+ img.tiles[i].tileComps[comp].resLevels[r].precinctWidth =
+ img.tiles[0].tileComps[0].resLevels[r].precinctWidth;
+ img.tiles[i].tileComps[comp].resLevels[r].precinctHeight =
+ img.tiles[0].tileComps[0].resLevels[r].precinctHeight;
+ }
+ }
+ }
+ }
+ haveCOD = gTrue;
+ break;
+ case 0x53: // COC - coding style component
+ cover(22);
+ if (!haveCOD) {
+ error(errSyntaxError, getPos(),
+ "JPX COC marker segment before COD segment");
+ return jpxDecodeFatalError;
+ }
+ if ((img.nComps > 256 && !readUWord(&comp)) ||
+ (img.nComps <= 256 && !readUByte(&comp)) ||
+ comp >= img.nComps ||
+ !readUByte(&style) ||
+ !readUByte(&nDecompLevels) ||
+ !readUByte(&codeBlockW) ||
+ !readUByte(&codeBlockH) ||
+ !readUByte(&codeBlockStyle) ||
+ !readUByte(&transform)) {
+ error(errSyntaxError, getPos(), "Error in JPX COC marker segment");
+ return jpxDecodeFatalError;
+ }
+ if (nDecompLevels < 1 ||
+ nDecompLevels > 31 ||
+ codeBlockW > 8 ||
+ codeBlockH > 8) {
+ error(errSyntaxError, getPos(), "Error in JPX COC marker segment");
+ return jpxDecodeFatalError;
+ }
+ style = (img.tiles[0].tileComps[comp].style & ~1) | (style & 1);
+ codeBlockW += 2;
+ codeBlockH += 2;
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ img.tiles[i].tileComps[comp].style = style;
+ img.tiles[i].tileComps[comp].nDecompLevels = nDecompLevels;
+ img.tiles[i].tileComps[comp].codeBlockW = codeBlockW;
+ img.tiles[i].tileComps[comp].codeBlockH = codeBlockH;
+ img.tiles[i].tileComps[comp].codeBlockStyle = codeBlockStyle;
+ img.tiles[i].tileComps[comp].transform = transform;
+ img.tiles[i].tileComps[comp].resLevels =
+ (JPXResLevel *)greallocn(
+ img.tiles[i].tileComps[comp].resLevels,
+ nDecompLevels + 1,
+ sizeof(JPXResLevel));
+ for (r = 0; r <= nDecompLevels; ++r) {
+ img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;
+ }
+ }
+ for (r = 0; r <= nDecompLevels; ++r) {
+ if (style & 0x01) {
+ if (!readUByte(&precinctSize)) {
+ error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
+ return jpxDecodeFatalError;
+ }
+ img.tiles[0].tileComps[comp].resLevels[r].precinctWidth =
+ precinctSize & 0x0f;
+ img.tiles[0].tileComps[comp].resLevels[r].precinctHeight =
+ (precinctSize >> 4) & 0x0f;
+ } else {
+ img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = 15;
+ img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = 15;
+ }
+ }
+ for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
+ for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+ img.tiles[i].tileComps[comp].resLevels[r].precinctWidth =
+ img.tiles[0].tileComps[comp].resLevels[r].precinctWidth;
+ img.tiles[i].tileComps[comp].resLevels[r].precinctHeight =
+ img.tiles[0].tileComps[comp].resLevels[r].precinctHeight;
+ }
+ }
+ break;
+ case 0x5c: // QCD - quantization default
+ cover(23);
+ if (!haveSIZ) {
+ error(errSyntaxError, getPos(),
+ "JPX QCD marker segment before SIZ segment");
+ return jpxDecodeFatalError;
+ }
+ if (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return jpxDecodeFatalError;
+ }
+ if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) {
+ if (segLen <= 3) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return jpxDecodeFatalError;
+ }
+ img.tiles[0].tileComps[0].nQuantSteps = segLen - 3;
+ img.tiles[0].tileComps[0].quantSteps =
+ (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
+ img.tiles[0].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
+ if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return jpxDecodeFatalError;
+ }
+ }
+ } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) {
+ img.tiles[0].tileComps[0].nQuantSteps = 1;
+ img.tiles[0].tileComps[0].quantSteps =
+ (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
+ img.tiles[0].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return jpxDecodeFatalError;
+ }
+ } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) {
+ if (segLen < 5) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return jpxDecodeFatalError;
+ }
+ img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2;
+ img.tiles[0].tileComps[0].quantSteps =
+ (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
+ img.tiles[0].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
+ if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return jpxDecodeFatalError;
+ }
+ }
+ } else {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return jpxDecodeFatalError;
+ }
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ for (comp = 0; comp < img.nComps; ++comp) {
+ if (!(i == 0 && comp == 0)) {
+ img.tiles[i].tileComps[comp].quantStyle =
+ img.tiles[0].tileComps[0].quantStyle;
+ img.tiles[i].tileComps[comp].nQuantSteps =
+ img.tiles[0].tileComps[0].nQuantSteps;
+ img.tiles[i].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[i].tileComps[comp].quantSteps,
+ img.tiles[0].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ for (j = 0; j < img.tiles[0].tileComps[0].nQuantSteps; ++j) {
+ img.tiles[i].tileComps[comp].quantSteps[j] =
+ img.tiles[0].tileComps[0].quantSteps[j];
+ }
+ }
+ }
+ }
+ haveQCD = gTrue;
+ break;
+ case 0x5d: // QCC - quantization component
+ cover(24);
+ if (!haveQCD) {
+ error(errSyntaxError, getPos(),
+ "JPX QCC marker segment before QCD segment");
+ return jpxDecodeFatalError;
+ }
+ if ((img.nComps > 256 && !readUWord(&comp)) ||
+ (img.nComps <= 256 && !readUByte(&comp)) ||
+ comp >= img.nComps ||
+ !readUByte(&img.tiles[0].tileComps[comp].quantStyle)) {
+ error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
+ return jpxDecodeFatalError;
+ }
+ if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x00) {
+ if (segLen <= (img.nComps > 256 ? 5U : 4U)) {
+ error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
+ return jpxDecodeFatalError;
+ }
+ img.tiles[0].tileComps[comp].nQuantSteps =
+ segLen - (img.nComps > 256 ? 5 : 4);
+ img.tiles[0].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
+ img.tiles[0].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
+ if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) {
+ error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
+ return jpxDecodeFatalError;
+ }
+ }
+ } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) {
+ img.tiles[0].tileComps[comp].nQuantSteps = 1;
+ img.tiles[0].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
+ img.tiles[0].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) {
+ error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
+ return jpxDecodeFatalError;
+ }
+ } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x02) {
+ if (segLen < (img.nComps > 256 ? 5U : 4U) + 2) {
+ error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
+ return jpxDecodeFatalError;
+ }
+ img.tiles[0].tileComps[comp].nQuantSteps =
+ (segLen - (img.nComps > 256 ? 5 : 4)) / 2;
+ img.tiles[0].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
+ img.tiles[0].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
+ if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return jpxDecodeFatalError;
+ }
+ }
+ } else {
+ error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
+ return jpxDecodeFatalError;
+ }
+ for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
+ img.tiles[i].tileComps[comp].quantStyle =
+ img.tiles[0].tileComps[comp].quantStyle;
+ img.tiles[i].tileComps[comp].nQuantSteps =
+ img.tiles[0].tileComps[comp].nQuantSteps;
+ img.tiles[i].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[i].tileComps[comp].quantSteps,
+ img.tiles[0].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ for (j = 0; j < img.tiles[0].tileComps[comp].nQuantSteps; ++j) {
+ img.tiles[i].tileComps[comp].quantSteps[j] =
+ img.tiles[0].tileComps[comp].quantSteps[j];
+ }
+ }
+ break;
+ case 0x5e: // RGN - region of interest
+ cover(25);
+#if 1 //~ ROI is unimplemented
+ error(errUnimplemented, -1, "got a JPX RGN segment");
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
+ return jpxDecodeFatalError;
+ }
+#else
+ if ((img.nComps > 256 && !readUWord(&comp)) ||
+ (img.nComps <= 256 && !readUByte(&comp)) ||
+ comp >= img.nComps ||
+ !readUByte(&compInfo[comp].defROI.style) ||
+ !readUByte(&compInfo[comp].defROI.shift)) {
+ error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
+ return jpxDecodeFatalError;
+ }
+#endif
+ break;
+ case 0x5f: // POC - progression order change
+ cover(26);
+#if 1 //~ progression order changes are unimplemented
+ error(errUnimplemented, -1, "got a JPX POC segment");
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
+ return jpxDecodeFatalError;
+ }
+#else
+ nProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
+ progs = (JPXProgOrder *)gmallocn(nProgs, sizeof(JPXProgOrder));
+ for (i = 0; i < nProgs; ++i) {
+ if (!readUByte(&progs[i].startRes) ||
+ !(img.nComps > 256 && readUWord(&progs[i].startComp)) ||
+ !(img.nComps <= 256 && readUByte(&progs[i].startComp)) ||
+ !readUWord(&progs[i].endLayer) ||
+ !readUByte(&progs[i].endRes) ||
+ !(img.nComps > 256 && readUWord(&progs[i].endComp)) ||
+ !(img.nComps <= 256 && readUByte(&progs[i].endComp)) ||
+ !readUByte(&progs[i].progOrder)) {
+ error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
+ return jpxDecodeFatalError;
+ }
+ }
+#endif
+ break;
+ case 0x60: // PPM - packed packet headers, main header
+ cover(27);
+#if 1 //~ packed packet headers are unimplemented
+ error(errUnimplemented, -1, "Got a JPX PPM segment");
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PPM marker segment");
+ return jpxDecodeFatalError;
+ }
+#endif
+ break;
+ case 0x55: // TLM - tile-part lengths
+ // skipped
+ cover(28);
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX TLM marker segment");
+ return jpxDecodeFatalError;
+ }
+ break;
+ case 0x57: // PLM - packet length, main header
+ // skipped
+ cover(29);
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PLM marker segment");
+ return jpxDecodeFatalError;
+ }
+ break;
+ case 0x63: // CRG - component registration
+ // skipped
+ cover(30);
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX CRG marker segment");
+ return jpxDecodeFatalError;
+ }
+ break;
+ case 0x64: // COM - comment
+ // skipped
+ cover(31);
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
+ return jpxDecodeFatalError;
+ }
+ break;
+ case 0x90: // SOT - start of tile
+ cover(32);
+ haveSOT = gTrue;
+ break;
+ default:
+ cover(33);
+ error(errSyntaxError, getPos(),
+ "Unknown marker segment {0:02x} in JPX stream", segType);
+ if (segLen > 2) {
+ bufStr->discardChars(segLen - 2);
+ }
+ break;
+ }
+ } while (!haveSOT);
+
+ if (!haveSIZ) {
+ error(errSyntaxError, getPos(),
+ "Missing SIZ marker segment in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ if (!haveCOD) {
+ error(errSyntaxError, getPos(),
+ "Missing COD marker segment in JPX stream");
+ return jpxDecodeFatalError;
+ }
+ if (!haveQCD) {
+ error(errSyntaxError, getPos(),
+ "Missing QCD marker segment in JPX stream");
+ return jpxDecodeFatalError;
+ }
+
+ //----- read the tile-parts
+ ok = gTrue;
+ while (1) {
+ if (!readTilePart()) {
+ ok = gFalse;
+ break;
+ }
+ if (!readMarkerHdr(&segType, &segLen)) {
+ error(errSyntaxError, getPos(), "Error in JPX codestream");
+ ok = gFalse;
+ break;
+ }
+ if (segType != 0x90) { // SOT - start of tile
+ break;
+ }
+ }
+
+ if (segType != 0xd9) { // EOC - end of codestream
+ error(errSyntaxError, getPos(), "Missing EOC marker in JPX codestream");
+ ok = gFalse;
+ }
+
+ //----- finish decoding the image
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ tile = &img.tiles[i];
+ if (!tile->init) {
+ error(errSyntaxError, getPos(), "Uninitialized tile in JPX codestream");
+ return jpxDecodeFatalError;
+ }
+ for (comp = 0; comp < img.nComps; ++comp) {
+ tileComp = &tile->tileComps[comp];
+ inverseTransform(tileComp);
+ }
+ if (!inverseMultiCompAndDC(tile)) {
+ return jpxDecodeFatalError;
+ }
+ }
+
+ //~ can free memory below tileComps here, and also tileComp.buf
+
+ return ok ? jpxDecodeOk : jpxDecodeNonFatalError;
+}
+
+GBool JPXStream::readTilePart() {
+ JPXTile *tile;
+ JPXTileComp *tileComp;
+ JPXResLevel *resLevel;
+ JPXPrecinct *precinct;
+ JPXSubband *subband;
+ JPXCodeBlock *cb;
+ int *sbCoeffs;
+ GBool haveSOD;
+ Guint tileIdx, tilePartLen, tilePartIdx, nTileParts;
+ GBool tilePartToEOC;
+ Guint style, progOrder, nLayers, multiComp, nDecompLevels;
+ Guint codeBlockW, codeBlockH, codeBlockStyle, transform;
+ Guint precinctSize, qStyle;
+ 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
+ if (!readUWord(&tileIdx) ||
+ !readULong(&tilePartLen) ||
+ !readUByte(&tilePartIdx) ||
+ !readUByte(&nTileParts)) {
+ error(errSyntaxError, getPos(), "Error in JPX SOT marker segment");
+ return gFalse;
+ }
+
+ // check tileIdx and tilePartIdx
+ // (this ignores nTileParts, because some encoders get it wrong)
+ if (tileIdx >= img.nXTiles * img.nYTiles ||
+ tilePartIdx != img.tiles[tileIdx].nextTilePart ||
+ (tilePartIdx > 0 && !img.tiles[tileIdx].init) ||
+ (tilePartIdx == 0 && img.tiles[tileIdx].init)) {
+ error(errSyntaxError, getPos(), "Weird tile-part header in JPX stream");
+ return gFalse;
+ }
+ ++img.tiles[tileIdx].nextTilePart;
+
+ tilePartToEOC = tilePartLen == 0;
+ tilePartLen -= 12; // subtract size of SOT segment
+
+ haveSOD = gFalse;
+ do {
+ if (!readMarkerHdr(&segType, &segLen)) {
+ error(errSyntaxError, getPos(), "Error in JPX tile-part codestream");
+ return gFalse;
+ }
+ tilePartLen -= 2 + segLen;
+ 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) ||
+ !readUByte(&multiComp) ||
+ !readUByte(&nDecompLevels) ||
+ !readUByte(&codeBlockW) ||
+ !readUByte(&codeBlockH) ||
+ !readUByte(&codeBlockStyle) ||
+ !readUByte(&transform)) {
+ error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
+ return gFalse;
+ }
+ if (nDecompLevels < 1 ||
+ nDecompLevels > 31 ||
+ codeBlockW > 8 ||
+ codeBlockH > 8) {
+ error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
+ return gFalse;
+ }
+ codeBlockW += 2;
+ codeBlockH += 2;
+ img.tiles[tileIdx].progOrder = progOrder;
+ img.tiles[tileIdx].nLayers = nLayers;
+ img.tiles[tileIdx].multiComp = multiComp;
+ for (comp = 0; comp < img.nComps; ++comp) {
+ img.tiles[tileIdx].tileComps[comp].style = style;
+ img.tiles[tileIdx].tileComps[comp].nDecompLevels = nDecompLevels;
+ img.tiles[tileIdx].tileComps[comp].codeBlockW = codeBlockW;
+ img.tiles[tileIdx].tileComps[comp].codeBlockH = codeBlockH;
+ img.tiles[tileIdx].tileComps[comp].codeBlockStyle = codeBlockStyle;
+ img.tiles[tileIdx].tileComps[comp].transform = transform;
+ img.tiles[tileIdx].tileComps[comp].resLevels =
+ (JPXResLevel *)greallocn(
+ img.tiles[tileIdx].tileComps[comp].resLevels,
+ nDecompLevels + 1,
+ sizeof(JPXResLevel));
+ for (r = 0; r <= nDecompLevels; ++r) {
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;
+ }
+ }
+ for (r = 0; r <= nDecompLevels; ++r) {
+ if (style & 0x01) {
+ if (!readUByte(&precinctSize)) {
+ error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
+ return gFalse;
+ }
+ img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth =
+ precinctSize & 0x0f;
+ img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight =
+ (precinctSize >> 4) & 0x0f;
+ } else {
+ img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = 15;
+ img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = 15;
+ }
+ }
+ for (comp = 1; comp < img.nComps; ++comp) {
+ for (r = 0; r <= nDecompLevels; ++r) {
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth =
+ img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth;
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight =
+ img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight;
+ }
+ }
+ 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 ||
+ !readUByte(&style) ||
+ !readUByte(&nDecompLevels) ||
+ !readUByte(&codeBlockW) ||
+ !readUByte(&codeBlockH) ||
+ !readUByte(&codeBlockStyle) ||
+ !readUByte(&transform)) {
+ error(errSyntaxError, getPos(), "Error in JPX COC marker segment");
+ return gFalse;
+ }
+ if (nDecompLevels < 1 ||
+ nDecompLevels > 31 ||
+ codeBlockW > 8 ||
+ codeBlockH > 8) {
+ error(errSyntaxError, getPos(), "Error in JPX COC marker segment");
+ return gFalse;
+ }
+ img.tiles[tileIdx].tileComps[comp].style =
+ (img.tiles[tileIdx].tileComps[comp].style & ~1) | (style & 1);
+ img.tiles[tileIdx].tileComps[comp].nDecompLevels = nDecompLevels;
+ img.tiles[tileIdx].tileComps[comp].codeBlockW = codeBlockW + 2;
+ img.tiles[tileIdx].tileComps[comp].codeBlockH = codeBlockH + 2;
+ img.tiles[tileIdx].tileComps[comp].codeBlockStyle = codeBlockStyle;
+ img.tiles[tileIdx].tileComps[comp].transform = transform;
+ img.tiles[tileIdx].tileComps[comp].resLevels =
+ (JPXResLevel *)greallocn(
+ img.tiles[tileIdx].tileComps[comp].resLevels,
+ nDecompLevels + 1,
+ sizeof(JPXResLevel));
+ for (r = 0; r <= nDecompLevels; ++r) {
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;
+ }
+ for (r = 0; r <= nDecompLevels; ++r) {
+ if (style & 0x01) {
+ if (!readUByte(&precinctSize)) {
+ error(errSyntaxError, getPos(), "Error in JPX COD marker segment");
+ return gFalse;
+ }
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth =
+ precinctSize & 0x0f;
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight =
+ (precinctSize >> 4) & 0x0f;
+ } else {
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = 15;
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = 15;
+ }
+ }
+ 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;
+ }
+ if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x00) {
+ if (segLen <= 3) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ img.tiles[tileIdx].tileComps[0].nQuantSteps = segLen - 3;
+ img.tiles[tileIdx].tileComps[0].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
+ img.tiles[tileIdx].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) {
+ if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ }
+ } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x01) {
+ img.tiles[tileIdx].tileComps[0].nQuantSteps = 1;
+ img.tiles[tileIdx].tileComps[0].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
+ img.tiles[tileIdx].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[0])) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x02) {
+ if (segLen < 5) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ img.tiles[tileIdx].tileComps[0].nQuantSteps = (segLen - 3) / 2;
+ img.tiles[tileIdx].tileComps[0].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
+ img.tiles[tileIdx].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) {
+ if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ }
+ } else {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ for (comp = 1; comp < img.nComps; ++comp) {
+ img.tiles[tileIdx].tileComps[comp].quantStyle =
+ img.tiles[tileIdx].tileComps[0].quantStyle;
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps =
+ img.tiles[tileIdx].tileComps[0].nQuantSteps;
+ img.tiles[tileIdx].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+ img.tiles[tileIdx].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ for (j = 0; j < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++j) {
+ img.tiles[tileIdx].tileComps[comp].quantSteps[j] =
+ img.tiles[tileIdx].tileComps[0].quantSteps[j];
+ }
+ }
+ 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 ||
+ !readUByte(&img.tiles[tileIdx].tileComps[comp].quantStyle)) {
+ error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x00) {
+ if (segLen <= (img.nComps > 256 ? 5U : 4U)) {
+ error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps =
+ segLen - (img.nComps > 256 ? 5 : 4);
+ img.tiles[tileIdx].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) {
+ if (!readUByte(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) {
+ error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ }
+ } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f)
+ == 0x01) {
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps = 1;
+ img.tiles[tileIdx].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[0])) {
+ error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f)
+ == 0x02) {
+ if (segLen < (img.nComps > 256 ? 5U : 4U) + 2) {
+ error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps =
+ (segLen - (img.nComps > 256 ? 5 : 4)) / 2;
+ img.tiles[tileIdx].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) {
+ if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) {
+ error(errSyntaxError, getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ }
+ } else {
+ error(errSyntaxError, getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ 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 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
+ return gFalse;
+ }
+#else
+ if ((img.nComps > 256 && !readUWord(&comp)) ||
+ (img.nComps <= 256 && !readUByte(&comp)) ||
+ comp >= img.nComps ||
+ !readUByte(&compInfo[comp].roi.style) ||
+ !readUByte(&compInfo[comp].roi.shift)) {
+ error(errSyntaxError, getPos(), "Error in JPX RGN marker segment");
+ return gFalse;
+ }
+#endif
+ break;
+ case 0x5f: // POC - progression order change
+ cover(39);
+#if 1 //~ progression order changes are unimplemented
+ error(errUnimplemented, -1, "Got a JPX POC segment");
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
+ return gFalse;
+ }
+#else
+ nTileProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
+ tileProgs = (JPXProgOrder *)gmallocn(nTileProgs, sizeof(JPXProgOrder));
+ for (i = 0; i < nTileProgs; ++i) {
+ if (!readUByte(&tileProgs[i].startRes) ||
+ !(img.nComps > 256 && readUWord(&tileProgs[i].startComp)) ||
+ !(img.nComps <= 256 && readUByte(&tileProgs[i].startComp)) ||
+ !readUWord(&tileProgs[i].endLayer) ||
+ !readUByte(&tileProgs[i].endRes) ||
+ !(img.nComps > 256 && readUWord(&tileProgs[i].endComp)) ||
+ !(img.nComps <= 256 && readUByte(&tileProgs[i].endComp)) ||
+ !readUByte(&tileProgs[i].progOrder)) {
+ error(errSyntaxError, getPos(), "Error in JPX POC marker segment");
+ return gFalse;
+ }
+ }
+#endif
+ break;
+ case 0x61: // PPT - packed packet headers, tile-part hdr
+ cover(40);
+#if 1 //~ packed packet headers are unimplemented
+ error(errUnimplemented, -1, "Got a JPX PPT segment");
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PPT marker segment");
+ return gFalse;
+ }
+#endif
+ case 0x58: // PLT - packet length, tile-part header
+ // skipped
+ cover(41);
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX PLT marker segment");
+ return gFalse;
+ }
+ break;
+ case 0x64: // COM - comment
+ // skipped
+ cover(42);
+ if (segLen > 2 &&
+ bufStr->discardChars(segLen - 2) != segLen - 2) {
+ error(errSyntaxError, getPos(), "Error in JPX COM marker segment");
+ return gFalse;
+ }
+ break;
+ case 0x93: // SOD - start of data
+ cover(43);
+ haveSOD = gTrue;
+ break;
+ default:
+ cover(44);
+ error(errSyntaxError, getPos(),
+ "Unknown marker segment {0:02x} in JPX tile-part stream",
+ segType);
+ if (segLen > 2) {
+ bufStr->discardChars(segLen - 2);
+ }
+ break;
+ }
+ } while (!haveSOD);
+
+ for (comp = 0; comp < img.nComps; ++comp) {
+ tileComp = &img.tiles[tileIdx].tileComps[comp];
+ qStyle = tileComp->quantStyle & 0x1f;
+ if ((qStyle == 0 && tileComp->nQuantSteps < 3 * tileComp->nDecompLevels) ||
+ (qStyle == 1 && tileComp->nQuantSteps < 1) ||
+ (qStyle == 2 && tileComp->nQuantSteps < 3 * tileComp->nDecompLevels)) {
+ error(errSyntaxError, getPos(), "Too few quant steps in JPX tile part");
+ return gFalse;
+ }
+ }
+
+ //----- initialize the tile, precincts, and code-blocks
+ if (tilePartIdx == 0) {
+ tile = &img.tiles[tileIdx];
+ i = tileIdx / img.nXTiles;
+ j = tileIdx % img.nXTiles;
+ if ((tile->x0 = img.xTileOffset + j * img.xTileSize) < img.xOffset) {
+ tile->x0 = img.xOffset;
+ }
+ if ((tile->y0 = img.yTileOffset + i * img.yTileSize) < img.yOffset) {
+ tile->y0 = img.yOffset;
+ }
+ if ((tile->x1 = img.xTileOffset + (j + 1) * img.xTileSize) > img.xSize) {
+ tile->x1 = img.xSize;
+ }
+ if ((tile->y1 = img.yTileOffset + (i + 1) * img.yTileSize) > img.ySize) {
+ tile->y1 = img.ySize;
+ }
+ tile->comp = 0;
+ tile->res = 0;
+ tile->precinct = 0;
+ tile->layer = 0;
+ tile->done = gFalse;
+ tile->maxNDecompLevels = 0;
+ for (comp = 0; comp < img.nComps; ++comp) {
+ tileComp = &tile->tileComps[comp];
+ if (tileComp->nDecompLevels > tile->maxNDecompLevels) {
+ tile->maxNDecompLevels = tileComp->nDecompLevels;
+ }
+ tileComp->x0 = jpxCeilDiv(tile->x0, tileComp->hSep);
+ tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->vSep);
+ tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep);
+ tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->vSep);
+ tileComp->w = jpxCeilDivPow2(tileComp->x1, reduction)
+ - jpxCeilDivPow2(tileComp->x0, reduction);
+ tileComp->h = jpxCeilDivPow2(tileComp->y1, reduction)
+ - jpxCeilDivPow2(tileComp->y0, reduction);
+ if (tileComp->w == 0 || tileComp->h == 0) {
+ error(errSyntaxError, getPos(),
+ "Invalid tile size or sample separation in JPX stream");
+ return gFalse;
+ }
+ tileComp->data = (int *)gmallocn(tileComp->w * tileComp->h, sizeof(int));
+ if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) {
+ n = tileComp->x1 - tileComp->x0;
+ } else {
+ n = tileComp->y1 - tileComp->y0;
+ }
+ tileComp->buf = (int *)gmallocn(n + 8, sizeof(int));
+ for (r = 0; r <= tileComp->nDecompLevels; ++r) {
+ resLevel = &tileComp->resLevels[r];
+ 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
+ // AND the subbands are all completely empty
+ 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;
+ resLevel->by1[0] = resLevel->y1;
+ resLevel->empty = resLevel->empty &&
+ (resLevel->bx0[0] == resLevel->bx1[0] ||
+ resLevel->by0[0] == resLevel->by1[0]);
+ } else {
+ 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]) &&
+ (resLevel->bx0[1] == resLevel->bx1[1] ||
+ resLevel->by0[1] == resLevel->by1[1]) &&
+ (resLevel->bx0[2] == resLevel->bx1[2] ||
+ resLevel->by0[2] == resLevel->by1[2]);
+ }
+ 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;
+ }
+ 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;
+ }
+ 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;
+ }
+ ++cb;
+ }
+ }
+ }
+ ++precinct;
+ }
+ }
+ }
+ }
+ tile->init = gTrue;
+ }
+
+ return readTilePartData(tileIdx, tilePartLen, tilePartToEOC);
+}
+
+GBool JPXStream::readTilePartData(Guint tileIdx,
+ Guint tilePartLen, GBool tilePartToEOC) {
+ JPXTile *tile;
+ JPXTileComp *tileComp;
+ JPXResLevel *resLevel;
+ JPXPrecinct *precinct;
+ JPXSubband *subband;
+ JPXCodeBlock *cb;
+ Guint ttVal;
+ Guint bits, cbX, cbY, nx, ny, i, j, n, sb;
+ int level;
+
+ tile = &img.tiles[tileIdx];
+
+ // if the tile is finished, just skip this tile part
+ if (tile->done) {
+ bufStr->discardChars(tilePartLen);
+ return gTrue;
+ }
+
+ // read all packets from this tile-part
+ while (1) {
+ if (tilePartToEOC) {
+ //~ peek for an EOC marker
+ cover(93);
+ } else if (tilePartLen == 0) {
+ break;
+ }
+
+ tileComp = &tile->tileComps[tile->comp];
+ resLevel = &tileComp->resLevels[tile->res];
+ precinct = &resLevel->precincts[tile->precinct];
+
+ if (resLevel->empty) {
+ goto nextPacket;
+ }
+
+ //----- packet header
+
+ // setup
+ startBitBuf(tilePartLen);
+ if (tileComp->style & 0x02) {
+ skipSOP();
+ }
+
+ // zero-length flag
+ if (!readBits(1, &bits)) {
+ goto err;
+ }
+ if (!bits) {
+ // packet is empty -- clear all code-block inclusion flags
+ cover(45);
+ for (sb = 0; sb < (Guint)(tile->res == 0 ? 1 : 3); ++sb) {
+ subband = &precinct->subbands[sb];
+ for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+ for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+ cb = &subband->cbs[cbY * subband->nXCBs + cbX];
+ cb->included = gFalse;
+ }
+ }
+ }
+ } else {
+
+ for (sb = 0; sb < (Guint)(tile->res == 0 ? 1 : 3); ++sb) {
+ subband = &precinct->subbands[sb];
+ for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+ for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+ cb = &subband->cbs[cbY * subband->nXCBs + cbX];
+
+ // skip code-blocks with no coefficients
+ if (cb->x0 >= cb->x1 || cb->y0 >= cb->y1) {
+ cover(46);
+ cb->included = gFalse;
+ continue;
+ }
+
+ // code-block inclusion
+ if (cb->seen) {
+ cover(47);
+ if (!readBits(1, &cb->included)) {
+ goto err;
+ }
+ } else {
+ cover(48);
+ ttVal = 0;
+ i = 0;
+ for (level = subband->maxTTLevel; level >= 0; --level) {
+ nx = jpxCeilDivPow2(subband->nXCBs, level);
+ ny = jpxCeilDivPow2(subband->nYCBs, level);
+ j = i + (cbY >> level) * nx + (cbX >> level);
+ if (!subband->inclusion[j].finished &&
+ !subband->inclusion[j].val) {
+ subband->inclusion[j].val = ttVal;
+ } else {
+ ttVal = subband->inclusion[j].val;
+ }
+ while (!subband->inclusion[j].finished &&
+ ttVal <= tile->layer) {
+ if (!readBits(1, &bits)) {
+ goto err;
+ }
+ if (bits == 1) {
+ subband->inclusion[j].finished = gTrue;
+ } else {
+ ++ttVal;
+ }
+ }
+ subband->inclusion[j].val = ttVal;
+ if (ttVal > tile->layer) {
+ break;
+ }
+ i += nx * ny;
+ }
+ cb->included = level < 0;
+ }
+
+ if (cb->included) {
+ cover(49);
+
+ // zero bit-plane count
+ if (!cb->seen) {
+ cover(50);
+ ttVal = 0;
+ i = 0;
+ for (level = subband->maxTTLevel; level >= 0; --level) {
+ nx = jpxCeilDivPow2(subband->nXCBs, level);
+ ny = jpxCeilDivPow2(subband->nYCBs, level);
+ j = i + (cbY >> level) * nx + (cbX >> level);
+ if (!subband->zeroBitPlane[j].finished &&
+ !subband->zeroBitPlane[j].val) {
+ subband->zeroBitPlane[j].val = ttVal;
+ } else {
+ ttVal = subband->zeroBitPlane[j].val;
+ }
+ while (!subband->zeroBitPlane[j].finished) {
+ if (!readBits(1, &bits)) {
+ goto err;
+ }
+ if (bits == 1) {
+ subband->zeroBitPlane[j].finished = gTrue;
+ } else {
+ ++ttVal;
+ }
+ }
+ subband->zeroBitPlane[j].val = ttVal;
+ i += nx * ny;
+ }
+ cb->nZeroBitPlanes = ttVal;
+ }
+
+ // number of coding passes
+ if (!readBits(1, &bits)) {
+ goto err;
+ }
+ if (bits == 0) {
+ cover(51);
+ cb->nCodingPasses = 1;
+ } else {
+ if (!readBits(1, &bits)) {
+ goto err;
+ }
+ if (bits == 0) {
+ cover(52);
+ cb->nCodingPasses = 2;
+ } else {
+ cover(53);
+ if (!readBits(2, &bits)) {
+ goto err;
+ }
+ if (bits < 3) {
+ cover(54);
+ cb->nCodingPasses = 3 + bits;
+ } else {
+ cover(55);
+ if (!readBits(5, &bits)) {
+ goto err;
+ }
+ if (bits < 31) {
+ cover(56);
+ cb->nCodingPasses = 6 + bits;
+ } else {
+ cover(57);
+ if (!readBits(7, &bits)) {
+ goto err;
+ }
+ cb->nCodingPasses = 37 + bits;
+ }
+ }
+ }
+ }
+
+ // update Lblock
+ while (1) {
+ if (!readBits(1, &bits)) {
+ goto err;
+ }
+ if (!bits) {
+ break;
+ }
+ ++cb->lBlock;
+ }
+
+ // one codeword segment for each of the coding passes
+ if (tileComp->codeBlockStyle & 0x04) {
+ if (cb->nCodingPasses > cb->dataLenSize) {
+ cb->dataLenSize = cb->nCodingPasses;
+ cb->dataLen = (Guint *)greallocn(cb->dataLen,
+ cb->dataLenSize,
+ sizeof(Guint));
+ }
+
+ // read the lengths
+ for (i = 0; i < cb->nCodingPasses; ++i) {
+ if (!readBits(cb->lBlock, &cb->dataLen[i])) {
+ goto err;
+ }
+ }
+
+ // one codeword segment for all of the coding passes
+ } else {
+
+ // read the length
+ for (n = cb->lBlock, i = cb->nCodingPasses >> 1;
+ i;
+ ++n, i >>= 1) ;
+ if (!readBits(n, &cb->dataLen[0])) {
+ goto err;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (tileComp->style & 0x04) {
+ skipEPH();
+ }
+ tilePartLen = finishBitBuf();
+
+ //----- packet data
+
+ for (sb = 0; sb < (Guint)(tile->res == 0 ? 1 : 3); ++sb) {
+ subband = &precinct->subbands[sb];
+ for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+ for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+ cb = &subband->cbs[cbY * subband->nXCBs + cbX];
+ if (cb->included) {
+ if (!readCodeBlockData(tileComp, resLevel, precinct, subband,
+ tile->res, sb, cb)) {
+ return gFalse;
+ }
+ if (tileComp->codeBlockStyle & 0x04) {
+ for (i = 0; i < cb->nCodingPasses; ++i) {
+ tilePartLen -= cb->dataLen[i];
+ }
+ } else {
+ tilePartLen -= cb->dataLen[0];
+ }
+ cb->seen = gTrue;
+ }
+ }
+ }
+ }
+
+ //----- next packet
+
+ nextPacket:
+ switch (tile->progOrder) {
+ case 0: // layer, resolution level, component, precinct
+ cover(58);
+ resLevel = &tile->tileComps[tile->comp].resLevels[tile->res];
+ if (++tile->precinct == resLevel->nPrecincts) {
+ tile->precinct = 0;
+ if (++tile->comp == img.nComps) {
+ tile->comp = 0;
+ if (++tile->res == tile->maxNDecompLevels + 1) {
+ tile->res = 0;
+ if (++tile->layer == tile->nLayers) {
+ tile->layer = 0;
+ tile->done = gTrue;
+ }
+ }
+ }
+ }
+ break;
+ case 1: // resolution level, layer, component, precinct
+ cover(59);
+ 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
+ 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;
+ 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
+ cover(61);
+ //~ this is incorrect if there are subsampled components (?)
+ if (++tile->layer == tile->nLayers) {
+ tile->layer = 0;
+ if (++tile->res == tile->maxNDecompLevels + 1) {
+ tile->res = 0;
+ if (++tile->comp == img.nComps) {
+ tile->comp = 0;
+ 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
+ cover(62);
+ if (++tile->layer == tile->nLayers) {
+ tile->layer = 0;
+ if (++tile->res == tile->maxNDecompLevels + 1) {
+ tile->res = 0;
+ 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;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return gTrue;
+
+ err:
+ error(errSyntaxError, getPos(), "Error in JPX stream");
+ return gFalse;
+}
+
+GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
+ JPXResLevel *resLevel,
+ JPXPrecinct *precinct,
+ JPXSubband *subband,
+ Guint res, Guint sb,
+ JPXCodeBlock *cb) {
+ int *coeff0, *coeff1, *coeff;
+ char *touched0, *touched1, *touched;
+ Guint horiz, vert, diag, all, cx, xorBit;
+ int horizSign, vertSign, bit;
+ int segSym;
+ Guint n, i, x, y0, y1;
+
+ if (res > tileComp->nDecompLevels - reduction) {
+ // skip the codeblock data
+ if (tileComp->codeBlockStyle & 0x04) {
+ n = 0;
+ for (i = 0; i < cb->nCodingPasses; ++i) {
+ n += cb->dataLen[i];
+ }
+ } else {
+ n = cb->dataLen[0];
+ }
+ bufStr->discardChars(n);
+ return gTrue;
+ }
+
+ if (cb->arithDecoder) {
+ cover(63);
+ cb->arithDecoder->restart(cb->dataLen[0]);
+ } else {
+ cover(64);
+ cb->arithDecoder = new JArithmeticDecoder();
+ cb->arithDecoder->setStream(bufStr, cb->dataLen[0]);
+ cb->arithDecoder->start();
+ cb->stats = new JArithmeticDecoderStats(jpxNContexts);
+ cb->stats->setEntry(jpxContextSigProp, 4, 0);
+ cb->stats->setEntry(jpxContextRunLength, 3, 0);
+ cb->stats->setEntry(jpxContextUniform, 46, 0);
+ }
+
+ for (i = 0; i < cb->nCodingPasses; ++i) {
+ if ((tileComp->codeBlockStyle & 0x04) && i > 0) {
+ cb->arithDecoder->setStream(bufStr, cb->dataLen[i]);
+ cb->arithDecoder->start();
+ }
+
+ switch (cb->nextPass) {
+
+ //----- significance propagation pass
+ case jpxPassSigProp:
+ cover(65);
+ for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched;
+ y0 < cb->y1;
+ y0 += 4, coeff0 += 4 * tileComp->w,
+ 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 += resLevel->cbW) {
+ if (!*coeff) {
+ horiz = vert = diag = 0;
+ horizSign = vertSign = 2;
+ if (x > cb->x0) {
+ if (coeff[-1]) {
+ ++horiz;
+ horizSign += coeff[-1] < 0 ? -1 : 1;
+ }
+ if (y0+y1 > cb->y0) {
+ diag += coeff[-(int)tileComp->w - 1] ? 1 : 0;
+ }
+ if (y0+y1 < cb->y1 - 1 &&
+ (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) {
+ diag += coeff[tileComp->w - 1] ? 1 : 0;
+ }
+ }
+ if (x < cb->x1 - 1) {
+ if (coeff[1]) {
+ ++horiz;
+ horizSign += coeff[1] < 0 ? -1 : 1;
+ }
+ if (y0+y1 > cb->y0) {
+ diag += coeff[-(int)tileComp->w + 1] ? 1 : 0;
+ }
+ if (y0+y1 < cb->y1 - 1 &&
+ (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) {
+ diag += coeff[tileComp->w + 1] ? 1 : 0;
+ }
+ }
+ if (y0+y1 > cb->y0) {
+ if (coeff[-(int)tileComp->w]) {
+ ++vert;
+ vertSign += coeff[-(int)tileComp->w] < 0 ? -1 : 1;
+ }
+ }
+ if (y0+y1 < cb->y1 - 1 &&
+ (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) {
+ if (coeff[tileComp->w]) {
+ ++vert;
+ vertSign += coeff[tileComp->w] < 0 ? -1 : 1;
+ }
+ }
+ cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];
+ if (cx != 0) {
+ if (cb->arithDecoder->decodeBit(cx, cb->stats)) {
+ cx = signContext[horizSign][vertSign][0];
+ xorBit = signContext[horizSign][vertSign][1];
+ if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+ *coeff = -1;
+ } else {
+ *coeff = 1;
+ }
+ }
+ *touched = 1;
+ }
+ }
+ }
+ }
+ }
+ ++cb->nextPass;
+ break;
+
+ //----- magnitude refinement pass
+ case jpxPassMagRef:
+ cover(66);
+ for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched;
+ y0 < cb->y1;
+ y0 += 4, coeff0 += 4 * tileComp->w,
+ 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 += resLevel->cbW) {
+ if (*coeff && !*touched) {
+ if (*coeff == 1 || *coeff == -1) {
+ all = 0;
+ if (x > cb->x0) {
+ all += coeff[-1] ? 1 : 0;
+ if (y0+y1 > cb->y0) {
+ all += coeff[-(int)tileComp->w - 1] ? 1 : 0;
+ }
+ if (y0+y1 < cb->y1 - 1 &&
+ (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) {
+ all += coeff[tileComp->w - 1] ? 1 : 0;
+ }
+ }
+ if (x < cb->x1 - 1) {
+ all += coeff[1] ? 1 : 0;
+ if (y0+y1 > cb->y0) {
+ all += coeff[-(int)tileComp->w + 1] ? 1 : 0;
+ }
+ if (y0+y1 < cb->y1 - 1 &&
+ (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) {
+ all += coeff[tileComp->w + 1] ? 1 : 0;
+ }
+ }
+ if (y0+y1 > cb->y0) {
+ all += coeff[-(int)tileComp->w] ? 1 : 0;
+ }
+ if (y0+y1 < cb->y1 - 1 &&
+ (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) {
+ all += coeff[tileComp->w] ? 1 : 0;
+ }
+ cx = all ? 15 : 14;
+ } else {
+ cx = 16;
+ }
+ bit = cb->arithDecoder->decodeBit(cx, cb->stats);
+ if (*coeff < 0) {
+ *coeff = (*coeff << 1) - bit;
+ } else {
+ *coeff = (*coeff << 1) + bit;
+ }
+ *touched = 1;
+ }
+ }
+ }
+ }
+ ++cb->nextPass;
+ break;
+
+ //----- cleanup pass
+ case jpxPassCleanup:
+ cover(67);
+ for (y0 = cb->y0, coeff0 = cb->coeffs, touched0 = cb->touched;
+ y0 < cb->y1;
+ y0 += 4, coeff0 += 4 * tileComp->w,
+ touched0 += 4 << resLevel->codeBlockW) {
+ for (x = cb->x0, coeff1 = coeff0, touched1 = touched0;
+ x < cb->x1;
+ ++x, ++coeff1, ++touched1) {
+ y1 = 0;
+ if (y0 + 3 < cb->y1 &&
+ !(*touched1) &&
+ !(touched1[resLevel->cbW]) &&
+ !(touched1[2 * resLevel->cbW]) &&
+ !(touched1[3 * resLevel->cbW]) &&
+ (x == cb->x0 || y0 == cb->y0 ||
+ !coeff1[-(int)tileComp->w - 1]) &&
+ (y0 == cb->y0 ||
+ !coeff1[-(int)tileComp->w]) &&
+ (x == cb->x1 - 1 || y0 == cb->y0 ||
+ !coeff1[-(int)tileComp->w + 1]) &&
+ (x == cb->x0 ||
+ (!coeff1[-1] &&
+ !coeff1[tileComp->w - 1] &&
+ !coeff1[2 * tileComp->w - 1] &&
+ !coeff1[3 * tileComp->w - 1])) &&
+ (x == cb->x1 - 1 ||
+ (!coeff1[1] &&
+ !coeff1[tileComp->w + 1] &&
+ !coeff1[2 * tileComp->w + 1] &&
+ !coeff1[3 * tileComp->w + 1])) &&
+ ((tileComp->codeBlockStyle & 0x08) ||
+ ((x == cb->x0 || y0+4 == cb->y1 ||
+ !coeff1[4 * tileComp->w - 1]) &&
+ (y0+4 == cb->y1 ||
+ !coeff1[4 * tileComp->w]) &&
+ (x == cb->x1 - 1 || y0+4 == cb->y1 ||
+ !coeff1[4 * tileComp->w + 1])))) {
+ if (cb->arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) {
+ y1 = cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats);
+ y1 = (y1 << 1) |
+ cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats);
+ coeff = &coeff1[y1 * tileComp->w];
+ cx = signContext[2][2][0];
+ xorBit = signContext[2][2][1];
+ if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+ *coeff = -1;
+ } else {
+ *coeff = 1;
+ }
+ ++y1;
+ } else {
+ y1 = 4;
+ }
+ }
+ for (coeff = &coeff1[y1 * tileComp->w],
+ touched = &touched1[y1 << resLevel->codeBlockW];
+ y1 < 4 && y0 + y1 < cb->y1;
+ ++y1, coeff += tileComp->w, touched += resLevel->cbW) {
+ if (!*touched) {
+ horiz = vert = diag = 0;
+ horizSign = vertSign = 2;
+ if (x > cb->x0) {
+ if (coeff[-1]) {
+ ++horiz;
+ horizSign += coeff[-1] < 0 ? -1 : 1;
+ }
+ if (y0+y1 > cb->y0) {
+ diag += coeff[-(int)tileComp->w - 1] ? 1 : 0;
+ }
+ if (y0+y1 < cb->y1 - 1 &&
+ (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) {
+ diag += coeff[tileComp->w - 1] ? 1 : 0;
+ }
+ }
+ if (x < cb->x1 - 1) {
+ if (coeff[1]) {
+ ++horiz;
+ horizSign += coeff[1] < 0 ? -1 : 1;
+ }
+ if (y0+y1 > cb->y0) {
+ diag += coeff[-(int)tileComp->w + 1] ? 1 : 0;
+ }
+ if (y0+y1 < cb->y1 - 1 &&
+ (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) {
+ diag += coeff[tileComp->w + 1] ? 1 : 0;
+ }
+ }
+ if (y0+y1 > cb->y0) {
+ if (coeff[-(int)tileComp->w]) {
+ ++vert;
+ vertSign += coeff[-(int)tileComp->w] < 0 ? -1 : 1;
+ }
+ }
+ if (y0+y1 < cb->y1 - 1 &&
+ (!(tileComp->codeBlockStyle & 0x08) || y1 < 3)) {
+ if (coeff[tileComp->w]) {
+ ++vert;
+ vertSign += coeff[tileComp->w] < 0 ? -1 : 1;
+ }
+ }
+ cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];
+ if (cb->arithDecoder->decodeBit(cx, cb->stats)) {
+ cx = signContext[horizSign][vertSign][0];
+ xorBit = signContext[horizSign][vertSign][1];
+ if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+ *coeff = -1;
+ } else {
+ *coeff = 1;
+ }
+ }
+ } else {
+ *touched = 0;
+ }
+ }
+ }
+ }
+ ++cb->len;
+ // look for a segmentation symbol
+ if (tileComp->codeBlockStyle & 0x20) {
+ segSym = cb->arithDecoder->decodeBit(jpxContextUniform,
+ cb->stats) << 3;
+ segSym |= cb->arithDecoder->decodeBit(jpxContextUniform,
+ cb->stats) << 2;
+ segSym |= cb->arithDecoder->decodeBit(jpxContextUniform,
+ cb->stats) << 1;
+ segSym |= cb->arithDecoder->decodeBit(jpxContextUniform,
+ cb->stats);
+ if (segSym != 0x0a) {
+ // in theory this should be a fatal error, but it seems to
+ // be problematic
+ error(errSyntaxWarning, getPos(),
+ "Missing or invalid segmentation symbol in JPX stream");
+ }
+ }
+ cb->nextPass = jpxPassSigProp;
+ break;
+ }
+
+ if (tileComp->codeBlockStyle & 0x02) {
+ cb->stats->reset();
+ cb->stats->setEntry(jpxContextSigProp, 4, 0);
+ cb->stats->setEntry(jpxContextRunLength, 3, 0);
+ cb->stats->setEntry(jpxContextUniform, 46, 0);
+ }
+
+ if (tileComp->codeBlockStyle & 0x04) {
+ cb->arithDecoder->cleanup();
+ }
+ }
+
+ cb->arithDecoder->cleanup();
+ return gTrue;
+}
+
+// Inverse quantization, and wavelet transform (IDWT). This also does
+// the initial shift to convert to fixed point format.
+void JPXStream::inverseTransform(JPXTileComp *tileComp) {
+ JPXResLevel *resLevel;
+ JPXPrecinct *precinct;
+ JPXSubband *subband;
+ JPXCodeBlock *cb;
+ int *coeff0, *coeff;
+ char *touched0, *touched;
+ Guint qStyle, guard, eps, shift;
+ int shift2;
+ double mu;
+ int val;
+ Guint r, pre, cbX, cbY, x, y;
+
+ cover(68);
+
+ //----- (NL)LL subband (resolution level 0)
+
+ resLevel = &tileComp->resLevels[0];
+
+ // i-quant parameters
+ qStyle = tileComp->quantStyle & 0x1f;
+ guard = (tileComp->quantStyle >> 5) & 7;
+ if (qStyle == 0) {
+ cover(69);
+ eps = (tileComp->quantSteps[0] >> 3) & 0x1f;
+ shift = guard + eps - 1;
+ mu = 0; // make gcc happy
+ } else {
+ cover(70);
+ shift = guard - 1 + tileComp->prec;
+ mu = (double)(0x800 + (tileComp->quantSteps[0] & 0x7ff)) / 2048.0;
+ }
+ if (tileComp->transform == 0) {
+ cover(71);
+ shift += fracBits - tileComp->prec;
+ }
+
+ // do fixed point adjustment and dequantization on (NL)LL
+ 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 {
+ cover(95);
+ val >>= -shift2;
+ }
+ if (qStyle == 0) {
+ cover(96);
+ if (tileComp->transform == 0) {
+ cover(97);
+ val &= -1 << (fracBits - tileComp->prec);
+ }
+ } else {
+ cover(98);
+ val = (int)((double)val * mu);
+ }
+ }
+ *coeff = val;
+ }
+ }
+ ++cb;
+ }
+ }
+ }
+
+ //----- IDWT for each level
+
+ for (r = 1; r <= tileComp->nDecompLevels - reduction; ++r) {
+ resLevel = &tileComp->resLevels[r];
+
+ // (n)LL is already in the upper-left corner of the
+ // tile-component data array -- interleave with (n)HL/LH/HH
+ // and inverse transform to get (n-1)LL, which will be stored
+ // in the upper-left corner of the tile-component data array
+ inverseTransformLevel(tileComp, r, resLevel);
+ }
+}
+
+// Do one level of the inverse transform:
+// - take (n)LL, (n)HL, (n)LH, and (n)HH from the upper-left corner
+// of the tile-component data array
+// - leave the resulting (n-1)LL in the same place
+void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
+ Guint r, JPXResLevel *resLevel) {
+ JPXPrecinct *precinct;
+ JPXSubband *subband;
+ JPXCodeBlock *cb;
+ int *coeff0, *coeff;
+ char *touched0, *touched;
+ Guint qStyle, guard, eps, shift, t;
+ int shift2;
+ double mu;
+ int val;
+ int *dataPtr, *bufPtr;
+ Guint nx1, nx2, ny1, ny2, offset;
+ Guint x, y, sb, pre, cbX, cbY;
+
+ qStyle = tileComp->quantStyle & 0x1f;
+ guard = (tileComp->quantStyle >> 5) & 7;
+
+ //----- compute subband bounds
+
+ // 0 nx1 nx2
+ // | | |
+ // v v v
+ // +----+----+
+ // | LL | HL | <- 0
+ // +----+----+
+ // | LH | HH | <- ny1
+ // +----+----+
+ // <- ny2
+ 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;
+ }
+
+ //----- fixed-point adjustment and dequantization
+
+ for (sb = 0; sb < 3; ++sb) {
+
+ // i-quant parameters
+ if (qStyle == 0) {
+ cover(100);
+ eps = (tileComp->quantSteps[3*r - 2 + sb] >> 3) & 0x1f;
+ shift = guard + eps - 1;
+ mu = 0; // make gcc happy
+ } else {
+ cover(101);
+ shift = guard + tileComp->prec;
+ if (sb == 2) {
+ cover(102);
+ ++shift;
+ }
+ t = tileComp->quantSteps[qStyle == 1 ? 0 : (3*r - 2 + sb)];
+ mu = (double)(0x800 + (t & 0x7ff)) / 2048.0;
+ }
+ if (tileComp->transform == 0) {
+ cover(103);
+ shift += fracBits - tileComp->prec;
+ }
+
+ // fixed point adjustment and dequantization
+
+ 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 {
+ cover(75);
+ val >>= -shift2;
+ }
+ if (qStyle == 0) {
+ cover(76);
+ if (tileComp->transform == 0) {
+ val &= -1 << (fracBits - tileComp->prec);
+ }
+ } else {
+ cover(77);
+ val = (int)((double)val * mu);
+ }
+ }
+ *coeff = val;
+ }
+ }
+ ++cb;
+ }
+ }
+ }
+ }
+
+ //----- inverse transform
+
+ // horizontal (row) transforms
+ offset = 3 + (resLevel->x0 & 1);
+ for (y = 0, dataPtr = tileComp->data; y < ny2; ++y, dataPtr += tileComp->w) {
+ if (resLevel->bx0[0] == resLevel->bx0[1]) {
+ // fetch LL/LH
+ for (x = 0, bufPtr = tileComp->buf + offset;
+ x < nx1;
+ ++x, bufPtr += 2) {
+ *bufPtr = dataPtr[x];
+ }
+ // fetch HL/HH
+ for (x = nx1, bufPtr = tileComp->buf + offset + 1;
+ x < nx2;
+ ++x, bufPtr += 2) {
+ *bufPtr = dataPtr[x];
+ }
+ } else {
+ // fetch LL/LH
+ for (x = 0, bufPtr = tileComp->buf + offset + 1;
+ x < nx1;
+ ++x, bufPtr += 2) {
+ *bufPtr = dataPtr[x];
+ }
+ // fetch HL/HH
+ for (x = nx1, bufPtr = tileComp->buf + offset;
+ x < nx2;
+ ++x, bufPtr += 2) {
+ *bufPtr = dataPtr[x];
+ }
+ }
+ inverseTransform1D(tileComp, tileComp->buf, offset, nx2);
+ for (x = 0, bufPtr = tileComp->buf + offset; x < nx2; ++x, ++bufPtr) {
+ dataPtr[x] = *bufPtr;
+ }
+ }
+
+ // vertical (column) transforms
+ offset = 3 + (resLevel->y0 & 1);
+ for (x = 0, dataPtr = tileComp->data; x < nx2; ++x, ++dataPtr) {
+ if (resLevel->by0[0] == resLevel->by0[1]) {
+ // fetch LL/HL
+ for (y = 0, bufPtr = tileComp->buf + offset;
+ y < ny1;
+ ++y, bufPtr += 2) {
+ *bufPtr = dataPtr[y * tileComp->w];
+ }
+ // fetch LH/HH
+ for (y = ny1, bufPtr = tileComp->buf + offset + 1;
+ y < ny2;
+ ++y, bufPtr += 2) {
+ *bufPtr = dataPtr[y * tileComp->w];
+ }
+ } else {
+ // fetch LL/HL
+ for (y = 0, bufPtr = tileComp->buf + offset + 1;
+ y < ny1;
+ ++y, bufPtr += 2) {
+ *bufPtr = dataPtr[y * tileComp->w];
+ }
+ // fetch LH/HH
+ for (y = ny1, bufPtr = tileComp->buf + offset;
+ y < ny2;
+ ++y, bufPtr += 2) {
+ *bufPtr = dataPtr[y * tileComp->w];
+ }
+ }
+ inverseTransform1D(tileComp, tileComp->buf, offset, ny2);
+ for (y = 0, bufPtr = tileComp->buf + offset; y < ny2; ++y, ++bufPtr) {
+ dataPtr[y * tileComp->w] = *bufPtr;
+ }
+ }
+}
+
+void JPXStream::inverseTransform1D(JPXTileComp *tileComp, int *data,
+ Guint offset, Guint n) {
+ Guint end, i;
+
+ //----- special case for length = 1
+ if (n == 1) {
+ cover(79);
+ if (offset == 4) {
+ cover(104);
+ *data >>= 1;
+ }
+
+ } else {
+ cover(80);
+
+ end = offset + n;
+
+ //----- extend right
+ data[end] = data[end - 2];
+ if (n == 2) {
+ cover(81);
+ data[end+1] = data[offset + 1];
+ data[end+2] = data[offset];
+ data[end+3] = data[offset + 1];
+ } else {
+ cover(82);
+ data[end+1] = data[end - 3];
+ if (n == 3) {
+ cover(105);
+ data[end+2] = data[offset + 1];
+ data[end+3] = data[offset + 2];
+ } else {
+ cover(106);
+ data[end+2] = data[end - 4];
+ if (n == 4) {
+ cover(107);
+ data[end+3] = data[offset + 1];
+ } else {
+ cover(108);
+ data[end+3] = data[end - 5];
+ }
+ }
+ }
+
+ //----- extend left
+ data[offset - 1] = data[offset + 1];
+ data[offset - 2] = data[offset + 2];
+ data[offset - 3] = data[offset + 3];
+ if (offset == 4) {
+ cover(83);
+ data[0] = data[offset + 4];
+ }
+
+ //----- 9-7 irreversible filter
+
+ if (tileComp->transform == 0) {
+ cover(84);
+ // step 1 (even)
+ for (i = 1; i <= end + 2; i += 2) {
+ data[i] = (int)(idwtKappa * data[i]);
+ }
+ // step 2 (odd)
+ for (i = 0; i <= end + 3; i += 2) {
+ data[i] = (int)(idwtIKappa * data[i]);
+ }
+ // step 3 (even)
+ for (i = 1; i <= end + 2; i += 2) {
+ data[i] = (int)(data[i] - idwtDelta * (data[i-1] + data[i+1]));
+ }
+ // step 4 (odd)
+ for (i = 2; i <= end + 1; i += 2) {
+ data[i] = (int)(data[i] - idwtGamma * (data[i-1] + data[i+1]));
+ }
+ // step 5 (even)
+ for (i = 3; i <= end; i += 2) {
+ data[i] = (int)(data[i] - idwtBeta * (data[i-1] + data[i+1]));
+ }
+ // step 6 (odd)
+ for (i = 4; i <= end - 1; i += 2) {
+ data[i] = (int)(data[i] - idwtAlpha * (data[i-1] + data[i+1]));
+ }
+
+ //----- 5-3 reversible filter
+
+ } else {
+ cover(85);
+ // step 1 (even)
+ for (i = 3; i <= end; i += 2) {
+ data[i] -= (data[i-1] + data[i+1] + 2) >> 2;
+ }
+ // step 2 (odd)
+ for (i = 4; i < end; i += 2) {
+ data[i] += (data[i-1] + data[i+1]) >> 1;
+ }
+ }
+ }
+}
+
+// Inverse multi-component transform and DC level shift. This also
+// converts fixed point samples back to integers.
+GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
+ JPXTileComp *tileComp;
+ int coeff, d0, d1, d2, t, minVal, maxVal, zeroVal;
+ int *dataPtr;
+ Guint j, comp, x, y;
+
+ //----- inverse multi-component transform
+
+ if (tile->multiComp == 1) {
+ cover(86);
+ if (img.nComps < 3 ||
+ tile->tileComps[0].hSep != tile->tileComps[1].hSep ||
+ tile->tileComps[0].vSep != tile->tileComps[1].vSep ||
+ tile->tileComps[1].hSep != tile->tileComps[2].hSep ||
+ tile->tileComps[1].vSep != tile->tileComps[2].vSep) {
+ return gFalse;
+ }
+
+ // inverse irreversible multiple component transform
+ if (tile->tileComps[0].transform == 0) {
+ cover(87);
+ j = 0;
+ for (y = 0; y < tile->tileComps[0].h; ++y) {
+ for (x = 0; x < tile->tileComps[0].w; ++x) {
+ d0 = tile->tileComps[0].data[j];
+ d1 = tile->tileComps[1].data[j];
+ d2 = tile->tileComps[2].data[j];
+ tile->tileComps[0].data[j] = (int)(d0 + 1.402 * d2 + 0.5);
+ tile->tileComps[1].data[j] =
+ (int)(d0 - 0.34413 * d1 - 0.71414 * d2 + 0.5);
+ tile->tileComps[2].data[j] = (int)(d0 + 1.772 * d1 + 0.5);
+ ++j;
+ }
+ }
+
+ // inverse reversible multiple component transform
+ } else {
+ cover(88);
+ j = 0;
+ for (y = 0; y < tile->tileComps[0].h; ++y) {
+ for (x = 0; x < tile->tileComps[0].w; ++x) {
+ d0 = tile->tileComps[0].data[j];
+ d1 = tile->tileComps[1].data[j];
+ d2 = tile->tileComps[2].data[j];
+ tile->tileComps[1].data[j] = t = d0 - ((d2 + d1) >> 2);
+ tile->tileComps[0].data[j] = d2 + t;
+ tile->tileComps[2].data[j] = d1 + t;
+ ++j;
+ }
+ }
+ }
+ }
+
+ //----- DC level shift
+ for (comp = 0; comp < img.nComps; ++comp) {
+ tileComp = &tile->tileComps[comp];
+
+ // signed: clip
+ if (tileComp->sgned) {
+ cover(89);
+ minVal = -(1 << (tileComp->prec - 1));
+ maxVal = (1 << (tileComp->prec - 1)) - 1;
+ dataPtr = tileComp->data;
+ for (y = 0; y < tileComp->h; ++y) {
+ for (x = 0; x < tileComp->w; ++x) {
+ coeff = *dataPtr;
+ if (tileComp->transform == 0) {
+ cover(109);
+ coeff >>= fracBits - tileComp->prec;
+ }
+ if (coeff < minVal) {
+ cover(110);
+ coeff = minVal;
+ } else if (coeff > maxVal) {
+ cover(111);
+ coeff = maxVal;
+ }
+ *dataPtr++ = coeff;
+ }
+ }
+
+ // unsigned: inverse DC level shift and clip
+ } else {
+ cover(90);
+ maxVal = (1 << tileComp->prec) - 1;
+ zeroVal = 1 << (tileComp->prec - 1);
+ dataPtr = tileComp->data;
+ for (y = 0; y < tileComp->h; ++y) {
+ for (x = 0; x < tileComp->w; ++x) {
+ coeff = *dataPtr;
+ if (tileComp->transform == 0) {
+ cover(112);
+ coeff >>= fracBits - tileComp->prec;
+ }
+ coeff += zeroVal;
+ if (coeff < 0) {
+ cover(113);
+ coeff = 0;
+ } else if (coeff > maxVal) {
+ cover(114);
+ coeff = maxVal;
+ }
+ *dataPtr++ = coeff;
+ }
+ }
+ }
+ }
+
+ return gTrue;
+}
+
+GBool JPXStream::readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen) {
+ Guint len, lenH;
+
+ if (!readULong(&len) ||
+ !readULong(boxType)) {
+ return gFalse;
+ }
+ if (len == 1) {
+ if (!readULong(&lenH) || !readULong(&len)) {
+ return gFalse;
+ }
+ if (lenH) {
+ error(errSyntaxError, getPos(),
+ "JPX stream contains a box larger than 2^32 bytes");
+ return gFalse;
+ }
+ *boxLen = len;
+ *dataLen = len - 16;
+ } else if (len == 0) {
+ *boxLen = 0;
+ *dataLen = 0;
+ } else {
+ *boxLen = len;
+ *dataLen = len - 8;
+ }
+ return gTrue;
+}
+
+int JPXStream::readMarkerHdr(int *segType, Guint *segLen) {
+ int c;
+
+ do {
+ do {
+ if ((c = bufStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ } while (c != 0xff);
+ do {
+ if ((c = bufStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ } while (c == 0xff);
+ } while (c == 0x00);
+ *segType = c;
+ if ((c >= 0x30 && c <= 0x3f) ||
+ c == 0x4f || c == 0x92 || c == 0x93 || c == 0xd9) {
+ *segLen = 0;
+ return gTrue;
+ }
+ return readUWord(segLen);
+}
+
+GBool JPXStream::readUByte(Guint *x) {
+ int c0;
+
+ if ((c0 = bufStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = (Guint)c0;
+ return gTrue;
+}
+
+GBool JPXStream::readByte(int *x) {
+ int c0;
+
+ if ((c0 = bufStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = c0;
+ if (c0 & 0x80) {
+ *x |= -1 - 0xff;
+ }
+ return gTrue;
+}
+
+GBool JPXStream::readUWord(Guint *x) {
+ int c0, c1;
+
+ if ((c0 = bufStr->getChar()) == EOF ||
+ (c1 = bufStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = (Guint)((c0 << 8) | c1);
+ return gTrue;
+}
+
+GBool JPXStream::readULong(Guint *x) {
+ int c0, c1, c2, c3;
+
+ if ((c0 = bufStr->getChar()) == EOF ||
+ (c1 = bufStr->getChar()) == EOF ||
+ (c2 = bufStr->getChar()) == EOF ||
+ (c3 = bufStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
+ return gTrue;
+}
+
+GBool JPXStream::readNBytes(int nBytes, GBool signd, int *x) {
+ int y, c, i;
+
+ y = 0;
+ for (i = 0; i < nBytes; ++i) {
+ if ((c = bufStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ y = (y << 8) + c;
+ }
+ if (signd) {
+ if (y & (1 << (8 * nBytes - 1))) {
+ y |= -1 << (8 * nBytes);
+ }
+ }
+ *x = y;
+ return gTrue;
+}
+
+void JPXStream::startBitBuf(Guint byteCountA) {
+ bitBufLen = 0;
+ bitBufSkip = gFalse;
+ byteCount = byteCountA;
+}
+
+GBool JPXStream::readBits(int nBits, Guint *x) {
+ int c;
+
+ while (bitBufLen < nBits) {
+ if (byteCount == 0 || (c = bufStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ --byteCount;
+ if (bitBufSkip) {
+ bitBuf = (bitBuf << 7) | (c & 0x7f);
+ bitBufLen += 7;
+ } else {
+ bitBuf = (bitBuf << 8) | (c & 0xff);
+ bitBufLen += 8;
+ }
+ bitBufSkip = c == 0xff;
+ }
+ *x = (bitBuf >> (bitBufLen - nBits)) & ((1 << nBits) - 1);
+ bitBufLen -= nBits;
+ return gTrue;
+}
+
+void JPXStream::skipSOP() {
+ // SOP occurs at the start of the packet header, so we don't need to
+ // worry about bit-stuff prior to it
+ if (byteCount >= 6 &&
+ bufStr->lookChar(0) == 0xff &&
+ bufStr->lookChar(1) == 0x91) {
+ bufStr->discardChars(6);
+ byteCount -= 6;
+ bitBufLen = 0;
+ bitBufSkip = gFalse;
+ }
+}
+
+void JPXStream::skipEPH() {
+ int k;
+
+ k = bitBufSkip ? 1 : 0;
+ if (byteCount >= (Guint)(k + 2) &&
+ bufStr->lookChar(k) == 0xff &&
+ bufStr->lookChar(k + 1) == 0x92) {
+ bufStr->discardChars(k + 2);
+ byteCount -= k + 2;
+ bitBufLen = 0;
+ bitBufSkip = gFalse;
+ }
+}
+
+Guint JPXStream::finishBitBuf() {
+ if (bitBufSkip) {
+ bufStr->getChar();
+ --byteCount;
+ }
+ return byteCount;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h
new file mode 100644
index 00000000000..510826cd162
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/JPXStream.h
@@ -0,0 +1,359 @@
+//========================================================================
+//
+// JPXStream.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef JPXSTREAM_H
+#define JPXSTREAM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+#include "Stream.h"
+
+class JArithmeticDecoder;
+class JArithmeticDecoderStats;
+
+//------------------------------------------------------------------------
+
+enum JPXColorSpaceType {
+ jpxCSBiLevel = 0,
+ jpxCSYCbCr1 = 1,
+ jpxCSYCbCr2 = 3,
+ jpxCSYCBCr3 = 4,
+ jpxCSPhotoYCC = 9,
+ jpxCSCMY = 11,
+ jpxCSCMYK = 12,
+ jpxCSYCCK = 13,
+ jpxCSCIELab = 14,
+ jpxCSsRGB = 16,
+ jpxCSGrayscale = 17,
+ jpxCSBiLevel2 = 18,
+ jpxCSCIEJab = 19,
+ jpxCSCISesRGB = 20,
+ jpxCSROMMRGB = 21,
+ jpxCSsRGBYCbCr = 22,
+ jpxCSYPbPr1125 = 23,
+ jpxCSYPbPr1250 = 24
+};
+
+struct JPXColorSpecCIELab {
+ Guint rl, ol, ra, oa, rb, ob, il;
+};
+
+struct JPXColorSpecEnumerated {
+ JPXColorSpaceType type; // color space type
+ union {
+ JPXColorSpecCIELab cieLab;
+ };
+};
+
+struct JPXColorSpec {
+ Guint meth; // method
+ int prec; // precedence
+ union {
+ JPXColorSpecEnumerated enumerated;
+ };
+};
+
+//------------------------------------------------------------------------
+
+struct JPXPalette {
+ Guint nEntries; // number of entries in the palette
+ Guint nComps; // number of components in each entry
+ Guint *bpc; // bits per component, for each component
+ int *c; // color data:
+ // c[i*nComps+j] = entry i, component j
+};
+
+//------------------------------------------------------------------------
+
+struct JPXCompMap {
+ Guint nChannels; // number of channels
+ Guint *comp; // codestream components mapped to each channel
+ Guint *type; // 0 for direct use, 1 for palette mapping
+ Guint *pComp; // palette components to use
+};
+
+//------------------------------------------------------------------------
+
+struct JPXChannelDefn {
+ Guint nChannels; // number of channels
+ Guint *idx; // channel indexes
+ Guint *type; // channel types
+ Guint *assoc; // channel associations
+};
+
+//------------------------------------------------------------------------
+
+struct JPXTagTreeNode {
+ GBool finished; // true if this node is finished
+ Guint val; // current value
+};
+
+//------------------------------------------------------------------------
+
+struct JPXCodeBlock {
+ //----- size
+ Guint x0, y0, x1, y1; // bounds
+
+ //----- persistent state
+ GBool seen; // true if this code-block has already
+ // been seen
+ Guint lBlock; // base number of bits used for pkt data length
+ Guint nextPass; // next coding pass
+
+ //---- info from first packet
+ Guint nZeroBitPlanes; // number of zero bit planes
+
+ //----- info for the current packet
+ Guint included; // code-block inclusion in this packet:
+ // 0=not included, 1=included
+ Guint nCodingPasses; // number of coding passes in this pkt
+ Guint *dataLen; // data lengths (one per codeword segment)
+ Guint dataLenSize; // size of the dataLen array
+
+ //----- coefficient data
+ int *coeffs;
+ char *touched; // coefficient 'touched' flags
+ Gushort len; // coefficient length
+ JArithmeticDecoder // arithmetic decoder
+ *arithDecoder;
+ JArithmeticDecoderStats // arithmetic decoder stats
+ *stats;
+};
+
+//------------------------------------------------------------------------
+
+struct JPXSubband {
+ //----- computed
+ Guint nXCBs, nYCBs; // number of code-blocks in the x and y
+ // directions
+
+ //----- tag trees
+ Guint maxTTLevel; // max tag tree level
+ JPXTagTreeNode *inclusion; // inclusion tag tree for each subband
+ JPXTagTreeNode *zeroBitPlane; // zero-bit plane tag tree for each
+ // subband
+
+ //----- children
+ JPXCodeBlock *cbs; // the code-blocks (len = nXCBs * nYCBs)
+};
+
+//------------------------------------------------------------------------
+
+struct JPXPrecinct {
+ //----- children
+ JPXSubband *subbands; // the subbands
+};
+
+//------------------------------------------------------------------------
+
+struct JPXResLevel {
+ //----- from the COD and COC segments (main and tile)
+ Guint precinctWidth; // log2(precinct width)
+ Guint precinctHeight; // log2(precinct height)
+ Guint nPrecincts;
+
+ //----- computed
+ Guint x0, y0, x1, y1; // bounds of this tile-comp at this res level
+ Guint bx0[3], by0[3], // subband bounds
+ bx1[3], by1[3];
+ 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
+};
+
+//------------------------------------------------------------------------
+
+struct JPXTileComp {
+ //----- from the SIZ segment
+ GBool sgned; // 1 for signed, 0 for unsigned
+ Guint prec; // precision, in bits
+ Guint hSep; // horizontal separation of samples
+ Guint vSep; // vertical separation of samples
+
+ //----- from the COD and COC segments (main and tile)
+ Guint style; // coding style parameter (Scod / Scoc)
+ Guint nDecompLevels; // number of decomposition levels
+ Guint codeBlockW; // log2(code-block width)
+ Guint codeBlockH; // log2(code-block height)
+ Guint codeBlockStyle; // code-block style
+ Guint transform; // wavelet transformation
+
+ //----- from the QCD and QCC segments (main and tile)
+ Guint quantStyle; // quantization style
+ Guint *quantSteps; // quantization step size for each subband
+ Guint nQuantSteps; // number of entries in quantSteps
+
+ //----- computed
+ Guint x0, y0, x1, y1; // bounds of the tile-comp, in ref coords
+ Guint w, h; // data size = {x1 - x0, y1 - y0} >> reduction
+
+ //----- image data
+ int *data; // the decoded image data
+ int *buf; // intermediate buffer for the inverse
+ // transform
+
+ //----- children
+ JPXResLevel *resLevels; // the resolution levels
+ // (len = nDecompLevels + 1)
+};
+
+//------------------------------------------------------------------------
+
+struct JPXTile {
+ GBool init;
+
+ //----- from the COD segments (main and tile)
+ Guint progOrder; // progression order
+ Guint nLayers; // number of layers
+ Guint multiComp; // multiple component transformation
+
+ //----- computed
+ Guint x0, y0, x1, y1; // bounds of the tile, in ref coords
+ Guint maxNDecompLevels; // max number of decomposition levels used
+ // in any component in this tile
+
+ //----- progression order loop counters
+ Guint comp; // component
+ Guint res; // resolution level
+ Guint precinct; // precinct
+ Guint layer; // layer
+ GBool done; // set when this tile is done
+
+ //----- tile part info
+ Guint nextTilePart; // next expected tile-part
+
+ //----- children
+ JPXTileComp *tileComps; // the tile-components (len = JPXImage.nComps)
+};
+
+//------------------------------------------------------------------------
+
+struct JPXImage {
+ //----- from the SIZ segment
+ Guint xSize, ySize; // size of reference grid
+ Guint xOffset, yOffset; // image offset
+ Guint xTileSize, yTileSize; // size of tiles
+ Guint xTileOffset, // offset of first tile
+ yTileOffset;
+ Guint xSizeR, ySizeR; // size of reference grid >> reduction
+ Guint xOffsetR, yOffsetR; // image offset >> reduction
+ Guint nComps; // number of components
+
+ //----- computed
+ Guint nXTiles; // number of tiles in x direction
+ Guint nYTiles; // number of tiles in y direction
+
+ //----- children
+ JPXTile *tiles; // the tiles (len = nXTiles * nYTiles)
+};
+
+//------------------------------------------------------------------------
+
+enum JPXDecodeResult {
+ jpxDecodeOk,
+ jpxDecodeNonFatalError,
+ jpxDecodeFatalError
+};
+
+//------------------------------------------------------------------------
+
+class JPXStream: public FilterStream {
+public:
+
+ JPXStream(Stream *strA);
+ virtual ~JPXStream();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strJPX; }
+ virtual void reset();
+ virtual void close();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream);
+ virtual GBool isBinary(GBool last = gTrue);
+ virtual void getImageParams(int *bitsPerComponent,
+ StreamColorSpaceMode *csMode);
+ void reduceResolution(int reductionA) { reduction = reductionA; }
+
+private:
+
+ void fillReadBuf();
+ void getImageParams2(int *bitsPerComponent, StreamColorSpaceMode *csMode);
+ JPXDecodeResult readBoxes();
+ GBool readColorSpecBox(Guint dataLen);
+ JPXDecodeResult readCodestream(Guint len);
+ GBool readTilePart();
+ GBool readTilePartData(Guint tileIdx,
+ Guint tilePartLen, GBool tilePartToEOC);
+ GBool readCodeBlockData(JPXTileComp *tileComp,
+ JPXResLevel *resLevel,
+ JPXPrecinct *precinct,
+ JPXSubband *subband,
+ Guint res, Guint sb,
+ JPXCodeBlock *cb);
+ void inverseTransform(JPXTileComp *tileComp);
+ void inverseTransformLevel(JPXTileComp *tileComp,
+ Guint r, JPXResLevel *resLevel);
+ void inverseTransform1D(JPXTileComp *tileComp, int *data,
+ Guint offset, Guint n);
+ GBool inverseMultiCompAndDC(JPXTile *tile);
+ GBool readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen);
+ int readMarkerHdr(int *segType, Guint *segLen);
+ GBool readUByte(Guint *x);
+ GBool readByte(int *x);
+ GBool readUWord(Guint *x);
+ GBool readULong(Guint *x);
+ GBool readNBytes(int nBytes, GBool signd, int *x);
+ void startBitBuf(Guint byteCountA);
+ GBool readBits(int nBits, Guint *x);
+ void skipSOP();
+ void skipEPH();
+ Guint finishBitBuf();
+
+ BufStream *bufStr; // buffered stream (for lookahead)
+
+ Guint nComps; // number of components
+ Guint *bpc; // bits per component, for each component
+ Guint width, height; // image size
+ int reduction; // log2(reduction in resolution)
+ GBool haveImgHdr; // set if a JP2/JPX image header has been
+ // found
+ JPXColorSpec cs; // color specification
+ GBool haveCS; // set if a color spec has been found
+ JPXPalette palette; // the palette
+ GBool havePalette; // set if a palette has been found
+ JPXCompMap compMap; // the component mapping
+ GBool haveCompMap; // set if a component mapping has been found
+ JPXChannelDefn channelDefn; // channel definition
+ GBool haveChannelDefn; // set if a channel defn has been found
+
+ JPXImage img; // JPEG2000 decoder data
+ Guint bitBuf; // buffer for bit reads
+ int bitBufLen; // number of bits in bitBuf
+ GBool bitBufSkip; // true if next bit should be skipped
+ // (for bit stuffing)
+ Guint byteCount; // number of available bytes left
+
+ Guint curX, curY, curComp; // current position for lookChar/getChar
+ Guint readBuf; // read buffer
+ Guint readBufLen; // number of valid bits in readBuf
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc
new file mode 100644
index 00000000000..0c74dbb04ea
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.cc
@@ -0,0 +1,555 @@
+//========================================================================
+//
+// Lexer.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include "gmempp.h"
+#include "Lexer.h"
+#include "Error.h"
+
+//------------------------------------------------------------------------
+
+// A '1' in this array means the character is white space. A '1' or
+// '2' means the character ends a name or command.
+static char specialChars[256] = {
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, // 2x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 3x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 5x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx
+};
+
+//------------------------------------------------------------------------
+// Lexer
+//------------------------------------------------------------------------
+
+Lexer::Lexer(XRef *xref, Stream *str) {
+ Object obj;
+
+ curStr.initStream(str);
+ streams = new Array(xref);
+ streams->add(curStr.copy(&obj));
+ strPtr = 0;
+ freeArray = gTrue;
+ curStr.streamReset();
+}
+
+Lexer::Lexer(XRef *xref, Object *obj) {
+ Object obj2;
+
+ if (obj->isStream()) {
+ streams = new Array(xref);
+ freeArray = gTrue;
+ streams->add(obj->copy(&obj2));
+ } else {
+ streams = obj->getArray();
+ freeArray = gFalse;
+ }
+ strPtr = 0;
+ if (streams->getLength() > 0) {
+ streams->get(strPtr, &curStr);
+ curStr.streamReset();
+ }
+}
+
+Lexer::~Lexer() {
+ if (!curStr.isNone()) {
+ curStr.streamClose();
+ curStr.free();
+ }
+ if (freeArray) {
+ delete streams;
+ }
+}
+
+int Lexer::getChar() {
+ int c;
+
+ c = EOF;
+ while (!curStr.isNone() && (c = curStr.streamGetChar()) == EOF) {
+ curStr.streamClose();
+ curStr.free();
+ ++strPtr;
+ if (strPtr < streams->getLength()) {
+ streams->get(strPtr, &curStr);
+ curStr.streamReset();
+ }
+ }
+ return c;
+}
+
+int Lexer::lookChar() {
+ if (curStr.isNone()) {
+ return EOF;
+ }
+ return curStr.streamLookChar();
+}
+
+Object *Lexer::getObj(Object *obj) {
+ char *p;
+ int c, c2;
+ GBool comment, neg, doubleMinus, done, invalid;
+ int numParen;
+ int xi;
+ double xf, scale;
+ GString *s;
+ int n, m;
+
+ // skip whitespace and comments
+ comment = gFalse;
+ while (1) {
+ if ((c = getChar()) == EOF) {
+ return obj->initEOF();
+ }
+ if (comment) {
+ if (c == '\r' || c == '\n')
+ comment = gFalse;
+ } else if (c == '%') {
+ comment = gTrue;
+ } else if (specialChars[c] != 1) {
+ break;
+ }
+ }
+
+ // start reading token
+ switch (c) {
+
+ // number
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '+': case '-': case '.':
+ // Adobe's number lexer has some "interesting" behavior:
+ // "--123" is interpreted as 0
+ // "--123.4" is interpreted as -123.4 [I've seen this in the wild]
+ // "50-100" is interpreted as 50 [I've seen this in the wild]
+ // "50--100" is interpreted as 50
+ // "50-100.0" is an error -- but older versions of Acrobat may
+ // have interpreted it as 50100.0 (?)
+ // "50--100.0" is an error -- but older versions of Acrobat may
+ // have interpreted it as 50100.0 (?)
+ // "50.0-100" is interpreted as 50.0 (or maybe 50.0100?)
+ // "50.0--100" is interpreted as 50.0 (or maybe 50.0100?)
+ // "-50-100" is interpreted as -50
+ // "-" is interpreted as 0
+ // "-." is interpreted as 0.0
+ neg = gFalse;
+ doubleMinus = gFalse;
+ xf = xi = 0;
+ if (c == '+') {
+ // just ignore it
+ } else if (c == '-') {
+ neg = gTrue;
+ if (lookChar() == '-') {
+ doubleMinus = gTrue;
+ do {
+ getChar();
+ } while (lookChar() == '-');
+ }
+ } else if (c == '.') {
+ goto doReal;
+ } else {
+ xf = xi = c - '0';
+ }
+ while (1) {
+ c = lookChar();
+ if (isdigit(c)) {
+ getChar();
+ xi = xi * 10 + (c - '0');
+ if (xf < 1e20) {
+ xf = xf * 10 + (c - '0');
+ }
+ } else if (c == '.') {
+ getChar();
+ goto doReal;
+ } else {
+ break;
+ }
+ }
+ while ((c = lookChar()) == '-' || isdigit(c)) {
+ getChar();
+ }
+ if (neg) {
+ xi = -xi;
+ }
+ if (doubleMinus) {
+ xi = 0;
+ }
+ obj->initInt(xi);
+ break;
+ doReal:
+ scale = 0.1;
+ while (1) {
+ c = lookChar();
+ if (c == '-') {
+ error(errSyntaxWarning, getPos(), "Badly formatted number");
+ getChar();
+ continue;
+ }
+ if (!isdigit(c)) {
+ break;
+ }
+ getChar();
+ xf = xf + scale * (c - '0');
+ scale *= 0.1;
+ }
+ while ((c = lookChar()) == '-' || isdigit(c)) {
+ getChar();
+ }
+ if (neg) {
+ xf = -xf;
+ }
+ obj->initReal(xf);
+ break;
+
+ // string
+ case '(':
+ p = tokBuf;
+ n = 0;
+ numParen = 1;
+ done = gFalse;
+ s = NULL;
+ do {
+ c2 = EOF;
+ switch (c = getChar()) {
+
+ case EOF:
+ error(errSyntaxError, getPos(), "Unterminated string");
+ done = gTrue;
+ break;
+
+ case '(':
+ ++numParen;
+ c2 = c;
+ break;
+
+ case ')':
+ if (--numParen == 0) {
+ done = gTrue;
+ } else {
+ c2 = c;
+ }
+ 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':
+ c2 = '\n';
+ break;
+ case 'r':
+ c2 = '\r';
+ break;
+ case 't':
+ c2 = '\t';
+ break;
+ case 'b':
+ c2 = '\b';
+ break;
+ case 'f':
+ c2 = '\f';
+ break;
+ case '\\':
+ case '(':
+ case ')':
+ c2 = c;
+ break;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ c2 = c - '0';
+ c = lookChar();
+ if (c >= '0' && c <= '7') {
+ getChar();
+ c2 = (c2 << 3) + (c - '0');
+ c = lookChar();
+ if (c >= '0' && c <= '7') {
+ getChar();
+ c2 = (c2 << 3) + (c - '0');
+ }
+ }
+ break;
+ case '\r':
+ c = lookChar();
+ if (c == '\n') {
+ getChar();
+ }
+ break;
+ case '\n':
+ break;
+ case EOF:
+ error(errSyntaxError, getPos(), "Unterminated string");
+ done = gTrue;
+ break;
+ default:
+ c2 = c;
+ break;
+ }
+ break;
+
+ default:
+ c2 = c;
+ break;
+ }
+
+ if (c2 != EOF) {
+ if (n == tokBufSize) {
+ if (!s)
+ s = new GString(tokBuf, tokBufSize);
+ else
+ s->append(tokBuf, tokBufSize);
+ p = tokBuf;
+ n = 0;
+ }
+ *p++ = (char)c2;
+ ++n;
+ }
+ } while (!done);
+ if (!s)
+ s = new GString(tokBuf, n);
+ else
+ s->append(tokBuf, n);
+ obj->initString(s);
+ break;
+
+ // name
+ case '/':
+ p = tokBuf;
+ n = 0;
+ s = NULL;
+ invalid = gFalse;
+ while ((c = lookChar()) != EOF && !specialChars[c]) {
+ getChar();
+ if (c == '#') {
+ c2 = lookChar();
+ if (c2 >= '0' && c2 <= '9') {
+ c = c2 - '0';
+ } else if (c2 >= 'A' && c2 <= 'F') {
+ c = c2 - 'A' + 10;
+ } else if (c2 >= 'a' && c2 <= 'f') {
+ c = c2 - 'a' + 10;
+ } else {
+ error(errSyntaxError, getPos(), "Invalid hex escape in name");
+ goto notEscChar;
+ }
+ getChar();
+ c2 = lookChar();
+ if (c2 >= '0' && c2 <= '9') {
+ c = (c << 4) + (c2 - '0');
+ } else if (c2 >= 'A' && c2 <= 'F') {
+ c = (c << 4) + (c2 - 'A' + 10);
+ } else if (c2 >= 'a' && c2 <= 'f') {
+ c = (c << 4) + (c2 - 'a' + 10);
+ } else {
+ error(errSyntaxError, getPos(), "Invalid hex escape in name");
+ goto notEscChar;
+ }
+ getChar();
+ if (c == 0) {
+ invalid = gTrue;
+ }
+ }
+ notEscChar:
+ // the PDF spec claims that names are limited to 127 chars, but
+ // Distiller 8 will produce longer names, and Acrobat 8 will
+ // accept longer names
+ ++n;
+ if (n < tokBufSize) {
+ *p++ = (char)c;
+ } else if (n == tokBufSize) {
+ *p = (char)c;
+ s = new GString(tokBuf, n);
+ } else {
+ s->append((char)c);
+ }
+ }
+ if (invalid) {
+ error(errSyntaxError, getPos(), "Null character in name");
+ obj->initError();
+ if (s) {
+ delete s;
+ }
+ } else if (n < tokBufSize) {
+ *p = '\0';
+ obj->initName(tokBuf);
+ } else {
+ obj->initName(s->getCString());
+ delete s;
+ }
+ break;
+
+ // array punctuation
+ case '[':
+ case ']':
+ tokBuf[0] = (char)c;
+ tokBuf[1] = '\0';
+ obj->initCmd(tokBuf);
+ break;
+
+ // hex string or dict punctuation
+ case '<':
+ c = lookChar();
+
+ // dict punctuation
+ if (c == '<') {
+ getChar();
+ tokBuf[0] = tokBuf[1] = '<';
+ tokBuf[2] = '\0';
+ obj->initCmd(tokBuf);
+
+ // hex string
+ } else {
+ p = tokBuf;
+ m = n = 0;
+ c2 = 0;
+ s = NULL;
+ while (1) {
+ c = getChar();
+ if (c == '>') {
+ break;
+ } else if (c == EOF) {
+ error(errSyntaxError, getPos(), "Unterminated hex string");
+ break;
+ } else if (specialChars[c] != 1) {
+ c2 = c2 << 4;
+ if (c >= '0' && c <= '9')
+ c2 += c - '0';
+ else if (c >= 'A' && c <= 'F')
+ c2 += c - 'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ c2 += c - 'a' + 10;
+ else
+ error(errSyntaxError, getPos(),
+ "Illegal character <{0:02x}> in hex string", c);
+ if (++m == 2) {
+ if (n == tokBufSize) {
+ if (!s)
+ s = new GString(tokBuf, tokBufSize);
+ else
+ s->append(tokBuf, tokBufSize);
+ p = tokBuf;
+ n = 0;
+ }
+ *p++ = (char)c2;
+ ++n;
+ c2 = 0;
+ m = 0;
+ }
+ }
+ }
+ if (!s)
+ s = new GString(tokBuf, n);
+ else
+ s->append(tokBuf, n);
+ if (m == 1)
+ s->append((char)(c2 << 4));
+ obj->initString(s);
+ }
+ break;
+
+ // dict punctuation
+ case '>':
+ c = lookChar();
+ if (c == '>') {
+ getChar();
+ tokBuf[0] = tokBuf[1] = '>';
+ tokBuf[2] = '\0';
+ obj->initCmd(tokBuf);
+ } else {
+ error(errSyntaxError, getPos(), "Illegal character '>'");
+ obj->initError();
+ }
+ break;
+
+ // error
+ case ')':
+ case '{':
+ case '}':
+ error(errSyntaxError, getPos(), "Illegal character '{0:c}'", c);
+ obj->initError();
+ break;
+
+ // command
+ default:
+ p = tokBuf;
+ *p++ = (char)c;
+ n = 1;
+ while ((c = lookChar()) != EOF && !specialChars[c]) {
+ getChar();
+ if (++n == tokBufSize) {
+ error(errSyntaxError, getPos(), "Command token too long");
+ break;
+ }
+ *p++ = (char)c;
+ }
+ *p = '\0';
+ if (tokBuf[0] == 't' && !strcmp(tokBuf, "true")) {
+ obj->initBool(gTrue);
+ } else if (tokBuf[0] == 'f' && !strcmp(tokBuf, "false")) {
+ obj->initBool(gFalse);
+ } else if (tokBuf[0] == 'n' && !strcmp(tokBuf, "null")) {
+ obj->initNull();
+ } else {
+ obj->initCmd(tokBuf);
+ }
+ break;
+ }
+
+ return obj;
+}
+
+void Lexer::skipToNextLine() {
+ int c;
+
+ while (1) {
+ c = getChar();
+ if (c == EOF || c == '\n') {
+ return;
+ }
+ if (c == '\r') {
+ if ((c = lookChar()) == '\n') {
+ getChar();
+ }
+ return;
+ }
+ }
+}
+
+void Lexer::skipToEOF() {
+ while (getChar() != EOF) ;
+}
+
+GBool Lexer::isSpace(int c) {
+ return c >= 0 && c <= 0xff && specialChars[c] == 1;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.h
new file mode 100644
index 00000000000..d481c2c5a2b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Lexer.h
@@ -0,0 +1,85 @@
+//========================================================================
+//
+// Lexer.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef LEXER_H
+#define LEXER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+#include "Stream.h"
+
+class XRef;
+
+#define tokBufSize 128 // size of token buffer
+
+//------------------------------------------------------------------------
+// Lexer
+//------------------------------------------------------------------------
+
+class Lexer {
+public:
+
+ // Construct a lexer for a single stream. Deletes the stream when
+ // lexer is deleted.
+ Lexer(XRef *xref, Stream *str);
+
+ // Construct a lexer for a stream or array of streams (assumes obj
+ // is either a stream or array of streams).
+ Lexer(XRef *xref, Object *obj);
+
+ // Destructor.
+ ~Lexer();
+
+ // Get the next object from the input stream.
+ Object *getObj(Object *obj);
+
+ // Skip to the beginning of the next line in the input stream.
+ void skipToNextLine();
+
+ // Skip to the end of the input stream.
+ void skipToEOF();
+
+ // Skip over one character.
+ void skipChar() { getChar(); }
+
+ // Get stream index (for arrays of streams).
+ int getStreamIndex() { return strPtr; }
+
+ // Get stream.
+ Stream *getStream()
+ { return curStr.isNone() ? (Stream *)NULL : curStr.getStream(); }
+
+ // Get current position in file.
+ GFileOffset getPos()
+ { return curStr.isNone() ? -1 : curStr.streamGetPos(); }
+
+ // Set position in file.
+ void setPos(GFileOffset pos, int dir = 0)
+ { if (!curStr.isNone()) curStr.streamSetPos(pos, dir); }
+
+ // Returns true if <c> is a whitespace character.
+ static GBool isSpace(int c);
+
+private:
+
+ int getChar();
+ int lookChar();
+
+ Array *streams; // array of input streams
+ int strPtr; // index of current stream
+ Object curStr; // current stream
+ GBool freeArray; // should lexer free the streams array?
+ char tokBuf[tokBufSize]; // temporary token buffer
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Link.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Link.cc
new file mode 100644
index 00000000000..900f79ddf85
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Link.cc
@@ -0,0 +1,926 @@
+//========================================================================
+//
+// Link.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "Error.h"
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Link.h"
+
+//------------------------------------------------------------------------
+// LinkAction
+//------------------------------------------------------------------------
+
+LinkAction *LinkAction::parseDest(Object *obj) {
+ LinkAction *action;
+
+ action = new LinkGoTo(obj);
+ if (!action->isOk()) {
+ delete action;
+ return NULL;
+ }
+ return action;
+}
+
+LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
+ LinkAction *action;
+ Object obj2, obj3, obj4, obj5;
+
+ if (!obj->isDict()) {
+ error(errSyntaxWarning, -1, "Bad annotation action");
+ return NULL;
+ }
+
+ obj->dictLookup("S", &obj2);
+
+ // GoTo action
+ if (obj2.isName("GoTo")) {
+ obj->dictLookup("D", &obj3);
+ action = new LinkGoTo(&obj3);
+ obj3.free();
+
+ // GoToR action
+ } else if (obj2.isName("GoToR")) {
+ obj->dictLookup("F", &obj3);
+ obj->dictLookup("D", &obj4);
+ action = new LinkGoToR(&obj3, &obj4);
+ obj3.free();
+ obj4.free();
+
+ // Launch action
+ } else if (obj2.isName("Launch")) {
+ action = new LinkLaunch(obj);
+
+ // URI action
+ } else if (obj2.isName("URI")) {
+ obj->dictLookup("URI", &obj3);
+ action = new LinkURI(&obj3, baseURI);
+ obj3.free();
+
+ // Named action
+ } else if (obj2.isName("Named")) {
+ obj->dictLookup("N", &obj3);
+ action = new LinkNamed(&obj3);
+ obj3.free();
+
+ // Movie action
+ } else if (obj2.isName("Movie")) {
+ obj->dictLookupNF("Annot", &obj3);
+ obj->dictLookup("T", &obj4);
+ action = new LinkMovie(&obj3, &obj4);
+ obj3.free();
+ obj4.free();
+
+ // JavaScript action
+ } else if (obj2.isName("JavaScript")) {
+ obj->dictLookup("JS", &obj3);
+ action = new LinkJavaScript(&obj3);
+ obj3.free();
+
+ // SubmitForm action
+ } else if (obj2.isName("SubmitForm")) {
+ obj->dictLookup("F", &obj3);
+ obj->dictLookup("Fields", &obj4);
+ obj->dictLookup("Flags", &obj5);
+ action = new LinkSubmitForm(&obj3, &obj4, &obj5);
+ obj3.free();
+ obj4.free();
+ obj5.free();
+
+ // Hide action
+ } else if (obj2.isName("Hide")) {
+ obj->dictLookupNF("T", &obj3);
+ obj->dictLookup("H", &obj4);
+ action = new LinkHide(&obj3, &obj4);
+ obj3.free();
+ obj4.free();
+
+ // unknown action
+ } else if (obj2.isName()) {
+ action = new LinkUnknown(obj2.getName());
+
+ // action is missing or wrong type
+ } else {
+ error(errSyntaxWarning, -1, "Bad annotation action");
+ action = NULL;
+ }
+
+ obj2.free();
+
+ if (action && !action->isOk()) {
+ delete action;
+ return NULL;
+ }
+ return action;
+}
+
+GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
+ GString *name;
+ Object obj1;
+
+ name = NULL;
+
+ // string
+ if (fileSpecObj->isString()) {
+ name = fileSpecObj->getString()->copy();
+
+ // dictionary
+ } else if (fileSpecObj->isDict()) {
+#ifdef _WIN32
+ if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) {
+#else
+ if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
+#endif
+ obj1.free();
+ fileSpecObj->dictLookup("F", &obj1);
+ }
+ if (obj1.isString()) {
+ name = obj1.getString()->copy();
+ } else {
+ error(errSyntaxWarning, -1, "Illegal file spec in link");
+ }
+ obj1.free();
+
+ // error
+ } else {
+ error(errSyntaxWarning, -1, "Illegal file spec in link");
+ }
+
+ // system-dependent path manipulation
+ if (name) {
+#ifdef _WIN32
+ int i, j;
+
+ // "//...." --> "\...."
+ // "/x/...." --> "x:\...."
+ // "/server/share/...." --> "\\server\share\...."
+ // convert escaped slashes to slashes and unescaped slashes to backslashes
+ i = 0;
+ if (name->getChar(0) == '/') {
+ if (name->getLength() >= 2 && name->getChar(1) == '/') {
+ name->del(0);
+ i = 0;
+ } else if (name->getLength() >= 2 &&
+ ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') ||
+ (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) &&
+ (name->getLength() == 2 || name->getChar(2) == '/')) {
+ name->setChar(0, name->getChar(1));
+ name->setChar(1, ':');
+ i = 2;
+ } else {
+ for (j = 2; j < name->getLength(); ++j) {
+ if (name->getChar(j-1) != '\\' &&
+ name->getChar(j) == '/') {
+ break;
+ }
+ }
+ if (j < name->getLength()) {
+ name->setChar(0, '\\');
+ name->insert(0, '\\');
+ i = 2;
+ }
+ }
+ }
+ for (; i < name->getLength(); ++i) {
+ if (name->getChar(i) == '/') {
+ name->setChar(i, '\\');
+ } else if (name->getChar(i) == '\\' &&
+ i+1 < name->getLength() &&
+ name->getChar(i+1) == '/') {
+ name->del(i);
+ }
+ }
+#else
+ // no manipulation needed for Unix
+#endif
+ }
+
+ return name;
+}
+
+//------------------------------------------------------------------------
+// LinkDest
+//------------------------------------------------------------------------
+
+LinkDest::LinkDest(Array *a) {
+ Object obj1, obj2;
+
+ // initialize fields
+ left = bottom = right = top = zoom = 0;
+ ok = gFalse;
+
+ // get page
+ if (a->getLength() < 2) {
+ error(errSyntaxWarning, -1, "Annotation destination array is too short");
+ return;
+ }
+ a->getNF(0, &obj1);
+ if (obj1.isInt()) {
+ pageNum = obj1.getInt() + 1;
+ pageIsRef = gFalse;
+ } else if (obj1.isRef()) {
+ pageRef.num = obj1.getRefNum();
+ pageRef.gen = obj1.getRefGen();
+ pageIsRef = gTrue;
+ } else {
+ error(errSyntaxWarning, -1, "Bad annotation destination");
+ goto err2;
+ }
+ obj1.free();
+
+ // get destination type
+ a->get(1, &obj1);
+
+ // XYZ link
+ if (obj1.isName("XYZ")) {
+ kind = destXYZ;
+ if (a->getLength() < 3) {
+ changeLeft = gFalse;
+ } else {
+ a->get(2, &obj2);
+ if (obj2.isNull()) {
+ changeLeft = gFalse;
+ } else if (obj2.isNum()) {
+ changeLeft = gTrue;
+ left = obj2.getNum();
+ } else {
+ error(errSyntaxWarning, -1, "Bad annotation destination position");
+ goto err1;
+ }
+ obj2.free();
+ }
+ if (a->getLength() < 4) {
+ changeTop = gFalse;
+ } else {
+ a->get(3, &obj2);
+ if (obj2.isNull()) {
+ changeTop = gFalse;
+ } else if (obj2.isNum()) {
+ changeTop = gTrue;
+ top = obj2.getNum();
+ } else {
+ error(errSyntaxWarning, -1, "Bad annotation destination position");
+ goto err1;
+ }
+ obj2.free();
+ }
+ if (a->getLength() < 5) {
+ changeZoom = gFalse;
+ } else {
+ a->get(4, &obj2);
+ if (obj2.isNull()) {
+ changeZoom = gFalse;
+ } else if (obj2.isNum()) {
+ changeZoom = gTrue;
+ zoom = obj2.getNum();
+ } else {
+ error(errSyntaxWarning, -1, "Bad annotation destination position");
+ goto err1;
+ }
+ obj2.free();
+ }
+
+ // Fit link
+ } else if (obj1.isName("Fit")) {
+ if (a->getLength() < 2) {
+ error(errSyntaxWarning, -1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFit;
+
+ // FitH link
+ } else if (obj1.isName("FitH")) {
+ if (a->getLength() < 3) {
+ error(errSyntaxWarning, -1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFitH;
+ if (a->get(2, &obj2)->isNum()) {
+ top = obj2.getNum();
+ changeTop = gTrue;
+ } else if (obj2.isNull()) {
+ changeTop = gFalse;
+ } else {
+ error(errSyntaxWarning, -1, "Bad annotation destination position");
+ kind = destFit;
+ }
+ obj2.free();
+
+ // FitV link
+ } else if (obj1.isName("FitV")) {
+ if (a->getLength() < 3) {
+ error(errSyntaxWarning, -1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFitV;
+ if (a->get(2, &obj2)->isNum()) {
+ left = obj2.getNum();
+ changeLeft = gTrue;
+ } else if (obj2.isNull()) {
+ changeLeft = gFalse;
+ } else {
+ error(errSyntaxWarning, -1, "Bad annotation destination position");
+ kind = destFit;
+ }
+ obj2.free();
+
+ // FitR link
+ } else if (obj1.isName("FitR")) {
+ if (a->getLength() < 6) {
+ error(errSyntaxWarning, -1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFitR;
+ if (a->get(2, &obj2)->isNum()) {
+ left = obj2.getNum();
+ } else {
+ error(errSyntaxWarning, -1, "Bad annotation destination position");
+ kind = destFit;
+ }
+ obj2.free();
+ if (!a->get(3, &obj2)->isNum()) {
+ error(errSyntaxWarning, -1, "Bad annotation destination position");
+ kind = destFit;
+ }
+ bottom = obj2.getNum();
+ obj2.free();
+ if (!a->get(4, &obj2)->isNum()) {
+ error(errSyntaxWarning, -1, "Bad annotation destination position");
+ kind = destFit;
+ }
+ right = obj2.getNum();
+ obj2.free();
+ if (!a->get(5, &obj2)->isNum()) {
+ error(errSyntaxWarning, -1, "Bad annotation destination position");
+ kind = destFit;
+ }
+ top = obj2.getNum();
+ obj2.free();
+
+ // FitB link
+ } else if (obj1.isName("FitB")) {
+ if (a->getLength() < 2) {
+ error(errSyntaxWarning, -1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFitB;
+
+ // FitBH link
+ } else if (obj1.isName("FitBH")) {
+ if (a->getLength() < 3) {
+ error(errSyntaxWarning, -1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFitBH;
+ if (a->get(2, &obj2)->isNum()) {
+ top = obj2.getNum();
+ changeTop = gTrue;
+ } else if (obj2.isNull()) {
+ changeTop = gFalse;
+ } else {
+ error(errSyntaxWarning, -1, "Bad annotation destination position");
+ kind = destFit;
+ }
+ obj2.free();
+
+ // FitBV link
+ } else if (obj1.isName("FitBV")) {
+ if (a->getLength() < 3) {
+ error(errSyntaxWarning, -1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFitBV;
+ if (a->get(2, &obj2)->isNum()) {
+ left = obj2.getNum();
+ changeLeft = gTrue;
+ } else if (obj2.isNull()) {
+ changeLeft = gFalse;
+ } else {
+ error(errSyntaxWarning, -1, "Bad annotation destination position");
+ kind = destFit;
+ }
+ obj2.free();
+
+ // unknown link kind
+ } else {
+ error(errSyntaxWarning, -1, "Unknown annotation destination type");
+ goto err2;
+ }
+
+ obj1.free();
+ ok = gTrue;
+ return;
+
+ err1:
+ obj2.free();
+ err2:
+ obj1.free();
+}
+
+LinkDest::LinkDest(LinkDest *dest) {
+ kind = dest->kind;
+ pageIsRef = dest->pageIsRef;
+ if (pageIsRef)
+ pageRef = dest->pageRef;
+ else
+ pageNum = dest->pageNum;
+ left = dest->left;
+ bottom = dest->bottom;
+ right = dest->right;
+ top = dest->top;
+ zoom = dest->zoom;
+ changeLeft = dest->changeLeft;
+ changeTop = dest->changeTop;
+ changeZoom = dest->changeZoom;
+ ok = gTrue;
+}
+
+//------------------------------------------------------------------------
+// LinkGoTo
+//------------------------------------------------------------------------
+
+LinkGoTo::LinkGoTo(Object *destObj) {
+ dest = NULL;
+ namedDest = NULL;
+
+ // named destination
+ if (destObj->isName()) {
+ namedDest = new GString(destObj->getName());
+ } else if (destObj->isString()) {
+ namedDest = destObj->getString()->copy();
+
+ // destination dictionary
+ } else if (destObj->isArray()) {
+ dest = new LinkDest(destObj->getArray());
+ if (!dest->isOk()) {
+ delete dest;
+ dest = NULL;
+ }
+
+ // error
+ } else {
+ error(errSyntaxWarning, -1, "Illegal annotation destination");
+ }
+}
+
+LinkGoTo::~LinkGoTo() {
+ if (dest)
+ delete dest;
+ if (namedDest)
+ delete namedDest;
+}
+
+//------------------------------------------------------------------------
+// LinkGoToR
+//------------------------------------------------------------------------
+
+LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
+ dest = NULL;
+ namedDest = NULL;
+
+ // get file name
+ fileName = getFileSpecName(fileSpecObj);
+
+ // named destination
+ if (destObj->isName()) {
+ namedDest = new GString(destObj->getName());
+ } else if (destObj->isString()) {
+ namedDest = destObj->getString()->copy();
+
+ // destination dictionary
+ } else if (destObj->isArray()) {
+ dest = new LinkDest(destObj->getArray());
+ if (!dest->isOk()) {
+ delete dest;
+ dest = NULL;
+ }
+
+ // error
+ } else {
+ error(errSyntaxWarning, -1, "Illegal annotation destination");
+ }
+}
+
+LinkGoToR::~LinkGoToR() {
+ if (fileName)
+ delete fileName;
+ if (dest)
+ delete dest;
+ if (namedDest)
+ delete namedDest;
+}
+
+
+//------------------------------------------------------------------------
+// LinkLaunch
+//------------------------------------------------------------------------
+
+LinkLaunch::LinkLaunch(Object *actionObj) {
+ Object obj1, obj2;
+
+ fileName = NULL;
+ params = NULL;
+
+ if (actionObj->isDict()) {
+ if (!actionObj->dictLookup("F", &obj1)->isNull()) {
+ fileName = getFileSpecName(&obj1);
+ } else {
+ obj1.free();
+#ifdef _WIN32
+ if (actionObj->dictLookup("Win", &obj1)->isDict()) {
+ obj1.dictLookup("F", &obj2);
+ fileName = getFileSpecName(&obj2);
+ obj2.free();
+ if (obj1.dictLookup("P", &obj2)->isString()) {
+ params = obj2.getString()->copy();
+ }
+ obj2.free();
+ } else {
+ error(errSyntaxWarning, -1, "Bad launch-type link action");
+ }
+#else
+ //~ This hasn't been defined by Adobe yet, so assume it looks
+ //~ just like the Win dictionary until they say otherwise.
+ if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
+ obj1.dictLookup("F", &obj2);
+ fileName = getFileSpecName(&obj2);
+ obj2.free();
+ if (obj1.dictLookup("P", &obj2)->isString()) {
+ params = obj2.getString()->copy();
+ }
+ obj2.free();
+ } else {
+ error(errSyntaxWarning, -1, "Bad launch-type link action");
+ }
+#endif
+ }
+ obj1.free();
+ }
+}
+
+LinkLaunch::~LinkLaunch() {
+ if (fileName)
+ delete fileName;
+ if (params)
+ delete params;
+}
+
+//------------------------------------------------------------------------
+// LinkURI
+//------------------------------------------------------------------------
+
+LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
+ GString *uri2;
+ int n;
+ char c;
+
+ uri = NULL;
+ if (uriObj->isString()) {
+ uri2 = uriObj->getString();
+ n = (int)strcspn(uri2->getCString(), "/:");
+ if (n < uri2->getLength() && uri2->getChar(n) == ':') {
+ // "http:..." etc.
+ uri = uri2->copy();
+ } else if (!uri2->cmpN("www.", 4)) {
+ // "www.[...]" without the leading "http://"
+ uri = new GString("http://");
+ uri->append(uri2);
+ } else {
+ // relative URI
+ if (baseURI) {
+ uri = baseURI->copy();
+ c = uri->getChar(uri->getLength() - 1);
+ if (c != '/' && c != '?') {
+ uri->append('/');
+ }
+ if (uri2->getChar(0) == '/') {
+ uri->append(uri2->getCString() + 1, uri2->getLength() - 1);
+ } else {
+ uri->append(uri2);
+ }
+ } else {
+ uri = uri2->copy();
+ }
+ }
+ } else {
+ error(errSyntaxWarning, -1, "Illegal URI-type link");
+ }
+}
+
+LinkURI::~LinkURI() {
+ if (uri)
+ delete uri;
+}
+
+//------------------------------------------------------------------------
+// LinkNamed
+//------------------------------------------------------------------------
+
+LinkNamed::LinkNamed(Object *nameObj) {
+ name = NULL;
+ if (nameObj->isName()) {
+ name = new GString(nameObj->getName());
+ }
+}
+
+LinkNamed::~LinkNamed() {
+ if (name) {
+ delete name;
+ }
+}
+
+//------------------------------------------------------------------------
+// LinkMovie
+//------------------------------------------------------------------------
+
+LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
+ annotRef.num = -1;
+ title = NULL;
+ if (annotObj->isRef()) {
+ annotRef = annotObj->getRef();
+ } else if (titleObj->isString()) {
+ title = titleObj->getString()->copy();
+ } else {
+ error(errSyntaxError, -1,
+ "Movie action is missing both the Annot and T keys");
+ }
+}
+
+LinkMovie::~LinkMovie() {
+ if (title) {
+ delete title;
+ }
+}
+
+//------------------------------------------------------------------------
+// LinkJavaScript
+//------------------------------------------------------------------------
+
+LinkJavaScript::LinkJavaScript(Object *jsObj) {
+ char buf[4096];
+ int n;
+
+ if (jsObj->isString()) {
+ js = jsObj->getString()->copy();
+ } else if (jsObj->isStream()) {
+ js = new GString();
+ jsObj->streamReset();
+ while ((n = jsObj->getStream()->getBlock(buf, sizeof(buf))) > 0) {
+ js->append(buf, n);
+ }
+ jsObj->streamClose();
+ } else {
+ error(errSyntaxError, -1, "JavaScript action JS key is wrong type");
+ js = NULL;
+ }
+}
+
+LinkJavaScript::~LinkJavaScript() {
+ if (js) {
+ delete js;
+ }
+}
+
+//------------------------------------------------------------------------
+// LinkSubmitForm
+//------------------------------------------------------------------------
+
+LinkSubmitForm::LinkSubmitForm(Object *urlObj, Object *fieldsObj,
+ Object *flagsObj) {
+ if (urlObj->isString()) {
+ url = urlObj->getString()->copy();
+ } else {
+ error(errSyntaxError, -1, "SubmitForm action URL is wrong type");
+ url = NULL;
+ }
+
+ if (fieldsObj->isArray()) {
+ fieldsObj->copy(&fields);
+ } else {
+ if (!fieldsObj->isNull()) {
+ error(errSyntaxError, -1, "SubmitForm action Fields value is wrong type");
+ }
+ fields.initNull();
+ }
+
+ if (flagsObj->isInt()) {
+ flags = flagsObj->getInt();
+ } else {
+ if (!flagsObj->isNull()) {
+ error(errSyntaxError, -1, "SubmitForm action Flags value is wrong type");
+ }
+ flags = 0;
+ }
+}
+
+LinkSubmitForm::~LinkSubmitForm() {
+ if (url) {
+ delete url;
+ }
+ fields.free();
+}
+
+//------------------------------------------------------------------------
+// LinkHide
+//------------------------------------------------------------------------
+
+LinkHide::LinkHide(Object *fieldsObj, Object *hideFlagObj) {
+ if (fieldsObj->isRef() || fieldsObj->isString() || fieldsObj->isArray()) {
+ fieldsObj->copy(&fields);
+ } else {
+ error(errSyntaxError, -1, "Hide action T value is wrong type");
+ fields.initNull();
+ }
+
+ if (hideFlagObj->isBool()) {
+ hideFlag = hideFlagObj->getBool();
+ } else {
+ error(errSyntaxError, -1, "Hide action H value is wrong type");
+ hideFlag = gFalse;
+ }
+}
+
+LinkHide::~LinkHide() {
+ fields.free();
+}
+
+//------------------------------------------------------------------------
+// LinkUnknown
+//------------------------------------------------------------------------
+
+LinkUnknown::LinkUnknown(char *actionA) {
+ action = new GString(actionA);
+}
+
+LinkUnknown::~LinkUnknown() {
+ delete action;
+}
+
+//------------------------------------------------------------------------
+// Link
+//------------------------------------------------------------------------
+
+Link::Link(Dict *dict, GString *baseURI) {
+ Object obj1, obj2;
+ double t;
+
+ action = NULL;
+ ok = gFalse;
+
+ // get rectangle
+ if (!dict->lookup("Rect", &obj1)->isArray()) {
+ error(errSyntaxError, -1, "Annotation rectangle is wrong type");
+ goto err2;
+ }
+ if (!obj1.arrayGet(0, &obj2)->isNum()) {
+ error(errSyntaxError, -1, "Bad annotation rectangle");
+ goto err1;
+ }
+ x1 = obj2.getNum();
+ obj2.free();
+ if (!obj1.arrayGet(1, &obj2)->isNum()) {
+ error(errSyntaxError, -1, "Bad annotation rectangle");
+ goto err1;
+ }
+ y1 = obj2.getNum();
+ obj2.free();
+ if (!obj1.arrayGet(2, &obj2)->isNum()) {
+ error(errSyntaxError, -1, "Bad annotation rectangle");
+ goto err1;
+ }
+ x2 = obj2.getNum();
+ obj2.free();
+ if (!obj1.arrayGet(3, &obj2)->isNum()) {
+ error(errSyntaxError, -1, "Bad annotation rectangle");
+ goto err1;
+ }
+ y2 = obj2.getNum();
+ obj2.free();
+ obj1.free();
+ if (x1 > x2) {
+ t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+ if (y1 > y2) {
+ t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+
+ // look for destination
+ if (!dict->lookup("Dest", &obj1)->isNull()) {
+ action = LinkAction::parseDest(&obj1);
+
+ // look for action
+ } else {
+ obj1.free();
+ if (dict->lookup("A", &obj1)->isDict()) {
+ action = LinkAction::parseAction(&obj1, baseURI);
+ }
+ }
+ obj1.free();
+
+ // check for bad action
+ if (action) {
+ ok = gTrue;
+ }
+
+ return;
+
+ err1:
+ obj2.free();
+ err2:
+ obj1.free();
+}
+
+Link::~Link() {
+ if (action) {
+ delete action;
+ }
+}
+
+//------------------------------------------------------------------------
+// Links
+//------------------------------------------------------------------------
+
+Links::Links(Object *annots, GString *baseURI) {
+ Link *link;
+ Object obj1, obj2, obj3;
+ int size;
+ int i;
+
+ links = NULL;
+ size = 0;
+ numLinks = 0;
+
+ if (annots->isArray()) {
+ for (i = 0; i < annots->arrayGetLength(); ++i) {
+ if (annots->arrayGet(i, &obj1)->isDict()) {
+ obj1.dictLookup("Subtype", &obj2);
+ obj1.dictLookup("FT", &obj3);
+ if (obj2.isName("Link") ||
+ (obj2.isName("Widget") && (obj3.isName("Btn") || obj3.isNull()))) {
+ link = new Link(obj1.getDict(), baseURI);
+ if (link->isOk()) {
+ if (numLinks >= size) {
+ size += 16;
+ links = (Link **)greallocn(links, size, sizeof(Link *));
+ }
+ links[numLinks++] = link;
+ } else {
+ delete link;
+ }
+ }
+ obj3.free();
+ obj2.free();
+ }
+ obj1.free();
+ }
+ }
+}
+
+Links::~Links() {
+ int i;
+
+ for (i = 0; i < numLinks; ++i)
+ delete links[i];
+ gfree(links);
+}
+
+LinkAction *Links::find(double x, double y) {
+ int i;
+
+ for (i = numLinks - 1; i >= 0; --i) {
+ if (links[i]->inRect(x, y)) {
+ return links[i]->getAction();
+ }
+ }
+ return NULL;
+}
+
+GBool Links::onLink(double x, double y) {
+ int i;
+
+ for (i = 0; i < numLinks; ++i) {
+ if (links[i]->inRect(x, y))
+ return gTrue;
+ }
+ return gFalse;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Link.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Link.h
new file mode 100644
index 00000000000..480496ad176
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Link.h
@@ -0,0 +1,453 @@
+//========================================================================
+//
+// Link.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef LINK_H
+#define LINK_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+
+class GString;
+class Array;
+class Dict;
+
+//------------------------------------------------------------------------
+// LinkAction
+//------------------------------------------------------------------------
+
+enum LinkActionKind {
+ actionGoTo, // go to destination
+ actionGoToR, // go to destination in new file
+ actionLaunch, // launch app (or open document)
+ actionURI, // URI
+ actionNamed, // named action
+ actionMovie, // movie action
+ actionJavaScript, // run JavaScript
+ actionSubmitForm, // submit form
+ actionHide, // hide annotation
+ actionUnknown // anything else
+};
+
+class LinkAction {
+public:
+
+ // Destructor.
+ virtual ~LinkAction() {}
+
+ // Was the LinkAction created successfully?
+ virtual GBool isOk() = 0;
+
+ // Check link action type.
+ virtual LinkActionKind getKind() = 0;
+
+ // Parse a destination (old-style action) name, string, or array.
+ static LinkAction *parseDest(Object *obj);
+
+ // Parse an action dictionary.
+ static LinkAction *parseAction(Object *obj, GString *baseURI = NULL);
+
+ // Extract a file name from a file specification (string or
+ // dictionary).
+ static GString *getFileSpecName(Object *fileSpecObj);
+};
+
+//------------------------------------------------------------------------
+// LinkDest
+//------------------------------------------------------------------------
+
+enum LinkDestKind {
+ destXYZ,
+ destFit,
+ destFitH,
+ destFitV,
+ destFitR,
+ destFitB,
+ destFitBH,
+ destFitBV
+};
+
+class LinkDest {
+public:
+
+ // Build a LinkDest from the array.
+ LinkDest(Array *a);
+
+ // Copy a LinkDest.
+ LinkDest *copy() { return new LinkDest(this); }
+
+ // Was the LinkDest created successfully?
+ GBool isOk() { return ok; }
+
+ // Accessors.
+ LinkDestKind getKind() { return kind; }
+ GBool isPageRef() { return pageIsRef; }
+ int getPageNum() { return pageNum; }
+ Ref getPageRef() { return pageRef; }
+ double getLeft() { return left; }
+ double getBottom() { return bottom; }
+ double getRight() { return right; }
+ double getTop() { return top; }
+ double getZoom() { return zoom; }
+ GBool getChangeLeft() { return changeLeft; }
+ GBool getChangeTop() { return changeTop; }
+ GBool getChangeZoom() { return changeZoom; }
+
+private:
+
+ LinkDestKind kind; // destination type
+ GBool pageIsRef; // is the page a reference or number?
+ union {
+ Ref pageRef; // reference to page
+ int pageNum; // one-relative page number
+ };
+ double left, bottom; // position
+ double right, top;
+ double zoom; // zoom factor
+ GBool changeLeft, changeTop; // which position components to change:
+ GBool changeZoom; // destXYZ uses all three;
+ // destFitH/BH use changeTop;
+ // destFitV/BV use changeLeft
+ GBool ok; // set if created successfully
+
+ LinkDest(LinkDest *dest);
+};
+
+//------------------------------------------------------------------------
+// LinkGoTo
+//------------------------------------------------------------------------
+
+class LinkGoTo: public LinkAction {
+public:
+
+ // Build a LinkGoTo from a destination (dictionary, name, or string).
+ LinkGoTo(Object *destObj);
+
+ // Destructor.
+ virtual ~LinkGoTo();
+
+ // Was the LinkGoTo created successfully?
+ virtual GBool isOk() { return dest || namedDest; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionGoTo; }
+ LinkDest *getDest() { return dest; }
+ GString *getNamedDest() { return namedDest; }
+
+private:
+
+ LinkDest *dest; // regular destination (NULL for remote
+ // link with bad destination)
+ GString *namedDest; // named destination (only one of dest and
+ // and namedDest may be non-NULL)
+};
+
+//------------------------------------------------------------------------
+// LinkGoToR
+//------------------------------------------------------------------------
+
+class LinkGoToR: public LinkAction {
+public:
+
+ // Build a LinkGoToR from a file spec (dictionary) and destination
+ // (dictionary, name, or string).
+ LinkGoToR(Object *fileSpecObj, Object *destObj);
+
+ // Destructor.
+ virtual ~LinkGoToR();
+
+ // Was the LinkGoToR created successfully?
+ virtual GBool isOk() { return fileName && (dest || namedDest); }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionGoToR; }
+ GString *getFileName() { return fileName; }
+ LinkDest *getDest() { return dest; }
+ GString *getNamedDest() { return namedDest; }
+
+private:
+
+ GString *fileName; // file name
+ LinkDest *dest; // regular destination (NULL for remote
+ // link with bad destination)
+ GString *namedDest; // named destination (only one of dest and
+ // and namedDest may be non-NULL)
+};
+
+//------------------------------------------------------------------------
+// LinkLaunch
+//------------------------------------------------------------------------
+
+class LinkLaunch: public LinkAction {
+public:
+
+ // Build a LinkLaunch from an action dictionary.
+ LinkLaunch(Object *actionObj);
+
+ // Destructor.
+ virtual ~LinkLaunch();
+
+ // Was the LinkLaunch created successfully?
+ virtual GBool isOk() { return fileName != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionLaunch; }
+ GString *getFileName() { return fileName; }
+ GString *getParams() { return params; }
+
+private:
+
+ GString *fileName; // file name
+ GString *params; // parameters
+};
+
+//------------------------------------------------------------------------
+// LinkURI
+//------------------------------------------------------------------------
+
+class LinkURI: public LinkAction {
+public:
+
+ // Build a LinkURI given the URI (string) and base URI.
+ LinkURI(Object *uriObj, GString *baseURI);
+
+ // Destructor.
+ virtual ~LinkURI();
+
+ // Was the LinkURI created successfully?
+ virtual GBool isOk() { return uri != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionURI; }
+ GString *getURI() { return uri; }
+
+private:
+
+ GString *uri; // the URI
+};
+
+//------------------------------------------------------------------------
+// LinkNamed
+//------------------------------------------------------------------------
+
+class LinkNamed: public LinkAction {
+public:
+
+ // Build a LinkNamed given the action name.
+ LinkNamed(Object *nameObj);
+
+ virtual ~LinkNamed();
+
+ // Was the LinkNamed created successfully?
+ virtual GBool isOk() { return name != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionNamed; }
+ GString *getName() { return name; }
+
+private:
+
+ GString *name;
+};
+
+//------------------------------------------------------------------------
+// LinkMovie
+//------------------------------------------------------------------------
+
+class LinkMovie: public LinkAction {
+public:
+
+ LinkMovie(Object *annotObj, Object *titleObj);
+
+ virtual ~LinkMovie();
+
+ // Was the LinkMovie created successfully?
+ virtual GBool isOk() { return annotRef.num >= 0 || title != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionMovie; }
+ GBool hasAnnotRef() { return annotRef.num >= 0; }
+ Ref *getAnnotRef() { return &annotRef; }
+ GString *getTitle() { return title; }
+
+private:
+
+ Ref annotRef;
+ GString *title;
+};
+
+//------------------------------------------------------------------------
+// LinkJavaScript
+//------------------------------------------------------------------------
+
+class LinkJavaScript: public LinkAction {
+public:
+
+ LinkJavaScript(Object *jsObj);
+
+ virtual ~LinkJavaScript();
+
+ // Was the LinkJavaScript created successfully?
+ virtual GBool isOk() { return js != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionJavaScript; }
+ GString *getJS() { return js; }
+
+private:
+
+ GString *js;
+};
+
+//------------------------------------------------------------------------
+// LinkSubmitForm
+//------------------------------------------------------------------------
+
+class LinkSubmitForm: public LinkAction {
+public:
+
+ LinkSubmitForm(Object *urlObj, Object *fieldsObj, Object *flagsObj);
+
+ virtual ~LinkSubmitForm();
+
+ // Was the LinkSubmitForm created successfully?
+ virtual GBool isOk() { return url != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionSubmitForm; }
+ GString *getURL() { return url; }
+ Object *getFields() { return &fields; }
+ int getFlags() { return flags; }
+
+private:
+
+ GString *url;
+ Object fields;
+ int flags;
+};
+
+//------------------------------------------------------------------------
+// LinkHide
+//------------------------------------------------------------------------
+
+class LinkHide: public LinkAction {
+public:
+
+ LinkHide(Object *fieldsObj, Object *hideFlagObj);
+
+ virtual ~LinkHide();
+
+ // Was the LinkHide created successfully?
+ virtual GBool isOk() { return !fields.isNull(); }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionHide; }
+ Object *getFields() { return &fields; }
+ GBool getHideFlag() { return hideFlag; }
+
+private:
+
+ Object fields;
+ GBool hideFlag;
+};
+
+//------------------------------------------------------------------------
+// LinkUnknown
+//------------------------------------------------------------------------
+
+class LinkUnknown: public LinkAction {
+public:
+
+ // Build a LinkUnknown with the specified action type.
+ LinkUnknown(char *actionA);
+
+ // Destructor.
+ virtual ~LinkUnknown();
+
+ // Was the LinkUnknown create successfully?
+ virtual GBool isOk() { return action != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionUnknown; }
+ GString *getAction() { return action; }
+
+private:
+
+ GString *action; // action subtype
+};
+
+//------------------------------------------------------------------------
+// Link
+//------------------------------------------------------------------------
+
+class Link {
+public:
+
+ // Construct a link, given its dictionary.
+ Link(Dict *dict, GString *baseURI);
+
+ // Destructor.
+ ~Link();
+
+ // Was the link created successfully?
+ GBool isOk() { return ok; }
+
+ // Check if point is inside the link rectangle.
+ GBool inRect(double x, double y)
+ { return x1 <= x && x <= x2 && y1 <= y && y <= y2; }
+
+ // Get action.
+ LinkAction *getAction() { return action; }
+
+ // Get the link rectangle.
+ void getRect(double *xa1, double *ya1, double *xa2, double *ya2)
+ { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; }
+
+private:
+
+ double x1, y1; // lower left corner
+ double x2, y2; // upper right corner
+ LinkAction *action; // action
+ GBool ok; // is link valid?
+};
+
+//------------------------------------------------------------------------
+// Links
+//------------------------------------------------------------------------
+
+class Links {
+public:
+
+ // Extract links from array of annotations.
+ Links(Object *annots, GString *baseURI);
+
+ // Destructor.
+ ~Links();
+
+ // Iterate through list of links.
+ int getNumLinks() { return numLinks; }
+ Link *getLink(int i) { return links[i]; }
+
+ // If point <x>,<y> is in a link, return the associated action;
+ // else return NULL.
+ LinkAction *find(double x, double y);
+
+ // Return true if <x>,<y> is in a link.
+ GBool onLink(double x, double y);
+
+private:
+
+ Link **links;
+ int numLinks;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/NameToCharCode.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/NameToCharCode.cc
new file mode 100644
index 00000000000..f846d8e940d
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/NameToCharCode.cc
@@ -0,0 +1,117 @@
+//========================================================================
+//
+// NameToCharCode.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "NameToCharCode.h"
+
+//------------------------------------------------------------------------
+
+struct NameToCharCodeEntry {
+ char *name;
+ CharCode c;
+};
+
+//------------------------------------------------------------------------
+
+NameToCharCode::NameToCharCode() {
+ int i;
+
+ size = 31;
+ len = 0;
+ tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry));
+ for (i = 0; i < size; ++i) {
+ tab[i].name = NULL;
+ }
+}
+
+NameToCharCode::~NameToCharCode() {
+ int i;
+
+ for (i = 0; i < size; ++i) {
+ if (tab[i].name) {
+ gfree(tab[i].name);
+ }
+ }
+ gfree(tab);
+}
+
+void NameToCharCode::add(const char *name, CharCode c) {
+ NameToCharCodeEntry *oldTab;
+ int h, i, oldSize;
+
+ // expand the table if necessary
+ if (len >= size / 2) {
+ oldSize = size;
+ oldTab = tab;
+ size = 2*size + 1;
+ tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry));
+ for (h = 0; h < size; ++h) {
+ tab[h].name = NULL;
+ }
+ for (i = 0; i < oldSize; ++i) {
+ if (oldTab[i].name) {
+ h = hash(oldTab[i].name);
+ while (tab[h].name) {
+ if (++h == size) {
+ h = 0;
+ }
+ }
+ tab[h] = oldTab[i];
+ }
+ }
+ gfree(oldTab);
+ }
+
+ // add the new name
+ h = hash(name);
+ while (tab[h].name && strcmp(tab[h].name, name)) {
+ if (++h == size) {
+ h = 0;
+ }
+ }
+ if (!tab[h].name) {
+ tab[h].name = copyString(name);
+ }
+ tab[h].c = c;
+
+ ++len;
+}
+
+CharCode NameToCharCode::lookup(const char *name) {
+ int h;
+
+ h = hash(name);
+ while (tab[h].name) {
+ if (!strcmp(tab[h].name, name)) {
+ return tab[h].c;
+ }
+ if (++h == size) {
+ h = 0;
+ }
+ }
+ return 0;
+}
+
+int NameToCharCode::hash(const char *name) {
+ const char *p;
+ unsigned int h;
+
+ h = 0;
+ for (p = name; *p; ++p) {
+ h = 17 * h + (int)(*p & 0xff);
+ }
+ return (int)(h % size);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/NameToCharCode.h b/Build/source/libs/xpdf/xpdf-src/xpdf/NameToCharCode.h
new file mode 100644
index 00000000000..6548133cdcd
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/NameToCharCode.h
@@ -0,0 +1,42 @@
+//========================================================================
+//
+// NameToCharCode.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef NAMETOCHARCODE_H
+#define NAMETOCHARCODE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "CharTypes.h"
+
+struct NameToCharCodeEntry;
+
+//------------------------------------------------------------------------
+
+class NameToCharCode {
+public:
+
+ NameToCharCode();
+ ~NameToCharCode();
+
+ void add(const char *name, CharCode c);
+ CharCode lookup(const char *name);
+
+private:
+
+ int hash(const char *name);
+
+ NameToCharCodeEntry *tab;
+ int size;
+ int len;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/NameToUnicodeTable.h b/Build/source/libs/xpdf/xpdf-src/xpdf/NameToUnicodeTable.h
new file mode 100644
index 00000000000..5000bd3e284
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/NameToUnicodeTable.h
@@ -0,0 +1,4257 @@
+//========================================================================
+//
+// NameToUnicodeTable.h
+//
+// Copyright 2001-2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+static struct {
+ Unicode u;
+ const char *name;
+} nameToUnicodeTab[] = {
+ {0x0021, "!"},
+ {0x0023, "#"},
+ {0x0024, "$"},
+ {0x0025, "%"},
+ {0x0026, "&"},
+ {0x0027, "'"},
+ {0x0028, "("},
+ {0x0029, ")"},
+ {0x002a, "*"},
+ {0x002b, "+"},
+ {0x002c, ","},
+ {0x002d, "-"},
+ {0x002e, "."},
+ {0x002f, "/"},
+ {0x0030, "0"},
+ {0x0031, "1"},
+ {0x0032, "2"},
+ {0x0033, "3"},
+ {0x0034, "4"},
+ {0x0035, "5"},
+ {0x0036, "6"},
+ {0x0037, "7"},
+ {0x0038, "8"},
+ {0x0039, "9"},
+ {0x003a, ":"},
+ {0x003b, ";"},
+ {0x003c, "<"},
+ {0x003d, "="},
+ {0x003e, ">"},
+ {0x003f, "?"},
+ {0x0040, "@"},
+ {0x0041, "A"},
+ {0x00c6, "AE"},
+ {0x01fc, "AEacute"},
+ {0x01e2, "AEmacron"},
+ {0xf7e6, "AEsmall"},
+ {0x00c1, "Aacute"},
+ {0xf7e1, "Aacutesmall"},
+ {0x0102, "Abreve"},
+ {0x1eae, "Abreveacute"},
+ {0x04d0, "Abrevecyrillic"},
+ {0x1eb6, "Abrevedotbelow"},
+ {0x1eb0, "Abrevegrave"},
+ {0x1eb2, "Abrevehookabove"},
+ {0x1eb4, "Abrevetilde"},
+ {0x01cd, "Acaron"},
+ {0x24b6, "Acircle"},
+ {0x00c2, "Acircumflex"},
+ {0x1ea4, "Acircumflexacute"},
+ {0x1eac, "Acircumflexdotbelow"},
+ {0x1ea6, "Acircumflexgrave"},
+ {0x1ea8, "Acircumflexhookabove"},
+ {0xf7e2, "Acircumflexsmall"},
+ {0x1eaa, "Acircumflextilde"},
+ {0xf6c9, "Acute"},
+ {0xf7b4, "Acutesmall"},
+ {0x0410, "Acyrillic"},
+ {0x0200, "Adblgrave"},
+ {0x00c4, "Adieresis"},
+ {0x04d2, "Adieresiscyrillic"},
+ {0x01de, "Adieresismacron"},
+ {0xf7e4, "Adieresissmall"},
+ {0x1ea0, "Adotbelow"},
+ {0x01e0, "Adotmacron"},
+ {0x00c0, "Agrave"},
+ {0xf7e0, "Agravesmall"},
+ {0x1ea2, "Ahookabove"},
+ {0x04d4, "Aiecyrillic"},
+ {0x0202, "Ainvertedbreve"},
+ {0x0391, "Alpha"},
+ {0x0386, "Alphatonos"},
+ {0x0100, "Amacron"},
+ {0xff21, "Amonospace"},
+ {0x0104, "Aogonek"},
+ {0x00c5, "Aring"},
+ {0x01fa, "Aringacute"},
+ {0x1e00, "Aringbelow"},
+ {0xf7e5, "Aringsmall"},
+ {0xf761, "Asmall"},
+ {0x00c3, "Atilde"},
+ {0xf7e3, "Atildesmall"},
+ {0x0531, "Aybarmenian"},
+ {0x0042, "B"},
+ {0x24b7, "Bcircle"},
+ {0x1e02, "Bdotaccent"},
+ {0x1e04, "Bdotbelow"},
+ {0x0411, "Becyrillic"},
+ {0x0532, "Benarmenian"},
+ {0x0392, "Beta"},
+ {0x0181, "Bhook"},
+ {0x1e06, "Blinebelow"},
+ {0xff22, "Bmonospace"},
+ {0xf6f4, "Brevesmall"},
+ {0xf762, "Bsmall"},
+ {0x0182, "Btopbar"},
+ {0x0043, "C"},
+ {0x053e, "Caarmenian"},
+ {0x0106, "Cacute"},
+ {0xf6ca, "Caron"},
+ {0xf6f5, "Caronsmall"},
+ {0x010c, "Ccaron"},
+ {0x00c7, "Ccedilla"},
+ {0x1e08, "Ccedillaacute"},
+ {0xf7e7, "Ccedillasmall"},
+ {0x24b8, "Ccircle"},
+ {0x0108, "Ccircumflex"},
+ {0x010a, "Cdot"},
+ {0x010a, "Cdotaccent"},
+ {0xf7b8, "Cedillasmall"},
+ {0x0549, "Chaarmenian"},
+ {0x04bc, "Cheabkhasiancyrillic"},
+ {0x0427, "Checyrillic"},
+ {0x04be, "Chedescenderabkhasiancyrillic"},
+ {0x04b6, "Chedescendercyrillic"},
+ {0x04f4, "Chedieresiscyrillic"},
+ {0x0543, "Cheharmenian"},
+ {0x04cb, "Chekhakassiancyrillic"},
+ {0x04b8, "Cheverticalstrokecyrillic"},
+ {0x03a7, "Chi"},
+ {0x0187, "Chook"},
+ {0xf6f6, "Circumflexsmall"},
+ {0xff23, "Cmonospace"},
+ {0x0551, "Coarmenian"},
+ {0xf763, "Csmall"},
+ {0x0044, "D"},
+ {0x01f1, "DZ"},
+ {0x01c4, "DZcaron"},
+ {0x0534, "Daarmenian"},
+ {0x0189, "Dafrican"},
+ {0x010e, "Dcaron"},
+ {0x1e10, "Dcedilla"},
+ {0x24b9, "Dcircle"},
+ {0x1e12, "Dcircumflexbelow"},
+ {0x0110, "Dcroat"},
+ {0x1e0a, "Ddotaccent"},
+ {0x1e0c, "Ddotbelow"},
+ {0x0414, "Decyrillic"},
+ {0x03ee, "Deicoptic"},
+ {0x2206, "Delta"},
+ {0x0394, "Deltagreek"},
+ {0x018a, "Dhook"},
+ {0xf6cb, "Dieresis"},
+ {0xf6cc, "DieresisAcute"},
+ {0xf6cd, "DieresisGrave"},
+ {0xf7a8, "Dieresissmall"},
+ {0x03dc, "Digammagreek"},
+ {0x0402, "Djecyrillic"},
+ {0x1e0e, "Dlinebelow"},
+ {0xff24, "Dmonospace"},
+ {0xf6f7, "Dotaccentsmall"},
+ {0x0110, "Dslash"},
+ {0xf764, "Dsmall"},
+ {0x018b, "Dtopbar"},
+ {0x01f2, "Dz"},
+ {0x01c5, "Dzcaron"},
+ {0x04e0, "Dzeabkhasiancyrillic"},
+ {0x0405, "Dzecyrillic"},
+ {0x040f, "Dzhecyrillic"},
+ {0x0045, "E"},
+ {0x00c9, "Eacute"},
+ {0xf7e9, "Eacutesmall"},
+ {0x0114, "Ebreve"},
+ {0x011a, "Ecaron"},
+ {0x1e1c, "Ecedillabreve"},
+ {0x0535, "Echarmenian"},
+ {0x24ba, "Ecircle"},
+ {0x00ca, "Ecircumflex"},
+ {0x1ebe, "Ecircumflexacute"},
+ {0x1e18, "Ecircumflexbelow"},
+ {0x1ec6, "Ecircumflexdotbelow"},
+ {0x1ec0, "Ecircumflexgrave"},
+ {0x1ec2, "Ecircumflexhookabove"},
+ {0xf7ea, "Ecircumflexsmall"},
+ {0x1ec4, "Ecircumflextilde"},
+ {0x0404, "Ecyrillic"},
+ {0x0204, "Edblgrave"},
+ {0x00cb, "Edieresis"},
+ {0xf7eb, "Edieresissmall"},
+ {0x0116, "Edot"},
+ {0x0116, "Edotaccent"},
+ {0x1eb8, "Edotbelow"},
+ {0x0424, "Efcyrillic"},
+ {0x00c8, "Egrave"},
+ {0xf7e8, "Egravesmall"},
+ {0x0537, "Eharmenian"},
+ {0x1eba, "Ehookabove"},
+ {0x2167, "Eightroman"},
+ {0x0206, "Einvertedbreve"},
+ {0x0464, "Eiotifiedcyrillic"},
+ {0x041b, "Elcyrillic"},
+ {0x216a, "Elevenroman"},
+ {0x0112, "Emacron"},
+ {0x1e16, "Emacronacute"},
+ {0x1e14, "Emacrongrave"},
+ {0x041c, "Emcyrillic"},
+ {0xff25, "Emonospace"},
+ {0x041d, "Encyrillic"},
+ {0x04a2, "Endescendercyrillic"},
+ {0x014a, "Eng"},
+ {0x04a4, "Enghecyrillic"},
+ {0x04c7, "Enhookcyrillic"},
+ {0x0118, "Eogonek"},
+ {0x0190, "Eopen"},
+ {0x0395, "Epsilon"},
+ {0x0388, "Epsilontonos"},
+ {0x0420, "Ercyrillic"},
+ {0x018e, "Ereversed"},
+ {0x042d, "Ereversedcyrillic"},
+ {0x0421, "Escyrillic"},
+ {0x04aa, "Esdescendercyrillic"},
+ {0x01a9, "Esh"},
+ {0xf765, "Esmall"},
+ {0x0397, "Eta"},
+ {0x0538, "Etarmenian"},
+ {0x0389, "Etatonos"},
+ {0x00d0, "Eth"},
+ {0xf7f0, "Ethsmall"},
+ {0x1ebc, "Etilde"},
+ {0x1e1a, "Etildebelow"},
+ {0x20ac, "Euro"},
+ {0x01b7, "Ezh"},
+ {0x01ee, "Ezhcaron"},
+ {0x01b8, "Ezhreversed"},
+ {0x0046, "F"},
+ {0x24bb, "Fcircle"},
+ {0x1e1e, "Fdotaccent"},
+ {0x0556, "Feharmenian"},
+ {0x03e4, "Feicoptic"},
+ {0x0191, "Fhook"},
+ {0x0472, "Fitacyrillic"},
+ {0x2164, "Fiveroman"},
+ {0xff26, "Fmonospace"},
+ {0x2163, "Fourroman"},
+ {0xf766, "Fsmall"},
+ {0x0047, "G"},
+ {0x3387, "GBsquare"},
+ {0x01f4, "Gacute"},
+ {0x0393, "Gamma"},
+ {0x0194, "Gammaafrican"},
+ {0x03ea, "Gangiacoptic"},
+ {0x011e, "Gbreve"},
+ {0x01e6, "Gcaron"},
+ {0x0122, "Gcedilla"},
+ {0x24bc, "Gcircle"},
+ {0x011c, "Gcircumflex"},
+ {0x0122, "Gcommaaccent"},
+ {0x0120, "Gdot"},
+ {0x0120, "Gdotaccent"},
+ {0x0413, "Gecyrillic"},
+ {0x0542, "Ghadarmenian"},
+ {0x0494, "Ghemiddlehookcyrillic"},
+ {0x0492, "Ghestrokecyrillic"},
+ {0x0490, "Gheupturncyrillic"},
+ {0x0193, "Ghook"},
+ {0x0533, "Gimarmenian"},
+ {0x0403, "Gjecyrillic"},
+ {0x1e20, "Gmacron"},
+ {0xff27, "Gmonospace"},
+ {0xf6ce, "Grave"},
+ {0xf760, "Gravesmall"},
+ {0xf767, "Gsmall"},
+ {0x029b, "Gsmallhook"},
+ {0x01e4, "Gstroke"},
+ {0x0048, "H"},
+ {0x25cf, "H18533"},
+ {0x25aa, "H18543"},
+ {0x25ab, "H18551"},
+ {0x25a1, "H22073"},
+ {0x33cb, "HPsquare"},
+ {0x04a8, "Haabkhasiancyrillic"},
+ {0x04b2, "Hadescendercyrillic"},
+ {0x042a, "Hardsigncyrillic"},
+ {0x0126, "Hbar"},
+ {0x1e2a, "Hbrevebelow"},
+ {0x1e28, "Hcedilla"},
+ {0x24bd, "Hcircle"},
+ {0x0124, "Hcircumflex"},
+ {0x1e26, "Hdieresis"},
+ {0x1e22, "Hdotaccent"},
+ {0x1e24, "Hdotbelow"},
+ {0xff28, "Hmonospace"},
+ {0x0540, "Hoarmenian"},
+ {0x03e8, "Horicoptic"},
+ {0xf768, "Hsmall"},
+ {0xf6cf, "Hungarumlaut"},
+ {0xf6f8, "Hungarumlautsmall"},
+ {0x3390, "Hzsquare"},
+ {0x0049, "I"},
+ {0x042f, "IAcyrillic"},
+ {0x0132, "IJ"},
+ {0x042e, "IUcyrillic"},
+ {0x00cd, "Iacute"},
+ {0xf7ed, "Iacutesmall"},
+ {0x012c, "Ibreve"},
+ {0x01cf, "Icaron"},
+ {0x24be, "Icircle"},
+ {0x00ce, "Icircumflex"},
+ {0xf7ee, "Icircumflexsmall"},
+ {0x0406, "Icyrillic"},
+ {0x0208, "Idblgrave"},
+ {0x00cf, "Idieresis"},
+ {0x1e2e, "Idieresisacute"},
+ {0x04e4, "Idieresiscyrillic"},
+ {0xf7ef, "Idieresissmall"},
+ {0x0130, "Idot"},
+ {0x0130, "Idotaccent"},
+ {0x1eca, "Idotbelow"},
+ {0x04d6, "Iebrevecyrillic"},
+ {0x0415, "Iecyrillic"},
+ {0x2111, "Ifraktur"},
+ {0x00cc, "Igrave"},
+ {0xf7ec, "Igravesmall"},
+ {0x1ec8, "Ihookabove"},
+ {0x0418, "Iicyrillic"},
+ {0x020a, "Iinvertedbreve"},
+ {0x0419, "Iishortcyrillic"},
+ {0x012a, "Imacron"},
+ {0x04e2, "Imacroncyrillic"},
+ {0xff29, "Imonospace"},
+ {0x053b, "Iniarmenian"},
+ {0x0401, "Iocyrillic"},
+ {0x012e, "Iogonek"},
+ {0x0399, "Iota"},
+ {0x0196, "Iotaafrican"},
+ {0x03aa, "Iotadieresis"},
+ {0x038a, "Iotatonos"},
+ {0xf769, "Ismall"},
+ {0x0197, "Istroke"},
+ {0x0128, "Itilde"},
+ {0x1e2c, "Itildebelow"},
+ {0x0474, "Izhitsacyrillic"},
+ {0x0476, "Izhitsadblgravecyrillic"},
+ {0x004a, "J"},
+ {0x0541, "Jaarmenian"},
+ {0x24bf, "Jcircle"},
+ {0x0134, "Jcircumflex"},
+ {0x0408, "Jecyrillic"},
+ {0x054b, "Jheharmenian"},
+ {0xff2a, "Jmonospace"},
+ {0xf76a, "Jsmall"},
+ {0x004b, "K"},
+ {0x3385, "KBsquare"},
+ {0x33cd, "KKsquare"},
+ {0x04a0, "Kabashkircyrillic"},
+ {0x1e30, "Kacute"},
+ {0x041a, "Kacyrillic"},
+ {0x049a, "Kadescendercyrillic"},
+ {0x04c3, "Kahookcyrillic"},
+ {0x039a, "Kappa"},
+ {0x049e, "Kastrokecyrillic"},
+ {0x049c, "Kaverticalstrokecyrillic"},
+ {0x01e8, "Kcaron"},
+ {0x0136, "Kcedilla"},
+ {0x24c0, "Kcircle"},
+ {0x0136, "Kcommaaccent"},
+ {0x1e32, "Kdotbelow"},
+ {0x0554, "Keharmenian"},
+ {0x053f, "Kenarmenian"},
+ {0x0425, "Khacyrillic"},
+ {0x03e6, "Kheicoptic"},
+ {0x0198, "Khook"},
+ {0x040c, "Kjecyrillic"},
+ {0x1e34, "Klinebelow"},
+ {0xff2b, "Kmonospace"},
+ {0x0480, "Koppacyrillic"},
+ {0x03de, "Koppagreek"},
+ {0x046e, "Ksicyrillic"},
+ {0xf76b, "Ksmall"},
+ {0x004c, "L"},
+ {0x01c7, "LJ"},
+ {0xf6bf, "LL"},
+ {0x0139, "Lacute"},
+ {0x039b, "Lambda"},
+ {0x013d, "Lcaron"},
+ {0x013b, "Lcedilla"},
+ {0x24c1, "Lcircle"},
+ {0x1e3c, "Lcircumflexbelow"},
+ {0x013b, "Lcommaaccent"},
+ {0x013f, "Ldot"},
+ {0x013f, "Ldotaccent"},
+ {0x1e36, "Ldotbelow"},
+ {0x1e38, "Ldotbelowmacron"},
+ {0x053c, "Liwnarmenian"},
+ {0x01c8, "Lj"},
+ {0x0409, "Ljecyrillic"},
+ {0x1e3a, "Llinebelow"},
+ {0xff2c, "Lmonospace"},
+ {0x0141, "Lslash"},
+ {0xf6f9, "Lslashsmall"},
+ {0xf76c, "Lsmall"},
+ {0x004d, "M"},
+ {0x3386, "MBsquare"},
+ {0xf6d0, "Macron"},
+ {0xf7af, "Macronsmall"},
+ {0x1e3e, "Macute"},
+ {0x24c2, "Mcircle"},
+ {0x1e40, "Mdotaccent"},
+ {0x1e42, "Mdotbelow"},
+ {0x0544, "Menarmenian"},
+ {0xff2d, "Mmonospace"},
+ {0xf76d, "Msmall"},
+ {0x019c, "Mturned"},
+ {0x039c, "Mu"},
+ {0x004e, "N"},
+ {0x01ca, "NJ"},
+ {0x0143, "Nacute"},
+ {0x0147, "Ncaron"},
+ {0x0145, "Ncedilla"},
+ {0x24c3, "Ncircle"},
+ {0x1e4a, "Ncircumflexbelow"},
+ {0x0145, "Ncommaaccent"},
+ {0x1e44, "Ndotaccent"},
+ {0x1e46, "Ndotbelow"},
+ {0x019d, "Nhookleft"},
+ {0x2168, "Nineroman"},
+ {0x01cb, "Nj"},
+ {0x040a, "Njecyrillic"},
+ {0x1e48, "Nlinebelow"},
+ {0xff2e, "Nmonospace"},
+ {0x0546, "Nowarmenian"},
+ {0xf76e, "Nsmall"},
+ {0x00d1, "Ntilde"},
+ {0xf7f1, "Ntildesmall"},
+ {0x039d, "Nu"},
+ {0x004f, "O"},
+ {0x0152, "OE"},
+ {0xf6fa, "OEsmall"},
+ {0x00d3, "Oacute"},
+ {0xf7f3, "Oacutesmall"},
+ {0x04e8, "Obarredcyrillic"},
+ {0x04ea, "Obarreddieresiscyrillic"},
+ {0x014e, "Obreve"},
+ {0x01d1, "Ocaron"},
+ {0x019f, "Ocenteredtilde"},
+ {0x24c4, "Ocircle"},
+ {0x00d4, "Ocircumflex"},
+ {0x1ed0, "Ocircumflexacute"},
+ {0x1ed8, "Ocircumflexdotbelow"},
+ {0x1ed2, "Ocircumflexgrave"},
+ {0x1ed4, "Ocircumflexhookabove"},
+ {0xf7f4, "Ocircumflexsmall"},
+ {0x1ed6, "Ocircumflextilde"},
+ {0x041e, "Ocyrillic"},
+ {0x0150, "Odblacute"},
+ {0x020c, "Odblgrave"},
+ {0x00d6, "Odieresis"},
+ {0x04e6, "Odieresiscyrillic"},
+ {0xf7f6, "Odieresissmall"},
+ {0x1ecc, "Odotbelow"},
+ {0xf6fb, "Ogoneksmall"},
+ {0x00d2, "Ograve"},
+ {0xf7f2, "Ogravesmall"},
+ {0x0555, "Oharmenian"},
+ {0x2126, "Ohm"},
+ {0x1ece, "Ohookabove"},
+ {0x01a0, "Ohorn"},
+ {0x1eda, "Ohornacute"},
+ {0x1ee2, "Ohorndotbelow"},
+ {0x1edc, "Ohorngrave"},
+ {0x1ede, "Ohornhookabove"},
+ {0x1ee0, "Ohorntilde"},
+ {0x0150, "Ohungarumlaut"},
+ {0x01a2, "Oi"},
+ {0x020e, "Oinvertedbreve"},
+ {0x014c, "Omacron"},
+ {0x1e52, "Omacronacute"},
+ {0x1e50, "Omacrongrave"},
+ {0x2126, "Omega"},
+ {0x0460, "Omegacyrillic"},
+ {0x03a9, "Omegagreek"},
+ {0x047a, "Omegaroundcyrillic"},
+ {0x047c, "Omegatitlocyrillic"},
+ {0x038f, "Omegatonos"},
+ {0x039f, "Omicron"},
+ {0x038c, "Omicrontonos"},
+ {0xff2f, "Omonospace"},
+ {0x2160, "Oneroman"},
+ {0x01ea, "Oogonek"},
+ {0x01ec, "Oogonekmacron"},
+ {0x0186, "Oopen"},
+ {0x00d8, "Oslash"},
+ {0x01fe, "Oslashacute"},
+ {0xf7f8, "Oslashsmall"},
+ {0xf76f, "Osmall"},
+ {0x01fe, "Ostrokeacute"},
+ {0x047e, "Otcyrillic"},
+ {0x00d5, "Otilde"},
+ {0x1e4c, "Otildeacute"},
+ {0x1e4e, "Otildedieresis"},
+ {0xf7f5, "Otildesmall"},
+ {0x0050, "P"},
+ {0x1e54, "Pacute"},
+ {0x24c5, "Pcircle"},
+ {0x1e56, "Pdotaccent"},
+ {0x041f, "Pecyrillic"},
+ {0x054a, "Peharmenian"},
+ {0x04a6, "Pemiddlehookcyrillic"},
+ {0x03a6, "Phi"},
+ {0x01a4, "Phook"},
+ {0x03a0, "Pi"},
+ {0x0553, "Piwrarmenian"},
+ {0xff30, "Pmonospace"},
+ {0x03a8, "Psi"},
+ {0x0470, "Psicyrillic"},
+ {0xf770, "Psmall"},
+ {0x0051, "Q"},
+ {0x24c6, "Qcircle"},
+ {0xff31, "Qmonospace"},
+ {0xf771, "Qsmall"},
+ {0x0052, "R"},
+ {0x054c, "Raarmenian"},
+ {0x0154, "Racute"},
+ {0x0158, "Rcaron"},
+ {0x0156, "Rcedilla"},
+ {0x24c7, "Rcircle"},
+ {0x0156, "Rcommaaccent"},
+ {0x0210, "Rdblgrave"},
+ {0x1e58, "Rdotaccent"},
+ {0x1e5a, "Rdotbelow"},
+ {0x1e5c, "Rdotbelowmacron"},
+ {0x0550, "Reharmenian"},
+ {0x211c, "Rfraktur"},
+ {0x03a1, "Rho"},
+ {0xf6fc, "Ringsmall"},
+ {0x0212, "Rinvertedbreve"},
+ {0x1e5e, "Rlinebelow"},
+ {0xff32, "Rmonospace"},
+ {0xf772, "Rsmall"},
+ {0x0281, "Rsmallinverted"},
+ {0x02b6, "Rsmallinvertedsuperior"},
+ {0x0053, "S"},
+ {0x250c, "SF010000"},
+ {0x2514, "SF020000"},
+ {0x2510, "SF030000"},
+ {0x2518, "SF040000"},
+ {0x253c, "SF050000"},
+ {0x252c, "SF060000"},
+ {0x2534, "SF070000"},
+ {0x251c, "SF080000"},
+ {0x2524, "SF090000"},
+ {0x2500, "SF100000"},
+ {0x2502, "SF110000"},
+ {0x2561, "SF190000"},
+ {0x2562, "SF200000"},
+ {0x2556, "SF210000"},
+ {0x2555, "SF220000"},
+ {0x2563, "SF230000"},
+ {0x2551, "SF240000"},
+ {0x2557, "SF250000"},
+ {0x255d, "SF260000"},
+ {0x255c, "SF270000"},
+ {0x255b, "SF280000"},
+ {0x255e, "SF360000"},
+ {0x255f, "SF370000"},
+ {0x255a, "SF380000"},
+ {0x2554, "SF390000"},
+ {0x2569, "SF400000"},
+ {0x2566, "SF410000"},
+ {0x2560, "SF420000"},
+ {0x2550, "SF430000"},
+ {0x256c, "SF440000"},
+ {0x2567, "SF450000"},
+ {0x2568, "SF460000"},
+ {0x2564, "SF470000"},
+ {0x2565, "SF480000"},
+ {0x2559, "SF490000"},
+ {0x2558, "SF500000"},
+ {0x2552, "SF510000"},
+ {0x2553, "SF520000"},
+ {0x256b, "SF530000"},
+ {0x256a, "SF540000"},
+ {0x015a, "Sacute"},
+ {0x1e64, "Sacutedotaccent"},
+ {0x03e0, "Sampigreek"},
+ {0x0160, "Scaron"},
+ {0x1e66, "Scarondotaccent"},
+ {0xf6fd, "Scaronsmall"},
+ {0x015e, "Scedilla"},
+ {0x018f, "Schwa"},
+ {0x04d8, "Schwacyrillic"},
+ {0x04da, "Schwadieresiscyrillic"},
+ {0x24c8, "Scircle"},
+ {0x015c, "Scircumflex"},
+ {0x0218, "Scommaaccent"},
+ {0x1e60, "Sdotaccent"},
+ {0x1e62, "Sdotbelow"},
+ {0x1e68, "Sdotbelowdotaccent"},
+ {0x054d, "Seharmenian"},
+ {0x2166, "Sevenroman"},
+ {0x0547, "Shaarmenian"},
+ {0x0428, "Shacyrillic"},
+ {0x0429, "Shchacyrillic"},
+ {0x03e2, "Sheicoptic"},
+ {0x04ba, "Shhacyrillic"},
+ {0x03ec, "Shimacoptic"},
+ {0x03a3, "Sigma"},
+ {0x2165, "Sixroman"},
+ {0xff33, "Smonospace"},
+ {0x042c, "Softsigncyrillic"},
+ {0xf773, "Ssmall"},
+ {0x03da, "Stigmagreek"},
+ {0x0054, "T"},
+ {0x03a4, "Tau"},
+ {0x0166, "Tbar"},
+ {0x0164, "Tcaron"},
+ {0x0162, "Tcedilla"},
+ {0x24c9, "Tcircle"},
+ {0x1e70, "Tcircumflexbelow"},
+ {0x0162, "Tcommaaccent"},
+ {0x1e6a, "Tdotaccent"},
+ {0x1e6c, "Tdotbelow"},
+ {0x0422, "Tecyrillic"},
+ {0x04ac, "Tedescendercyrillic"},
+ {0x2169, "Tenroman"},
+ {0x04b4, "Tetsecyrillic"},
+ {0x0398, "Theta"},
+ {0x01ac, "Thook"},
+ {0x00de, "Thorn"},
+ {0xf7fe, "Thornsmall"},
+ {0x2162, "Threeroman"},
+ {0xf6fe, "Tildesmall"},
+ {0x054f, "Tiwnarmenian"},
+ {0x1e6e, "Tlinebelow"},
+ {0xff34, "Tmonospace"},
+ {0x0539, "Toarmenian"},
+ {0x01bc, "Tonefive"},
+ {0x0184, "Tonesix"},
+ {0x01a7, "Tonetwo"},
+ {0x01ae, "Tretroflexhook"},
+ {0x0426, "Tsecyrillic"},
+ {0x040b, "Tshecyrillic"},
+ {0xf774, "Tsmall"},
+ {0x216b, "Twelveroman"},
+ {0x2161, "Tworoman"},
+ {0x0055, "U"},
+ {0x00da, "Uacute"},
+ {0xf7fa, "Uacutesmall"},
+ {0x016c, "Ubreve"},
+ {0x01d3, "Ucaron"},
+ {0x24ca, "Ucircle"},
+ {0x00db, "Ucircumflex"},
+ {0x1e76, "Ucircumflexbelow"},
+ {0xf7fb, "Ucircumflexsmall"},
+ {0x0423, "Ucyrillic"},
+ {0x0170, "Udblacute"},
+ {0x0214, "Udblgrave"},
+ {0x00dc, "Udieresis"},
+ {0x01d7, "Udieresisacute"},
+ {0x1e72, "Udieresisbelow"},
+ {0x01d9, "Udieresiscaron"},
+ {0x04f0, "Udieresiscyrillic"},
+ {0x01db, "Udieresisgrave"},
+ {0x01d5, "Udieresismacron"},
+ {0xf7fc, "Udieresissmall"},
+ {0x1ee4, "Udotbelow"},
+ {0x00d9, "Ugrave"},
+ {0xf7f9, "Ugravesmall"},
+ {0x1ee6, "Uhookabove"},
+ {0x01af, "Uhorn"},
+ {0x1ee8, "Uhornacute"},
+ {0x1ef0, "Uhorndotbelow"},
+ {0x1eea, "Uhorngrave"},
+ {0x1eec, "Uhornhookabove"},
+ {0x1eee, "Uhorntilde"},
+ {0x0170, "Uhungarumlaut"},
+ {0x04f2, "Uhungarumlautcyrillic"},
+ {0x0216, "Uinvertedbreve"},
+ {0x0478, "Ukcyrillic"},
+ {0x016a, "Umacron"},
+ {0x04ee, "Umacroncyrillic"},
+ {0x1e7a, "Umacrondieresis"},
+ {0xff35, "Umonospace"},
+ {0x0172, "Uogonek"},
+ {0x03a5, "Upsilon"},
+ {0x03d2, "Upsilon1"},
+ {0x03d3, "Upsilonacutehooksymbolgreek"},
+ {0x01b1, "Upsilonafrican"},
+ {0x03ab, "Upsilondieresis"},
+ {0x03d4, "Upsilondieresishooksymbolgreek"},
+ {0x03d2, "Upsilonhooksymbol"},
+ {0x038e, "Upsilontonos"},
+ {0x016e, "Uring"},
+ {0x040e, "Ushortcyrillic"},
+ {0xf775, "Usmall"},
+ {0x04ae, "Ustraightcyrillic"},
+ {0x04b0, "Ustraightstrokecyrillic"},
+ {0x0168, "Utilde"},
+ {0x1e78, "Utildeacute"},
+ {0x1e74, "Utildebelow"},
+ {0x0056, "V"},
+ {0x24cb, "Vcircle"},
+ {0x1e7e, "Vdotbelow"},
+ {0x0412, "Vecyrillic"},
+ {0x054e, "Vewarmenian"},
+ {0x01b2, "Vhook"},
+ {0xff36, "Vmonospace"},
+ {0x0548, "Voarmenian"},
+ {0xf776, "Vsmall"},
+ {0x1e7c, "Vtilde"},
+ {0x0057, "W"},
+ {0x1e82, "Wacute"},
+ {0x24cc, "Wcircle"},
+ {0x0174, "Wcircumflex"},
+ {0x1e84, "Wdieresis"},
+ {0x1e86, "Wdotaccent"},
+ {0x1e88, "Wdotbelow"},
+ {0x1e80, "Wgrave"},
+ {0xff37, "Wmonospace"},
+ {0xf777, "Wsmall"},
+ {0x0058, "X"},
+ {0x24cd, "Xcircle"},
+ {0x1e8c, "Xdieresis"},
+ {0x1e8a, "Xdotaccent"},
+ {0x053d, "Xeharmenian"},
+ {0x039e, "Xi"},
+ {0xff38, "Xmonospace"},
+ {0xf778, "Xsmall"},
+ {0x0059, "Y"},
+ {0x00dd, "Yacute"},
+ {0xf7fd, "Yacutesmall"},
+ {0x0462, "Yatcyrillic"},
+ {0x24ce, "Ycircle"},
+ {0x0176, "Ycircumflex"},
+ {0x0178, "Ydieresis"},
+ {0xf7ff, "Ydieresissmall"},
+ {0x1e8e, "Ydotaccent"},
+ {0x1ef4, "Ydotbelow"},
+ {0x042b, "Yericyrillic"},
+ {0x04f8, "Yerudieresiscyrillic"},
+ {0x1ef2, "Ygrave"},
+ {0x01b3, "Yhook"},
+ {0x1ef6, "Yhookabove"},
+ {0x0545, "Yiarmenian"},
+ {0x0407, "Yicyrillic"},
+ {0x0552, "Yiwnarmenian"},
+ {0xff39, "Ymonospace"},
+ {0xf779, "Ysmall"},
+ {0x1ef8, "Ytilde"},
+ {0x046a, "Yusbigcyrillic"},
+ {0x046c, "Yusbigiotifiedcyrillic"},
+ {0x0466, "Yuslittlecyrillic"},
+ {0x0468, "Yuslittleiotifiedcyrillic"},
+ {0x005a, "Z"},
+ {0x0536, "Zaarmenian"},
+ {0x0179, "Zacute"},
+ {0x017d, "Zcaron"},
+ {0xf6ff, "Zcaronsmall"},
+ {0x24cf, "Zcircle"},
+ {0x1e90, "Zcircumflex"},
+ {0x017b, "Zdot"},
+ {0x017b, "Zdotaccent"},
+ {0x1e92, "Zdotbelow"},
+ {0x0417, "Zecyrillic"},
+ {0x0498, "Zedescendercyrillic"},
+ {0x04de, "Zedieresiscyrillic"},
+ {0x0396, "Zeta"},
+ {0x053a, "Zhearmenian"},
+ {0x04c1, "Zhebrevecyrillic"},
+ {0x0416, "Zhecyrillic"},
+ {0x0496, "Zhedescendercyrillic"},
+ {0x04dc, "Zhedieresiscyrillic"},
+ {0x1e94, "Zlinebelow"},
+ {0xff3a, "Zmonospace"},
+ {0xf77a, "Zsmall"},
+ {0x01b5, "Zstroke"},
+ {0x0022, "\""},
+ {0x005c, "\\"},
+ {0x005d, "]"},
+ {0x005e, "^"},
+ {0x005f, "_"},
+ {0x0060, "`"},
+ {0x0061, "a"},
+ {0x0986, "aabengali"},
+ {0x00e1, "aacute"},
+ {0x0906, "aadeva"},
+ {0x0a86, "aagujarati"},
+ {0x0a06, "aagurmukhi"},
+ {0x0a3e, "aamatragurmukhi"},
+ {0x3303, "aarusquare"},
+ {0x09be, "aavowelsignbengali"},
+ {0x093e, "aavowelsigndeva"},
+ {0x0abe, "aavowelsigngujarati"},
+ {0x055f, "abbreviationmarkarmenian"},
+ {0x0970, "abbreviationsigndeva"},
+ {0x0985, "abengali"},
+ {0x311a, "abopomofo"},
+ {0x0103, "abreve"},
+ {0x1eaf, "abreveacute"},
+ {0x04d1, "abrevecyrillic"},
+ {0x1eb7, "abrevedotbelow"},
+ {0x1eb1, "abrevegrave"},
+ {0x1eb3, "abrevehookabove"},
+ {0x1eb5, "abrevetilde"},
+ {0x01ce, "acaron"},
+ {0x24d0, "acircle"},
+ {0x00e2, "acircumflex"},
+ {0x1ea5, "acircumflexacute"},
+ {0x1ead, "acircumflexdotbelow"},
+ {0x1ea7, "acircumflexgrave"},
+ {0x1ea9, "acircumflexhookabove"},
+ {0x1eab, "acircumflextilde"},
+ {0x00b4, "acute"},
+ {0x0317, "acutebelowcmb"},
+ {0x0301, "acutecmb"},
+ {0x0301, "acutecomb"},
+ {0x0954, "acutedeva"},
+ {0x02cf, "acutelowmod"},
+ {0x0341, "acutetonecmb"},
+ {0x0430, "acyrillic"},
+ {0x0201, "adblgrave"},
+ {0x0a71, "addakgurmukhi"},
+ {0x0905, "adeva"},
+ {0x00e4, "adieresis"},
+ {0x04d3, "adieresiscyrillic"},
+ {0x01df, "adieresismacron"},
+ {0x1ea1, "adotbelow"},
+ {0x01e1, "adotmacron"},
+ {0x00e6, "ae"},
+ {0x01fd, "aeacute"},
+ {0x3150, "aekorean"},
+ {0x01e3, "aemacron"},
+ {0x2015, "afii00208"},
+ {0x20a4, "afii08941"},
+ {0x0410, "afii10017"},
+ {0x0411, "afii10018"},
+ {0x0412, "afii10019"},
+ {0x0413, "afii10020"},
+ {0x0414, "afii10021"},
+ {0x0415, "afii10022"},
+ {0x0401, "afii10023"},
+ {0x0416, "afii10024"},
+ {0x0417, "afii10025"},
+ {0x0418, "afii10026"},
+ {0x0419, "afii10027"},
+ {0x041a, "afii10028"},
+ {0x041b, "afii10029"},
+ {0x041c, "afii10030"},
+ {0x041d, "afii10031"},
+ {0x041e, "afii10032"},
+ {0x041f, "afii10033"},
+ {0x0420, "afii10034"},
+ {0x0421, "afii10035"},
+ {0x0422, "afii10036"},
+ {0x0423, "afii10037"},
+ {0x0424, "afii10038"},
+ {0x0425, "afii10039"},
+ {0x0426, "afii10040"},
+ {0x0427, "afii10041"},
+ {0x0428, "afii10042"},
+ {0x0429, "afii10043"},
+ {0x042a, "afii10044"},
+ {0x042b, "afii10045"},
+ {0x042c, "afii10046"},
+ {0x042d, "afii10047"},
+ {0x042e, "afii10048"},
+ {0x042f, "afii10049"},
+ {0x0490, "afii10050"},
+ {0x0402, "afii10051"},
+ {0x0403, "afii10052"},
+ {0x0404, "afii10053"},
+ {0x0405, "afii10054"},
+ {0x0406, "afii10055"},
+ {0x0407, "afii10056"},
+ {0x0408, "afii10057"},
+ {0x0409, "afii10058"},
+ {0x040a, "afii10059"},
+ {0x040b, "afii10060"},
+ {0x040c, "afii10061"},
+ {0x040e, "afii10062"},
+ {0xf6c4, "afii10063"},
+ {0xf6c5, "afii10064"},
+ {0x0430, "afii10065"},
+ {0x0431, "afii10066"},
+ {0x0432, "afii10067"},
+ {0x0433, "afii10068"},
+ {0x0434, "afii10069"},
+ {0x0435, "afii10070"},
+ {0x0451, "afii10071"},
+ {0x0436, "afii10072"},
+ {0x0437, "afii10073"},
+ {0x0438, "afii10074"},
+ {0x0439, "afii10075"},
+ {0x043a, "afii10076"},
+ {0x043b, "afii10077"},
+ {0x043c, "afii10078"},
+ {0x043d, "afii10079"},
+ {0x043e, "afii10080"},
+ {0x043f, "afii10081"},
+ {0x0440, "afii10082"},
+ {0x0441, "afii10083"},
+ {0x0442, "afii10084"},
+ {0x0443, "afii10085"},
+ {0x0444, "afii10086"},
+ {0x0445, "afii10087"},
+ {0x0446, "afii10088"},
+ {0x0447, "afii10089"},
+ {0x0448, "afii10090"},
+ {0x0449, "afii10091"},
+ {0x044a, "afii10092"},
+ {0x044b, "afii10093"},
+ {0x044c, "afii10094"},
+ {0x044d, "afii10095"},
+ {0x044e, "afii10096"},
+ {0x044f, "afii10097"},
+ {0x0491, "afii10098"},
+ {0x0452, "afii10099"},
+ {0x0453, "afii10100"},
+ {0x0454, "afii10101"},
+ {0x0455, "afii10102"},
+ {0x0456, "afii10103"},
+ {0x0457, "afii10104"},
+ {0x0458, "afii10105"},
+ {0x0459, "afii10106"},
+ {0x045a, "afii10107"},
+ {0x045b, "afii10108"},
+ {0x045c, "afii10109"},
+ {0x045e, "afii10110"},
+ {0x040f, "afii10145"},
+ {0x0462, "afii10146"},
+ {0x0472, "afii10147"},
+ {0x0474, "afii10148"},
+ {0xf6c6, "afii10192"},
+ {0x045f, "afii10193"},
+ {0x0463, "afii10194"},
+ {0x0473, "afii10195"},
+ {0x0475, "afii10196"},
+ {0xf6c7, "afii10831"},
+ {0xf6c8, "afii10832"},
+ {0x04d9, "afii10846"},
+ {0x200e, "afii299"},
+ {0x200f, "afii300"},
+ {0x200d, "afii301"},
+ {0x066a, "afii57381"},
+ {0x060c, "afii57388"},
+ {0x0660, "afii57392"},
+ {0x0661, "afii57393"},
+ {0x0662, "afii57394"},
+ {0x0663, "afii57395"},
+ {0x0664, "afii57396"},
+ {0x0665, "afii57397"},
+ {0x0666, "afii57398"},
+ {0x0667, "afii57399"},
+ {0x0668, "afii57400"},
+ {0x0669, "afii57401"},
+ {0x061b, "afii57403"},
+ {0x061f, "afii57407"},
+ {0x0621, "afii57409"},
+ {0x0622, "afii57410"},
+ {0x0623, "afii57411"},
+ {0x0624, "afii57412"},
+ {0x0625, "afii57413"},
+ {0x0626, "afii57414"},
+ {0x0627, "afii57415"},
+ {0x0628, "afii57416"},
+ {0x0629, "afii57417"},
+ {0x062a, "afii57418"},
+ {0x062b, "afii57419"},
+ {0x062c, "afii57420"},
+ {0x062d, "afii57421"},
+ {0x062e, "afii57422"},
+ {0x062f, "afii57423"},
+ {0x0630, "afii57424"},
+ {0x0631, "afii57425"},
+ {0x0632, "afii57426"},
+ {0x0633, "afii57427"},
+ {0x0634, "afii57428"},
+ {0x0635, "afii57429"},
+ {0x0636, "afii57430"},
+ {0x0637, "afii57431"},
+ {0x0638, "afii57432"},
+ {0x0639, "afii57433"},
+ {0x063a, "afii57434"},
+ {0x0640, "afii57440"},
+ {0x0641, "afii57441"},
+ {0x0642, "afii57442"},
+ {0x0643, "afii57443"},
+ {0x0644, "afii57444"},
+ {0x0645, "afii57445"},
+ {0x0646, "afii57446"},
+ {0x0648, "afii57448"},
+ {0x0649, "afii57449"},
+ {0x064a, "afii57450"},
+ {0x064b, "afii57451"},
+ {0x064c, "afii57452"},
+ {0x064d, "afii57453"},
+ {0x064e, "afii57454"},
+ {0x064f, "afii57455"},
+ {0x0650, "afii57456"},
+ {0x0651, "afii57457"},
+ {0x0652, "afii57458"},
+ {0x0647, "afii57470"},
+ {0x06a4, "afii57505"},
+ {0x067e, "afii57506"},
+ {0x0686, "afii57507"},
+ {0x0698, "afii57508"},
+ {0x06af, "afii57509"},
+ {0x0679, "afii57511"},
+ {0x0688, "afii57512"},
+ {0x0691, "afii57513"},
+ {0x06ba, "afii57514"},
+ {0x06d2, "afii57519"},
+ {0x06d5, "afii57534"},
+ {0x20aa, "afii57636"},
+ {0x05be, "afii57645"},
+ {0x05c3, "afii57658"},
+ {0x05d0, "afii57664"},
+ {0x05d1, "afii57665"},
+ {0x05d2, "afii57666"},
+ {0x05d3, "afii57667"},
+ {0x05d4, "afii57668"},
+ {0x05d5, "afii57669"},
+ {0x05d6, "afii57670"},
+ {0x05d7, "afii57671"},
+ {0x05d8, "afii57672"},
+ {0x05d9, "afii57673"},
+ {0x05da, "afii57674"},
+ {0x05db, "afii57675"},
+ {0x05dc, "afii57676"},
+ {0x05dd, "afii57677"},
+ {0x05de, "afii57678"},
+ {0x05df, "afii57679"},
+ {0x05e0, "afii57680"},
+ {0x05e1, "afii57681"},
+ {0x05e2, "afii57682"},
+ {0x05e3, "afii57683"},
+ {0x05e4, "afii57684"},
+ {0x05e5, "afii57685"},
+ {0x05e6, "afii57686"},
+ {0x05e7, "afii57687"},
+ {0x05e8, "afii57688"},
+ {0x05e9, "afii57689"},
+ {0x05ea, "afii57690"},
+ {0xfb2a, "afii57694"},
+ {0xfb2b, "afii57695"},
+ {0xfb4b, "afii57700"},
+ {0xfb1f, "afii57705"},
+ {0x05f0, "afii57716"},
+ {0x05f1, "afii57717"},
+ {0x05f2, "afii57718"},
+ {0xfb35, "afii57723"},
+ {0x05b4, "afii57793"},
+ {0x05b5, "afii57794"},
+ {0x05b6, "afii57795"},
+ {0x05bb, "afii57796"},
+ {0x05b8, "afii57797"},
+ {0x05b7, "afii57798"},
+ {0x05b0, "afii57799"},
+ {0x05b2, "afii57800"},
+ {0x05b1, "afii57801"},
+ {0x05b3, "afii57802"},
+ {0x05c2, "afii57803"},
+ {0x05c1, "afii57804"},
+ {0x05b9, "afii57806"},
+ {0x05bc, "afii57807"},
+ {0x05bd, "afii57839"},
+ {0x05bf, "afii57841"},
+ {0x05c0, "afii57842"},
+ {0x02bc, "afii57929"},
+ {0x2105, "afii61248"},
+ {0x2113, "afii61289"},
+ {0x2116, "afii61352"},
+ {0x202c, "afii61573"},
+ {0x202d, "afii61574"},
+ {0x202e, "afii61575"},
+ {0x200c, "afii61664"},
+ {0x066d, "afii63167"},
+ {0x02bd, "afii64937"},
+ {0x00e0, "agrave"},
+ {0x0a85, "agujarati"},
+ {0x0a05, "agurmukhi"},
+ {0x3042, "ahiragana"},
+ {0x1ea3, "ahookabove"},
+ {0x0990, "aibengali"},
+ {0x311e, "aibopomofo"},
+ {0x0910, "aideva"},
+ {0x04d5, "aiecyrillic"},
+ {0x0a90, "aigujarati"},
+ {0x0a10, "aigurmukhi"},
+ {0x0a48, "aimatragurmukhi"},
+ {0x0639, "ainarabic"},
+ {0xfeca, "ainfinalarabic"},
+ {0xfecb, "aininitialarabic"},
+ {0xfecc, "ainmedialarabic"},
+ {0x0203, "ainvertedbreve"},
+ {0x09c8, "aivowelsignbengali"},
+ {0x0948, "aivowelsigndeva"},
+ {0x0ac8, "aivowelsigngujarati"},
+ {0x30a2, "akatakana"},
+ {0xff71, "akatakanahalfwidth"},
+ {0x314f, "akorean"},
+ {0x05d0, "alef"},
+ {0x0627, "alefarabic"},
+ {0xfb30, "alefdageshhebrew"},
+ {0xfe8e, "aleffinalarabic"},
+ {0x0623, "alefhamzaabovearabic"},
+ {0xfe84, "alefhamzaabovefinalarabic"},
+ {0x0625, "alefhamzabelowarabic"},
+ {0xfe88, "alefhamzabelowfinalarabic"},
+ {0x05d0, "alefhebrew"},
+ {0xfb4f, "aleflamedhebrew"},
+ {0x0622, "alefmaddaabovearabic"},
+ {0xfe82, "alefmaddaabovefinalarabic"},
+ {0x0649, "alefmaksuraarabic"},
+ {0xfef0, "alefmaksurafinalarabic"},
+ {0xfef3, "alefmaksurainitialarabic"},
+ {0xfef4, "alefmaksuramedialarabic"},
+ {0xfb2e, "alefpatahhebrew"},
+ {0xfb2f, "alefqamatshebrew"},
+ {0x2135, "aleph"},
+ {0x224c, "allequal"},
+ {0x03b1, "alpha"},
+ {0x03ac, "alphatonos"},
+ {0x0101, "amacron"},
+ {0xff41, "amonospace"},
+ {0x0026, "ampersand"},
+ {0xff06, "ampersandmonospace"},
+ {0xf726, "ampersandsmall"},
+ {0x33c2, "amsquare"},
+ {0x3122, "anbopomofo"},
+ {0x3124, "angbopomofo"},
+ {0x0e5a, "angkhankhuthai"},
+ {0x2220, "angle"},
+ {0x3008, "anglebracketleft"},
+ {0xfe3f, "anglebracketleftvertical"},
+ {0x3009, "anglebracketright"},
+ {0xfe40, "anglebracketrightvertical"},
+ {0x2329, "angleleft"},
+ {0x232a, "angleright"},
+ {0x212b, "angstrom"},
+ {0x0387, "anoteleia"},
+ {0x0952, "anudattadeva"},
+ {0x0982, "anusvarabengali"},
+ {0x0902, "anusvaradeva"},
+ {0x0a82, "anusvaragujarati"},
+ {0x0105, "aogonek"},
+ {0x3300, "apaatosquare"},
+ {0x249c, "aparen"},
+ {0x055a, "apostrophearmenian"},
+ {0x02bc, "apostrophemod"},
+ {0xf8ff, "apple"},
+ {0x2250, "approaches"},
+ {0x2248, "approxequal"},
+ {0x2252, "approxequalorimage"},
+ {0x2245, "approximatelyequal"},
+ {0x318e, "araeaekorean"},
+ {0x318d, "araeakorean"},
+ {0x2312, "arc"},
+ {0x1e9a, "arighthalfring"},
+ {0x00e5, "aring"},
+ {0x01fb, "aringacute"},
+ {0x1e01, "aringbelow"},
+ {0x2194, "arrowboth"},
+ {0x21e3, "arrowdashdown"},
+ {0x21e0, "arrowdashleft"},
+ {0x21e2, "arrowdashright"},
+ {0x21e1, "arrowdashup"},
+ {0x21d4, "arrowdblboth"},
+ {0x21d3, "arrowdbldown"},
+ {0x21d0, "arrowdblleft"},
+ {0x21d2, "arrowdblright"},
+ {0x21d1, "arrowdblup"},
+ {0x2193, "arrowdown"},
+ {0x2199, "arrowdownleft"},
+ {0x2198, "arrowdownright"},
+ {0x21e9, "arrowdownwhite"},
+ {0x02c5, "arrowheaddownmod"},
+ {0x02c2, "arrowheadleftmod"},
+ {0x02c3, "arrowheadrightmod"},
+ {0x02c4, "arrowheadupmod"},
+ {0xf8e7, "arrowhorizex"},
+ {0x2190, "arrowleft"},
+ {0x21d0, "arrowleftdbl"},
+ {0x21cd, "arrowleftdblstroke"},
+ {0x21c6, "arrowleftoverright"},
+ {0x21e6, "arrowleftwhite"},
+ {0x2192, "arrowright"},
+ {0x21cf, "arrowrightdblstroke"},
+ {0x279e, "arrowrightheavy"},
+ {0x21c4, "arrowrightoverleft"},
+ {0x21e8, "arrowrightwhite"},
+ {0x21e4, "arrowtableft"},
+ {0x21e5, "arrowtabright"},
+ {0x2191, "arrowup"},
+ {0x2195, "arrowupdn"},
+ {0x21a8, "arrowupdnbse"},
+ {0x21a8, "arrowupdownbase"},
+ {0x2196, "arrowupleft"},
+ {0x21c5, "arrowupleftofdown"},
+ {0x2197, "arrowupright"},
+ {0x21e7, "arrowupwhite"},
+ {0xf8e6, "arrowvertex"},
+ {0x005e, "asciicircum"},
+ {0xff3e, "asciicircummonospace"},
+ {0x007e, "asciitilde"},
+ {0xff5e, "asciitildemonospace"},
+ {0x0251, "ascript"},
+ {0x0252, "ascriptturned"},
+ {0x3041, "asmallhiragana"},
+ {0x30a1, "asmallkatakana"},
+ {0xff67, "asmallkatakanahalfwidth"},
+ {0x002a, "asterisk"},
+ {0x066d, "asteriskaltonearabic"},
+ {0x066d, "asteriskarabic"},
+ {0x2217, "asteriskmath"},
+ {0xff0a, "asteriskmonospace"},
+ {0xfe61, "asterisksmall"},
+ {0x2042, "asterism"},
+ {0xf6e9, "asuperior"},
+ {0x2243, "asymptoticallyequal"},
+ {0x0040, "at"},
+ {0x00e3, "atilde"},
+ {0xff20, "atmonospace"},
+ {0xfe6b, "atsmall"},
+ {0x0250, "aturned"},
+ {0x0994, "aubengali"},
+ {0x3120, "aubopomofo"},
+ {0x0914, "audeva"},
+ {0x0a94, "augujarati"},
+ {0x0a14, "augurmukhi"},
+ {0x09d7, "aulengthmarkbengali"},
+ {0x0a4c, "aumatragurmukhi"},
+ {0x09cc, "auvowelsignbengali"},
+ {0x094c, "auvowelsigndeva"},
+ {0x0acc, "auvowelsigngujarati"},
+ {0x093d, "avagrahadeva"},
+ {0x0561, "aybarmenian"},
+ {0x05e2, "ayin"},
+ {0xfb20, "ayinaltonehebrew"},
+ {0x05e2, "ayinhebrew"},
+ {0x0062, "b"},
+ {0x09ac, "babengali"},
+ {0x005c, "backslash"},
+ {0xff3c, "backslashmonospace"},
+ {0x092c, "badeva"},
+ {0x0aac, "bagujarati"},
+ {0x0a2c, "bagurmukhi"},
+ {0x3070, "bahiragana"},
+ {0x0e3f, "bahtthai"},
+ {0x30d0, "bakatakana"},
+ {0x007c, "bar"},
+ {0xff5c, "barmonospace"},
+ {0x3105, "bbopomofo"},
+ {0x24d1, "bcircle"},
+ {0x1e03, "bdotaccent"},
+ {0x1e05, "bdotbelow"},
+ {0x266c, "beamedsixteenthnotes"},
+ {0x2235, "because"},
+ {0x0431, "becyrillic"},
+ {0x0628, "beharabic"},
+ {0xfe90, "behfinalarabic"},
+ {0xfe91, "behinitialarabic"},
+ {0x3079, "behiragana"},
+ {0xfe92, "behmedialarabic"},
+ {0xfc9f, "behmeeminitialarabic"},
+ {0xfc08, "behmeemisolatedarabic"},
+ {0xfc6d, "behnoonfinalarabic"},
+ {0x30d9, "bekatakana"},
+ {0x0562, "benarmenian"},
+ {0x05d1, "bet"},
+ {0x03b2, "beta"},
+ {0x03d0, "betasymbolgreek"},
+ {0xfb31, "betdagesh"},
+ {0xfb31, "betdageshhebrew"},
+ {0x05d1, "bethebrew"},
+ {0xfb4c, "betrafehebrew"},
+ {0x09ad, "bhabengali"},
+ {0x092d, "bhadeva"},
+ {0x0aad, "bhagujarati"},
+ {0x0a2d, "bhagurmukhi"},
+ {0x0253, "bhook"},
+ {0x3073, "bihiragana"},
+ {0x30d3, "bikatakana"},
+ {0x0298, "bilabialclick"},
+ {0x0a02, "bindigurmukhi"},
+ {0x3331, "birusquare"},
+ {0x25cf, "blackcircle"},
+ {0x25c6, "blackdiamond"},
+ {0x25bc, "blackdownpointingtriangle"},
+ {0x25c4, "blackleftpointingpointer"},
+ {0x25c0, "blackleftpointingtriangle"},
+ {0x3010, "blacklenticularbracketleft"},
+ {0xfe3b, "blacklenticularbracketleftvertical"},
+ {0x3011, "blacklenticularbracketright"},
+ {0xfe3c, "blacklenticularbracketrightvertical"},
+ {0x25e3, "blacklowerlefttriangle"},
+ {0x25e2, "blacklowerrighttriangle"},
+ {0x25ac, "blackrectangle"},
+ {0x25ba, "blackrightpointingpointer"},
+ {0x25b6, "blackrightpointingtriangle"},
+ {0x25aa, "blacksmallsquare"},
+ {0x263b, "blacksmilingface"},
+ {0x25a0, "blacksquare"},
+ {0x2605, "blackstar"},
+ {0x25e4, "blackupperlefttriangle"},
+ {0x25e5, "blackupperrighttriangle"},
+ {0x25b4, "blackuppointingsmalltriangle"},
+ {0x25b2, "blackuppointingtriangle"},
+ {0x2423, "blank"},
+ {0x1e07, "blinebelow"},
+ {0x2588, "block"},
+ {0xff42, "bmonospace"},
+ {0x0e1a, "bobaimaithai"},
+ {0x307c, "bohiragana"},
+ {0x30dc, "bokatakana"},
+ {0x249d, "bparen"},
+ {0x33c3, "bqsquare"},
+ {0xf8f4, "braceex"},
+ {0x007b, "braceleft"},
+ {0xf8f3, "braceleftbt"},
+ {0xf8f2, "braceleftmid"},
+ {0xff5b, "braceleftmonospace"},
+ {0xfe5b, "braceleftsmall"},
+ {0xf8f1, "bracelefttp"},
+ {0xfe37, "braceleftvertical"},
+ {0x007d, "braceright"},
+ {0xf8fe, "bracerightbt"},
+ {0xf8fd, "bracerightmid"},
+ {0xff5d, "bracerightmonospace"},
+ {0xfe5c, "bracerightsmall"},
+ {0xf8fc, "bracerighttp"},
+ {0xfe38, "bracerightvertical"},
+ {0x005b, "bracketleft"},
+ {0xf8f0, "bracketleftbt"},
+ {0xf8ef, "bracketleftex"},
+ {0xff3b, "bracketleftmonospace"},
+ {0xf8ee, "bracketlefttp"},
+ {0x005d, "bracketright"},
+ {0xf8fb, "bracketrightbt"},
+ {0xf8fa, "bracketrightex"},
+ {0xff3d, "bracketrightmonospace"},
+ {0xf8f9, "bracketrighttp"},
+ {0x02d8, "breve"},
+ {0x032e, "brevebelowcmb"},
+ {0x0306, "brevecmb"},
+ {0x032f, "breveinvertedbelowcmb"},
+ {0x0311, "breveinvertedcmb"},
+ {0x0361, "breveinverteddoublecmb"},
+ {0x032a, "bridgebelowcmb"},
+ {0x033a, "bridgeinvertedbelowcmb"},
+ {0x00a6, "brokenbar"},
+ {0x0180, "bstroke"},
+ {0xf6ea, "bsuperior"},
+ {0x0183, "btopbar"},
+ {0x3076, "buhiragana"},
+ {0x30d6, "bukatakana"},
+ {0x2022, "bullet"},
+ {0x25d8, "bulletinverse"},
+ {0x2219, "bulletoperator"},
+ {0x25ce, "bullseye"},
+ {0x0063, "c"},
+ {0x056e, "caarmenian"},
+ {0x099a, "cabengali"},
+ {0x0107, "cacute"},
+ {0x091a, "cadeva"},
+ {0x0a9a, "cagujarati"},
+ {0x0a1a, "cagurmukhi"},
+ {0x3388, "calsquare"},
+ {0x0981, "candrabindubengali"},
+ {0x0310, "candrabinducmb"},
+ {0x0901, "candrabindudeva"},
+ {0x0a81, "candrabindugujarati"},
+ {0x21ea, "capslock"},
+ {0x2105, "careof"},
+ {0x02c7, "caron"},
+ {0x032c, "caronbelowcmb"},
+ {0x030c, "caroncmb"},
+ {0x21b5, "carriagereturn"},
+ {0x3118, "cbopomofo"},
+ {0x010d, "ccaron"},
+ {0x00e7, "ccedilla"},
+ {0x1e09, "ccedillaacute"},
+ {0x24d2, "ccircle"},
+ {0x0109, "ccircumflex"},
+ {0x0255, "ccurl"},
+ {0x010b, "cdot"},
+ {0x010b, "cdotaccent"},
+ {0x33c5, "cdsquare"},
+ {0x00b8, "cedilla"},
+ {0x0327, "cedillacmb"},
+ {0x00a2, "cent"},
+ {0x2103, "centigrade"},
+ {0xf6df, "centinferior"},
+ {0xffe0, "centmonospace"},
+ {0xf7a2, "centoldstyle"},
+ {0xf6e0, "centsuperior"},
+ {0x0579, "chaarmenian"},
+ {0x099b, "chabengali"},
+ {0x091b, "chadeva"},
+ {0x0a9b, "chagujarati"},
+ {0x0a1b, "chagurmukhi"},
+ {0x3114, "chbopomofo"},
+ {0x04bd, "cheabkhasiancyrillic"},
+ {0x2713, "checkmark"},
+ {0x0447, "checyrillic"},
+ {0x04bf, "chedescenderabkhasiancyrillic"},
+ {0x04b7, "chedescendercyrillic"},
+ {0x04f5, "chedieresiscyrillic"},
+ {0x0573, "cheharmenian"},
+ {0x04cc, "chekhakassiancyrillic"},
+ {0x04b9, "cheverticalstrokecyrillic"},
+ {0x03c7, "chi"},
+ {0x3277, "chieuchacirclekorean"},
+ {0x3217, "chieuchaparenkorean"},
+ {0x3269, "chieuchcirclekorean"},
+ {0x314a, "chieuchkorean"},
+ {0x3209, "chieuchparenkorean"},
+ {0x0e0a, "chochangthai"},
+ {0x0e08, "chochanthai"},
+ {0x0e09, "chochingthai"},
+ {0x0e0c, "chochoethai"},
+ {0x0188, "chook"},
+ {0x3276, "cieucacirclekorean"},
+ {0x3216, "cieucaparenkorean"},
+ {0x3268, "cieuccirclekorean"},
+ {0x3148, "cieuckorean"},
+ {0x3208, "cieucparenkorean"},
+ {0x321c, "cieucuparenkorean"},
+ {0x25cb, "circle"},
+ {0x2297, "circlemultiply"},
+ {0x2299, "circleot"},
+ {0x2295, "circleplus"},
+ {0x3036, "circlepostalmark"},
+ {0x25d0, "circlewithlefthalfblack"},
+ {0x25d1, "circlewithrighthalfblack"},
+ {0x02c6, "circumflex"},
+ {0x032d, "circumflexbelowcmb"},
+ {0x0302, "circumflexcmb"},
+ {0x2327, "clear"},
+ {0x01c2, "clickalveolar"},
+ {0x01c0, "clickdental"},
+ {0x01c1, "clicklateral"},
+ {0x01c3, "clickretroflex"},
+ {0x2663, "club"},
+ {0x2663, "clubsuitblack"},
+ {0x2667, "clubsuitwhite"},
+ {0x33a4, "cmcubedsquare"},
+ {0xff43, "cmonospace"},
+ {0x33a0, "cmsquaredsquare"},
+ {0x0581, "coarmenian"},
+ {0x003a, "colon"},
+ {0x20a1, "colonmonetary"},
+ {0xff1a, "colonmonospace"},
+ {0x20a1, "colonsign"},
+ {0xfe55, "colonsmall"},
+ {0x02d1, "colontriangularhalfmod"},
+ {0x02d0, "colontriangularmod"},
+ {0x002c, "comma"},
+ {0x0313, "commaabovecmb"},
+ {0x0315, "commaaboverightcmb"},
+ {0xf6c3, "commaaccent"},
+ {0x060c, "commaarabic"},
+ {0x055d, "commaarmenian"},
+ {0xf6e1, "commainferior"},
+ {0xff0c, "commamonospace"},
+ {0x0314, "commareversedabovecmb"},
+ {0x02bd, "commareversedmod"},
+ {0xfe50, "commasmall"},
+ {0xf6e2, "commasuperior"},
+ {0x0312, "commaturnedabovecmb"},
+ {0x02bb, "commaturnedmod"},
+ {0x263c, "compass"},
+ {0x2245, "congruent"},
+ {0x222e, "contourintegral"},
+ {0x2303, "control"},
+ {0x0006, "controlACK"},
+ {0x0007, "controlBEL"},
+ {0x0008, "controlBS"},
+ {0x0018, "controlCAN"},
+ {0x000d, "controlCR"},
+ {0x0011, "controlDC1"},
+ {0x0012, "controlDC2"},
+ {0x0013, "controlDC3"},
+ {0x0014, "controlDC4"},
+ {0x007f, "controlDEL"},
+ {0x0010, "controlDLE"},
+ {0x0019, "controlEM"},
+ {0x0005, "controlENQ"},
+ {0x0004, "controlEOT"},
+ {0x001b, "controlESC"},
+ {0x0017, "controlETB"},
+ {0x0003, "controlETX"},
+ {0x000c, "controlFF"},
+ {0x001c, "controlFS"},
+ {0x001d, "controlGS"},
+ {0x0009, "controlHT"},
+ {0x000a, "controlLF"},
+ {0x0015, "controlNAK"},
+ {0x001e, "controlRS"},
+ {0x000f, "controlSI"},
+ {0x000e, "controlSO"},
+ {0x0002, "controlSOT"},
+ {0x0001, "controlSTX"},
+ {0x001a, "controlSUB"},
+ {0x0016, "controlSYN"},
+ {0x001f, "controlUS"},
+ {0x000b, "controlVT"},
+ {0x00a9, "copyright"},
+ {0x00a9, "copyrightsans"},
+ {0x00a9, "copyrightserif"},
+ {0x300c, "cornerbracketleft"},
+ {0xff62, "cornerbracketlefthalfwidth"},
+ {0xfe41, "cornerbracketleftvertical"},
+ {0x300d, "cornerbracketright"},
+ {0xff63, "cornerbracketrighthalfwidth"},
+ {0xfe42, "cornerbracketrightvertical"},
+ {0x337f, "corporationsquare"},
+ {0x33c7, "cosquare"},
+ {0x33c6, "coverkgsquare"},
+ {0x249e, "cparen"},
+ {0x20a2, "cruzeiro"},
+ {0x0297, "cstretched"},
+ {0x22cf, "curlyand"},
+ {0x22ce, "curlyor"},
+ {0x00a4, "currency"},
+ {0xf6d1, "cyrBreve"},
+ {0xf6d2, "cyrFlex"},
+ {0xf6d4, "cyrbreve"},
+ {0xf6d5, "cyrflex"},
+ {0x0064, "d"},
+ {0x0564, "daarmenian"},
+ {0x09a6, "dabengali"},
+ {0x0636, "dadarabic"},
+ {0x0926, "dadeva"},
+ {0xfebe, "dadfinalarabic"},
+ {0xfebf, "dadinitialarabic"},
+ {0xfec0, "dadmedialarabic"},
+ {0x05bc, "dagesh"},
+ {0x05bc, "dageshhebrew"},
+ {0x2020, "dagger"},
+ {0x2021, "daggerdbl"},
+ {0x0aa6, "dagujarati"},
+ {0x0a26, "dagurmukhi"},
+ {0x3060, "dahiragana"},
+ {0x30c0, "dakatakana"},
+ {0x062f, "dalarabic"},
+ {0x05d3, "dalet"},
+ {0xfb33, "daletdagesh"},
+ {0xfb33, "daletdageshhebrew"},
+ {0x05d3, "dalethebrew"},
+ {0xfeaa, "dalfinalarabic"},
+ {0x064f, "dammaarabic"},
+ {0x064f, "dammalowarabic"},
+ {0x064c, "dammatanaltonearabic"},
+ {0x064c, "dammatanarabic"},
+ {0x0964, "danda"},
+ {0x05a7, "dargahebrew"},
+ {0x05a7, "dargalefthebrew"},
+ {0x0485, "dasiapneumatacyrilliccmb"},
+ {0xf6d3, "dblGrave"},
+ {0x300a, "dblanglebracketleft"},
+ {0xfe3d, "dblanglebracketleftvertical"},
+ {0x300b, "dblanglebracketright"},
+ {0xfe3e, "dblanglebracketrightvertical"},
+ {0x032b, "dblarchinvertedbelowcmb"},
+ {0x21d4, "dblarrowleft"},
+ {0x21d2, "dblarrowright"},
+ {0x0965, "dbldanda"},
+ {0xf6d6, "dblgrave"},
+ {0x030f, "dblgravecmb"},
+ {0x222c, "dblintegral"},
+ {0x2017, "dbllowline"},
+ {0x0333, "dbllowlinecmb"},
+ {0x033f, "dbloverlinecmb"},
+ {0x02ba, "dblprimemod"},
+ {0x2016, "dblverticalbar"},
+ {0x030e, "dblverticallineabovecmb"},
+ {0x3109, "dbopomofo"},
+ {0x33c8, "dbsquare"},
+ {0x010f, "dcaron"},
+ {0x1e11, "dcedilla"},
+ {0x24d3, "dcircle"},
+ {0x1e13, "dcircumflexbelow"},
+ {0x0111, "dcroat"},
+ {0x09a1, "ddabengali"},
+ {0x0921, "ddadeva"},
+ {0x0aa1, "ddagujarati"},
+ {0x0a21, "ddagurmukhi"},
+ {0x0688, "ddalarabic"},
+ {0xfb89, "ddalfinalarabic"},
+ {0x095c, "dddhadeva"},
+ {0x09a2, "ddhabengali"},
+ {0x0922, "ddhadeva"},
+ {0x0aa2, "ddhagujarati"},
+ {0x0a22, "ddhagurmukhi"},
+ {0x1e0b, "ddotaccent"},
+ {0x1e0d, "ddotbelow"},
+ {0x066b, "decimalseparatorarabic"},
+ {0x066b, "decimalseparatorpersian"},
+ {0x0434, "decyrillic"},
+ {0x00b0, "degree"},
+ {0x05ad, "dehihebrew"},
+ {0x3067, "dehiragana"},
+ {0x03ef, "deicoptic"},
+ {0x30c7, "dekatakana"},
+ {0x232b, "deleteleft"},
+ {0x2326, "deleteright"},
+ {0x03b4, "delta"},
+ {0x018d, "deltaturned"},
+ {0x09f8, "denominatorminusonenumeratorbengali"},
+ {0x02a4, "dezh"},
+ {0x09a7, "dhabengali"},
+ {0x0927, "dhadeva"},
+ {0x0aa7, "dhagujarati"},
+ {0x0a27, "dhagurmukhi"},
+ {0x0257, "dhook"},
+ {0x0385, "dialytikatonos"},
+ {0x0344, "dialytikatonoscmb"},
+ {0x2666, "diamond"},
+ {0x2662, "diamondsuitwhite"},
+ {0x00a8, "dieresis"},
+ {0xf6d7, "dieresisacute"},
+ {0x0324, "dieresisbelowcmb"},
+ {0x0308, "dieresiscmb"},
+ {0xf6d8, "dieresisgrave"},
+ {0x0385, "dieresistonos"},
+ {0x3062, "dihiragana"},
+ {0x30c2, "dikatakana"},
+ {0x3003, "dittomark"},
+ {0x00f7, "divide"},
+ {0x2223, "divides"},
+ {0x2215, "divisionslash"},
+ {0x0452, "djecyrillic"},
+ {0x2593, "dkshade"},
+ {0x1e0f, "dlinebelow"},
+ {0x3397, "dlsquare"},
+ {0x0111, "dmacron"},
+ {0xff44, "dmonospace"},
+ {0x2584, "dnblock"},
+ {0x0e0e, "dochadathai"},
+ {0x0e14, "dodekthai"},
+ {0x3069, "dohiragana"},
+ {0x30c9, "dokatakana"},
+ {0x0024, "dollar"},
+ {0xf6e3, "dollarinferior"},
+ {0xff04, "dollarmonospace"},
+ {0xf724, "dollaroldstyle"},
+ {0xfe69, "dollarsmall"},
+ {0xf6e4, "dollarsuperior"},
+ {0x20ab, "dong"},
+ {0x3326, "dorusquare"},
+ {0x02d9, "dotaccent"},
+ {0x0307, "dotaccentcmb"},
+ {0x0323, "dotbelowcmb"},
+ {0x0323, "dotbelowcomb"},
+ {0x30fb, "dotkatakana"},
+ {0x0131, "dotlessi"},
+ {0xf6be, "dotlessj"},
+ {0x0284, "dotlessjstrokehook"},
+ {0x22c5, "dotmath"},
+ {0x25cc, "dottedcircle"},
+ {0xfb1f, "doubleyodpatah"},
+ {0xfb1f, "doubleyodpatahhebrew"},
+ {0x031e, "downtackbelowcmb"},
+ {0x02d5, "downtackmod"},
+ {0x249f, "dparen"},
+ {0xf6eb, "dsuperior"},
+ {0x0256, "dtail"},
+ {0x018c, "dtopbar"},
+ {0x3065, "duhiragana"},
+ {0x30c5, "dukatakana"},
+ {0x01f3, "dz"},
+ {0x02a3, "dzaltone"},
+ {0x01c6, "dzcaron"},
+ {0x02a5, "dzcurl"},
+ {0x04e1, "dzeabkhasiancyrillic"},
+ {0x0455, "dzecyrillic"},
+ {0x045f, "dzhecyrillic"},
+ {0x0065, "e"},
+ {0x00e9, "eacute"},
+ {0x2641, "earth"},
+ {0x098f, "ebengali"},
+ {0x311c, "ebopomofo"},
+ {0x0115, "ebreve"},
+ {0x090d, "ecandradeva"},
+ {0x0a8d, "ecandragujarati"},
+ {0x0945, "ecandravowelsigndeva"},
+ {0x0ac5, "ecandravowelsigngujarati"},
+ {0x011b, "ecaron"},
+ {0x1e1d, "ecedillabreve"},
+ {0x0565, "echarmenian"},
+ {0x0587, "echyiwnarmenian"},
+ {0x24d4, "ecircle"},
+ {0x00ea, "ecircumflex"},
+ {0x1ebf, "ecircumflexacute"},
+ {0x1e19, "ecircumflexbelow"},
+ {0x1ec7, "ecircumflexdotbelow"},
+ {0x1ec1, "ecircumflexgrave"},
+ {0x1ec3, "ecircumflexhookabove"},
+ {0x1ec5, "ecircumflextilde"},
+ {0x0454, "ecyrillic"},
+ {0x0205, "edblgrave"},
+ {0x090f, "edeva"},
+ {0x00eb, "edieresis"},
+ {0x0117, "edot"},
+ {0x0117, "edotaccent"},
+ {0x1eb9, "edotbelow"},
+ {0x0a0f, "eegurmukhi"},
+ {0x0a47, "eematragurmukhi"},
+ {0x0444, "efcyrillic"},
+ {0x00e8, "egrave"},
+ {0x0a8f, "egujarati"},
+ {0x0567, "eharmenian"},
+ {0x311d, "ehbopomofo"},
+ {0x3048, "ehiragana"},
+ {0x1ebb, "ehookabove"},
+ {0x311f, "eibopomofo"},
+ {0x0038, "eight"},
+ {0x0668, "eightarabic"},
+ {0x09ee, "eightbengali"},
+ {0x2467, "eightcircle"},
+ {0x2791, "eightcircleinversesansserif"},
+ {0x096e, "eightdeva"},
+ {0x2471, "eighteencircle"},
+ {0x2485, "eighteenparen"},
+ {0x2499, "eighteenperiod"},
+ {0x0aee, "eightgujarati"},
+ {0x0a6e, "eightgurmukhi"},
+ {0x0668, "eighthackarabic"},
+ {0x3028, "eighthangzhou"},
+ {0x266b, "eighthnotebeamed"},
+ {0x3227, "eightideographicparen"},
+ {0x2088, "eightinferior"},
+ {0xff18, "eightmonospace"},
+ {0xf738, "eightoldstyle"},
+ {0x247b, "eightparen"},
+ {0x248f, "eightperiod"},
+ {0x06f8, "eightpersian"},
+ {0x2177, "eightroman"},
+ {0x2078, "eightsuperior"},
+ {0x0e58, "eightthai"},
+ {0x0207, "einvertedbreve"},
+ {0x0465, "eiotifiedcyrillic"},
+ {0x30a8, "ekatakana"},
+ {0xff74, "ekatakanahalfwidth"},
+ {0x0a74, "ekonkargurmukhi"},
+ {0x3154, "ekorean"},
+ {0x043b, "elcyrillic"},
+ {0x2208, "element"},
+ {0x246a, "elevencircle"},
+ {0x247e, "elevenparen"},
+ {0x2492, "elevenperiod"},
+ {0x217a, "elevenroman"},
+ {0x2026, "ellipsis"},
+ {0x22ee, "ellipsisvertical"},
+ {0x0113, "emacron"},
+ {0x1e17, "emacronacute"},
+ {0x1e15, "emacrongrave"},
+ {0x043c, "emcyrillic"},
+ {0x2014, "emdash"},
+ {0xfe31, "emdashvertical"},
+ {0xff45, "emonospace"},
+ {0x055b, "emphasismarkarmenian"},
+ {0x2205, "emptyset"},
+ {0x3123, "enbopomofo"},
+ {0x043d, "encyrillic"},
+ {0x2013, "endash"},
+ {0xfe32, "endashvertical"},
+ {0x04a3, "endescendercyrillic"},
+ {0x014b, "eng"},
+ {0x3125, "engbopomofo"},
+ {0x04a5, "enghecyrillic"},
+ {0x04c8, "enhookcyrillic"},
+ {0x2002, "enspace"},
+ {0x0119, "eogonek"},
+ {0x3153, "eokorean"},
+ {0x025b, "eopen"},
+ {0x029a, "eopenclosed"},
+ {0x025c, "eopenreversed"},
+ {0x025e, "eopenreversedclosed"},
+ {0x025d, "eopenreversedhook"},
+ {0x24a0, "eparen"},
+ {0x03b5, "epsilon"},
+ {0x03ad, "epsilontonos"},
+ {0x003d, "equal"},
+ {0xff1d, "equalmonospace"},
+ {0xfe66, "equalsmall"},
+ {0x207c, "equalsuperior"},
+ {0x2261, "equivalence"},
+ {0x3126, "erbopomofo"},
+ {0x0440, "ercyrillic"},
+ {0x0258, "ereversed"},
+ {0x044d, "ereversedcyrillic"},
+ {0x0441, "escyrillic"},
+ {0x04ab, "esdescendercyrillic"},
+ {0x0283, "esh"},
+ {0x0286, "eshcurl"},
+ {0x090e, "eshortdeva"},
+ {0x0946, "eshortvowelsigndeva"},
+ {0x01aa, "eshreversedloop"},
+ {0x0285, "eshsquatreversed"},
+ {0x3047, "esmallhiragana"},
+ {0x30a7, "esmallkatakana"},
+ {0xff6a, "esmallkatakanahalfwidth"},
+ {0x212e, "estimated"},
+ {0xf6ec, "esuperior"},
+ {0x03b7, "eta"},
+ {0x0568, "etarmenian"},
+ {0x03ae, "etatonos"},
+ {0x00f0, "eth"},
+ {0x1ebd, "etilde"},
+ {0x1e1b, "etildebelow"},
+ {0x0591, "etnahtafoukhhebrew"},
+ {0x0591, "etnahtafoukhlefthebrew"},
+ {0x0591, "etnahtahebrew"},
+ {0x0591, "etnahtalefthebrew"},
+ {0x01dd, "eturned"},
+ {0x3161, "eukorean"},
+ {0x20ac, "euro"},
+ {0x09c7, "evowelsignbengali"},
+ {0x0947, "evowelsigndeva"},
+ {0x0ac7, "evowelsigngujarati"},
+ {0x0021, "exclam"},
+ {0x055c, "exclamarmenian"},
+ {0x203c, "exclamdbl"},
+ {0x00a1, "exclamdown"},
+ {0xf7a1, "exclamdownsmall"},
+ {0x0021, "exclamleft"},
+ {0xff01, "exclammonospace"},
+ {0xf721, "exclamsmall"},
+ {0x2203, "existential"},
+ {0x0292, "ezh"},
+ {0x01ef, "ezhcaron"},
+ {0x0293, "ezhcurl"},
+ {0x01b9, "ezhreversed"},
+ {0x01ba, "ezhtail"},
+ {0x0066, "f"},
+ {0x095e, "fadeva"},
+ {0x0a5e, "fagurmukhi"},
+ {0x2109, "fahrenheit"},
+ {0x064e, "fathaarabic"},
+ {0x064e, "fathalowarabic"},
+ {0x064b, "fathatanarabic"},
+ {0x3108, "fbopomofo"},
+ {0x24d5, "fcircle"},
+ {0x1e1f, "fdotaccent"},
+ {0x0641, "feharabic"},
+ {0x0586, "feharmenian"},
+ {0xfed2, "fehfinalarabic"},
+ {0xfed3, "fehinitialarabic"},
+ {0xfed4, "fehmedialarabic"},
+ {0x03e5, "feicoptic"},
+ {0x2640, "female"},
+ {0xfb00, "ff"},
+ {0xfb03, "ffi"},
+ {0xfb04, "ffl"},
+ {0xfb01, "fi"},
+ {0x246e, "fifteencircle"},
+ {0x2482, "fifteenparen"},
+ {0x2496, "fifteenperiod"},
+ {0x2012, "figuredash"},
+ {0x25a0, "filledbox"},
+ {0x25ac, "filledrect"},
+ {0x05da, "finalkaf"},
+ {0xfb3a, "finalkafdagesh"},
+ {0xfb3a, "finalkafdageshhebrew"},
+ {0x05da, "finalkafhebrew"},
+ {0x05dd, "finalmem"},
+ {0x05dd, "finalmemhebrew"},
+ {0x05df, "finalnun"},
+ {0x05df, "finalnunhebrew"},
+ {0x05e3, "finalpe"},
+ {0x05e3, "finalpehebrew"},
+ {0x05e5, "finaltsadi"},
+ {0x05e5, "finaltsadihebrew"},
+ {0x02c9, "firsttonechinese"},
+ {0x25c9, "fisheye"},
+ {0x0473, "fitacyrillic"},
+ {0x0035, "five"},
+ {0x0665, "fivearabic"},
+ {0x09eb, "fivebengali"},
+ {0x2464, "fivecircle"},
+ {0x278e, "fivecircleinversesansserif"},
+ {0x096b, "fivedeva"},
+ {0x215d, "fiveeighths"},
+ {0x0aeb, "fivegujarati"},
+ {0x0a6b, "fivegurmukhi"},
+ {0x0665, "fivehackarabic"},
+ {0x3025, "fivehangzhou"},
+ {0x3224, "fiveideographicparen"},
+ {0x2085, "fiveinferior"},
+ {0xff15, "fivemonospace"},
+ {0xf735, "fiveoldstyle"},
+ {0x2478, "fiveparen"},
+ {0x248c, "fiveperiod"},
+ {0x06f5, "fivepersian"},
+ {0x2174, "fiveroman"},
+ {0x2075, "fivesuperior"},
+ {0x0e55, "fivethai"},
+ {0xfb02, "fl"},
+ {0x0192, "florin"},
+ {0xff46, "fmonospace"},
+ {0x3399, "fmsquare"},
+ {0x0e1f, "fofanthai"},
+ {0x0e1d, "fofathai"},
+ {0x0e4f, "fongmanthai"},
+ {0x2200, "forall"},
+ {0x0034, "four"},
+ {0x0664, "fourarabic"},
+ {0x09ea, "fourbengali"},
+ {0x2463, "fourcircle"},
+ {0x278d, "fourcircleinversesansserif"},
+ {0x096a, "fourdeva"},
+ {0x0aea, "fourgujarati"},
+ {0x0a6a, "fourgurmukhi"},
+ {0x0664, "fourhackarabic"},
+ {0x3024, "fourhangzhou"},
+ {0x3223, "fourideographicparen"},
+ {0x2084, "fourinferior"},
+ {0xff14, "fourmonospace"},
+ {0x09f7, "fournumeratorbengali"},
+ {0xf734, "fouroldstyle"},
+ {0x2477, "fourparen"},
+ {0x248b, "fourperiod"},
+ {0x06f4, "fourpersian"},
+ {0x2173, "fourroman"},
+ {0x2074, "foursuperior"},
+ {0x246d, "fourteencircle"},
+ {0x2481, "fourteenparen"},
+ {0x2495, "fourteenperiod"},
+ {0x0e54, "fourthai"},
+ {0x02cb, "fourthtonechinese"},
+ {0x24a1, "fparen"},
+ {0x2044, "fraction"},
+ {0x20a3, "franc"},
+ {0x0067, "g"},
+ {0x0997, "gabengali"},
+ {0x01f5, "gacute"},
+ {0x0917, "gadeva"},
+ {0x06af, "gafarabic"},
+ {0xfb93, "gaffinalarabic"},
+ {0xfb94, "gafinitialarabic"},
+ {0xfb95, "gafmedialarabic"},
+ {0x0a97, "gagujarati"},
+ {0x0a17, "gagurmukhi"},
+ {0x304c, "gahiragana"},
+ {0x30ac, "gakatakana"},
+ {0x03b3, "gamma"},
+ {0x0263, "gammalatinsmall"},
+ {0x02e0, "gammasuperior"},
+ {0x03eb, "gangiacoptic"},
+ {0x310d, "gbopomofo"},
+ {0x011f, "gbreve"},
+ {0x01e7, "gcaron"},
+ {0x0123, "gcedilla"},
+ {0x24d6, "gcircle"},
+ {0x011d, "gcircumflex"},
+ {0x0123, "gcommaaccent"},
+ {0x0121, "gdot"},
+ {0x0121, "gdotaccent"},
+ {0x0433, "gecyrillic"},
+ {0x3052, "gehiragana"},
+ {0x30b2, "gekatakana"},
+ {0x2251, "geometricallyequal"},
+ {0x059c, "gereshaccenthebrew"},
+ {0x05f3, "gereshhebrew"},
+ {0x059d, "gereshmuqdamhebrew"},
+ {0x00df, "germandbls"},
+ {0x059e, "gershayimaccenthebrew"},
+ {0x05f4, "gershayimhebrew"},
+ {0x3013, "getamark"},
+ {0x0998, "ghabengali"},
+ {0x0572, "ghadarmenian"},
+ {0x0918, "ghadeva"},
+ {0x0a98, "ghagujarati"},
+ {0x0a18, "ghagurmukhi"},
+ {0x063a, "ghainarabic"},
+ {0xfece, "ghainfinalarabic"},
+ {0xfecf, "ghaininitialarabic"},
+ {0xfed0, "ghainmedialarabic"},
+ {0x0495, "ghemiddlehookcyrillic"},
+ {0x0493, "ghestrokecyrillic"},
+ {0x0491, "gheupturncyrillic"},
+ {0x095a, "ghhadeva"},
+ {0x0a5a, "ghhagurmukhi"},
+ {0x0260, "ghook"},
+ {0x3393, "ghzsquare"},
+ {0x304e, "gihiragana"},
+ {0x30ae, "gikatakana"},
+ {0x0563, "gimarmenian"},
+ {0x05d2, "gimel"},
+ {0xfb32, "gimeldagesh"},
+ {0xfb32, "gimeldageshhebrew"},
+ {0x05d2, "gimelhebrew"},
+ {0x0453, "gjecyrillic"},
+ {0x01be, "glottalinvertedstroke"},
+ {0x0294, "glottalstop"},
+ {0x0296, "glottalstopinverted"},
+ {0x02c0, "glottalstopmod"},
+ {0x0295, "glottalstopreversed"},
+ {0x02c1, "glottalstopreversedmod"},
+ {0x02e4, "glottalstopreversedsuperior"},
+ {0x02a1, "glottalstopstroke"},
+ {0x02a2, "glottalstopstrokereversed"},
+ {0x1e21, "gmacron"},
+ {0xff47, "gmonospace"},
+ {0x3054, "gohiragana"},
+ {0x30b4, "gokatakana"},
+ {0x24a2, "gparen"},
+ {0x33ac, "gpasquare"},
+ {0x2207, "gradient"},
+ {0x0060, "grave"},
+ {0x0316, "gravebelowcmb"},
+ {0x0300, "gravecmb"},
+ {0x0300, "gravecomb"},
+ {0x0953, "gravedeva"},
+ {0x02ce, "gravelowmod"},
+ {0xff40, "gravemonospace"},
+ {0x0340, "gravetonecmb"},
+ {0x003e, "greater"},
+ {0x2265, "greaterequal"},
+ {0x22db, "greaterequalorless"},
+ {0xff1e, "greatermonospace"},
+ {0x2273, "greaterorequivalent"},
+ {0x2277, "greaterorless"},
+ {0x2267, "greateroverequal"},
+ {0xfe65, "greatersmall"},
+ {0x0261, "gscript"},
+ {0x01e5, "gstroke"},
+ {0x3050, "guhiragana"},
+ {0x00ab, "guillemotleft"},
+ {0x00bb, "guillemotright"},
+ {0x2039, "guilsinglleft"},
+ {0x203a, "guilsinglright"},
+ {0x30b0, "gukatakana"},
+ {0x3318, "guramusquare"},
+ {0x33c9, "gysquare"},
+ {0x0068, "h"},
+ {0x04a9, "haabkhasiancyrillic"},
+ {0x06c1, "haaltonearabic"},
+ {0x09b9, "habengali"},
+ {0x04b3, "hadescendercyrillic"},
+ {0x0939, "hadeva"},
+ {0x0ab9, "hagujarati"},
+ {0x0a39, "hagurmukhi"},
+ {0x062d, "haharabic"},
+ {0xfea2, "hahfinalarabic"},
+ {0xfea3, "hahinitialarabic"},
+ {0x306f, "hahiragana"},
+ {0xfea4, "hahmedialarabic"},
+ {0x332a, "haitusquare"},
+ {0x30cf, "hakatakana"},
+ {0xff8a, "hakatakanahalfwidth"},
+ {0x0a4d, "halantgurmukhi"},
+ {0x0621, "hamzaarabic"},
+ {0x0621, "hamzalowarabic"},
+ {0x3164, "hangulfiller"},
+ {0x044a, "hardsigncyrillic"},
+ {0x21bc, "harpoonleftbarbup"},
+ {0x21c0, "harpoonrightbarbup"},
+ {0x33ca, "hasquare"},
+ {0x05b2, "hatafpatah"},
+ {0x05b2, "hatafpatah16"},
+ {0x05b2, "hatafpatah23"},
+ {0x05b2, "hatafpatah2f"},
+ {0x05b2, "hatafpatahhebrew"},
+ {0x05b2, "hatafpatahnarrowhebrew"},
+ {0x05b2, "hatafpatahquarterhebrew"},
+ {0x05b2, "hatafpatahwidehebrew"},
+ {0x05b3, "hatafqamats"},
+ {0x05b3, "hatafqamats1b"},
+ {0x05b3, "hatafqamats28"},
+ {0x05b3, "hatafqamats34"},
+ {0x05b3, "hatafqamatshebrew"},
+ {0x05b3, "hatafqamatsnarrowhebrew"},
+ {0x05b3, "hatafqamatsquarterhebrew"},
+ {0x05b3, "hatafqamatswidehebrew"},
+ {0x05b1, "hatafsegol"},
+ {0x05b1, "hatafsegol17"},
+ {0x05b1, "hatafsegol24"},
+ {0x05b1, "hatafsegol30"},
+ {0x05b1, "hatafsegolhebrew"},
+ {0x05b1, "hatafsegolnarrowhebrew"},
+ {0x05b1, "hatafsegolquarterhebrew"},
+ {0x05b1, "hatafsegolwidehebrew"},
+ {0x0127, "hbar"},
+ {0x310f, "hbopomofo"},
+ {0x1e2b, "hbrevebelow"},
+ {0x1e29, "hcedilla"},
+ {0x24d7, "hcircle"},
+ {0x0125, "hcircumflex"},
+ {0x1e27, "hdieresis"},
+ {0x1e23, "hdotaccent"},
+ {0x1e25, "hdotbelow"},
+ {0x05d4, "he"},
+ {0x2665, "heart"},
+ {0x2665, "heartsuitblack"},
+ {0x2661, "heartsuitwhite"},
+ {0xfb34, "hedagesh"},
+ {0xfb34, "hedageshhebrew"},
+ {0x06c1, "hehaltonearabic"},
+ {0x0647, "heharabic"},
+ {0x05d4, "hehebrew"},
+ {0xfba7, "hehfinalaltonearabic"},
+ {0xfeea, "hehfinalalttwoarabic"},
+ {0xfeea, "hehfinalarabic"},
+ {0xfba5, "hehhamzaabovefinalarabic"},
+ {0xfba4, "hehhamzaaboveisolatedarabic"},
+ {0xfba8, "hehinitialaltonearabic"},
+ {0xfeeb, "hehinitialarabic"},
+ {0x3078, "hehiragana"},
+ {0xfba9, "hehmedialaltonearabic"},
+ {0xfeec, "hehmedialarabic"},
+ {0x337b, "heiseierasquare"},
+ {0x30d8, "hekatakana"},
+ {0xff8d, "hekatakanahalfwidth"},
+ {0x3336, "hekutaarusquare"},
+ {0x0267, "henghook"},
+ {0x3339, "herutusquare"},
+ {0x05d7, "het"},
+ {0x05d7, "hethebrew"},
+ {0x0266, "hhook"},
+ {0x02b1, "hhooksuperior"},
+ {0x327b, "hieuhacirclekorean"},
+ {0x321b, "hieuhaparenkorean"},
+ {0x326d, "hieuhcirclekorean"},
+ {0x314e, "hieuhkorean"},
+ {0x320d, "hieuhparenkorean"},
+ {0x3072, "hihiragana"},
+ {0x30d2, "hikatakana"},
+ {0xff8b, "hikatakanahalfwidth"},
+ {0x05b4, "hiriq"},
+ {0x05b4, "hiriq14"},
+ {0x05b4, "hiriq21"},
+ {0x05b4, "hiriq2d"},
+ {0x05b4, "hiriqhebrew"},
+ {0x05b4, "hiriqnarrowhebrew"},
+ {0x05b4, "hiriqquarterhebrew"},
+ {0x05b4, "hiriqwidehebrew"},
+ {0x1e96, "hlinebelow"},
+ {0xff48, "hmonospace"},
+ {0x0570, "hoarmenian"},
+ {0x0e2b, "hohipthai"},
+ {0x307b, "hohiragana"},
+ {0x30db, "hokatakana"},
+ {0xff8e, "hokatakanahalfwidth"},
+ {0x05b9, "holam"},
+ {0x05b9, "holam19"},
+ {0x05b9, "holam26"},
+ {0x05b9, "holam32"},
+ {0x05b9, "holamhebrew"},
+ {0x05b9, "holamnarrowhebrew"},
+ {0x05b9, "holamquarterhebrew"},
+ {0x05b9, "holamwidehebrew"},
+ {0x0e2e, "honokhukthai"},
+ {0x0309, "hookabovecomb"},
+ {0x0309, "hookcmb"},
+ {0x0321, "hookpalatalizedbelowcmb"},
+ {0x0322, "hookretroflexbelowcmb"},
+ {0x3342, "hoonsquare"},
+ {0x03e9, "horicoptic"},
+ {0x2015, "horizontalbar"},
+ {0x031b, "horncmb"},
+ {0x2668, "hotsprings"},
+ {0x2302, "house"},
+ {0x24a3, "hparen"},
+ {0x02b0, "hsuperior"},
+ {0x0265, "hturned"},
+ {0x3075, "huhiragana"},
+ {0x3333, "huiitosquare"},
+ {0x30d5, "hukatakana"},
+ {0xff8c, "hukatakanahalfwidth"},
+ {0x02dd, "hungarumlaut"},
+ {0x030b, "hungarumlautcmb"},
+ {0x0195, "hv"},
+ {0x002d, "hyphen"},
+ {0xf6e5, "hypheninferior"},
+ {0xff0d, "hyphenmonospace"},
+ {0xfe63, "hyphensmall"},
+ {0xf6e6, "hyphensuperior"},
+ {0x2010, "hyphentwo"},
+ {0x0069, "i"},
+ {0x00ed, "iacute"},
+ {0x044f, "iacyrillic"},
+ {0x0987, "ibengali"},
+ {0x3127, "ibopomofo"},
+ {0x012d, "ibreve"},
+ {0x01d0, "icaron"},
+ {0x24d8, "icircle"},
+ {0x00ee, "icircumflex"},
+ {0x0456, "icyrillic"},
+ {0x0209, "idblgrave"},
+ {0x328f, "ideographearthcircle"},
+ {0x328b, "ideographfirecircle"},
+ {0x323f, "ideographicallianceparen"},
+ {0x323a, "ideographiccallparen"},
+ {0x32a5, "ideographiccentrecircle"},
+ {0x3006, "ideographicclose"},
+ {0x3001, "ideographiccomma"},
+ {0xff64, "ideographiccommaleft"},
+ {0x3237, "ideographiccongratulationparen"},
+ {0x32a3, "ideographiccorrectcircle"},
+ {0x322f, "ideographicearthparen"},
+ {0x323d, "ideographicenterpriseparen"},
+ {0x329d, "ideographicexcellentcircle"},
+ {0x3240, "ideographicfestivalparen"},
+ {0x3296, "ideographicfinancialcircle"},
+ {0x3236, "ideographicfinancialparen"},
+ {0x322b, "ideographicfireparen"},
+ {0x3232, "ideographichaveparen"},
+ {0x32a4, "ideographichighcircle"},
+ {0x3005, "ideographiciterationmark"},
+ {0x3298, "ideographiclaborcircle"},
+ {0x3238, "ideographiclaborparen"},
+ {0x32a7, "ideographicleftcircle"},
+ {0x32a6, "ideographiclowcircle"},
+ {0x32a9, "ideographicmedicinecircle"},
+ {0x322e, "ideographicmetalparen"},
+ {0x322a, "ideographicmoonparen"},
+ {0x3234, "ideographicnameparen"},
+ {0x3002, "ideographicperiod"},
+ {0x329e, "ideographicprintcircle"},
+ {0x3243, "ideographicreachparen"},
+ {0x3239, "ideographicrepresentparen"},
+ {0x323e, "ideographicresourceparen"},
+ {0x32a8, "ideographicrightcircle"},
+ {0x3299, "ideographicsecretcircle"},
+ {0x3242, "ideographicselfparen"},
+ {0x3233, "ideographicsocietyparen"},
+ {0x3000, "ideographicspace"},
+ {0x3235, "ideographicspecialparen"},
+ {0x3231, "ideographicstockparen"},
+ {0x323b, "ideographicstudyparen"},
+ {0x3230, "ideographicsunparen"},
+ {0x323c, "ideographicsuperviseparen"},
+ {0x322c, "ideographicwaterparen"},
+ {0x322d, "ideographicwoodparen"},
+ {0x3007, "ideographiczero"},
+ {0x328e, "ideographmetalcircle"},
+ {0x328a, "ideographmooncircle"},
+ {0x3294, "ideographnamecircle"},
+ {0x3290, "ideographsuncircle"},
+ {0x328c, "ideographwatercircle"},
+ {0x328d, "ideographwoodcircle"},
+ {0x0907, "ideva"},
+ {0x00ef, "idieresis"},
+ {0x1e2f, "idieresisacute"},
+ {0x04e5, "idieresiscyrillic"},
+ {0x1ecb, "idotbelow"},
+ {0x04d7, "iebrevecyrillic"},
+ {0x0435, "iecyrillic"},
+ {0x3275, "ieungacirclekorean"},
+ {0x3215, "ieungaparenkorean"},
+ {0x3267, "ieungcirclekorean"},
+ {0x3147, "ieungkorean"},
+ {0x3207, "ieungparenkorean"},
+ {0x00ec, "igrave"},
+ {0x0a87, "igujarati"},
+ {0x0a07, "igurmukhi"},
+ {0x3044, "ihiragana"},
+ {0x1ec9, "ihookabove"},
+ {0x0988, "iibengali"},
+ {0x0438, "iicyrillic"},
+ {0x0908, "iideva"},
+ {0x0a88, "iigujarati"},
+ {0x0a08, "iigurmukhi"},
+ {0x0a40, "iimatragurmukhi"},
+ {0x020b, "iinvertedbreve"},
+ {0x0439, "iishortcyrillic"},
+ {0x09c0, "iivowelsignbengali"},
+ {0x0940, "iivowelsigndeva"},
+ {0x0ac0, "iivowelsigngujarati"},
+ {0x0133, "ij"},
+ {0x30a4, "ikatakana"},
+ {0xff72, "ikatakanahalfwidth"},
+ {0x3163, "ikorean"},
+ {0x02dc, "ilde"},
+ {0x05ac, "iluyhebrew"},
+ {0x012b, "imacron"},
+ {0x04e3, "imacroncyrillic"},
+ {0x2253, "imageorapproximatelyequal"},
+ {0x0a3f, "imatragurmukhi"},
+ {0xff49, "imonospace"},
+ {0x2206, "increment"},
+ {0x221e, "infinity"},
+ {0x056b, "iniarmenian"},
+ {0x222b, "integral"},
+ {0x2321, "integralbottom"},
+ {0x2321, "integralbt"},
+ {0xf8f5, "integralex"},
+ {0x2320, "integraltop"},
+ {0x2320, "integraltp"},
+ {0x2229, "intersection"},
+ {0x3305, "intisquare"},
+ {0x25d8, "invbullet"},
+ {0x25d9, "invcircle"},
+ {0x263b, "invsmileface"},
+ {0x0451, "iocyrillic"},
+ {0x012f, "iogonek"},
+ {0x03b9, "iota"},
+ {0x03ca, "iotadieresis"},
+ {0x0390, "iotadieresistonos"},
+ {0x0269, "iotalatin"},
+ {0x03af, "iotatonos"},
+ {0x24a4, "iparen"},
+ {0x0a72, "irigurmukhi"},
+ {0x3043, "ismallhiragana"},
+ {0x30a3, "ismallkatakana"},
+ {0xff68, "ismallkatakanahalfwidth"},
+ {0x09fa, "issharbengali"},
+ {0x0268, "istroke"},
+ {0xf6ed, "isuperior"},
+ {0x309d, "iterationhiragana"},
+ {0x30fd, "iterationkatakana"},
+ {0x0129, "itilde"},
+ {0x1e2d, "itildebelow"},
+ {0x3129, "iubopomofo"},
+ {0x044e, "iucyrillic"},
+ {0x09bf, "ivowelsignbengali"},
+ {0x093f, "ivowelsigndeva"},
+ {0x0abf, "ivowelsigngujarati"},
+ {0x0475, "izhitsacyrillic"},
+ {0x0477, "izhitsadblgravecyrillic"},
+ {0x006a, "j"},
+ {0x0571, "jaarmenian"},
+ {0x099c, "jabengali"},
+ {0x091c, "jadeva"},
+ {0x0a9c, "jagujarati"},
+ {0x0a1c, "jagurmukhi"},
+ {0x3110, "jbopomofo"},
+ {0x01f0, "jcaron"},
+ {0x24d9, "jcircle"},
+ {0x0135, "jcircumflex"},
+ {0x029d, "jcrossedtail"},
+ {0x025f, "jdotlessstroke"},
+ {0x0458, "jecyrillic"},
+ {0x062c, "jeemarabic"},
+ {0xfe9e, "jeemfinalarabic"},
+ {0xfe9f, "jeeminitialarabic"},
+ {0xfea0, "jeemmedialarabic"},
+ {0x0698, "jeharabic"},
+ {0xfb8b, "jehfinalarabic"},
+ {0x099d, "jhabengali"},
+ {0x091d, "jhadeva"},
+ {0x0a9d, "jhagujarati"},
+ {0x0a1d, "jhagurmukhi"},
+ {0x057b, "jheharmenian"},
+ {0x3004, "jis"},
+ {0xff4a, "jmonospace"},
+ {0x24a5, "jparen"},
+ {0x02b2, "jsuperior"},
+ {0x006b, "k"},
+ {0x04a1, "kabashkircyrillic"},
+ {0x0995, "kabengali"},
+ {0x1e31, "kacute"},
+ {0x043a, "kacyrillic"},
+ {0x049b, "kadescendercyrillic"},
+ {0x0915, "kadeva"},
+ {0x05db, "kaf"},
+ {0x0643, "kafarabic"},
+ {0xfb3b, "kafdagesh"},
+ {0xfb3b, "kafdageshhebrew"},
+ {0xfeda, "kaffinalarabic"},
+ {0x05db, "kafhebrew"},
+ {0xfedb, "kafinitialarabic"},
+ {0xfedc, "kafmedialarabic"},
+ {0xfb4d, "kafrafehebrew"},
+ {0x0a95, "kagujarati"},
+ {0x0a15, "kagurmukhi"},
+ {0x304b, "kahiragana"},
+ {0x04c4, "kahookcyrillic"},
+ {0x30ab, "kakatakana"},
+ {0xff76, "kakatakanahalfwidth"},
+ {0x03ba, "kappa"},
+ {0x03f0, "kappasymbolgreek"},
+ {0x3171, "kapyeounmieumkorean"},
+ {0x3184, "kapyeounphieuphkorean"},
+ {0x3178, "kapyeounpieupkorean"},
+ {0x3179, "kapyeounssangpieupkorean"},
+ {0x330d, "karoriisquare"},
+ {0x0640, "kashidaautoarabic"},
+ {0x0640, "kashidaautonosidebearingarabic"},
+ {0x30f5, "kasmallkatakana"},
+ {0x3384, "kasquare"},
+ {0x0650, "kasraarabic"},
+ {0x064d, "kasratanarabic"},
+ {0x049f, "kastrokecyrillic"},
+ {0xff70, "katahiraprolongmarkhalfwidth"},
+ {0x049d, "kaverticalstrokecyrillic"},
+ {0x310e, "kbopomofo"},
+ {0x3389, "kcalsquare"},
+ {0x01e9, "kcaron"},
+ {0x0137, "kcedilla"},
+ {0x24da, "kcircle"},
+ {0x0137, "kcommaaccent"},
+ {0x1e33, "kdotbelow"},
+ {0x0584, "keharmenian"},
+ {0x3051, "kehiragana"},
+ {0x30b1, "kekatakana"},
+ {0xff79, "kekatakanahalfwidth"},
+ {0x056f, "kenarmenian"},
+ {0x30f6, "kesmallkatakana"},
+ {0x0138, "kgreenlandic"},
+ {0x0996, "khabengali"},
+ {0x0445, "khacyrillic"},
+ {0x0916, "khadeva"},
+ {0x0a96, "khagujarati"},
+ {0x0a16, "khagurmukhi"},
+ {0x062e, "khaharabic"},
+ {0xfea6, "khahfinalarabic"},
+ {0xfea7, "khahinitialarabic"},
+ {0xfea8, "khahmedialarabic"},
+ {0x03e7, "kheicoptic"},
+ {0x0959, "khhadeva"},
+ {0x0a59, "khhagurmukhi"},
+ {0x3278, "khieukhacirclekorean"},
+ {0x3218, "khieukhaparenkorean"},
+ {0x326a, "khieukhcirclekorean"},
+ {0x314b, "khieukhkorean"},
+ {0x320a, "khieukhparenkorean"},
+ {0x0e02, "khokhaithai"},
+ {0x0e05, "khokhonthai"},
+ {0x0e03, "khokhuatthai"},
+ {0x0e04, "khokhwaithai"},
+ {0x0e5b, "khomutthai"},
+ {0x0199, "khook"},
+ {0x0e06, "khorakhangthai"},
+ {0x3391, "khzsquare"},
+ {0x304d, "kihiragana"},
+ {0x30ad, "kikatakana"},
+ {0xff77, "kikatakanahalfwidth"},
+ {0x3315, "kiroguramusquare"},
+ {0x3316, "kiromeetorusquare"},
+ {0x3314, "kirosquare"},
+ {0x326e, "kiyeokacirclekorean"},
+ {0x320e, "kiyeokaparenkorean"},
+ {0x3260, "kiyeokcirclekorean"},
+ {0x3131, "kiyeokkorean"},
+ {0x3200, "kiyeokparenkorean"},
+ {0x3133, "kiyeoksioskorean"},
+ {0x045c, "kjecyrillic"},
+ {0x1e35, "klinebelow"},
+ {0x3398, "klsquare"},
+ {0x33a6, "kmcubedsquare"},
+ {0xff4b, "kmonospace"},
+ {0x33a2, "kmsquaredsquare"},
+ {0x3053, "kohiragana"},
+ {0x33c0, "kohmsquare"},
+ {0x0e01, "kokaithai"},
+ {0x30b3, "kokatakana"},
+ {0xff7a, "kokatakanahalfwidth"},
+ {0x331e, "kooposquare"},
+ {0x0481, "koppacyrillic"},
+ {0x327f, "koreanstandardsymbol"},
+ {0x0343, "koroniscmb"},
+ {0x24a6, "kparen"},
+ {0x33aa, "kpasquare"},
+ {0x046f, "ksicyrillic"},
+ {0x33cf, "ktsquare"},
+ {0x029e, "kturned"},
+ {0x304f, "kuhiragana"},
+ {0x30af, "kukatakana"},
+ {0xff78, "kukatakanahalfwidth"},
+ {0x33b8, "kvsquare"},
+ {0x33be, "kwsquare"},
+ {0x006c, "l"},
+ {0x09b2, "labengali"},
+ {0x013a, "lacute"},
+ {0x0932, "ladeva"},
+ {0x0ab2, "lagujarati"},
+ {0x0a32, "lagurmukhi"},
+ {0x0e45, "lakkhangyaothai"},
+ {0xfefc, "lamaleffinalarabic"},
+ {0xfef8, "lamalefhamzaabovefinalarabic"},
+ {0xfef7, "lamalefhamzaaboveisolatedarabic"},
+ {0xfefa, "lamalefhamzabelowfinalarabic"},
+ {0xfef9, "lamalefhamzabelowisolatedarabic"},
+ {0xfefb, "lamalefisolatedarabic"},
+ {0xfef6, "lamalefmaddaabovefinalarabic"},
+ {0xfef5, "lamalefmaddaaboveisolatedarabic"},
+ {0x0644, "lamarabic"},
+ {0x03bb, "lambda"},
+ {0x019b, "lambdastroke"},
+ {0x05dc, "lamed"},
+ {0xfb3c, "lameddagesh"},
+ {0xfb3c, "lameddageshhebrew"},
+ {0x05dc, "lamedhebrew"},
+ {0xfede, "lamfinalarabic"},
+ {0xfcca, "lamhahinitialarabic"},
+ {0xfedf, "laminitialarabic"},
+ {0xfcc9, "lamjeeminitialarabic"},
+ {0xfccb, "lamkhahinitialarabic"},
+ {0xfdf2, "lamlamhehisolatedarabic"},
+ {0xfee0, "lammedialarabic"},
+ {0xfd88, "lammeemhahinitialarabic"},
+ {0xfccc, "lammeeminitialarabic"},
+ {0x25ef, "largecircle"},
+ {0x019a, "lbar"},
+ {0x026c, "lbelt"},
+ {0x310c, "lbopomofo"},
+ {0x013e, "lcaron"},
+ {0x013c, "lcedilla"},
+ {0x24db, "lcircle"},
+ {0x1e3d, "lcircumflexbelow"},
+ {0x013c, "lcommaaccent"},
+ {0x0140, "ldot"},
+ {0x0140, "ldotaccent"},
+ {0x1e37, "ldotbelow"},
+ {0x1e39, "ldotbelowmacron"},
+ {0x031a, "leftangleabovecmb"},
+ {0x0318, "lefttackbelowcmb"},
+ {0x003c, "less"},
+ {0x2264, "lessequal"},
+ {0x22da, "lessequalorgreater"},
+ {0xff1c, "lessmonospace"},
+ {0x2272, "lessorequivalent"},
+ {0x2276, "lessorgreater"},
+ {0x2266, "lessoverequal"},
+ {0xfe64, "lesssmall"},
+ {0x026e, "lezh"},
+ {0x258c, "lfblock"},
+ {0x026d, "lhookretroflex"},
+ {0x20a4, "lira"},
+ {0x056c, "liwnarmenian"},
+ {0x01c9, "lj"},
+ {0x0459, "ljecyrillic"},
+ {0xf6c0, "ll"},
+ {0x0933, "lladeva"},
+ {0x0ab3, "llagujarati"},
+ {0x1e3b, "llinebelow"},
+ {0x0934, "llladeva"},
+ {0x09e1, "llvocalicbengali"},
+ {0x0961, "llvocalicdeva"},
+ {0x09e3, "llvocalicvowelsignbengali"},
+ {0x0963, "llvocalicvowelsigndeva"},
+ {0x026b, "lmiddletilde"},
+ {0xff4c, "lmonospace"},
+ {0x33d0, "lmsquare"},
+ {0x0e2c, "lochulathai"},
+ {0x2227, "logicaland"},
+ {0x00ac, "logicalnot"},
+ {0x2310, "logicalnotreversed"},
+ {0x2228, "logicalor"},
+ {0x0e25, "lolingthai"},
+ {0x017f, "longs"},
+ {0xfe4e, "lowlinecenterline"},
+ {0x0332, "lowlinecmb"},
+ {0xfe4d, "lowlinedashed"},
+ {0x25ca, "lozenge"},
+ {0x24a7, "lparen"},
+ {0x0142, "lslash"},
+ {0x2113, "lsquare"},
+ {0xf6ee, "lsuperior"},
+ {0x2591, "ltshade"},
+ {0x0e26, "luthai"},
+ {0x098c, "lvocalicbengali"},
+ {0x090c, "lvocalicdeva"},
+ {0x09e2, "lvocalicvowelsignbengali"},
+ {0x0962, "lvocalicvowelsigndeva"},
+ {0x33d3, "lxsquare"},
+ {0x006d, "m"},
+ {0x09ae, "mabengali"},
+ {0x00af, "macron"},
+ {0x0331, "macronbelowcmb"},
+ {0x0304, "macroncmb"},
+ {0x02cd, "macronlowmod"},
+ {0xffe3, "macronmonospace"},
+ {0x1e3f, "macute"},
+ {0x092e, "madeva"},
+ {0x0aae, "magujarati"},
+ {0x0a2e, "magurmukhi"},
+ {0x05a4, "mahapakhhebrew"},
+ {0x05a4, "mahapakhlefthebrew"},
+ {0x307e, "mahiragana"},
+ {0xf895, "maichattawalowleftthai"},
+ {0xf894, "maichattawalowrightthai"},
+ {0x0e4b, "maichattawathai"},
+ {0xf893, "maichattawaupperleftthai"},
+ {0xf88c, "maieklowleftthai"},
+ {0xf88b, "maieklowrightthai"},
+ {0x0e48, "maiekthai"},
+ {0xf88a, "maiekupperleftthai"},
+ {0xf884, "maihanakatleftthai"},
+ {0x0e31, "maihanakatthai"},
+ {0xf889, "maitaikhuleftthai"},
+ {0x0e47, "maitaikhuthai"},
+ {0xf88f, "maitholowleftthai"},
+ {0xf88e, "maitholowrightthai"},
+ {0x0e49, "maithothai"},
+ {0xf88d, "maithoupperleftthai"},
+ {0xf892, "maitrilowleftthai"},
+ {0xf891, "maitrilowrightthai"},
+ {0x0e4a, "maitrithai"},
+ {0xf890, "maitriupperleftthai"},
+ {0x0e46, "maiyamokthai"},
+ {0x30de, "makatakana"},
+ {0xff8f, "makatakanahalfwidth"},
+ {0x2642, "male"},
+ {0x3347, "mansyonsquare"},
+ {0x05be, "maqafhebrew"},
+ {0x2642, "mars"},
+ {0x05af, "masoracirclehebrew"},
+ {0x3383, "masquare"},
+ {0x3107, "mbopomofo"},
+ {0x33d4, "mbsquare"},
+ {0x24dc, "mcircle"},
+ {0x33a5, "mcubedsquare"},
+ {0x1e41, "mdotaccent"},
+ {0x1e43, "mdotbelow"},
+ {0x0645, "meemarabic"},
+ {0xfee2, "meemfinalarabic"},
+ {0xfee3, "meeminitialarabic"},
+ {0xfee4, "meemmedialarabic"},
+ {0xfcd1, "meemmeeminitialarabic"},
+ {0xfc48, "meemmeemisolatedarabic"},
+ {0x334d, "meetorusquare"},
+ {0x3081, "mehiragana"},
+ {0x337e, "meizierasquare"},
+ {0x30e1, "mekatakana"},
+ {0xff92, "mekatakanahalfwidth"},
+ {0x05de, "mem"},
+ {0xfb3e, "memdagesh"},
+ {0xfb3e, "memdageshhebrew"},
+ {0x05de, "memhebrew"},
+ {0x0574, "menarmenian"},
+ {0x05a5, "merkhahebrew"},
+ {0x05a6, "merkhakefulahebrew"},
+ {0x05a6, "merkhakefulalefthebrew"},
+ {0x05a5, "merkhalefthebrew"},
+ {0x0271, "mhook"},
+ {0x3392, "mhzsquare"},
+ {0xff65, "middledotkatakanahalfwidth"},
+ {0x00b7, "middot"},
+ {0x3272, "mieumacirclekorean"},
+ {0x3212, "mieumaparenkorean"},
+ {0x3264, "mieumcirclekorean"},
+ {0x3141, "mieumkorean"},
+ {0x3170, "mieumpansioskorean"},
+ {0x3204, "mieumparenkorean"},
+ {0x316e, "mieumpieupkorean"},
+ {0x316f, "mieumsioskorean"},
+ {0x307f, "mihiragana"},
+ {0x30df, "mikatakana"},
+ {0xff90, "mikatakanahalfwidth"},
+ {0x2212, "minus"},
+ {0x0320, "minusbelowcmb"},
+ {0x2296, "minuscircle"},
+ {0x02d7, "minusmod"},
+ {0x2213, "minusplus"},
+ {0x2032, "minute"},
+ {0x334a, "miribaarusquare"},
+ {0x3349, "mirisquare"},
+ {0x0270, "mlonglegturned"},
+ {0x3396, "mlsquare"},
+ {0x33a3, "mmcubedsquare"},
+ {0xff4d, "mmonospace"},
+ {0x339f, "mmsquaredsquare"},
+ {0x3082, "mohiragana"},
+ {0x33c1, "mohmsquare"},
+ {0x30e2, "mokatakana"},
+ {0xff93, "mokatakanahalfwidth"},
+ {0x33d6, "molsquare"},
+ {0x0e21, "momathai"},
+ {0x33a7, "moverssquare"},
+ {0x33a8, "moverssquaredsquare"},
+ {0x24a8, "mparen"},
+ {0x33ab, "mpasquare"},
+ {0x33b3, "mssquare"},
+ {0xf6ef, "msuperior"},
+ {0x026f, "mturned"},
+ {0x00b5, "mu"},
+ {0x00b5, "mu1"},
+ {0x3382, "muasquare"},
+ {0x226b, "muchgreater"},
+ {0x226a, "muchless"},
+ {0x338c, "mufsquare"},
+ {0x03bc, "mugreek"},
+ {0x338d, "mugsquare"},
+ {0x3080, "muhiragana"},
+ {0x30e0, "mukatakana"},
+ {0xff91, "mukatakanahalfwidth"},
+ {0x3395, "mulsquare"},
+ {0x00d7, "multiply"},
+ {0x339b, "mumsquare"},
+ {0x05a3, "munahhebrew"},
+ {0x05a3, "munahlefthebrew"},
+ {0x266a, "musicalnote"},
+ {0x266b, "musicalnotedbl"},
+ {0x266d, "musicflatsign"},
+ {0x266f, "musicsharpsign"},
+ {0x33b2, "mussquare"},
+ {0x33b6, "muvsquare"},
+ {0x33bc, "muwsquare"},
+ {0x33b9, "mvmegasquare"},
+ {0x33b7, "mvsquare"},
+ {0x33bf, "mwmegasquare"},
+ {0x33bd, "mwsquare"},
+ {0x006e, "n"},
+ {0x09a8, "nabengali"},
+ {0x2207, "nabla"},
+ {0x0144, "nacute"},
+ {0x0928, "nadeva"},
+ {0x0aa8, "nagujarati"},
+ {0x0a28, "nagurmukhi"},
+ {0x306a, "nahiragana"},
+ {0x30ca, "nakatakana"},
+ {0xff85, "nakatakanahalfwidth"},
+ {0x0149, "napostrophe"},
+ {0x3381, "nasquare"},
+ {0x310b, "nbopomofo"},
+ {0x00a0, "nbspace"},
+ {0x0148, "ncaron"},
+ {0x0146, "ncedilla"},
+ {0x24dd, "ncircle"},
+ {0x1e4b, "ncircumflexbelow"},
+ {0x0146, "ncommaaccent"},
+ {0x1e45, "ndotaccent"},
+ {0x1e47, "ndotbelow"},
+ {0x306d, "nehiragana"},
+ {0x30cd, "nekatakana"},
+ {0xff88, "nekatakanahalfwidth"},
+ {0x20aa, "newsheqelsign"},
+ {0x338b, "nfsquare"},
+ {0x0999, "ngabengali"},
+ {0x0919, "ngadeva"},
+ {0x0a99, "ngagujarati"},
+ {0x0a19, "ngagurmukhi"},
+ {0x0e07, "ngonguthai"},
+ {0x3093, "nhiragana"},
+ {0x0272, "nhookleft"},
+ {0x0273, "nhookretroflex"},
+ {0x326f, "nieunacirclekorean"},
+ {0x320f, "nieunaparenkorean"},
+ {0x3135, "nieuncieuckorean"},
+ {0x3261, "nieuncirclekorean"},
+ {0x3136, "nieunhieuhkorean"},
+ {0x3134, "nieunkorean"},
+ {0x3168, "nieunpansioskorean"},
+ {0x3201, "nieunparenkorean"},
+ {0x3167, "nieunsioskorean"},
+ {0x3166, "nieuntikeutkorean"},
+ {0x306b, "nihiragana"},
+ {0x30cb, "nikatakana"},
+ {0xff86, "nikatakanahalfwidth"},
+ {0xf899, "nikhahitleftthai"},
+ {0x0e4d, "nikhahitthai"},
+ {0x0039, "nine"},
+ {0x0669, "ninearabic"},
+ {0x09ef, "ninebengali"},
+ {0x2468, "ninecircle"},
+ {0x2792, "ninecircleinversesansserif"},
+ {0x096f, "ninedeva"},
+ {0x0aef, "ninegujarati"},
+ {0x0a6f, "ninegurmukhi"},
+ {0x0669, "ninehackarabic"},
+ {0x3029, "ninehangzhou"},
+ {0x3228, "nineideographicparen"},
+ {0x2089, "nineinferior"},
+ {0xff19, "ninemonospace"},
+ {0xf739, "nineoldstyle"},
+ {0x247c, "nineparen"},
+ {0x2490, "nineperiod"},
+ {0x06f9, "ninepersian"},
+ {0x2178, "nineroman"},
+ {0x2079, "ninesuperior"},
+ {0x2472, "nineteencircle"},
+ {0x2486, "nineteenparen"},
+ {0x249a, "nineteenperiod"},
+ {0x0e59, "ninethai"},
+ {0x01cc, "nj"},
+ {0x045a, "njecyrillic"},
+ {0x30f3, "nkatakana"},
+ {0xff9d, "nkatakanahalfwidth"},
+ {0x019e, "nlegrightlong"},
+ {0x1e49, "nlinebelow"},
+ {0xff4e, "nmonospace"},
+ {0x339a, "nmsquare"},
+ {0x09a3, "nnabengali"},
+ {0x0923, "nnadeva"},
+ {0x0aa3, "nnagujarati"},
+ {0x0a23, "nnagurmukhi"},
+ {0x0929, "nnnadeva"},
+ {0x306e, "nohiragana"},
+ {0x30ce, "nokatakana"},
+ {0xff89, "nokatakanahalfwidth"},
+ {0x00a0, "nonbreakingspace"},
+ {0x00a0, "nobreakspace"},
+ {0x0e13, "nonenthai"},
+ {0x0e19, "nonuthai"},
+ {0x0646, "noonarabic"},
+ {0xfee6, "noonfinalarabic"},
+ {0x06ba, "noonghunnaarabic"},
+ {0xfb9f, "noonghunnafinalarabic"},
+ {0xfee7, "nooninitialarabic"},
+ {0xfcd2, "noonjeeminitialarabic"},
+ {0xfc4b, "noonjeemisolatedarabic"},
+ {0xfee8, "noonmedialarabic"},
+ {0xfcd5, "noonmeeminitialarabic"},
+ {0xfc4e, "noonmeemisolatedarabic"},
+ {0xfc8d, "noonnoonfinalarabic"},
+ {0x220c, "notcontains"},
+ {0x2209, "notelement"},
+ {0x2209, "notelementof"},
+ {0x2260, "notequal"},
+ {0x226f, "notgreater"},
+ {0x2271, "notgreaternorequal"},
+ {0x2279, "notgreaternorless"},
+ {0x2262, "notidentical"},
+ {0x226e, "notless"},
+ {0x2270, "notlessnorequal"},
+ {0x2226, "notparallel"},
+ {0x2280, "notprecedes"},
+ {0x2284, "notsubset"},
+ {0x2281, "notsucceeds"},
+ {0x2285, "notsuperset"},
+ {0x0576, "nowarmenian"},
+ {0x24a9, "nparen"},
+ {0x33b1, "nssquare"},
+ {0x207f, "nsuperior"},
+ {0x00f1, "ntilde"},
+ {0x03bd, "nu"},
+ {0x306c, "nuhiragana"},
+ {0x30cc, "nukatakana"},
+ {0xff87, "nukatakanahalfwidth"},
+ {0x09bc, "nuktabengali"},
+ {0x093c, "nuktadeva"},
+ {0x0abc, "nuktagujarati"},
+ {0x0a3c, "nuktagurmukhi"},
+ {0x0023, "numbersign"},
+ {0xff03, "numbersignmonospace"},
+ {0xfe5f, "numbersignsmall"},
+ {0x0374, "numeralsigngreek"},
+ {0x0375, "numeralsignlowergreek"},
+ {0x2116, "numero"},
+ {0x05e0, "nun"},
+ {0xfb40, "nundagesh"},
+ {0xfb40, "nundageshhebrew"},
+ {0x05e0, "nunhebrew"},
+ {0x33b5, "nvsquare"},
+ {0x33bb, "nwsquare"},
+ {0x099e, "nyabengali"},
+ {0x091e, "nyadeva"},
+ {0x0a9e, "nyagujarati"},
+ {0x0a1e, "nyagurmukhi"},
+ {0x006f, "o"},
+ {0x00f3, "oacute"},
+ {0x0e2d, "oangthai"},
+ {0x0275, "obarred"},
+ {0x04e9, "obarredcyrillic"},
+ {0x04eb, "obarreddieresiscyrillic"},
+ {0x0993, "obengali"},
+ {0x311b, "obopomofo"},
+ {0x014f, "obreve"},
+ {0x0911, "ocandradeva"},
+ {0x0a91, "ocandragujarati"},
+ {0x0949, "ocandravowelsigndeva"},
+ {0x0ac9, "ocandravowelsigngujarati"},
+ {0x01d2, "ocaron"},
+ {0x24de, "ocircle"},
+ {0x00f4, "ocircumflex"},
+ {0x1ed1, "ocircumflexacute"},
+ {0x1ed9, "ocircumflexdotbelow"},
+ {0x1ed3, "ocircumflexgrave"},
+ {0x1ed5, "ocircumflexhookabove"},
+ {0x1ed7, "ocircumflextilde"},
+ {0x043e, "ocyrillic"},
+ {0x0151, "odblacute"},
+ {0x020d, "odblgrave"},
+ {0x0913, "odeva"},
+ {0x00f6, "odieresis"},
+ {0x04e7, "odieresiscyrillic"},
+ {0x1ecd, "odotbelow"},
+ {0x0153, "oe"},
+ {0x315a, "oekorean"},
+ {0x02db, "ogonek"},
+ {0x0328, "ogonekcmb"},
+ {0x00f2, "ograve"},
+ {0x0a93, "ogujarati"},
+ {0x0585, "oharmenian"},
+ {0x304a, "ohiragana"},
+ {0x1ecf, "ohookabove"},
+ {0x01a1, "ohorn"},
+ {0x1edb, "ohornacute"},
+ {0x1ee3, "ohorndotbelow"},
+ {0x1edd, "ohorngrave"},
+ {0x1edf, "ohornhookabove"},
+ {0x1ee1, "ohorntilde"},
+ {0x0151, "ohungarumlaut"},
+ {0x01a3, "oi"},
+ {0x020f, "oinvertedbreve"},
+ {0x30aa, "okatakana"},
+ {0xff75, "okatakanahalfwidth"},
+ {0x3157, "okorean"},
+ {0x05ab, "olehebrew"},
+ {0x014d, "omacron"},
+ {0x1e53, "omacronacute"},
+ {0x1e51, "omacrongrave"},
+ {0x0950, "omdeva"},
+ {0x03c9, "omega"},
+ {0x03d6, "omega1"},
+ {0x0461, "omegacyrillic"},
+ {0x0277, "omegalatinclosed"},
+ {0x047b, "omegaroundcyrillic"},
+ {0x047d, "omegatitlocyrillic"},
+ {0x03ce, "omegatonos"},
+ {0x0ad0, "omgujarati"},
+ {0x03bf, "omicron"},
+ {0x03cc, "omicrontonos"},
+ {0xff4f, "omonospace"},
+ {0x0031, "one"},
+ {0x0661, "onearabic"},
+ {0x09e7, "onebengali"},
+ {0x2460, "onecircle"},
+ {0x278a, "onecircleinversesansserif"},
+ {0x0967, "onedeva"},
+ {0x2024, "onedotenleader"},
+ {0x215b, "oneeighth"},
+ {0xf6dc, "onefitted"},
+ {0x0ae7, "onegujarati"},
+ {0x0a67, "onegurmukhi"},
+ {0x0661, "onehackarabic"},
+ {0x00bd, "onehalf"},
+ {0x3021, "onehangzhou"},
+ {0x3220, "oneideographicparen"},
+ {0x2081, "oneinferior"},
+ {0xff11, "onemonospace"},
+ {0x09f4, "onenumeratorbengali"},
+ {0xf731, "oneoldstyle"},
+ {0x2474, "oneparen"},
+ {0x2488, "oneperiod"},
+ {0x06f1, "onepersian"},
+ {0x00bc, "onequarter"},
+ {0x2170, "oneroman"},
+ {0x00b9, "onesuperior"},
+ {0x0e51, "onethai"},
+ {0x2153, "onethird"},
+ {0x01eb, "oogonek"},
+ {0x01ed, "oogonekmacron"},
+ {0x0a13, "oogurmukhi"},
+ {0x0a4b, "oomatragurmukhi"},
+ {0x0254, "oopen"},
+ {0x24aa, "oparen"},
+ {0x25e6, "openbullet"},
+ {0x2325, "option"},
+ {0x00aa, "ordfeminine"},
+ {0x00ba, "ordmasculine"},
+ {0x221f, "orthogonal"},
+ {0x0912, "oshortdeva"},
+ {0x094a, "oshortvowelsigndeva"},
+ {0x00f8, "oslash"},
+ {0x01ff, "oslashacute"},
+ {0x3049, "osmallhiragana"},
+ {0x30a9, "osmallkatakana"},
+ {0xff6b, "osmallkatakanahalfwidth"},
+ {0x01ff, "ostrokeacute"},
+ {0xf6f0, "osuperior"},
+ {0x047f, "otcyrillic"},
+ {0x00f5, "otilde"},
+ {0x1e4d, "otildeacute"},
+ {0x1e4f, "otildedieresis"},
+ {0x3121, "oubopomofo"},
+ {0x203e, "overline"},
+ {0xfe4a, "overlinecenterline"},
+ {0x0305, "overlinecmb"},
+ {0xfe49, "overlinedashed"},
+ {0xfe4c, "overlinedblwavy"},
+ {0xfe4b, "overlinewavy"},
+ {0x00af, "overscore"},
+ {0x09cb, "ovowelsignbengali"},
+ {0x094b, "ovowelsigndeva"},
+ {0x0acb, "ovowelsigngujarati"},
+ {0x0070, "p"},
+ {0x3380, "paampssquare"},
+ {0x332b, "paasentosquare"},
+ {0x09aa, "pabengali"},
+ {0x1e55, "pacute"},
+ {0x092a, "padeva"},
+ {0x21df, "pagedown"},
+ {0x21de, "pageup"},
+ {0x0aaa, "pagujarati"},
+ {0x0a2a, "pagurmukhi"},
+ {0x3071, "pahiragana"},
+ {0x0e2f, "paiyannoithai"},
+ {0x30d1, "pakatakana"},
+ {0x0484, "palatalizationcyrilliccmb"},
+ {0x04c0, "palochkacyrillic"},
+ {0x317f, "pansioskorean"},
+ {0x00b6, "paragraph"},
+ {0x2225, "parallel"},
+ {0x0028, "parenleft"},
+ {0xfd3e, "parenleftaltonearabic"},
+ {0xf8ed, "parenleftbt"},
+ {0xf8ec, "parenleftex"},
+ {0x208d, "parenleftinferior"},
+ {0xff08, "parenleftmonospace"},
+ {0xfe59, "parenleftsmall"},
+ {0x207d, "parenleftsuperior"},
+ {0xf8eb, "parenlefttp"},
+ {0xfe35, "parenleftvertical"},
+ {0x0029, "parenright"},
+ {0xfd3f, "parenrightaltonearabic"},
+ {0xf8f8, "parenrightbt"},
+ {0xf8f7, "parenrightex"},
+ {0x208e, "parenrightinferior"},
+ {0xff09, "parenrightmonospace"},
+ {0xfe5a, "parenrightsmall"},
+ {0x207e, "parenrightsuperior"},
+ {0xf8f6, "parenrighttp"},
+ {0xfe36, "parenrightvertical"},
+ {0x2202, "partialdiff"},
+ {0x05c0, "paseqhebrew"},
+ {0x0599, "pashtahebrew"},
+ {0x33a9, "pasquare"},
+ {0x05b7, "patah"},
+ {0x05b7, "patah11"},
+ {0x05b7, "patah1d"},
+ {0x05b7, "patah2a"},
+ {0x05b7, "patahhebrew"},
+ {0x05b7, "patahnarrowhebrew"},
+ {0x05b7, "patahquarterhebrew"},
+ {0x05b7, "patahwidehebrew"},
+ {0x05a1, "pazerhebrew"},
+ {0x3106, "pbopomofo"},
+ {0x24df, "pcircle"},
+ {0x1e57, "pdotaccent"},
+ {0x05e4, "pe"},
+ {0x043f, "pecyrillic"},
+ {0xfb44, "pedagesh"},
+ {0xfb44, "pedageshhebrew"},
+ {0x333b, "peezisquare"},
+ {0xfb43, "pefinaldageshhebrew"},
+ {0x067e, "peharabic"},
+ {0x057a, "peharmenian"},
+ {0x05e4, "pehebrew"},
+ {0xfb57, "pehfinalarabic"},
+ {0xfb58, "pehinitialarabic"},
+ {0x307a, "pehiragana"},
+ {0xfb59, "pehmedialarabic"},
+ {0x30da, "pekatakana"},
+ {0x04a7, "pemiddlehookcyrillic"},
+ {0xfb4e, "perafehebrew"},
+ {0x0025, "percent"},
+ {0x066a, "percentarabic"},
+ {0xff05, "percentmonospace"},
+ {0xfe6a, "percentsmall"},
+ {0x002e, "period"},
+ {0x0589, "periodarmenian"},
+ {0x00b7, "periodcentered"},
+ {0xff61, "periodhalfwidth"},
+ {0xf6e7, "periodinferior"},
+ {0xff0e, "periodmonospace"},
+ {0xfe52, "periodsmall"},
+ {0xf6e8, "periodsuperior"},
+ {0x0342, "perispomenigreekcmb"},
+ {0x22a5, "perpendicular"},
+ {0x2030, "perthousand"},
+ {0x20a7, "peseta"},
+ {0x338a, "pfsquare"},
+ {0x09ab, "phabengali"},
+ {0x092b, "phadeva"},
+ {0x0aab, "phagujarati"},
+ {0x0a2b, "phagurmukhi"},
+ {0x03c6, "phi"},
+ {0x03d5, "phi1"},
+ {0x327a, "phieuphacirclekorean"},
+ {0x321a, "phieuphaparenkorean"},
+ {0x326c, "phieuphcirclekorean"},
+ {0x314d, "phieuphkorean"},
+ {0x320c, "phieuphparenkorean"},
+ {0x0278, "philatin"},
+ {0x0e3a, "phinthuthai"},
+ {0x03d5, "phisymbolgreek"},
+ {0x01a5, "phook"},
+ {0x0e1e, "phophanthai"},
+ {0x0e1c, "phophungthai"},
+ {0x0e20, "phosamphaothai"},
+ {0x03c0, "pi"},
+ {0x3273, "pieupacirclekorean"},
+ {0x3213, "pieupaparenkorean"},
+ {0x3176, "pieupcieuckorean"},
+ {0x3265, "pieupcirclekorean"},
+ {0x3172, "pieupkiyeokkorean"},
+ {0x3142, "pieupkorean"},
+ {0x3205, "pieupparenkorean"},
+ {0x3174, "pieupsioskiyeokkorean"},
+ {0x3144, "pieupsioskorean"},
+ {0x3175, "pieupsiostikeutkorean"},
+ {0x3177, "pieupthieuthkorean"},
+ {0x3173, "pieuptikeutkorean"},
+ {0x3074, "pihiragana"},
+ {0x30d4, "pikatakana"},
+ {0x03d6, "pisymbolgreek"},
+ {0x0583, "piwrarmenian"},
+ {0x002b, "plus"},
+ {0x031f, "plusbelowcmb"},
+ {0x2295, "pluscircle"},
+ {0x00b1, "plusminus"},
+ {0x02d6, "plusmod"},
+ {0xff0b, "plusmonospace"},
+ {0xfe62, "plussmall"},
+ {0x207a, "plussuperior"},
+ {0xff50, "pmonospace"},
+ {0x33d8, "pmsquare"},
+ {0x307d, "pohiragana"},
+ {0x261f, "pointingindexdownwhite"},
+ {0x261c, "pointingindexleftwhite"},
+ {0x261e, "pointingindexrightwhite"},
+ {0x261d, "pointingindexupwhite"},
+ {0x30dd, "pokatakana"},
+ {0x0e1b, "poplathai"},
+ {0x3012, "postalmark"},
+ {0x3020, "postalmarkface"},
+ {0x24ab, "pparen"},
+ {0x227a, "precedes"},
+ {0x211e, "prescription"},
+ {0x02b9, "primemod"},
+ {0x2035, "primereversed"},
+ {0x220f, "product"},
+ {0x2305, "projective"},
+ {0x30fc, "prolongedkana"},
+ {0x2318, "propellor"},
+ {0x2282, "propersubset"},
+ {0x2283, "propersuperset"},
+ {0x2237, "proportion"},
+ {0x221d, "proportional"},
+ {0x03c8, "psi"},
+ {0x0471, "psicyrillic"},
+ {0x0486, "psilipneumatacyrilliccmb"},
+ {0x33b0, "pssquare"},
+ {0x3077, "puhiragana"},
+ {0x30d7, "pukatakana"},
+ {0x33b4, "pvsquare"},
+ {0x33ba, "pwsquare"},
+ {0x0071, "q"},
+ {0x0958, "qadeva"},
+ {0x05a8, "qadmahebrew"},
+ {0x0642, "qafarabic"},
+ {0xfed6, "qaffinalarabic"},
+ {0xfed7, "qafinitialarabic"},
+ {0xfed8, "qafmedialarabic"},
+ {0x05b8, "qamats"},
+ {0x05b8, "qamats10"},
+ {0x05b8, "qamats1a"},
+ {0x05b8, "qamats1c"},
+ {0x05b8, "qamats27"},
+ {0x05b8, "qamats29"},
+ {0x05b8, "qamats33"},
+ {0x05b8, "qamatsde"},
+ {0x05b8, "qamatshebrew"},
+ {0x05b8, "qamatsnarrowhebrew"},
+ {0x05b8, "qamatsqatanhebrew"},
+ {0x05b8, "qamatsqatannarrowhebrew"},
+ {0x05b8, "qamatsqatanquarterhebrew"},
+ {0x05b8, "qamatsqatanwidehebrew"},
+ {0x05b8, "qamatsquarterhebrew"},
+ {0x05b8, "qamatswidehebrew"},
+ {0x059f, "qarneyparahebrew"},
+ {0x3111, "qbopomofo"},
+ {0x24e0, "qcircle"},
+ {0x02a0, "qhook"},
+ {0xff51, "qmonospace"},
+ {0x05e7, "qof"},
+ {0xfb47, "qofdagesh"},
+ {0xfb47, "qofdageshhebrew"},
+ {0x05e7, "qofhebrew"},
+ {0x24ac, "qparen"},
+ {0x2669, "quarternote"},
+ {0x05bb, "qubuts"},
+ {0x05bb, "qubuts18"},
+ {0x05bb, "qubuts25"},
+ {0x05bb, "qubuts31"},
+ {0x05bb, "qubutshebrew"},
+ {0x05bb, "qubutsnarrowhebrew"},
+ {0x05bb, "qubutsquarterhebrew"},
+ {0x05bb, "qubutswidehebrew"},
+ {0x003f, "question"},
+ {0x061f, "questionarabic"},
+ {0x055e, "questionarmenian"},
+ {0x00bf, "questiondown"},
+ {0xf7bf, "questiondownsmall"},
+ {0x037e, "questiongreek"},
+ {0xff1f, "questionmonospace"},
+ {0xf73f, "questionsmall"},
+ {0x0022, "quotedbl"},
+ {0x201e, "quotedblbase"},
+ {0x201c, "quotedblleft"},
+ {0xff02, "quotedblmonospace"},
+ {0x301e, "quotedblprime"},
+ {0x301d, "quotedblprimereversed"},
+ {0x201d, "quotedblright"},
+ {0x2018, "quoteleft"},
+ {0x201b, "quoteleftreversed"},
+ {0x201b, "quotereversed"},
+ {0x2019, "quoteright"},
+ {0x0149, "quoterightn"},
+ {0x201a, "quotesinglbase"},
+ {0x0027, "quotesingle"},
+ {0xff07, "quotesinglemonospace"},
+ {0x0072, "r"},
+ {0x057c, "raarmenian"},
+ {0x09b0, "rabengali"},
+ {0x0155, "racute"},
+ {0x0930, "radeva"},
+ {0x221a, "radical"},
+ {0xf8e5, "radicalex"},
+ {0x33ae, "radoverssquare"},
+ {0x33af, "radoverssquaredsquare"},
+ {0x33ad, "radsquare"},
+ {0x05bf, "rafe"},
+ {0x05bf, "rafehebrew"},
+ {0x0ab0, "ragujarati"},
+ {0x0a30, "ragurmukhi"},
+ {0x3089, "rahiragana"},
+ {0x30e9, "rakatakana"},
+ {0xff97, "rakatakanahalfwidth"},
+ {0x09f1, "ralowerdiagonalbengali"},
+ {0x09f0, "ramiddlediagonalbengali"},
+ {0x0264, "ramshorn"},
+ {0x2236, "ratio"},
+ {0x3116, "rbopomofo"},
+ {0x0159, "rcaron"},
+ {0x0157, "rcedilla"},
+ {0x24e1, "rcircle"},
+ {0x0157, "rcommaaccent"},
+ {0x0211, "rdblgrave"},
+ {0x1e59, "rdotaccent"},
+ {0x1e5b, "rdotbelow"},
+ {0x1e5d, "rdotbelowmacron"},
+ {0x203b, "referencemark"},
+ {0x2286, "reflexsubset"},
+ {0x2287, "reflexsuperset"},
+ {0x00ae, "registered"},
+ {0x00ae, "registersans"},
+ {0x00ae, "registerserif"},
+ {0x0631, "reharabic"},
+ {0x0580, "reharmenian"},
+ {0xfeae, "rehfinalarabic"},
+ {0x308c, "rehiragana"},
+ {0x30ec, "rekatakana"},
+ {0xff9a, "rekatakanahalfwidth"},
+ {0x05e8, "resh"},
+ {0xfb48, "reshdageshhebrew"},
+ {0x05e8, "reshhebrew"},
+ {0x223d, "reversedtilde"},
+ {0x0597, "reviahebrew"},
+ {0x0597, "reviamugrashhebrew"},
+ {0x2310, "revlogicalnot"},
+ {0x027e, "rfishhook"},
+ {0x027f, "rfishhookreversed"},
+ {0x09dd, "rhabengali"},
+ {0x095d, "rhadeva"},
+ {0x03c1, "rho"},
+ {0x027d, "rhook"},
+ {0x027b, "rhookturned"},
+ {0x02b5, "rhookturnedsuperior"},
+ {0x03f1, "rhosymbolgreek"},
+ {0x02de, "rhotichookmod"},
+ {0x3271, "rieulacirclekorean"},
+ {0x3211, "rieulaparenkorean"},
+ {0x3263, "rieulcirclekorean"},
+ {0x3140, "rieulhieuhkorean"},
+ {0x313a, "rieulkiyeokkorean"},
+ {0x3169, "rieulkiyeoksioskorean"},
+ {0x3139, "rieulkorean"},
+ {0x313b, "rieulmieumkorean"},
+ {0x316c, "rieulpansioskorean"},
+ {0x3203, "rieulparenkorean"},
+ {0x313f, "rieulphieuphkorean"},
+ {0x313c, "rieulpieupkorean"},
+ {0x316b, "rieulpieupsioskorean"},
+ {0x313d, "rieulsioskorean"},
+ {0x313e, "rieulthieuthkorean"},
+ {0x316a, "rieultikeutkorean"},
+ {0x316d, "rieulyeorinhieuhkorean"},
+ {0x221f, "rightangle"},
+ {0x0319, "righttackbelowcmb"},
+ {0x22bf, "righttriangle"},
+ {0x308a, "rihiragana"},
+ {0x30ea, "rikatakana"},
+ {0xff98, "rikatakanahalfwidth"},
+ {0x02da, "ring"},
+ {0x0325, "ringbelowcmb"},
+ {0x030a, "ringcmb"},
+ {0x02bf, "ringhalfleft"},
+ {0x0559, "ringhalfleftarmenian"},
+ {0x031c, "ringhalfleftbelowcmb"},
+ {0x02d3, "ringhalfleftcentered"},
+ {0x02be, "ringhalfright"},
+ {0x0339, "ringhalfrightbelowcmb"},
+ {0x02d2, "ringhalfrightcentered"},
+ {0x0213, "rinvertedbreve"},
+ {0x3351, "rittorusquare"},
+ {0x1e5f, "rlinebelow"},
+ {0x027c, "rlongleg"},
+ {0x027a, "rlonglegturned"},
+ {0xff52, "rmonospace"},
+ {0x308d, "rohiragana"},
+ {0x30ed, "rokatakana"},
+ {0xff9b, "rokatakanahalfwidth"},
+ {0x0e23, "roruathai"},
+ {0x24ad, "rparen"},
+ {0x09dc, "rrabengali"},
+ {0x0931, "rradeva"},
+ {0x0a5c, "rragurmukhi"},
+ {0x0691, "rreharabic"},
+ {0xfb8d, "rrehfinalarabic"},
+ {0x09e0, "rrvocalicbengali"},
+ {0x0960, "rrvocalicdeva"},
+ {0x0ae0, "rrvocalicgujarati"},
+ {0x09c4, "rrvocalicvowelsignbengali"},
+ {0x0944, "rrvocalicvowelsigndeva"},
+ {0x0ac4, "rrvocalicvowelsigngujarati"},
+ {0xf6f1, "rsuperior"},
+ {0x2590, "rtblock"},
+ {0x0279, "rturned"},
+ {0x02b4, "rturnedsuperior"},
+ {0x308b, "ruhiragana"},
+ {0x30eb, "rukatakana"},
+ {0xff99, "rukatakanahalfwidth"},
+ {0x09f2, "rupeemarkbengali"},
+ {0x09f3, "rupeesignbengali"},
+ {0xf6dd, "rupiah"},
+ {0x0e24, "ruthai"},
+ {0x098b, "rvocalicbengali"},
+ {0x090b, "rvocalicdeva"},
+ {0x0a8b, "rvocalicgujarati"},
+ {0x09c3, "rvocalicvowelsignbengali"},
+ {0x0943, "rvocalicvowelsigndeva"},
+ {0x0ac3, "rvocalicvowelsigngujarati"},
+ {0x0073, "s"},
+ {0x09b8, "sabengali"},
+ {0x015b, "sacute"},
+ {0x1e65, "sacutedotaccent"},
+ {0x0635, "sadarabic"},
+ {0x0938, "sadeva"},
+ {0xfeba, "sadfinalarabic"},
+ {0xfebb, "sadinitialarabic"},
+ {0xfebc, "sadmedialarabic"},
+ {0x0ab8, "sagujarati"},
+ {0x0a38, "sagurmukhi"},
+ {0x3055, "sahiragana"},
+ {0x30b5, "sakatakana"},
+ {0xff7b, "sakatakanahalfwidth"},
+ {0xfdfa, "sallallahoualayhewasallamarabic"},
+ {0x05e1, "samekh"},
+ {0xfb41, "samekhdagesh"},
+ {0xfb41, "samekhdageshhebrew"},
+ {0x05e1, "samekhhebrew"},
+ {0x0e32, "saraaathai"},
+ {0x0e41, "saraaethai"},
+ {0x0e44, "saraaimaimalaithai"},
+ {0x0e43, "saraaimaimuanthai"},
+ {0x0e33, "saraamthai"},
+ {0x0e30, "saraathai"},
+ {0x0e40, "saraethai"},
+ {0xf886, "saraiileftthai"},
+ {0x0e35, "saraiithai"},
+ {0xf885, "saraileftthai"},
+ {0x0e34, "saraithai"},
+ {0x0e42, "saraothai"},
+ {0xf888, "saraueeleftthai"},
+ {0x0e37, "saraueethai"},
+ {0xf887, "saraueleftthai"},
+ {0x0e36, "sarauethai"},
+ {0x0e38, "sarauthai"},
+ {0x0e39, "sarauuthai"},
+ {0x3119, "sbopomofo"},
+ {0x0161, "scaron"},
+ {0x1e67, "scarondotaccent"},
+ {0x015f, "scedilla"},
+ {0x0259, "schwa"},
+ {0x04d9, "schwacyrillic"},
+ {0x04db, "schwadieresiscyrillic"},
+ {0x025a, "schwahook"},
+ {0x24e2, "scircle"},
+ {0x015d, "scircumflex"},
+ {0x0219, "scommaaccent"},
+ {0x1e61, "sdotaccent"},
+ {0x1e63, "sdotbelow"},
+ {0x1e69, "sdotbelowdotaccent"},
+ {0x033c, "seagullbelowcmb"},
+ {0x2033, "second"},
+ {0x02ca, "secondtonechinese"},
+ {0x00a7, "section"},
+ {0x0633, "seenarabic"},
+ {0xfeb2, "seenfinalarabic"},
+ {0xfeb3, "seeninitialarabic"},
+ {0xfeb4, "seenmedialarabic"},
+ {0x05b6, "segol"},
+ {0x05b6, "segol13"},
+ {0x05b6, "segol1f"},
+ {0x05b6, "segol2c"},
+ {0x05b6, "segolhebrew"},
+ {0x05b6, "segolnarrowhebrew"},
+ {0x05b6, "segolquarterhebrew"},
+ {0x0592, "segoltahebrew"},
+ {0x05b6, "segolwidehebrew"},
+ {0x057d, "seharmenian"},
+ {0x305b, "sehiragana"},
+ {0x30bb, "sekatakana"},
+ {0xff7e, "sekatakanahalfwidth"},
+ {0x003b, "semicolon"},
+ {0x061b, "semicolonarabic"},
+ {0xff1b, "semicolonmonospace"},
+ {0xfe54, "semicolonsmall"},
+ {0x309c, "semivoicedmarkkana"},
+ {0xff9f, "semivoicedmarkkanahalfwidth"},
+ {0x3322, "sentisquare"},
+ {0x3323, "sentosquare"},
+ {0x0037, "seven"},
+ {0x0667, "sevenarabic"},
+ {0x09ed, "sevenbengali"},
+ {0x2466, "sevencircle"},
+ {0x2790, "sevencircleinversesansserif"},
+ {0x096d, "sevendeva"},
+ {0x215e, "seveneighths"},
+ {0x0aed, "sevengujarati"},
+ {0x0a6d, "sevengurmukhi"},
+ {0x0667, "sevenhackarabic"},
+ {0x3027, "sevenhangzhou"},
+ {0x3226, "sevenideographicparen"},
+ {0x2087, "seveninferior"},
+ {0xff17, "sevenmonospace"},
+ {0xf737, "sevenoldstyle"},
+ {0x247a, "sevenparen"},
+ {0x248e, "sevenperiod"},
+ {0x06f7, "sevenpersian"},
+ {0x2176, "sevenroman"},
+ {0x2077, "sevensuperior"},
+ {0x2470, "seventeencircle"},
+ {0x2484, "seventeenparen"},
+ {0x2498, "seventeenperiod"},
+ {0x0e57, "seventhai"},
+ {0x00ad, "sfthyphen"},
+ {0x0577, "shaarmenian"},
+ {0x09b6, "shabengali"},
+ {0x0448, "shacyrillic"},
+ {0x0651, "shaddaarabic"},
+ {0xfc61, "shaddadammaarabic"},
+ {0xfc5e, "shaddadammatanarabic"},
+ {0xfc60, "shaddafathaarabic"},
+ {0xfc62, "shaddakasraarabic"},
+ {0xfc5f, "shaddakasratanarabic"},
+ {0x2592, "shade"},
+ {0x2593, "shadedark"},
+ {0x2591, "shadelight"},
+ {0x2592, "shademedium"},
+ {0x0936, "shadeva"},
+ {0x0ab6, "shagujarati"},
+ {0x0a36, "shagurmukhi"},
+ {0x0593, "shalshelethebrew"},
+ {0x3115, "shbopomofo"},
+ {0x0449, "shchacyrillic"},
+ {0x0634, "sheenarabic"},
+ {0xfeb6, "sheenfinalarabic"},
+ {0xfeb7, "sheeninitialarabic"},
+ {0xfeb8, "sheenmedialarabic"},
+ {0x03e3, "sheicoptic"},
+ {0x20aa, "sheqel"},
+ {0x20aa, "sheqelhebrew"},
+ {0x05b0, "sheva"},
+ {0x05b0, "sheva115"},
+ {0x05b0, "sheva15"},
+ {0x05b0, "sheva22"},
+ {0x05b0, "sheva2e"},
+ {0x05b0, "shevahebrew"},
+ {0x05b0, "shevanarrowhebrew"},
+ {0x05b0, "shevaquarterhebrew"},
+ {0x05b0, "shevawidehebrew"},
+ {0x04bb, "shhacyrillic"},
+ {0x03ed, "shimacoptic"},
+ {0x05e9, "shin"},
+ {0xfb49, "shindagesh"},
+ {0xfb49, "shindageshhebrew"},
+ {0xfb2c, "shindageshshindot"},
+ {0xfb2c, "shindageshshindothebrew"},
+ {0xfb2d, "shindageshsindot"},
+ {0xfb2d, "shindageshsindothebrew"},
+ {0x05c1, "shindothebrew"},
+ {0x05e9, "shinhebrew"},
+ {0xfb2a, "shinshindot"},
+ {0xfb2a, "shinshindothebrew"},
+ {0xfb2b, "shinsindot"},
+ {0xfb2b, "shinsindothebrew"},
+ {0x0282, "shook"},
+ {0x03c3, "sigma"},
+ {0x03c2, "sigma1"},
+ {0x03c2, "sigmafinal"},
+ {0x03f2, "sigmalunatesymbolgreek"},
+ {0x3057, "sihiragana"},
+ {0x30b7, "sikatakana"},
+ {0xff7c, "sikatakanahalfwidth"},
+ {0x05bd, "siluqhebrew"},
+ {0x05bd, "siluqlefthebrew"},
+ {0x223c, "similar"},
+ {0x05c2, "sindothebrew"},
+ {0x3274, "siosacirclekorean"},
+ {0x3214, "siosaparenkorean"},
+ {0x317e, "sioscieuckorean"},
+ {0x3266, "sioscirclekorean"},
+ {0x317a, "sioskiyeokkorean"},
+ {0x3145, "sioskorean"},
+ {0x317b, "siosnieunkorean"},
+ {0x3206, "siosparenkorean"},
+ {0x317d, "siospieupkorean"},
+ {0x317c, "siostikeutkorean"},
+ {0x0036, "six"},
+ {0x0666, "sixarabic"},
+ {0x09ec, "sixbengali"},
+ {0x2465, "sixcircle"},
+ {0x278f, "sixcircleinversesansserif"},
+ {0x096c, "sixdeva"},
+ {0x0aec, "sixgujarati"},
+ {0x0a6c, "sixgurmukhi"},
+ {0x0666, "sixhackarabic"},
+ {0x3026, "sixhangzhou"},
+ {0x3225, "sixideographicparen"},
+ {0x2086, "sixinferior"},
+ {0xff16, "sixmonospace"},
+ {0xf736, "sixoldstyle"},
+ {0x2479, "sixparen"},
+ {0x248d, "sixperiod"},
+ {0x06f6, "sixpersian"},
+ {0x2175, "sixroman"},
+ {0x2076, "sixsuperior"},
+ {0x246f, "sixteencircle"},
+ {0x09f9, "sixteencurrencydenominatorbengali"},
+ {0x2483, "sixteenparen"},
+ {0x2497, "sixteenperiod"},
+ {0x0e56, "sixthai"},
+ {0x002f, "slash"},
+ {0xff0f, "slashmonospace"},
+ {0x017f, "slong"},
+ {0x1e9b, "slongdotaccent"},
+ {0x263a, "smileface"},
+ {0xff53, "smonospace"},
+ {0x05c3, "sofpasuqhebrew"},
+ {0x00ad, "softhyphen"},
+ {0x044c, "softsigncyrillic"},
+ {0x305d, "sohiragana"},
+ {0x30bd, "sokatakana"},
+ {0xff7f, "sokatakanahalfwidth"},
+ {0x0338, "soliduslongoverlaycmb"},
+ {0x0337, "solidusshortoverlaycmb"},
+ {0x0e29, "sorusithai"},
+ {0x0e28, "sosalathai"},
+ {0x0e0b, "sosothai"},
+ {0x0e2a, "sosuathai"},
+ {0x0020, "space"},
+ {0x0020, "spacehackarabic"},
+ {0x2660, "spade"},
+ {0x2660, "spadesuitblack"},
+ {0x2664, "spadesuitwhite"},
+ {0x24ae, "sparen"},
+ {0x033b, "squarebelowcmb"},
+ {0x33c4, "squarecc"},
+ {0x339d, "squarecm"},
+ {0x25a9, "squarediagonalcrosshatchfill"},
+ {0x25a4, "squarehorizontalfill"},
+ {0x338f, "squarekg"},
+ {0x339e, "squarekm"},
+ {0x33ce, "squarekmcapital"},
+ {0x33d1, "squareln"},
+ {0x33d2, "squarelog"},
+ {0x338e, "squaremg"},
+ {0x33d5, "squaremil"},
+ {0x339c, "squaremm"},
+ {0x33a1, "squaremsquared"},
+ {0x25a6, "squareorthogonalcrosshatchfill"},
+ {0x25a7, "squareupperlefttolowerrightfill"},
+ {0x25a8, "squareupperrighttolowerleftfill"},
+ {0x25a5, "squareverticalfill"},
+ {0x25a3, "squarewhitewithsmallblack"},
+ {0x33db, "srsquare"},
+ {0x09b7, "ssabengali"},
+ {0x0937, "ssadeva"},
+ {0x0ab7, "ssagujarati"},
+ {0x3149, "ssangcieuckorean"},
+ {0x3185, "ssanghieuhkorean"},
+ {0x3180, "ssangieungkorean"},
+ {0x3132, "ssangkiyeokkorean"},
+ {0x3165, "ssangnieunkorean"},
+ {0x3143, "ssangpieupkorean"},
+ {0x3146, "ssangsioskorean"},
+ {0x3138, "ssangtikeutkorean"},
+ {0xf6f2, "ssuperior"},
+ {0x00a3, "sterling"},
+ {0xffe1, "sterlingmonospace"},
+ {0x0336, "strokelongoverlaycmb"},
+ {0x0335, "strokeshortoverlaycmb"},
+ {0x2282, "subset"},
+ {0x228a, "subsetnotequal"},
+ {0x2286, "subsetorequal"},
+ {0x227b, "succeeds"},
+ {0x220b, "suchthat"},
+ {0x3059, "suhiragana"},
+ {0x30b9, "sukatakana"},
+ {0xff7d, "sukatakanahalfwidth"},
+ {0x0652, "sukunarabic"},
+ {0x2211, "summation"},
+ {0x263c, "sun"},
+ {0x2283, "superset"},
+ {0x228b, "supersetnotequal"},
+ {0x2287, "supersetorequal"},
+ {0x33dc, "svsquare"},
+ {0x337c, "syouwaerasquare"},
+ {0x0074, "t"},
+ {0x09a4, "tabengali"},
+ {0x22a4, "tackdown"},
+ {0x22a3, "tackleft"},
+ {0x0924, "tadeva"},
+ {0x0aa4, "tagujarati"},
+ {0x0a24, "tagurmukhi"},
+ {0x0637, "taharabic"},
+ {0xfec2, "tahfinalarabic"},
+ {0xfec3, "tahinitialarabic"},
+ {0x305f, "tahiragana"},
+ {0xfec4, "tahmedialarabic"},
+ {0x337d, "taisyouerasquare"},
+ {0x30bf, "takatakana"},
+ {0xff80, "takatakanahalfwidth"},
+ {0x0640, "tatweelarabic"},
+ {0x03c4, "tau"},
+ {0x05ea, "tav"},
+ {0xfb4a, "tavdages"},
+ {0xfb4a, "tavdagesh"},
+ {0xfb4a, "tavdageshhebrew"},
+ {0x05ea, "tavhebrew"},
+ {0x0167, "tbar"},
+ {0x310a, "tbopomofo"},
+ {0x0165, "tcaron"},
+ {0x02a8, "tccurl"},
+ {0x0163, "tcedilla"},
+ {0x0686, "tcheharabic"},
+ {0xfb7b, "tchehfinalarabic"},
+ {0xfb7c, "tchehinitialarabic"},
+ {0xfb7d, "tchehmedialarabic"},
+ {0x24e3, "tcircle"},
+ {0x1e71, "tcircumflexbelow"},
+ {0x0163, "tcommaaccent"},
+ {0x1e97, "tdieresis"},
+ {0x1e6b, "tdotaccent"},
+ {0x1e6d, "tdotbelow"},
+ {0x0442, "tecyrillic"},
+ {0x04ad, "tedescendercyrillic"},
+ {0x062a, "teharabic"},
+ {0xfe96, "tehfinalarabic"},
+ {0xfca2, "tehhahinitialarabic"},
+ {0xfc0c, "tehhahisolatedarabic"},
+ {0xfe97, "tehinitialarabic"},
+ {0x3066, "tehiragana"},
+ {0xfca1, "tehjeeminitialarabic"},
+ {0xfc0b, "tehjeemisolatedarabic"},
+ {0x0629, "tehmarbutaarabic"},
+ {0xfe94, "tehmarbutafinalarabic"},
+ {0xfe98, "tehmedialarabic"},
+ {0xfca4, "tehmeeminitialarabic"},
+ {0xfc0e, "tehmeemisolatedarabic"},
+ {0xfc73, "tehnoonfinalarabic"},
+ {0x30c6, "tekatakana"},
+ {0xff83, "tekatakanahalfwidth"},
+ {0x2121, "telephone"},
+ {0x260e, "telephoneblack"},
+ {0x05a0, "telishagedolahebrew"},
+ {0x05a9, "telishaqetanahebrew"},
+ {0x2469, "tencircle"},
+ {0x3229, "tenideographicparen"},
+ {0x247d, "tenparen"},
+ {0x2491, "tenperiod"},
+ {0x2179, "tenroman"},
+ {0x02a7, "tesh"},
+ {0x05d8, "tet"},
+ {0xfb38, "tetdagesh"},
+ {0xfb38, "tetdageshhebrew"},
+ {0x05d8, "tethebrew"},
+ {0x04b5, "tetsecyrillic"},
+ {0x059b, "tevirhebrew"},
+ {0x059b, "tevirlefthebrew"},
+ {0x09a5, "thabengali"},
+ {0x0925, "thadeva"},
+ {0x0aa5, "thagujarati"},
+ {0x0a25, "thagurmukhi"},
+ {0x0630, "thalarabic"},
+ {0xfeac, "thalfinalarabic"},
+ {0xf898, "thanthakhatlowleftthai"},
+ {0xf897, "thanthakhatlowrightthai"},
+ {0x0e4c, "thanthakhatthai"},
+ {0xf896, "thanthakhatupperleftthai"},
+ {0x062b, "theharabic"},
+ {0xfe9a, "thehfinalarabic"},
+ {0xfe9b, "thehinitialarabic"},
+ {0xfe9c, "thehmedialarabic"},
+ {0x2203, "thereexists"},
+ {0x2234, "therefore"},
+ {0x03b8, "theta"},
+ {0x03d1, "theta1"},
+ {0x03d1, "thetasymbolgreek"},
+ {0x3279, "thieuthacirclekorean"},
+ {0x3219, "thieuthaparenkorean"},
+ {0x326b, "thieuthcirclekorean"},
+ {0x314c, "thieuthkorean"},
+ {0x320b, "thieuthparenkorean"},
+ {0x246c, "thirteencircle"},
+ {0x2480, "thirteenparen"},
+ {0x2494, "thirteenperiod"},
+ {0x0e11, "thonangmonthothai"},
+ {0x01ad, "thook"},
+ {0x0e12, "thophuthaothai"},
+ {0x00fe, "thorn"},
+ {0x0e17, "thothahanthai"},
+ {0x0e10, "thothanthai"},
+ {0x0e18, "thothongthai"},
+ {0x0e16, "thothungthai"},
+ {0x0482, "thousandcyrillic"},
+ {0x066c, "thousandsseparatorarabic"},
+ {0x066c, "thousandsseparatorpersian"},
+ {0x0033, "three"},
+ {0x0663, "threearabic"},
+ {0x09e9, "threebengali"},
+ {0x2462, "threecircle"},
+ {0x278c, "threecircleinversesansserif"},
+ {0x0969, "threedeva"},
+ {0x215c, "threeeighths"},
+ {0x0ae9, "threegujarati"},
+ {0x0a69, "threegurmukhi"},
+ {0x0663, "threehackarabic"},
+ {0x3023, "threehangzhou"},
+ {0x3222, "threeideographicparen"},
+ {0x2083, "threeinferior"},
+ {0xff13, "threemonospace"},
+ {0x09f6, "threenumeratorbengali"},
+ {0xf733, "threeoldstyle"},
+ {0x2476, "threeparen"},
+ {0x248a, "threeperiod"},
+ {0x06f3, "threepersian"},
+ {0x00be, "threequarters"},
+ {0xf6de, "threequartersemdash"},
+ {0x2172, "threeroman"},
+ {0x00b3, "threesuperior"},
+ {0x0e53, "threethai"},
+ {0x3394, "thzsquare"},
+ {0x3061, "tihiragana"},
+ {0x30c1, "tikatakana"},
+ {0xff81, "tikatakanahalfwidth"},
+ {0x3270, "tikeutacirclekorean"},
+ {0x3210, "tikeutaparenkorean"},
+ {0x3262, "tikeutcirclekorean"},
+ {0x3137, "tikeutkorean"},
+ {0x3202, "tikeutparenkorean"},
+ {0x02dc, "tilde"},
+ {0x0330, "tildebelowcmb"},
+ {0x0303, "tildecmb"},
+ {0x0303, "tildecomb"},
+ {0x0360, "tildedoublecmb"},
+ {0x223c, "tildeoperator"},
+ {0x0334, "tildeoverlaycmb"},
+ {0x033e, "tildeverticalcmb"},
+ {0x2297, "timescircle"},
+ {0x0596, "tipehahebrew"},
+ {0x0596, "tipehalefthebrew"},
+ {0x0a70, "tippigurmukhi"},
+ {0x0483, "titlocyrilliccmb"},
+ {0x057f, "tiwnarmenian"},
+ {0x1e6f, "tlinebelow"},
+ {0xff54, "tmonospace"},
+ {0x0569, "toarmenian"},
+ {0x3068, "tohiragana"},
+ {0x30c8, "tokatakana"},
+ {0xff84, "tokatakanahalfwidth"},
+ {0x02e5, "tonebarextrahighmod"},
+ {0x02e9, "tonebarextralowmod"},
+ {0x02e6, "tonebarhighmod"},
+ {0x02e8, "tonebarlowmod"},
+ {0x02e7, "tonebarmidmod"},
+ {0x01bd, "tonefive"},
+ {0x0185, "tonesix"},
+ {0x01a8, "tonetwo"},
+ {0x0384, "tonos"},
+ {0x3327, "tonsquare"},
+ {0x0e0f, "topatakthai"},
+ {0x3014, "tortoiseshellbracketleft"},
+ {0xfe5d, "tortoiseshellbracketleftsmall"},
+ {0xfe39, "tortoiseshellbracketleftvertical"},
+ {0x3015, "tortoiseshellbracketright"},
+ {0xfe5e, "tortoiseshellbracketrightsmall"},
+ {0xfe3a, "tortoiseshellbracketrightvertical"},
+ {0x0e15, "totaothai"},
+ {0x01ab, "tpalatalhook"},
+ {0x24af, "tparen"},
+ {0x2122, "trademark"},
+ {0x2122, "trademarksans"},
+ {0x2122, "trademarkserif"},
+ {0x0288, "tretroflexhook"},
+ {0x25bc, "triagdn"},
+ {0x25c4, "triaglf"},
+ {0x25ba, "triagrt"},
+ {0x25b2, "triagup"},
+ {0x02a6, "ts"},
+ {0x05e6, "tsadi"},
+ {0xfb46, "tsadidagesh"},
+ {0xfb46, "tsadidageshhebrew"},
+ {0x05e6, "tsadihebrew"},
+ {0x0446, "tsecyrillic"},
+ {0x05b5, "tsere"},
+ {0x05b5, "tsere12"},
+ {0x05b5, "tsere1e"},
+ {0x05b5, "tsere2b"},
+ {0x05b5, "tserehebrew"},
+ {0x05b5, "tserenarrowhebrew"},
+ {0x05b5, "tserequarterhebrew"},
+ {0x05b5, "tserewidehebrew"},
+ {0x045b, "tshecyrillic"},
+ {0xf6f3, "tsuperior"},
+ {0x099f, "ttabengali"},
+ {0x091f, "ttadeva"},
+ {0x0a9f, "ttagujarati"},
+ {0x0a1f, "ttagurmukhi"},
+ {0x0679, "tteharabic"},
+ {0xfb67, "ttehfinalarabic"},
+ {0xfb68, "ttehinitialarabic"},
+ {0xfb69, "ttehmedialarabic"},
+ {0x09a0, "tthabengali"},
+ {0x0920, "tthadeva"},
+ {0x0aa0, "tthagujarati"},
+ {0x0a20, "tthagurmukhi"},
+ {0x0287, "tturned"},
+ {0x3064, "tuhiragana"},
+ {0x30c4, "tukatakana"},
+ {0xff82, "tukatakanahalfwidth"},
+ {0x3063, "tusmallhiragana"},
+ {0x30c3, "tusmallkatakana"},
+ {0xff6f, "tusmallkatakanahalfwidth"},
+ {0x246b, "twelvecircle"},
+ {0x247f, "twelveparen"},
+ {0x2493, "twelveperiod"},
+ {0x217b, "twelveroman"},
+ {0x2473, "twentycircle"},
+ {0x5344, "twentyhangzhou"},
+ {0x2487, "twentyparen"},
+ {0x249b, "twentyperiod"},
+ {0x0032, "two"},
+ {0x0662, "twoarabic"},
+ {0x09e8, "twobengali"},
+ {0x2461, "twocircle"},
+ {0x278b, "twocircleinversesansserif"},
+ {0x0968, "twodeva"},
+ {0x2025, "twodotenleader"},
+ {0x2025, "twodotleader"},
+ {0xfe30, "twodotleadervertical"},
+ {0x0ae8, "twogujarati"},
+ {0x0a68, "twogurmukhi"},
+ {0x0662, "twohackarabic"},
+ {0x3022, "twohangzhou"},
+ {0x3221, "twoideographicparen"},
+ {0x2082, "twoinferior"},
+ {0xff12, "twomonospace"},
+ {0x09f5, "twonumeratorbengali"},
+ {0xf732, "twooldstyle"},
+ {0x2475, "twoparen"},
+ {0x2489, "twoperiod"},
+ {0x06f2, "twopersian"},
+ {0x2171, "tworoman"},
+ {0x01bb, "twostroke"},
+ {0x00b2, "twosuperior"},
+ {0x0e52, "twothai"},
+ {0x2154, "twothirds"},
+ {0x0075, "u"},
+ {0x00fa, "uacute"},
+ {0x0289, "ubar"},
+ {0x0989, "ubengali"},
+ {0x3128, "ubopomofo"},
+ {0x016d, "ubreve"},
+ {0x01d4, "ucaron"},
+ {0x24e4, "ucircle"},
+ {0x00fb, "ucircumflex"},
+ {0x1e77, "ucircumflexbelow"},
+ {0x0443, "ucyrillic"},
+ {0x0951, "udattadeva"},
+ {0x0171, "udblacute"},
+ {0x0215, "udblgrave"},
+ {0x0909, "udeva"},
+ {0x00fc, "udieresis"},
+ {0x01d8, "udieresisacute"},
+ {0x1e73, "udieresisbelow"},
+ {0x01da, "udieresiscaron"},
+ {0x04f1, "udieresiscyrillic"},
+ {0x01dc, "udieresisgrave"},
+ {0x01d6, "udieresismacron"},
+ {0x1ee5, "udotbelow"},
+ {0x00f9, "ugrave"},
+ {0x0a89, "ugujarati"},
+ {0x0a09, "ugurmukhi"},
+ {0x3046, "uhiragana"},
+ {0x1ee7, "uhookabove"},
+ {0x01b0, "uhorn"},
+ {0x1ee9, "uhornacute"},
+ {0x1ef1, "uhorndotbelow"},
+ {0x1eeb, "uhorngrave"},
+ {0x1eed, "uhornhookabove"},
+ {0x1eef, "uhorntilde"},
+ {0x0171, "uhungarumlaut"},
+ {0x04f3, "uhungarumlautcyrillic"},
+ {0x0217, "uinvertedbreve"},
+ {0x30a6, "ukatakana"},
+ {0xff73, "ukatakanahalfwidth"},
+ {0x0479, "ukcyrillic"},
+ {0x315c, "ukorean"},
+ {0x016b, "umacron"},
+ {0x04ef, "umacroncyrillic"},
+ {0x1e7b, "umacrondieresis"},
+ {0x0a41, "umatragurmukhi"},
+ {0xff55, "umonospace"},
+ {0x005f, "underscore"},
+ {0x2017, "underscoredbl"},
+ {0xff3f, "underscoremonospace"},
+ {0xfe33, "underscorevertical"},
+ {0xfe4f, "underscorewavy"},
+ {0x222a, "union"},
+ {0x2200, "universal"},
+ {0x0173, "uogonek"},
+ {0x24b0, "uparen"},
+ {0x2580, "upblock"},
+ {0x05c4, "upperdothebrew"},
+ {0x03c5, "upsilon"},
+ {0x03cb, "upsilondieresis"},
+ {0x03b0, "upsilondieresistonos"},
+ {0x028a, "upsilonlatin"},
+ {0x03cd, "upsilontonos"},
+ {0x031d, "uptackbelowcmb"},
+ {0x02d4, "uptackmod"},
+ {0x0a73, "uragurmukhi"},
+ {0x016f, "uring"},
+ {0x045e, "ushortcyrillic"},
+ {0x3045, "usmallhiragana"},
+ {0x30a5, "usmallkatakana"},
+ {0xff69, "usmallkatakanahalfwidth"},
+ {0x04af, "ustraightcyrillic"},
+ {0x04b1, "ustraightstrokecyrillic"},
+ {0x0169, "utilde"},
+ {0x1e79, "utildeacute"},
+ {0x1e75, "utildebelow"},
+ {0x098a, "uubengali"},
+ {0x090a, "uudeva"},
+ {0x0a8a, "uugujarati"},
+ {0x0a0a, "uugurmukhi"},
+ {0x0a42, "uumatragurmukhi"},
+ {0x09c2, "uuvowelsignbengali"},
+ {0x0942, "uuvowelsigndeva"},
+ {0x0ac2, "uuvowelsigngujarati"},
+ {0x09c1, "uvowelsignbengali"},
+ {0x0941, "uvowelsigndeva"},
+ {0x0ac1, "uvowelsigngujarati"},
+ {0x0076, "v"},
+ {0x0935, "vadeva"},
+ {0x0ab5, "vagujarati"},
+ {0x0a35, "vagurmukhi"},
+ {0x30f7, "vakatakana"},
+ {0x05d5, "vav"},
+ {0xfb35, "vavdagesh"},
+ {0xfb35, "vavdagesh65"},
+ {0xfb35, "vavdageshhebrew"},
+ {0x05d5, "vavhebrew"},
+ {0xfb4b, "vavholam"},
+ {0xfb4b, "vavholamhebrew"},
+ {0x05f0, "vavvavhebrew"},
+ {0x05f1, "vavyodhebrew"},
+ {0x24e5, "vcircle"},
+ {0x1e7f, "vdotbelow"},
+ {0x0432, "vecyrillic"},
+ {0x06a4, "veharabic"},
+ {0xfb6b, "vehfinalarabic"},
+ {0xfb6c, "vehinitialarabic"},
+ {0xfb6d, "vehmedialarabic"},
+ {0x30f9, "vekatakana"},
+ {0x2640, "venus"},
+ {0x007c, "verticalbar"},
+ {0x030d, "verticallineabovecmb"},
+ {0x0329, "verticallinebelowcmb"},
+ {0x02cc, "verticallinelowmod"},
+ {0x02c8, "verticallinemod"},
+ {0x057e, "vewarmenian"},
+ {0x028b, "vhook"},
+ {0x30f8, "vikatakana"},
+ {0x09cd, "viramabengali"},
+ {0x094d, "viramadeva"},
+ {0x0acd, "viramagujarati"},
+ {0x0983, "visargabengali"},
+ {0x0903, "visargadeva"},
+ {0x0a83, "visargagujarati"},
+ {0xff56, "vmonospace"},
+ {0x0578, "voarmenian"},
+ {0x309e, "voicediterationhiragana"},
+ {0x30fe, "voicediterationkatakana"},
+ {0x309b, "voicedmarkkana"},
+ {0xff9e, "voicedmarkkanahalfwidth"},
+ {0x30fa, "vokatakana"},
+ {0x24b1, "vparen"},
+ {0x1e7d, "vtilde"},
+ {0x028c, "vturned"},
+ {0x3094, "vuhiragana"},
+ {0x30f4, "vukatakana"},
+ {0x0077, "w"},
+ {0x1e83, "wacute"},
+ {0x3159, "waekorean"},
+ {0x308f, "wahiragana"},
+ {0x30ef, "wakatakana"},
+ {0xff9c, "wakatakanahalfwidth"},
+ {0x3158, "wakorean"},
+ {0x308e, "wasmallhiragana"},
+ {0x30ee, "wasmallkatakana"},
+ {0x3357, "wattosquare"},
+ {0x301c, "wavedash"},
+ {0xfe34, "wavyunderscorevertical"},
+ {0x0648, "wawarabic"},
+ {0xfeee, "wawfinalarabic"},
+ {0x0624, "wawhamzaabovearabic"},
+ {0xfe86, "wawhamzaabovefinalarabic"},
+ {0x33dd, "wbsquare"},
+ {0x24e6, "wcircle"},
+ {0x0175, "wcircumflex"},
+ {0x1e85, "wdieresis"},
+ {0x1e87, "wdotaccent"},
+ {0x1e89, "wdotbelow"},
+ {0x3091, "wehiragana"},
+ {0x2118, "weierstrass"},
+ {0x30f1, "wekatakana"},
+ {0x315e, "wekorean"},
+ {0x315d, "weokorean"},
+ {0x1e81, "wgrave"},
+ {0x25e6, "whitebullet"},
+ {0x25cb, "whitecircle"},
+ {0x25d9, "whitecircleinverse"},
+ {0x300e, "whitecornerbracketleft"},
+ {0xfe43, "whitecornerbracketleftvertical"},
+ {0x300f, "whitecornerbracketright"},
+ {0xfe44, "whitecornerbracketrightvertical"},
+ {0x25c7, "whitediamond"},
+ {0x25c8, "whitediamondcontainingblacksmalldiamond"},
+ {0x25bf, "whitedownpointingsmalltriangle"},
+ {0x25bd, "whitedownpointingtriangle"},
+ {0x25c3, "whiteleftpointingsmalltriangle"},
+ {0x25c1, "whiteleftpointingtriangle"},
+ {0x3016, "whitelenticularbracketleft"},
+ {0x3017, "whitelenticularbracketright"},
+ {0x25b9, "whiterightpointingsmalltriangle"},
+ {0x25b7, "whiterightpointingtriangle"},
+ {0x25ab, "whitesmallsquare"},
+ {0x263a, "whitesmilingface"},
+ {0x25a1, "whitesquare"},
+ {0x2606, "whitestar"},
+ {0x260f, "whitetelephone"},
+ {0x3018, "whitetortoiseshellbracketleft"},
+ {0x3019, "whitetortoiseshellbracketright"},
+ {0x25b5, "whiteuppointingsmalltriangle"},
+ {0x25b3, "whiteuppointingtriangle"},
+ {0x3090, "wihiragana"},
+ {0x30f0, "wikatakana"},
+ {0x315f, "wikorean"},
+ {0xff57, "wmonospace"},
+ {0x3092, "wohiragana"},
+ {0x30f2, "wokatakana"},
+ {0xff66, "wokatakanahalfwidth"},
+ {0x20a9, "won"},
+ {0xffe6, "wonmonospace"},
+ {0x0e27, "wowaenthai"},
+ {0x24b2, "wparen"},
+ {0x1e98, "wring"},
+ {0x02b7, "wsuperior"},
+ {0x028d, "wturned"},
+ {0x01bf, "wynn"},
+ {0x0078, "x"},
+ {0x033d, "xabovecmb"},
+ {0x3112, "xbopomofo"},
+ {0x24e7, "xcircle"},
+ {0x1e8d, "xdieresis"},
+ {0x1e8b, "xdotaccent"},
+ {0x056d, "xeharmenian"},
+ {0x03be, "xi"},
+ {0xff58, "xmonospace"},
+ {0x24b3, "xparen"},
+ {0x02e3, "xsuperior"},
+ {0x0079, "y"},
+ {0x334e, "yaadosquare"},
+ {0x09af, "yabengali"},
+ {0x00fd, "yacute"},
+ {0x092f, "yadeva"},
+ {0x3152, "yaekorean"},
+ {0x0aaf, "yagujarati"},
+ {0x0a2f, "yagurmukhi"},
+ {0x3084, "yahiragana"},
+ {0x30e4, "yakatakana"},
+ {0xff94, "yakatakanahalfwidth"},
+ {0x3151, "yakorean"},
+ {0x0e4e, "yamakkanthai"},
+ {0x3083, "yasmallhiragana"},
+ {0x30e3, "yasmallkatakana"},
+ {0xff6c, "yasmallkatakanahalfwidth"},
+ {0x0463, "yatcyrillic"},
+ {0x24e8, "ycircle"},
+ {0x0177, "ycircumflex"},
+ {0x00ff, "ydieresis"},
+ {0x1e8f, "ydotaccent"},
+ {0x1ef5, "ydotbelow"},
+ {0x064a, "yeharabic"},
+ {0x06d2, "yehbarreearabic"},
+ {0xfbaf, "yehbarreefinalarabic"},
+ {0xfef2, "yehfinalarabic"},
+ {0x0626, "yehhamzaabovearabic"},
+ {0xfe8a, "yehhamzaabovefinalarabic"},
+ {0xfe8b, "yehhamzaaboveinitialarabic"},
+ {0xfe8c, "yehhamzaabovemedialarabic"},
+ {0xfef3, "yehinitialarabic"},
+ {0xfef4, "yehmedialarabic"},
+ {0xfcdd, "yehmeeminitialarabic"},
+ {0xfc58, "yehmeemisolatedarabic"},
+ {0xfc94, "yehnoonfinalarabic"},
+ {0x06d1, "yehthreedotsbelowarabic"},
+ {0x3156, "yekorean"},
+ {0x00a5, "yen"},
+ {0xffe5, "yenmonospace"},
+ {0x3155, "yeokorean"},
+ {0x3186, "yeorinhieuhkorean"},
+ {0x05aa, "yerahbenyomohebrew"},
+ {0x05aa, "yerahbenyomolefthebrew"},
+ {0x044b, "yericyrillic"},
+ {0x04f9, "yerudieresiscyrillic"},
+ {0x3181, "yesieungkorean"},
+ {0x3183, "yesieungpansioskorean"},
+ {0x3182, "yesieungsioskorean"},
+ {0x059a, "yetivhebrew"},
+ {0x1ef3, "ygrave"},
+ {0x01b4, "yhook"},
+ {0x1ef7, "yhookabove"},
+ {0x0575, "yiarmenian"},
+ {0x0457, "yicyrillic"},
+ {0x3162, "yikorean"},
+ {0x262f, "yinyang"},
+ {0x0582, "yiwnarmenian"},
+ {0xff59, "ymonospace"},
+ {0x05d9, "yod"},
+ {0xfb39, "yoddagesh"},
+ {0xfb39, "yoddageshhebrew"},
+ {0x05d9, "yodhebrew"},
+ {0x05f2, "yodyodhebrew"},
+ {0xfb1f, "yodyodpatahhebrew"},
+ {0x3088, "yohiragana"},
+ {0x3189, "yoikorean"},
+ {0x30e8, "yokatakana"},
+ {0xff96, "yokatakanahalfwidth"},
+ {0x315b, "yokorean"},
+ {0x3087, "yosmallhiragana"},
+ {0x30e7, "yosmallkatakana"},
+ {0xff6e, "yosmallkatakanahalfwidth"},
+ {0x03f3, "yotgreek"},
+ {0x3188, "yoyaekorean"},
+ {0x3187, "yoyakorean"},
+ {0x0e22, "yoyakthai"},
+ {0x0e0d, "yoyingthai"},
+ {0x24b4, "yparen"},
+ {0x037a, "ypogegrammeni"},
+ {0x0345, "ypogegrammenigreekcmb"},
+ {0x01a6, "yr"},
+ {0x1e99, "yring"},
+ {0x02b8, "ysuperior"},
+ {0x1ef9, "ytilde"},
+ {0x028e, "yturned"},
+ {0x3086, "yuhiragana"},
+ {0x318c, "yuikorean"},
+ {0x30e6, "yukatakana"},
+ {0xff95, "yukatakanahalfwidth"},
+ {0x3160, "yukorean"},
+ {0x046b, "yusbigcyrillic"},
+ {0x046d, "yusbigiotifiedcyrillic"},
+ {0x0467, "yuslittlecyrillic"},
+ {0x0469, "yuslittleiotifiedcyrillic"},
+ {0x3085, "yusmallhiragana"},
+ {0x30e5, "yusmallkatakana"},
+ {0xff6d, "yusmallkatakanahalfwidth"},
+ {0x318b, "yuyekorean"},
+ {0x318a, "yuyeokorean"},
+ {0x09df, "yyabengali"},
+ {0x095f, "yyadeva"},
+ {0x007a, "z"},
+ {0x0566, "zaarmenian"},
+ {0x017a, "zacute"},
+ {0x095b, "zadeva"},
+ {0x0a5b, "zagurmukhi"},
+ {0x0638, "zaharabic"},
+ {0xfec6, "zahfinalarabic"},
+ {0xfec7, "zahinitialarabic"},
+ {0x3056, "zahiragana"},
+ {0xfec8, "zahmedialarabic"},
+ {0x0632, "zainarabic"},
+ {0xfeb0, "zainfinalarabic"},
+ {0x30b6, "zakatakana"},
+ {0x0595, "zaqefgadolhebrew"},
+ {0x0594, "zaqefqatanhebrew"},
+ {0x0598, "zarqahebrew"},
+ {0x05d6, "zayin"},
+ {0xfb36, "zayindagesh"},
+ {0xfb36, "zayindageshhebrew"},
+ {0x05d6, "zayinhebrew"},
+ {0x3117, "zbopomofo"},
+ {0x017e, "zcaron"},
+ {0x24e9, "zcircle"},
+ {0x1e91, "zcircumflex"},
+ {0x0291, "zcurl"},
+ {0x017c, "zdot"},
+ {0x017c, "zdotaccent"},
+ {0x1e93, "zdotbelow"},
+ {0x0437, "zecyrillic"},
+ {0x0499, "zedescendercyrillic"},
+ {0x04df, "zedieresiscyrillic"},
+ {0x305c, "zehiragana"},
+ {0x30bc, "zekatakana"},
+ {0x0030, "zero"},
+ {0x0660, "zeroarabic"},
+ {0x09e6, "zerobengali"},
+ {0x0966, "zerodeva"},
+ {0x0ae6, "zerogujarati"},
+ {0x0a66, "zerogurmukhi"},
+ {0x0660, "zerohackarabic"},
+ {0x2080, "zeroinferior"},
+ {0xff10, "zeromonospace"},
+ {0xf730, "zerooldstyle"},
+ {0x06f0, "zeropersian"},
+ {0x2070, "zerosuperior"},
+ {0x0e50, "zerothai"},
+ {0xfeff, "zerowidthjoiner"},
+ {0x200c, "zerowidthnonjoiner"},
+ {0x200b, "zerowidthspace"},
+ {0x03b6, "zeta"},
+ {0x3113, "zhbopomofo"},
+ {0x056a, "zhearmenian"},
+ {0x04c2, "zhebrevecyrillic"},
+ {0x0436, "zhecyrillic"},
+ {0x0497, "zhedescendercyrillic"},
+ {0x04dd, "zhedieresiscyrillic"},
+ {0x3058, "zihiragana"},
+ {0x30b8, "zikatakana"},
+ {0x05ae, "zinorhebrew"},
+ {0x1e95, "zlinebelow"},
+ {0xff5a, "zmonospace"},
+ {0x305e, "zohiragana"},
+ {0x30be, "zokatakana"},
+ {0x24b5, "zparen"},
+ {0x0290, "zretroflexhook"},
+ {0x01b6, "zstroke"},
+ {0x305a, "zuhiragana"},
+ {0x30ba, "zukatakana"},
+ {0x007b, "{"},
+ {0x007c, "|"},
+ {0x007d, "}"},
+ {0x007e, "~"},
+ { 0, NULL }
+};
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc
new file mode 100644
index 00000000000..898fbc99e2b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Object.cc
@@ -0,0 +1,245 @@
+//========================================================================
+//
+// Object.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "gmempp.h"
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Error.h"
+#include "Stream.h"
+#include "XRef.h"
+
+//------------------------------------------------------------------------
+// Object
+//------------------------------------------------------------------------
+
+const char *objTypeNames[numObjTypes] = {
+ "boolean",
+ "integer",
+ "real",
+ "string",
+ "name",
+ "null",
+ "array",
+ "dictionary",
+ "stream",
+ "ref",
+ "cmd",
+ "error",
+ "eof",
+ "none"
+};
+
+#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] =
+ {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);
+ array = new Array(xref);
+ return this;
+}
+
+Object *Object::initDict(XRef *xref) {
+ initObj(objDict);
+ dict = new Dict(xref);
+ return this;
+}
+
+Object *Object::initDict(Dict *dictA) {
+ initObj(objDict);
+ dict = dictA;
+ dict->incRef();
+ return this;
+}
+
+Object *Object::initStream(Stream *streamA) {
+ initObj(objStream);
+ stream = streamA;
+ return this;
+}
+
+Object *Object::copy(Object *obj) {
+ *obj = *this;
+ switch (type) {
+ case objString:
+ obj->string = string->copy();
+ break;
+ case objName:
+ obj->name = copyString(name);
+ break;
+ case objArray:
+ array->incRef();
+ break;
+ case objDict:
+ dict->incRef();
+ break;
+ case objStream:
+ obj->stream = stream->copy();
+ break;
+ case objCmd:
+ obj->cmd = copyString(cmd);
+ break;
+ default:
+ break;
+ }
+#ifdef DEBUG_MEM
+#if MULTITHREADED
+ gAtomicIncrement(&numAlloc[type]);
+#else
+ ++numAlloc[type];
+#endif
+#endif
+ return obj;
+}
+
+Object *Object::fetch(XRef *xref, Object *obj, int recursion) {
+ return (type == objRef && xref) ?
+ xref->fetch(ref.num, ref.gen, obj, recursion) : copy(obj);
+}
+
+void Object::free() {
+ switch (type) {
+ case objString:
+ delete string;
+ break;
+ case objName:
+ gfree(name);
+ break;
+ case objArray:
+ if (!array->decRef()) {
+ delete array;
+ }
+ break;
+ case objDict:
+ if (!dict->decRef()) {
+ delete dict;
+ }
+ break;
+ case objStream:
+ delete stream;
+ break;
+ case objCmd:
+ gfree(cmd);
+ break;
+ default:
+ break;
+ }
+#ifdef DEBUG_MEM
+#if MULTITHREADED
+ gAtomicDecrement(&numAlloc[type]);
+#else
+ --numAlloc[type];
+#endif
+#endif
+ type = objNone;
+}
+
+const char *Object::getTypeName() {
+ return objTypeNames[type];
+}
+
+void Object::print(FILE *f) {
+ Object obj;
+ int i;
+
+ switch (type) {
+ case objBool:
+ fprintf(f, "%s", booln ? "true" : "false");
+ break;
+ case objInt:
+ fprintf(f, "%d", intg);
+ break;
+ case objReal:
+ fprintf(f, "%g", real);
+ break;
+ case objString:
+ fprintf(f, "(");
+ fwrite(string->getCString(), 1, string->getLength(), f);
+ fprintf(f, ")");
+ break;
+ case objName:
+ fprintf(f, "/%s", name);
+ break;
+ case objNull:
+ fprintf(f, "null");
+ break;
+ case objArray:
+ fprintf(f, "[");
+ for (i = 0; i < arrayGetLength(); ++i) {
+ if (i > 0)
+ fprintf(f, " ");
+ arrayGetNF(i, &obj);
+ obj.print(f);
+ obj.free();
+ }
+ fprintf(f, "]");
+ break;
+ case objDict:
+ fprintf(f, "<<");
+ for (i = 0; i < dictGetLength(); ++i) {
+ fprintf(f, " /%s ", dictGetKey(i));
+ dictGetValNF(i, &obj);
+ obj.print(f);
+ obj.free();
+ }
+ fprintf(f, " >>");
+ break;
+ case objStream:
+ fprintf(f, "<stream>");
+ break;
+ case objRef:
+ fprintf(f, "%d %d R", ref.num, ref.gen);
+ break;
+ case objCmd:
+ fprintf(f, "%s", cmd);
+ break;
+ case objError:
+ fprintf(f, "<error>");
+ break;
+ case objEOF:
+ fprintf(f, "<EOF>");
+ break;
+ case objNone:
+ fprintf(f, "<none>");
+ break;
+ }
+}
+
+void Object::memCheck(FILE *f) {
+#ifdef DEBUG_MEM
+ int i;
+ long t;
+
+ t = 0;
+ 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) {
+ fprintf(f, " %-20s: %6ld\n", objTypeNames[i], numAlloc[i]);
+ }
+ }
+ }
+#endif
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Object.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Object.h
new file mode 100644
index 00000000000..cb636510149
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Object.h
@@ -0,0 +1,320 @@
+//========================================================================
+//
+// Object.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef OBJECT_H
+#define OBJECT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "gtypes.h"
+#include "gmem.h"
+#include "gfile.h"
+#include "GString.h"
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+class XRef;
+class Array;
+class Dict;
+class Stream;
+
+//------------------------------------------------------------------------
+// Ref
+//------------------------------------------------------------------------
+
+struct Ref {
+ int num; // object number
+ int gen; // generation number
+};
+
+//------------------------------------------------------------------------
+// object types
+//------------------------------------------------------------------------
+
+enum ObjType {
+ // simple objects
+ objBool, // boolean
+ objInt, // integer
+ objReal, // real
+ objString, // string
+ objName, // name
+ objNull, // null
+
+ // complex objects
+ objArray, // array
+ objDict, // dictionary
+ objStream, // stream
+ objRef, // indirect reference
+
+ // special objects
+ objCmd, // command name
+ objError, // error return from Lexer
+ objEOF, // end of file return from Lexer
+ objNone // uninitialized object
+};
+
+#define numObjTypes 14 // total number of object types
+
+//------------------------------------------------------------------------
+// Object
+//------------------------------------------------------------------------
+
+#ifdef DEBUG_MEM
+# if MULTITHREADED
+# define initObj(t) gAtomicIncrement(&numAlloc[type = t])
+# else
+# define initObj(t) ++numAlloc[type = t]
+# endif
+#else
+#define initObj(t) type = t
+#endif
+
+class Object {
+public:
+
+ // Default constructor.
+ Object():
+ type(objNone) {}
+
+ // Initialize an object.
+ Object *initBool(GBool boolnA)
+ { initObj(objBool); booln = boolnA; return this; }
+ Object *initInt(int intgA)
+ { initObj(objInt); intg = intgA; return this; }
+ Object *initReal(double realA)
+ { initObj(objReal); real = realA; return this; }
+ Object *initString(GString *stringA)
+ { initObj(objString); string = stringA; return this; }
+ Object *initName(const char *nameA)
+ { initObj(objName); name = copyString(nameA); return this; }
+ Object *initNull()
+ { initObj(objNull); return this; }
+ Object *initArray(XRef *xref);
+ Object *initDict(XRef *xref);
+ Object *initDict(Dict *dictA);
+ Object *initStream(Stream *streamA);
+ Object *initRef(int numA, int genA)
+ { initObj(objRef); ref.num = numA; ref.gen = genA; return this; }
+ Object *initCmd(char *cmdA)
+ { initObj(objCmd); cmd = copyString(cmdA); return this; }
+ Object *initError()
+ { initObj(objError); return this; }
+ Object *initEOF()
+ { initObj(objEOF); return this; }
+
+ // Copy an object.
+ Object *copy(Object *obj);
+
+ // If object is a Ref, fetch and return the referenced object.
+ // Otherwise, return a copy of the object.
+ Object *fetch(XRef *xref, Object *obj, int recursion = 0);
+
+ // Free object contents.
+ void free();
+
+ // Type checking.
+ ObjType getType() { return type; }
+ GBool isBool() { return type == objBool; }
+ GBool isInt() { return type == objInt; }
+ GBool isReal() { return type == objReal; }
+ GBool isNum() { return type == objInt || type == objReal; }
+ GBool isString() { return type == objString; }
+ GBool isName() { return type == objName; }
+ GBool isNull() { return type == objNull; }
+ GBool isArray() { return type == objArray; }
+ GBool isDict() { return type == objDict; }
+ GBool isStream() { return type == objStream; }
+ GBool isRef() { return type == objRef; }
+ GBool isCmd() { return type == objCmd; }
+ GBool isError() { return type == objError; }
+ GBool isEOF() { return type == objEOF; }
+ GBool isNone() { return type == objNone; }
+
+ // Special type checking.
+ GBool isName(const char *nameA)
+ { return type == objName && !strcmp(name, nameA); }
+ GBool isDict(const char *dictType);
+ GBool isStream(char *dictType);
+ GBool isCmd(const char *cmdA)
+ { return type == objCmd && !strcmp(cmd, cmdA); }
+
+ // Accessors. NB: these assume object is of correct type.
+ GBool getBool() { return booln; }
+ int getInt() { return intg; }
+ double getReal() { return real; }
+ double getNum() { return type == objInt ? (double)intg : real; }
+ GString *getString() { return string; }
+ char *getName() { return name; }
+ Array *getArray() { return array; }
+ Dict *getDict() { return dict; }
+ Stream *getStream() { return stream; }
+ Ref getRef() { return ref; }
+ int getRefNum() { return ref.num; }
+ int getRefGen() { return ref.gen; }
+ char *getCmd() { return cmd; }
+
+ // Array accessors.
+ int arrayGetLength();
+ void arrayAdd(Object *elem);
+ Object *arrayGet(int i, Object *obj, int recursion = 0);
+ Object *arrayGetNF(int i, Object *obj);
+
+ // Dict accessors.
+ int dictGetLength();
+ void dictAdd(char *key, Object *val);
+ GBool dictIs(const char *dictType);
+ Object *dictLookup(const char *key, Object *obj, int recursion = 0);
+ Object *dictLookupNF(const char *key, Object *obj);
+ char *dictGetKey(int i);
+ Object *dictGetVal(int i, Object *obj);
+ Object *dictGetValNF(int i, Object *obj);
+
+ // Stream accessors.
+ GBool streamIs(char *dictType);
+ void streamReset();
+ void streamClose();
+ int streamGetChar();
+ int streamLookChar();
+ int streamGetBlock(char *blk, int size);
+ char *streamGetLine(char *buf, int size);
+ GFileOffset streamGetPos();
+ void streamSetPos(GFileOffset pos, int dir = 0);
+ Dict *streamGetDict();
+
+ // Output.
+ const char *getTypeName();
+ void print(FILE *f = stdout);
+
+ // Memory testing.
+ static void memCheck(FILE *f);
+
+private:
+
+ ObjType type; // object type
+ union { // value for each type:
+ GBool booln; // boolean
+ int intg; // integer
+ double real; // real
+ GString *string; // string
+ char *name; // name
+ Array *array; // array
+ Dict *dict; // dictionary
+ Stream *stream; // stream
+ Ref ref; // indirect reference
+ char *cmd; // command
+ };
+
+#ifdef DEBUG_MEM
+#if MULTITHREADED
+ static GAtomicCounter // number of each type of object
+ numAlloc[numObjTypes]; // currently allocated
+#else
+ static int // number of each type of object
+ numAlloc[numObjTypes]; // currently allocated
+#endif
+#endif
+};
+
+//------------------------------------------------------------------------
+// Array accessors.
+//------------------------------------------------------------------------
+
+#include "Array.h"
+
+inline int Object::arrayGetLength()
+ { return array->getLength(); }
+
+inline void Object::arrayAdd(Object *elem)
+ { array->add(elem); }
+
+inline Object *Object::arrayGet(int i, Object *obj, int recursion)
+ { return array->get(i, obj, recursion); }
+
+inline Object *Object::arrayGetNF(int i, Object *obj)
+ { return array->getNF(i, obj); }
+
+//------------------------------------------------------------------------
+// Dict accessors.
+//------------------------------------------------------------------------
+
+#include "Dict.h"
+
+inline int Object::dictGetLength()
+ { return dict->getLength(); }
+
+inline void Object::dictAdd(char *key, Object *val)
+ { dict->add(key, val); }
+
+inline GBool Object::dictIs(const char *dictType)
+ { return dict->is(dictType); }
+
+inline GBool Object::isDict(const char *dictType)
+ { return type == objDict && dictIs(dictType); }
+
+inline Object *Object::dictLookup(const char *key, Object *obj, int recursion)
+ { return dict->lookup(key, obj, recursion); }
+
+inline Object *Object::dictLookupNF(const char *key, Object *obj)
+ { return dict->lookupNF(key, obj); }
+
+inline char *Object::dictGetKey(int i)
+ { return dict->getKey(i); }
+
+inline Object *Object::dictGetVal(int i, Object *obj)
+ { return dict->getVal(i, obj); }
+
+inline Object *Object::dictGetValNF(int i, Object *obj)
+ { return dict->getValNF(i, obj); }
+
+//------------------------------------------------------------------------
+// Stream accessors.
+//------------------------------------------------------------------------
+
+#include "Stream.h"
+
+inline GBool Object::streamIs(char *dictType)
+ { return stream->getDict()->is(dictType); }
+
+inline GBool Object::isStream(char *dictType)
+ { return type == objStream && streamIs(dictType); }
+
+inline void Object::streamReset()
+ { stream->reset(); }
+
+inline void Object::streamClose()
+ { stream->close(); }
+
+inline int Object::streamGetChar()
+ { return stream->getChar(); }
+
+inline int Object::streamLookChar()
+ { return stream->lookChar(); }
+
+inline int Object::streamGetBlock(char *blk, int size)
+ { return stream->getBlock(blk, size); }
+
+inline char *Object::streamGetLine(char *buf, int size)
+ { return stream->getLine(buf, size); }
+
+inline GFileOffset Object::streamGetPos()
+ { return stream->getPos(); }
+
+inline void Object::streamSetPos(GFileOffset pos, int dir)
+ { stream->setPos(pos, dir); }
+
+inline Dict *Object::streamGetDict()
+ { return stream->getDict(); }
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc
new file mode 100644
index 00000000000..a0fbb1b06c2
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.cc
@@ -0,0 +1,537 @@
+//========================================================================
+//
+// OptionalContent.cc
+//
+// Copyright 2008-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "Object.h"
+#include "PDFDoc.h"
+#include "TextString.h"
+#include "OptionalContent.h"
+
+//------------------------------------------------------------------------
+
+#define ocPolicyAllOn 1
+#define ocPolicyAnyOn 2
+#define ocPolicyAnyOff 3
+#define ocPolicyAllOff 4
+
+//------------------------------------------------------------------------
+
+// Max depth of nested visibility expressions. This is used to catch
+// infinite loops in the visibility expression object structure.
+#define visibilityExprRecursionLimit 50
+
+// Max depth of nested display nodes. This is used to catch infinite
+// loops in the "Order" object structure.
+#define displayNodeRecursionLimit 50
+
+//------------------------------------------------------------------------
+
+OptionalContent::OptionalContent(PDFDoc *doc) {
+ Object *ocProps;
+ Object ocgList, defView, uad, obj1, obj2, obj3, obj4;
+ Ref ref1;
+ OptionalContentGroup *ocg;
+ int i, j;
+
+ xref = doc->getXRef();
+ ocgs = new GList();
+ display = NULL;
+
+ if ((ocProps = doc->getCatalog()->getOCProperties())->isDict()) {
+ if (ocProps->dictLookup("OCGs", &ocgList)->isArray()) {
+
+ //----- read the OCG list
+ for (i = 0; i < ocgList.arrayGetLength(); ++i) {
+ if (ocgList.arrayGetNF(i, &obj1)->isRef()) {
+ ref1 = obj1.getRef();
+ obj1.fetch(xref, &obj2);
+ if ((ocg = OptionalContentGroup::parse(&ref1, &obj2))) {
+ ocgs->append(ocg);
+ }
+ obj2.free();
+ }
+ obj1.free();
+ }
+
+ //----- read the default viewing OCCD
+ if (ocProps->dictLookup("D", &defView)->isDict()) {
+
+ //----- read the usage app dicts
+ if (defView.dictLookup("AS", &obj1)->isArray()) {
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ if (obj1.arrayGet(i, &uad)->isDict()) {
+ if (uad.dictLookup("Event", &obj2)->isName("View")) {
+ if (uad.dictLookup("OCGs", &obj3)->isArray()) {
+ for (j = 0; j < obj3.arrayGetLength(); ++j) {
+ if (obj3.arrayGetNF(j, &obj4)->isRef()) {
+ ref1 = obj4.getRef();
+ if ((ocg = findOCG(&ref1))) {
+ ocg->setInViewUsageAppDict();
+ }
+ }
+ obj4.free();
+ }
+ }
+ obj3.free();
+ }
+ obj2.free();
+ }
+ uad.free();
+ }
+ }
+ obj1.free();
+
+ //----- initial state from OCCD
+ if (defView.dictLookup("OFF", &obj1)->isArray()) {
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ if (obj1.arrayGetNF(i, &obj2)->isRef()) {
+ ref1 = obj2.getRef();
+ if ((ocg = findOCG(&ref1))) {
+ ocg->setState(gFalse);
+ } else {
+ error(errSyntaxError, -1,
+ "Invalid OCG reference in OFF array in default viewing OCCD");
+ }
+ }
+ obj2.free();
+ }
+ }
+ obj1.free();
+
+ //----- initial state from OCG usage dict
+ for (i = 0; i < ocgs->getLength(); ++i) {
+ ocg = (OptionalContentGroup *)ocgs->get(i);
+ if (ocg->getInViewUsageAppDict() &&
+ ocg->getViewState() != ocUsageUnset) {
+ ocg->setState(ocg->getViewState() == ocUsageOn);
+ }
+ }
+
+ //----- display order
+ if (defView.dictLookup("Order", &obj1)->isArray()) {
+ display = OCDisplayNode::parse(&obj1, this, xref);
+ }
+ obj1.free();
+
+ } else {
+ error(errSyntaxError, -1, "Missing or invalid default viewing OCCD");
+ }
+ defView.free();
+
+ }
+ ocgList.free();
+ }
+
+ if (!display) {
+ display = new OCDisplayNode();
+ }
+}
+
+OptionalContent::~OptionalContent() {
+ deleteGList(ocgs, OptionalContentGroup);
+ delete display;
+}
+
+int OptionalContent::getNumOCGs() {
+ return ocgs->getLength();
+}
+
+OptionalContentGroup *OptionalContent::getOCG(int idx) {
+ return (OptionalContentGroup *)ocgs->get(idx);
+}
+
+OptionalContentGroup *OptionalContent::findOCG(Ref *ref) {
+ OptionalContentGroup *ocg;
+ int i;
+
+ for (i = 0; i < ocgs->getLength(); ++i) {
+ ocg = (OptionalContentGroup *)ocgs->get(i);
+ if (ocg->matches(ref)) {
+ return ocg;
+ }
+ }
+ return NULL;
+}
+
+GBool OptionalContent::evalOCObject(Object *obj, GBool *visible) {
+ OptionalContentGroup *ocg;
+ int policy;
+ Ref ref;
+ Object obj2, obj3, obj4, obj5;
+ GBool gotOCG;
+ int i;
+
+ if (obj->isNull()) {
+ return gFalse;
+ }
+ if (obj->isRef()) {
+ ref = obj->getRef();
+ if ((ocg = findOCG(&ref))) {
+ *visible = ocg->getState();
+ return gTrue;
+ }
+ }
+ obj->fetch(xref, &obj2);
+ if (!obj2.isDict("OCMD")) {
+ obj2.free();
+ return gFalse;
+ }
+ if (obj2.dictLookup("VE", &obj3)->isArray()) {
+ *visible = evalOCVisibilityExpr(&obj3, 0);
+ obj3.free();
+ } else {
+ obj3.free();
+ policy = ocPolicyAnyOn;
+ if (obj2.dictLookup("P", &obj3)->isName()) {
+ if (obj3.isName("AllOn")) {
+ policy = ocPolicyAllOn;
+ } else if (obj3.isName("AnyOn")) {
+ policy = ocPolicyAnyOn;
+ } else if (obj3.isName("AnyOff")) {
+ policy = ocPolicyAnyOff;
+ } else if (obj3.isName("AllOff")) {
+ policy = ocPolicyAllOff;
+ }
+ }
+ obj3.free();
+ obj2.dictLookupNF("OCGs", &obj3);
+ ocg = NULL;
+ if (obj3.isRef()) {
+ ref = obj3.getRef();
+ ocg = findOCG(&ref);
+ }
+ if (ocg) {
+ *visible = (policy == ocPolicyAllOn || policy == ocPolicyAnyOn) ?
+ ocg->getState() : !ocg->getState();
+ } else {
+ *visible = policy == ocPolicyAllOn || policy == ocPolicyAllOff;
+ if (!obj3.fetch(xref, &obj4)->isArray()) {
+ obj4.free();
+ obj3.free();
+ 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))) {
+ gotOCG = gTrue;
+ switch (policy) {
+ case ocPolicyAllOn:
+ *visible = *visible && ocg->getState();
+ break;
+ case ocPolicyAnyOn:
+ *visible = *visible || ocg->getState();
+ break;
+ case ocPolicyAnyOff:
+ *visible = *visible || !ocg->getState();
+ break;
+ case ocPolicyAllOff:
+ *visible = *visible && !ocg->getState();
+ break;
+ }
+ }
+ }
+ obj5.free();
+ }
+ if (!gotOCG) {
+ // if the "OCGs" array is "empty or contains references only
+ // to null or deleted objects", this OCMD doesn't have any
+ // effect
+ obj4.free();
+ obj3.free();
+ obj2.free();
+ return gFalse;
+ }
+ obj4.free();
+ }
+ obj3.free();
+ }
+ obj2.free();
+ return gTrue;
+}
+
+GBool OptionalContent::evalOCVisibilityExpr(Object *expr, int recursion) {
+ OptionalContentGroup *ocg;
+ Object expr2, op, obj;
+ Ref ref;
+ GBool ret;
+ int i;
+
+ if (recursion > visibilityExprRecursionLimit) {
+ error(errSyntaxError, -1,
+ "Loop detected in optional content visibility expression");
+ return gTrue;
+ }
+ if (expr->isRef()) {
+ ref = expr->getRef();
+ if ((ocg = findOCG(&ref))) {
+ return ocg->getState();
+ }
+ }
+ expr->fetch(xref, &expr2);
+ if (!expr2.isArray() || expr2.arrayGetLength() < 1) {
+ error(errSyntaxError, -1,
+ "Invalid optional content visibility expression");
+ expr2.free();
+ return gTrue;
+ }
+ expr2.arrayGet(0, &op);
+ if (op.isName("Not")) {
+ if (expr2.arrayGetLength() == 2) {
+ expr2.arrayGetNF(1, &obj);
+ ret = !evalOCVisibilityExpr(&obj, recursion + 1);
+ obj.free();
+ } else {
+ error(errSyntaxError, -1,
+ "Invalid optional content visibility expression");
+ ret = gTrue;
+ }
+ } else if (op.isName("And")) {
+ ret = gTrue;
+ for (i = 1; i < expr2.arrayGetLength() && ret; ++i) {
+ expr2.arrayGetNF(i, &obj);
+ ret = evalOCVisibilityExpr(&obj, recursion + 1);
+ obj.free();
+ }
+ } else if (op.isName("Or")) {
+ ret = gFalse;
+ for (i = 1; i < expr2.arrayGetLength() && !ret; ++i) {
+ expr2.arrayGetNF(i, &obj);
+ ret = evalOCVisibilityExpr(&obj, recursion + 1);
+ obj.free();
+ }
+ } else {
+ error(errSyntaxError, -1,
+ "Invalid optional content visibility expression");
+ ret = gTrue;
+ }
+ op.free();
+ expr2.free();
+ return ret;
+}
+
+//------------------------------------------------------------------------
+
+OptionalContentGroup *OptionalContentGroup::parse(Ref *refA, Object *obj) {
+ TextString *nameA;
+ Object obj1, obj2, obj3;
+ OCUsageState viewStateA, printStateA;
+
+ if (!obj->isDict()) {
+ return NULL;
+ }
+ if (!obj->dictLookup("Name", &obj1)->isString()) {
+ error(errSyntaxError, -1, "Missing or invalid Name in OCG");
+ obj1.free();
+ return NULL;
+ }
+ nameA = new TextString(obj1.getString());
+ obj1.free();
+
+ viewStateA = printStateA = ocUsageUnset;
+ if (obj->dictLookup("Usage", &obj1)->isDict()) {
+ if (obj1.dictLookup("View", &obj2)->isDict()) {
+ if (obj2.dictLookup("ViewState", &obj3)->isName()) {
+ if (obj3.isName("ON")) {
+ viewStateA = ocUsageOn;
+ } else {
+ viewStateA = ocUsageOff;
+ }
+ }
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("Print", &obj2)->isDict()) {
+ if (obj2.dictLookup("PrintState", &obj3)->isName()) {
+ if (obj3.isName("ON")) {
+ printStateA = ocUsageOn;
+ } else {
+ printStateA = ocUsageOff;
+ }
+ }
+ obj3.free();
+ }
+ obj2.free();
+ }
+ obj1.free();
+
+ return new OptionalContentGroup(refA, nameA, viewStateA, printStateA);
+}
+
+OptionalContentGroup::OptionalContentGroup(Ref *refA, TextString *nameA,
+ OCUsageState viewStateA,
+ OCUsageState printStateA) {
+ ref = *refA;
+ name = nameA;
+ viewState = viewStateA;
+ printState = printStateA;
+ state = gTrue;
+ inViewUsageAppDict = gFalse;
+}
+
+OptionalContentGroup::~OptionalContentGroup() {
+ delete name;
+}
+
+GBool OptionalContentGroup::matches(Ref *refA) {
+ return refA->num == ref.num && refA->gen == ref.gen;
+}
+
+Unicode *OptionalContentGroup::getName() {
+ return name->getUnicode();
+}
+
+int OptionalContentGroup::getNameLength() {
+ return name->getLength();
+}
+
+//------------------------------------------------------------------------
+
+OCDisplayNode *OCDisplayNode::parse(Object *obj, OptionalContent *oc,
+ XRef *xref, int recursion) {
+ Object obj2, obj3;
+ Ref ref;
+ OptionalContentGroup *ocgA;
+ OCDisplayNode *node, *child;
+ int i;
+
+ if (recursion > displayNodeRecursionLimit) {
+ error(errSyntaxError, -1, "Loop detected in optional content order");
+ return NULL;
+ }
+ if (obj->isRef()) {
+ ref = obj->getRef();
+ if ((ocgA = oc->findOCG(&ref))) {
+ return new OCDisplayNode(ocgA);
+ }
+ }
+ obj->fetch(xref, &obj2);
+ if (!obj2.isArray()) {
+ obj2.free();
+ return NULL;
+ }
+ i = 0;
+ if (obj2.arrayGetLength() >= 1) {
+ if (obj2.arrayGet(0, &obj3)->isString()) {
+ node = new OCDisplayNode(obj3.getString());
+ i = 1;
+ } else {
+ node = new OCDisplayNode();
+ }
+ obj3.free();
+ } else {
+ node = new OCDisplayNode();
+ }
+ for (; i < obj2.arrayGetLength(); ++i) {
+ obj2.arrayGetNF(i, &obj3);
+ if ((child = OCDisplayNode::parse(&obj3, oc, xref, recursion + 1))) {
+ if (!child->ocg && !child->name && node->getNumChildren() > 0) {
+ if (child->getNumChildren() > 0) {
+ node->getChild(node->getNumChildren() - 1)->
+ addChildren(child->takeChildren());
+ }
+ delete child;
+ } else {
+ node->addChild(child);
+ }
+ }
+ obj3.free();
+ }
+ obj2.free();
+ return node;
+}
+
+OCDisplayNode::OCDisplayNode() {
+ name = new TextString();
+ ocg = NULL;
+ parent = NULL;
+ children = NULL;
+}
+
+OCDisplayNode::OCDisplayNode(GString *nameA) {
+ name = new TextString(nameA);
+ ocg = NULL;
+ children = NULL;
+}
+
+OCDisplayNode::OCDisplayNode(OptionalContentGroup *ocgA) {
+ name = new TextString(ocgA->name);
+ ocg = ocgA;
+ children = NULL;
+}
+
+void OCDisplayNode::addChild(OCDisplayNode *child) {
+ if (!children) {
+ children = new GList();
+ }
+ children->append(child);
+ child->parent = this;
+}
+
+void OCDisplayNode::addChildren(GList *childrenA) {
+ int i;
+
+ if (!children) {
+ children = new GList();
+ }
+ children->append(childrenA);
+ for (i = 0; i < childrenA->getLength(); ++i) {
+ ((OCDisplayNode *)childrenA->get(i))->parent = this;
+ }
+ delete childrenA;
+}
+
+GList *OCDisplayNode::takeChildren() {
+ GList *childrenA;
+ int i;
+
+ childrenA = children;
+ children = NULL;
+ for (i = 0; i < childrenA->getLength(); ++i) {
+ ((OCDisplayNode *)childrenA->get(i))->parent = NULL;
+ }
+ return childrenA;
+}
+
+OCDisplayNode::~OCDisplayNode() {
+ delete name;
+ if (children) {
+ deleteGList(children, OCDisplayNode);
+ }
+}
+
+Unicode *OCDisplayNode::getName() {
+ return name->getUnicode();
+}
+
+int OCDisplayNode::getNameLength() {
+ return name->getLength();
+}
+
+int OCDisplayNode::getNumChildren() {
+ if (!children) {
+ return 0;
+ }
+ return children->getLength();
+}
+
+OCDisplayNode *OCDisplayNode::getChild(int idx) {
+ return (OCDisplayNode *)children->get(idx);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.h b/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.h
new file mode 100644
index 00000000000..f00ef296a0c
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/OptionalContent.h
@@ -0,0 +1,139 @@
+//========================================================================
+//
+// OptionalContent.h
+//
+// Copyright 2008-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef OPTIONALCONTENT_H
+#define OPTIONALCONTENT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+#include "CharTypes.h"
+
+class GString;
+class GList;
+class PDFDoc;
+class TextString;
+class XRef;
+class OptionalContentGroup;
+class OCDisplayNode;
+
+//------------------------------------------------------------------------
+
+class OptionalContent {
+public:
+
+ OptionalContent(PDFDoc *doc);
+ ~OptionalContent();
+
+ // Walk the list of optional content groups.
+ int getNumOCGs();
+ OptionalContentGroup *getOCG(int idx);
+
+ // Find an OCG by indirect reference.
+ OptionalContentGroup *findOCG(Ref *ref);
+
+ // Get the root node of the optional content group display tree
+ // (which does not necessarily include all of the OCGs).
+ OCDisplayNode *getDisplayRoot() { return display; }
+
+ // Evaluate an optional content object -- either an OCG or an OCMD.
+ // If <obj> is a valid OCG or OCMD, sets *<visible> and returns
+ // true; otherwise returns false.
+ GBool evalOCObject(Object *obj, GBool *visible);
+
+private:
+
+ GBool evalOCVisibilityExpr(Object *expr, int recursion);
+
+ XRef *xref;
+ GList *ocgs; // all OCGs [OptionalContentGroup]
+ OCDisplayNode *display; // root node of display tree
+};
+
+//------------------------------------------------------------------------
+
+// Values from the optional content usage dictionary.
+enum OCUsageState {
+ ocUsageOn,
+ ocUsageOff,
+ ocUsageUnset
+};
+
+//------------------------------------------------------------------------
+
+class OptionalContentGroup {
+public:
+
+ static OptionalContentGroup *parse(Ref *refA, Object *obj);
+ ~OptionalContentGroup();
+
+ GBool matches(Ref *refA);
+
+ Unicode *getName();
+ int getNameLength();
+ OCUsageState getViewState() { return viewState; }
+ OCUsageState getPrintState() { return printState; }
+ GBool getState() { return state; }
+ void setState(GBool stateA) { state = stateA; }
+ GBool getInViewUsageAppDict() { return inViewUsageAppDict; }
+ void setInViewUsageAppDict() { inViewUsageAppDict = gTrue; }
+
+private:
+
+ OptionalContentGroup(Ref *refA, TextString *nameA,
+ OCUsageState viewStateA, OCUsageState printStateA);
+
+ Ref ref;
+ TextString *name;
+ OCUsageState viewState, // suggested state when viewing
+ printState; // suggested state when printing
+ GBool state; // current state (on/off)
+ GBool inViewUsageAppDict; // true if this OCG is listed in a
+ // usage app dict with Event=View
+
+ friend class OCDisplayNode;
+};
+
+//------------------------------------------------------------------------
+
+class OCDisplayNode {
+public:
+
+ static OCDisplayNode *parse(Object *obj, OptionalContent *oc, XRef *xref,
+ int recursion = 0);
+ OCDisplayNode();
+ ~OCDisplayNode();
+
+ Unicode *getName();
+ int getNameLength();
+ OptionalContentGroup *getOCG() { return ocg; }
+ int getNumChildren();
+ OCDisplayNode *getChild(int idx);
+ OCDisplayNode *getParent() { return parent; }
+
+private:
+
+ OCDisplayNode(GString *nameA);
+ OCDisplayNode(OptionalContentGroup *ocgA);
+ void addChild(OCDisplayNode *child);
+ void addChildren(GList *childrenA);
+ GList *takeChildren();
+
+ TextString *name; // display name
+ OptionalContentGroup *ocg; // NULL for display labels
+ OCDisplayNode *parent; // parent node; NULL at root
+ GList *children; // NULL if there are no children
+ // [OCDisplayNode]
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Outline.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Outline.cc
new file mode 100644
index 00000000000..9593956f8d0
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Outline.cc
@@ -0,0 +1,186 @@
+//========================================================================
+//
+// Outline.cc
+//
+// Copyright 2002-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "Link.h"
+#include "TextString.h"
+#include "Outline.h"
+
+//------------------------------------------------------------------------
+
+Outline::Outline(Object *outlineObj, XRef *xref) {
+ Object first, last;
+
+ items = NULL;
+ if (!outlineObj->isDict()) {
+ return;
+ }
+ outlineObj->dictLookupNF("First", &first);
+ outlineObj->dictLookupNF("Last", &last);
+ if (first.isRef() && last.isRef()) {
+ items = OutlineItem::readItemList(&first, &last, NULL, xref);
+ }
+ first.free();
+ last.free();
+}
+
+Outline::~Outline() {
+ if (items) {
+ deleteGList(items, OutlineItem);
+ }
+}
+
+//------------------------------------------------------------------------
+
+OutlineItem::OutlineItem(Object *itemRefA, Dict *dict,
+ OutlineItem *parentA, XRef *xrefA) {
+ Object obj1;
+
+ xref = xrefA;
+ title = NULL;
+ action = NULL;
+ kids = NULL;
+ parent = parentA;
+
+ if (dict->lookup("Title", &obj1)->isString()) {
+ title = new TextString(obj1.getString());
+ }
+ obj1.free();
+
+ if (!dict->lookup("Dest", &obj1)->isNull()) {
+ action = LinkAction::parseDest(&obj1);
+ } else {
+ obj1.free();
+ if (!dict->lookup("A", &obj1)->isNull()) {
+ action = LinkAction::parseAction(&obj1);
+ }
+ }
+ obj1.free();
+
+ itemRefA->copy(&itemRef);
+ dict->lookupNF("First", &firstRef);
+ dict->lookupNF("Last", &lastRef);
+ dict->lookupNF("Next", &nextRef);
+
+ startsOpen = gFalse;
+ if (dict->lookup("Count", &obj1)->isInt()) {
+ if (obj1.getInt() > 0) {
+ startsOpen = gTrue;
+ }
+ }
+ obj1.free();
+
+ pageNum = -1;
+}
+
+OutlineItem::~OutlineItem() {
+ close();
+ if (title) {
+ delete title;
+ }
+ if (action) {
+ delete action;
+ }
+ itemRef.free();
+ firstRef.free();
+ lastRef.free();
+ nextRef.free();
+}
+
+GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef,
+ OutlineItem *parentA, XRef *xrefA) {
+ GList *items;
+ OutlineItem *item, *sibling;
+ Object obj;
+ Object *p;
+ OutlineItem *ancestor;
+ int i;
+
+ items = new GList();
+ if (!firstItemRef->isRef() || !lastItemRef->isRef()) {
+ return items;
+ }
+ p = firstItemRef;
+ do {
+ if (!p->fetch(xrefA, &obj)->isDict()) {
+ obj.free();
+ break;
+ }
+ item = new OutlineItem(p, obj.getDict(), parentA, xrefA);
+ obj.free();
+
+ // check for loops with parents
+ for (ancestor = parentA; ancestor; ancestor = ancestor->parent) {
+ if (p->getRefNum() == ancestor->itemRef.getRefNum() &&
+ p->getRefGen() == ancestor->itemRef.getRefGen()) {
+ error(errSyntaxError, -1, "Loop detected in outline");
+ break;
+ }
+ }
+ if (ancestor) {
+ delete item;
+ break;
+ }
+
+ // check for loops with siblings
+ for (i = 0; i < items->getLength(); ++i) {
+ sibling = (OutlineItem *)items->get(i);
+ if (p->getRefNum() == sibling->itemRef.getRefNum() &&
+ p->getRefGen() == sibling->itemRef.getRefGen()) {
+ error(errSyntaxError, -1, "Loop detected in outline");
+ break;
+ }
+ }
+ if (i < items->getLength()) {
+ delete item;
+ break;
+ }
+
+ items->append(item);
+ if (p->getRefNum() == lastItemRef->getRef().num &&
+ p->getRefGen() == lastItemRef->getRef().gen) {
+ break;
+ }
+ p = &item->nextRef;
+ if (!p->isRef()) {
+ break;
+ }
+ } while (p);
+ return items;
+}
+
+void OutlineItem::open() {
+ if (!kids) {
+ kids = readItemList(&firstRef, &lastRef, this, xref);
+ }
+}
+
+void OutlineItem::close() {
+ if (kids) {
+ deleteGList(kids, OutlineItem);
+ kids = NULL;
+ }
+}
+
+Unicode *OutlineItem::getTitle() {
+ return title ? title->getUnicode() : (Unicode *)NULL;
+}
+
+int OutlineItem::getTitleLength() {
+ return title ? title->getLength() : 0;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Outline.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Outline.h
new file mode 100644
index 00000000000..e9bce83cdad
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Outline.h
@@ -0,0 +1,84 @@
+//========================================================================
+//
+// Outline.h
+//
+// Copyright 2002-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef OUTLINE_H
+#define OUTLINE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+#include "CharTypes.h"
+
+class GString;
+class GList;
+class XRef;
+class LinkAction;
+class TextString;
+
+//------------------------------------------------------------------------
+
+class Outline {
+public:
+
+ Outline(Object *outlineObj, XRef *xref);
+ ~Outline();
+
+ GList *getItems() { return items; }
+
+private:
+
+ GList *items; // NULL if document has no outline
+ // [OutlineItem]
+};
+
+//------------------------------------------------------------------------
+
+class OutlineItem {
+public:
+
+ OutlineItem(Object *itemRefA, Dict *dict, OutlineItem *parentA, XRef *xrefA);
+ ~OutlineItem();
+
+ static GList *readItemList(Object *firstItemRef, Object *lastItemRef,
+ OutlineItem *parentA, XRef *xrefA);
+
+ void open();
+ void close();
+
+ Unicode *getTitle();
+ int getTitleLength();
+ TextString *getTitleTextString() { return title; }
+ LinkAction *getAction() { return action; }
+ GBool isOpen() { return startsOpen; }
+ GBool hasKids() { return firstRef.isRef(); }
+ GList *getKids() { return kids; }
+ OutlineItem *getParent() { return parent; }
+
+private:
+
+ friend class PDFDoc;
+
+ XRef *xref;
+ TextString *title; // may be NULL
+ LinkAction *action;
+ Object itemRef;
+ Object firstRef;
+ Object lastRef;
+ Object nextRef;
+ GBool startsOpen;
+ int pageNum; // page number (used by
+ // PDFDoc::getOutlineTargetPage)
+ GList *kids; // NULL unless this item is open [OutlineItem]
+ OutlineItem *parent;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc
new file mode 100644
index 00000000000..f2be88edfdc
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.cc
@@ -0,0 +1,140 @@
+//========================================================================
+//
+// OutputDev.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "gmempp.h"
+#include "Object.h"
+#include "Stream.h"
+#include "GfxState.h"
+#include "OutputDev.h"
+
+//------------------------------------------------------------------------
+// OutputDev
+//------------------------------------------------------------------------
+
+void OutputDev::setDefaultCTM(double *ctm) {
+ int i;
+ double det;
+
+ for (i = 0; i < 6; ++i) {
+ defCTM[i] = ctm[i];
+ }
+ det = 1 / (defCTM[0] * defCTM[3] - defCTM[1] * defCTM[2]);
+ defICTM[0] = defCTM[3] * det;
+ defICTM[1] = -defCTM[1] * det;
+ defICTM[2] = -defCTM[2] * det;
+ defICTM[3] = defCTM[0] * det;
+ defICTM[4] = (defCTM[2] * defCTM[5] - defCTM[3] * defCTM[4]) * det;
+ defICTM[5] = (defCTM[1] * defCTM[4] - defCTM[0] * defCTM[5]) * det;
+}
+
+void OutputDev::cvtDevToUser(double dx, double dy, double *ux, double *uy) {
+ *ux = defICTM[0] * dx + defICTM[2] * dy + defICTM[4];
+ *uy = defICTM[1] * dx + defICTM[3] * dy + defICTM[5];
+}
+
+void OutputDev::cvtUserToDev(double ux, double uy, double *dx, double *dy) {
+ *dx = defCTM[0] * ux + defCTM[2] * uy + defCTM[4];
+ *dy = defCTM[1] * ux + defCTM[3] * uy + defCTM[5];
+}
+
+void OutputDev::cvtUserToDev(double ux, double uy, int *dx, int *dy) {
+ *dx = (int)(defCTM[0] * ux + defCTM[2] * uy + defCTM[4] + 0.5);
+ *dy = (int)(defCTM[1] * ux + defCTM[3] * uy + defCTM[5] + 0.5);
+}
+
+void OutputDev::updateAll(GfxState *state) {
+ updateLineDash(state);
+ updateFlatness(state);
+ updateLineJoin(state);
+ updateLineCap(state);
+ updateMiterLimit(state);
+ updateLineWidth(state);
+ updateStrokeAdjust(state);
+ updateFillColorSpace(state);
+ updateFillColor(state);
+ updateStrokeColorSpace(state);
+ updateStrokeColor(state);
+ updateBlendMode(state);
+ updateFillOpacity(state);
+ updateStrokeOpacity(state);
+ updateFillOverprint(state);
+ updateStrokeOverprint(state);
+ updateOverprintMode(state);
+ updateTransfer(state);
+ updateFont(state);
+}
+
+GBool OutputDev::beginType3Char(GfxState *state, double x, double y,
+ double dx, double dy,
+ CharCode code, Unicode *u, int uLen) {
+ return gFalse;
+}
+
+void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate) {
+ if (inlineImg) {
+ str->reset();
+ str->discardChars(height * ((width + 7) / 8));
+ str->close();
+ }
+}
+
+void OutputDev::setSoftMaskFromImageMask(GfxState *state,
+ Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate) {
+ drawImageMask(state, ref, str, width, height, invert, inlineImg, interpolate);
+}
+
+void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg, GBool interpolate) {
+ if (inlineImg) {
+ str->reset();
+ str->discardChars(height * ((width * colorMap->getNumPixelComps() *
+ colorMap->getBits() + 7) / 8));
+ str->close();
+ }
+}
+
+void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GBool maskInvert, GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap, NULL, gFalse,
+ interpolate);
+}
+
+void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ double *matte, GBool interpolate) {
+ drawImage(state, ref, str, width, height, colorMap, NULL, gFalse,
+ interpolate);
+}
+
+#if OPI_SUPPORT
+void OutputDev::opiBegin(GfxState *state, Dict *opiDict) {
+}
+
+void OutputDev::opiEnd(GfxState *state, Dict *opiDict) {
+}
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h
new file mode 100644
index 00000000000..6fe7eeedb4e
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/OutputDev.h
@@ -0,0 +1,251 @@
+//========================================================================
+//
+// OutputDev.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef OUTPUTDEV_H
+#define OUTPUTDEV_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "CharTypes.h"
+
+class GString;
+class Gfx;
+class GfxState;
+struct GfxColor;
+class GfxColorSpace;
+class GfxImageColorMap;
+class GfxShading;
+class Stream;
+class Links;
+class Link;
+class Catalog;
+class Page;
+class Function;
+
+//------------------------------------------------------------------------
+// OutputDev
+//------------------------------------------------------------------------
+
+class OutputDev {
+public:
+
+ // Constructor.
+ OutputDev() {}
+
+ // Destructor.
+ virtual ~OutputDev() {}
+
+ //----- get info about output device
+
+ // Does this device use upside-down coordinates?
+ // (Upside-down means (0,0) is the top left corner of the page.)
+ virtual GBool upsideDown() = 0;
+
+ // Does this device use drawChar() or drawString()?
+ virtual GBool useDrawChar() = 0;
+
+ // Does this device use tilingPatternFill()? If this returns false,
+ // tiling pattern fills will be reduced to a series of other drawing
+ // operations.
+ virtual GBool useTilingPatternFill() { 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; }
+
+ // Does this device use beginType3Char/endType3Char? Otherwise,
+ // text in Type 3 fonts will be drawn with drawChar/drawString.
+ virtual GBool interpretType3Chars() = 0;
+
+ // Does this device need non-text content?
+ virtual GBool needNonText() { return gTrue; }
+
+ // Does this device require incCharCount to be called for text on
+ // non-shown layers?
+ virtual GBool needCharCount() { return gFalse; }
+
+
+
+ //----- initialization and control
+
+ // Set default transform matrix.
+ virtual void setDefaultCTM(double *ctm);
+
+ // Check to see if a page slice should be displayed. If this
+ // returns false, the page display is aborted. Typically, an
+ // OutputDev will use some alternate means to display the page
+ // before returning false.
+ virtual GBool checkPageSlice(Page *page, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool crop,
+ int sliceX, int sliceY, int sliceW, int sliceH,
+ GBool printing,
+ GBool (*abortCheckCbk)(void *data) = NULL,
+ void *abortCheckCbkData = NULL)
+ { return gTrue; }
+
+ // Start a page.
+ virtual void startPage(int pageNum, GfxState *state) {}
+
+ // End a page.
+ virtual void endPage() {}
+
+ //----- coordinate conversion
+
+ // Convert between device and user coordinates.
+ virtual void cvtDevToUser(double dx, double dy, double *ux, double *uy);
+ virtual void cvtUserToDev(double ux, double uy, double *dx, double *dy);
+ virtual void cvtUserToDev(double ux, double uy, int *dx, int *dy);
+
+ double *getDefCTM() { return defCTM; }
+ double *getDefICTM() { return defICTM; }
+
+ //----- save/restore graphics state
+ virtual void saveState(GfxState *state) {}
+ virtual void restoreState(GfxState *state) {}
+
+ //----- update graphics state
+ virtual void updateAll(GfxState *state);
+ virtual void updateCTM(GfxState *state, double m11, double m12,
+ double m21, double m22, double m31, double m32) {}
+ virtual void updateLineDash(GfxState *state) {}
+ virtual void updateFlatness(GfxState *state) {}
+ virtual void updateLineJoin(GfxState *state) {}
+ virtual void updateLineCap(GfxState *state) {}
+ virtual void updateMiterLimit(GfxState *state) {}
+ virtual void updateLineWidth(GfxState *state) {}
+ virtual void updateStrokeAdjust(GfxState *state) {}
+ virtual void updateFillColorSpace(GfxState *state) {}
+ virtual void updateStrokeColorSpace(GfxState *state) {}
+ virtual void updateFillColor(GfxState *state) {}
+ virtual void updateStrokeColor(GfxState *state) {}
+ virtual void updateBlendMode(GfxState *state) {}
+ virtual void updateFillOpacity(GfxState *state) {}
+ virtual void updateStrokeOpacity(GfxState *state) {}
+ virtual void updateFillOverprint(GfxState *state) {}
+ virtual void updateStrokeOverprint(GfxState *state) {}
+ virtual void updateOverprintMode(GfxState *state) {}
+ virtual void updateRenderingIntent(GfxState *state) {}
+ virtual void updateTransfer(GfxState *state) {}
+
+ //----- update text state
+ virtual void updateFont(GfxState *state) {}
+ virtual void updateTextMat(GfxState *state) {}
+ virtual void updateCharSpace(GfxState *state) {}
+ virtual void updateRender(GfxState *state) {}
+ virtual void updateRise(GfxState *state) {}
+ virtual void updateWordSpace(GfxState *state) {}
+ virtual void updateHorizScaling(GfxState *state) {}
+ virtual void updateTextPos(GfxState *state) {}
+ virtual void updateTextShift(GfxState *state, double shift) {}
+ virtual void saveTextPos(GfxState *state) {}
+ virtual void restoreTextPos(GfxState *state) {}
+
+ //----- path painting
+ virtual void stroke(GfxState *state) {}
+ virtual void fill(GfxState *state) {}
+ virtual void eoFill(GfxState *state) {}
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
+ int paintType, int tilingType, Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep) {}
+ virtual GBool shadedFill(GfxState *state, GfxShading *shading)
+ { return gFalse; }
+
+ //----- path clipping
+ virtual void clip(GfxState *state) {}
+ virtual void eoClip(GfxState *state) {}
+ virtual void clipToStrokePath(GfxState *state) {}
+
+ //----- text drawing
+ virtual void beginStringOp(GfxState *state) {}
+ virtual void endStringOp(GfxState *state) {}
+ virtual void beginString(GfxState *state, GString *s) {}
+ virtual void endString(GfxState *state) {}
+ virtual void drawChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ double originX, double originY,
+ CharCode code, int nBytes, Unicode *u, int uLen) {}
+ virtual void drawString(GfxState *state, GString *s) {}
+ virtual GBool beginType3Char(GfxState *state, double x, double y,
+ double dx, double dy,
+ CharCode code, Unicode *u, int uLen);
+ virtual void endType3Char(GfxState *state) {}
+ virtual void endTextObject(GfxState *state) {}
+ virtual void incCharCount(int nChars) {}
+ virtual void beginActualText(GfxState *state, Unicode *u, int uLen) {}
+ virtual void endActualText(GfxState *state) {}
+
+ //----- image drawing
+ virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate);
+ virtual void setSoftMaskFromImageMask(GfxState *state,
+ Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate);
+ virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg, GBool interpolate);
+ virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ 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,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ double *matte, GBool interpolate);
+
+#if OPI_SUPPORT
+ //----- OPI functions
+ virtual void opiBegin(GfxState *state, Dict *opiDict);
+ virtual void opiEnd(GfxState *state, Dict *opiDict);
+#endif
+
+ //----- Type 3 font operators
+ virtual void type3D0(GfxState *state, double wx, double wy) {}
+ virtual void type3D1(GfxState *state, double wx, double wy,
+ double llx, double lly, double urx, double ury) {}
+
+ //----- form XObjects
+ virtual void drawForm(Ref id) {}
+
+ //----- PostScript XObjects
+ virtual void psXObject(Stream *psStream, Stream *level1Stream) {}
+
+ //----- transparency groups and soft masks
+ virtual void beginTransparencyGroup(GfxState *state, double *bbox,
+ GfxColorSpace *blendingColorSpace,
+ GBool isolated, GBool knockout,
+ GBool forSoftMask) {}
+ virtual void endTransparencyGroup(GfxState *state) {}
+ virtual void paintTransparencyGroup(GfxState *state, double *bbox) {}
+ virtual void setSoftMask(GfxState *state, double *bbox, GBool alpha,
+ Function *transferFunc, GfxColor *backdropColor) {}
+ virtual void clearSoftMask(GfxState *state) {}
+
+ //----- links
+ virtual void processLink(Link *link) {}
+
+private:
+
+ double defCTM[6]; // default coordinate transform matrix
+ double defICTM[6]; // inverse of default CTM
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDF417Barcode.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PDF417Barcode.cc
new file mode 100644
index 00000000000..0dd35ee4902
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDF417Barcode.cc
@@ -0,0 +1,1916 @@
+//========================================================================
+//
+// PDF417Barcode.cc
+//
+// Copyright 2018 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <math.h>
+#include "gtypes.h"
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "PDF417Barcode.h"
+
+//------------------------------------------------------------------------
+
+#define maxDataCodewords 928
+#define maxErrorCorrectionLevel 8
+#define maxErrorCorrectionCodewords (1 << (maxErrorCorrectionLevel + 1))
+
+// mode latches
+#define textModeLatch 900
+#define byteModeLatchMult6 924
+#define byteModeLatchNonMult6 901
+#define byteModeShift 913
+#define numericModeLatch 902
+
+// text submode latches and shifts
+#define textAlphaLowerLatch 27
+#define textAlphaMixedLatch 28
+#define textAlphaPuncShift 29
+#define textLowerAlphaShift 27
+#define textLowerMixedLatch 28
+#define textLowerPuncShift 29
+#define textMixedPuncLatch 25
+#define textMixedLowerLatch 27
+#define textMixedAlphaLatch 28
+#define textMixedPuncShift 29
+#define textPuncAlphaLatch 29
+
+#define textSubmodeAlpha 0
+#define textSubmodeLower 1
+#define textSubmodeMixed 2
+#define textSubmodePunc 3
+#define textSubmodeAlphaShift 4
+#define textSubmodePuncShift 5
+
+// textCompaction[char][submode] = base-30 value
+// or 30 if char isn't available
+static char textCompaction[128][4] = {
+ { 30, 30, 30, 30 }, // 0
+ { 30, 30, 30, 30 }, // 1
+ { 30, 30, 30, 30 }, // 2
+ { 30, 30, 30, 30 }, // 3
+ { 30, 30, 30, 30 }, // 4
+ { 30, 30, 30, 30 }, // 5
+ { 30, 30, 30, 30 }, // 6
+ { 30, 30, 30, 30 }, // 7
+ { 30, 30, 30, 30 }, // 8
+ { 30, 30, 12, 12 }, // 9
+ { 30, 30, 30, 15 }, // 10
+ { 30, 30, 30, 30 }, // 11
+ { 30, 30, 30, 30 }, // 12
+ { 30, 30, 11, 11 }, // 13
+ { 30, 30, 30, 30 }, // 14
+ { 30, 30, 30, 30 }, // 15
+ { 30, 30, 30, 30 }, // 16
+ { 30, 30, 30, 30 }, // 17
+ { 30, 30, 30, 30 }, // 18
+ { 30, 30, 30, 30 }, // 19
+ { 30, 30, 30, 30 }, // 20
+ { 30, 30, 30, 30 }, // 21
+ { 30, 30, 30, 30 }, // 22
+ { 30, 30, 30, 30 }, // 23
+ { 30, 30, 30, 30 }, // 24
+ { 30, 30, 30, 30 }, // 25
+ { 30, 30, 30, 30 }, // 26
+ { 30, 30, 30, 30 }, // 27
+ { 30, 30, 30, 30 }, // 28
+ { 30, 30, 30, 30 }, // 29
+ { 30, 30, 30, 30 }, // 30
+ { 30, 30, 30, 30 }, // 31
+ { 26, 26, 26, 30 }, // 32
+ { 30, 30, 30, 10 }, // 33
+ { 30, 30, 30, 20 }, // 34
+ { 30, 30, 15, 30 }, // 35
+ { 30, 30, 18, 18 }, // 36
+ { 30, 30, 21, 30 }, // 37
+ { 30, 30, 10, 30 }, // 38
+ { 30, 30, 30, 28 }, // 39
+ { 30, 30, 30, 23 }, // 40
+ { 30, 30, 30, 24 }, // 41
+ { 30, 30, 22, 22 }, // 42
+ { 30, 30, 20, 30 }, // 43
+ { 30, 30, 13, 13 }, // 44
+ { 30, 30, 16, 16 }, // 45
+ { 30, 30, 17, 17 }, // 46
+ { 30, 30, 19, 19 }, // 47
+ { 30, 30, 0, 30 }, // 48
+ { 30, 30, 1, 30 }, // 49
+ { 30, 30, 2, 30 }, // 50
+ { 30, 30, 3, 30 }, // 51
+ { 30, 30, 4, 30 }, // 52
+ { 30, 30, 5, 30 }, // 53
+ { 30, 30, 6, 30 }, // 54
+ { 30, 30, 7, 30 }, // 55
+ { 30, 30, 8, 30 }, // 56
+ { 30, 30, 9, 30 }, // 57
+ { 30, 30, 14, 14 }, // 58
+ { 30, 30, 30, 0 }, // 59
+ { 30, 30, 30, 1 }, // 60
+ { 30, 30, 23, 30 }, // 61
+ { 30, 30, 30, 2 }, // 62
+ { 30, 30, 30, 25 }, // 63
+ { 30, 30, 30, 3 }, // 64
+ { 0, 30, 30, 30 }, // 65
+ { 1, 30, 30, 30 }, // 66
+ { 2, 30, 30, 30 }, // 67
+ { 3, 30, 30, 30 }, // 68
+ { 4, 30, 30, 30 }, // 69
+ { 5, 30, 30, 30 }, // 70
+ { 6, 30, 30, 30 }, // 71
+ { 7, 30, 30, 30 }, // 72
+ { 8, 30, 30, 30 }, // 73
+ { 9, 30, 30, 30 }, // 74
+ { 10, 30, 30, 30 }, // 75
+ { 11, 30, 30, 30 }, // 76
+ { 12, 30, 30, 30 }, // 77
+ { 13, 30, 30, 30 }, // 78
+ { 14, 30, 30, 30 }, // 79
+ { 15, 30, 30, 30 }, // 80
+ { 16, 30, 30, 30 }, // 81
+ { 17, 30, 30, 30 }, // 82
+ { 18, 30, 30, 30 }, // 83
+ { 19, 30, 30, 30 }, // 84
+ { 20, 30, 30, 30 }, // 85
+ { 21, 30, 30, 30 }, // 86
+ { 22, 30, 30, 30 }, // 87
+ { 23, 30, 30, 30 }, // 88
+ { 24, 30, 30, 30 }, // 89
+ { 25, 30, 30, 30 }, // 90
+ { 30, 30, 30, 4 }, // 91
+ { 30, 30, 30, 5 }, // 92
+ { 30, 30, 30, 6 }, // 93
+ { 30, 30, 24, 30 }, // 94
+ { 30, 30, 30, 7 }, // 95
+ { 30, 30, 30, 8 }, // 96
+ { 30, 0, 30, 30 }, // 97
+ { 30, 1, 30, 30 }, // 98
+ { 30, 2, 30, 30 }, // 30
+ { 30, 3, 30, 30 }, // 100
+ { 30, 4, 30, 30 }, // 101
+ { 30, 5, 30, 30 }, // 102
+ { 30, 6, 30, 30 }, // 103
+ { 30, 7, 30, 30 }, // 104
+ { 30, 8, 30, 30 }, // 105
+ { 30, 9, 30, 30 }, // 106
+ { 30, 10, 30, 30 }, // 107
+ { 30, 11, 30, 30 }, // 108
+ { 30, 12, 30, 30 }, // 109
+ { 30, 13, 30, 30 }, // 110
+ { 30, 14, 30, 30 }, // 111
+ { 30, 15, 30, 30 }, // 112
+ { 30, 16, 30, 30 }, // 113
+ { 30, 17, 30, 30 }, // 114
+ { 30, 18, 30, 30 }, // 115
+ { 30, 19, 30, 30 }, // 116
+ { 30, 20, 30, 30 }, // 117
+ { 30, 21, 30, 30 }, // 118
+ { 30, 22, 30, 30 }, // 119
+ { 30, 23, 30, 30 }, // 120
+ { 30, 24, 30, 30 }, // 121
+ { 30, 25, 30, 30 }, // 122
+ { 30, 30, 30, 26 }, // 123
+ { 30, 30, 30, 21 }, // 124
+ { 30, 30, 30, 27 }, // 125
+ { 30, 30, 30, 9 }, // 126
+ { 30, 30, 30, 30 } // 127
+};
+
+static int errorCorrectionCoeffLevel0[2] = {
+ 27, 917
+};
+
+static int errorCorrectionCoeffLevel1[4] = {
+ 522, 568, 723, 809
+};
+
+static int errorCorrectionCoeffLevel2[8] = {
+ 237, 308, 436, 284, 646, 653, 428, 379
+};
+
+static int errorCorrectionCoeffLevel3[16] = {
+ 274, 562, 232, 755, 599, 524, 801, 132,
+ 295, 116, 442, 428, 295, 42, 176, 65
+};
+
+static int errorCorrectionCoeffLevel4[32] = {
+ 361, 575, 922, 525, 176, 586, 640, 321,
+ 536, 742, 677, 742, 687, 284, 193, 517,
+ 273, 494, 263, 147, 593, 800, 571, 320,
+ 803, 133, 231, 390, 685, 330, 63, 410
+};
+
+static int errorCorrectionCoeffLevel5[64] = {
+ 539, 422, 6, 93, 862, 771, 453, 106,
+ 610, 287, 107, 505, 733, 877, 381, 612,
+ 723, 476, 462, 172, 430, 609, 858, 822,
+ 543, 376, 511, 400, 672, 762, 283, 184,
+ 440, 35, 519, 31, 460, 594, 225, 535,
+ 517, 352, 605, 158, 651, 201, 488, 502,
+ 648, 733, 717, 83, 404, 97, 280, 771,
+ 840, 629, 4, 381, 843, 623, 264, 543
+};
+
+static int errorCorrectionCoeffLevel6[128] = {
+ 521, 310, 864, 547, 858, 580, 296, 379,
+ 53, 779, 897, 444, 400, 925, 749, 415,
+ 822, 93, 217, 208, 928, 244, 583, 620,
+ 246, 148, 447, 631, 292, 908, 490, 704,
+ 516, 258, 457, 907, 594, 723, 674, 292,
+ 272, 96, 684, 432, 686, 606, 860, 569,
+ 193, 219, 129, 186, 236, 287, 192, 775,
+ 278, 173, 40, 379, 712, 463, 646, 776,
+ 171, 491, 297, 763, 156, 732, 95, 270,
+ 447, 90, 507, 48, 228, 821, 808, 898,
+ 784, 663, 627, 378, 382, 262, 380, 602,
+ 754, 336, 89, 614, 87, 432, 670, 616,
+ 157, 374, 242, 726, 600, 269, 375, 898,
+ 845, 454, 354, 130, 814, 587, 804, 34,
+ 211, 330, 539, 297, 827, 865, 37, 517,
+ 834, 315, 550, 86, 801, 4, 108, 539
+};
+
+static int errorCorrectionCoeffLevel7[256] = {
+ 524, 894, 75, 766, 882, 857, 74, 204,
+ 82, 586, 708, 250, 905, 786, 138, 720,
+ 858, 194, 311, 913, 275, 190, 375, 850,
+ 438, 733, 194, 280, 201, 280, 828, 757,
+ 710, 814, 919, 89, 68, 569, 11, 204,
+ 796, 605, 540, 913, 801, 700, 799, 137,
+ 439, 418, 592, 668, 353, 859, 370, 694,
+ 325, 240, 216, 257, 284, 549, 209, 884,
+ 315, 70, 329, 793, 490, 274, 877, 162,
+ 749, 812, 684, 461, 334, 376, 849, 521,
+ 307, 291, 803, 712, 19, 358, 399, 908,
+ 103, 511, 51, 8, 517, 225, 289, 470,
+ 637, 731, 66, 255, 917, 269, 463, 830,
+ 730, 433, 848, 585, 136, 538, 906, 90,
+ 2, 290, 743, 199, 655, 903, 329, 49,
+ 802, 580, 355, 588, 188, 462, 10, 134,
+ 628, 320, 479, 130, 739, 71, 263, 318,
+ 374, 601, 192, 605, 142, 673, 687, 234,
+ 722, 384, 177, 752, 607, 640, 455, 193,
+ 689, 707, 805, 641, 48, 60, 732, 621,
+ 895, 544, 261, 852, 655, 309, 697, 755,
+ 756, 60, 231, 773, 434, 421, 726, 528,
+ 503, 118, 49, 795, 32, 144, 500, 238,
+ 836, 394, 280, 566, 319, 9, 647, 550,
+ 73, 914, 342, 126, 32, 681, 331, 792,
+ 620, 60, 609, 441, 180, 791, 893, 754,
+ 605, 383, 228, 749, 760, 213, 54, 297,
+ 134, 54, 834, 299, 922, 191, 910, 532,
+ 609, 829, 189, 20, 167, 29, 872, 449,
+ 83, 402, 41, 656, 505, 579, 481, 173,
+ 404, 251, 688, 95, 497, 555, 642, 543,
+ 307, 159, 924, 558, 648, 55, 497, 10
+};
+
+static int errorCorrectionCoeffLevel8[512] = {
+ 352, 77, 373, 504, 35, 599, 428, 207,
+ 409, 574, 118, 498, 285, 380, 350, 492,
+ 197, 265, 920, 155, 914, 299, 229, 643,
+ 294, 871, 306, 88, 87, 193, 352, 781,
+ 846, 75, 327, 520, 435, 543, 203, 666,
+ 249, 346, 781, 621, 640, 268, 794, 534,
+ 539, 781, 408, 390, 644, 102, 476, 499,
+ 290, 632, 545, 37, 858, 916, 552, 41,
+ 542, 289, 122, 272, 383, 800, 485, 98,
+ 752, 472, 761, 107, 784, 860, 658, 741,
+ 290, 204, 681, 407, 855, 85, 99, 62,
+ 482, 180, 20, 297, 451, 593, 913, 142,
+ 808, 684, 287, 536, 561, 76, 653, 899,
+ 729, 567, 744, 390, 513, 192, 516, 258,
+ 240, 518, 794, 395, 768, 848, 51, 610,
+ 384, 168, 190, 826, 328, 596, 786, 303,
+ 570, 381, 415, 641, 156, 237, 151, 429,
+ 531, 207, 676, 710, 89, 168, 304, 402,
+ 40, 708, 575, 162, 864, 229, 65, 861,
+ 841, 512, 164, 477, 221, 92, 358, 785,
+ 288, 357, 850, 836, 827, 736, 707, 94,
+ 8, 494, 114, 521, 2, 499, 851, 543,
+ 152, 729, 771, 95, 248, 361, 578, 323,
+ 856, 797, 289, 51, 684, 466, 533, 820,
+ 669, 45, 902, 452, 167, 342, 244, 173,
+ 35, 463, 651, 51, 699, 591, 452, 578,
+ 37, 124, 298, 332, 552, 43, 427, 119,
+ 662, 777, 475, 850, 764, 364, 578, 911,
+ 283, 711, 472, 420, 245, 288, 594, 394,
+ 511, 327, 589, 777, 699, 688, 43, 408,
+ 842, 383, 721, 521, 560, 644, 714, 559,
+ 62, 145, 873, 663, 713, 159, 672, 729,
+ 624, 59, 193, 417, 158, 209, 563, 564,
+ 343, 693, 109, 608, 563, 365, 181, 772,
+ 677, 310, 248, 353, 708, 410, 579, 870,
+ 617, 841, 632, 860, 289, 536, 35, 777,
+ 618, 586, 424, 833, 77, 597, 346, 269,
+ 757, 632, 695, 751, 331, 247, 184, 45,
+ 787, 680, 18, 66, 407, 369, 54, 492,
+ 228, 613, 830, 922, 437, 519, 644, 905,
+ 789, 420, 305, 441, 207, 300, 892, 827,
+ 141, 537, 381, 662, 513, 56, 252, 341,
+ 242, 797, 838, 837, 720, 224, 307, 631,
+ 61, 87, 560, 310, 756, 665, 397, 808,
+ 851, 309, 473, 795, 378, 31, 647, 915,
+ 459, 806, 590, 731, 425, 216, 548, 249,
+ 321, 881, 699, 535, 673, 782, 210, 815,
+ 905, 303, 843, 922, 281, 73, 469, 791,
+ 660, 162, 498, 308, 155, 422, 907, 817,
+ 187, 62, 16, 425, 535, 336, 286, 437,
+ 375, 273, 610, 296, 183, 923, 116, 667,
+ 751, 353, 62, 366, 691, 379, 687, 842,
+ 37, 357, 720, 742, 330, 5, 39, 923,
+ 311, 424, 242, 749, 321, 54, 669, 316,
+ 342, 299, 534, 105, 667, 488, 640, 672,
+ 576, 540, 316, 486, 721, 610, 46, 656,
+ 447, 171, 616, 464, 190, 531, 297, 321,
+ 762, 752, 533, 175, 134, 14, 381, 433,
+ 717, 45, 111, 20, 596, 284, 736, 138,
+ 646, 411, 877, 669, 141, 919, 45, 780,
+ 407, 164, 332, 899, 165, 726, 600, 325,
+ 498, 655, 357, 752, 768, 223, 849, 647,
+ 63, 310, 863, 251, 366, 304, 282, 738,
+ 675, 410, 389, 244, 31, 121, 303, 263
+};
+
+static int *errorCorrectionCoeff[maxErrorCorrectionLevel + 1] = {
+ errorCorrectionCoeffLevel0,
+ errorCorrectionCoeffLevel1,
+ errorCorrectionCoeffLevel2,
+ errorCorrectionCoeffLevel3,
+ errorCorrectionCoeffLevel4,
+ errorCorrectionCoeffLevel5,
+ errorCorrectionCoeffLevel6,
+ errorCorrectionCoeffLevel7,
+ errorCorrectionCoeffLevel8
+};
+
+#define startPatternLength 8
+static char startPattern[startPatternLength] = {8,1,1,1,1,1,1,3};
+
+#define stopPatternLength 9
+static char stopPattern[stopPatternLength] = {7,1,1,3,1,1,1,2,1};
+
+#define patternLength 8
+
+// patterns[codeword][cluster][element] = element (bar or space) width
+static char patterns[929][3][patternLength] = {
+ {{3,1,1,1,1,1,3,6}, {5,1,1,1,1,1,2,5}, {2,1,1,1,1,1,5,5}},
+ {{4,1,1,1,1,1,4,4}, {6,1,1,1,1,1,3,3}, {3,1,1,1,1,1,6,3}},
+ {{5,1,1,1,1,1,5,2}, {4,1,1,1,1,2,1,6}, {1,1,1,1,1,2,4,6}},
+ {{3,1,1,1,1,2,3,5}, {5,1,1,1,1,2,2,4}, {2,1,1,1,1,2,5,4}},
+ {{4,1,1,1,1,2,4,3}, {6,1,1,1,1,2,3,2}, {3,1,1,1,1,2,6,2}},
+ {{5,1,1,1,1,2,5,1}, {4,1,1,1,1,3,1,5}, {1,1,1,1,1,3,4,5}},
+ {{2,1,1,1,1,3,2,6}, {5,1,1,1,1,3,2,3}, {2,1,1,1,1,3,5,3}},
+ {{3,1,1,1,1,3,3,4}, {6,1,1,1,1,3,3,1}, {3,1,1,1,1,3,6,1}},
+ {{2,1,1,1,1,4,2,5}, {4,1,1,1,1,4,1,4}, {1,1,1,1,1,4,4,4}},
+ {{1,1,1,1,1,5,1,6}, {5,1,1,1,1,4,2,2}, {2,1,1,1,1,4,5,2}},
+ {{2,1,1,1,1,5,2,4}, {4,1,1,1,1,5,1,3}, {1,1,1,1,1,5,4,3}},
+ {{1,1,1,1,1,6,1,5}, {5,1,1,1,1,5,2,1}, {6,1,1,1,2,1,1,4}},
+ {{2,1,1,1,2,1,3,6}, {4,1,1,1,1,6,1,2}, {1,1,1,1,2,1,5,5}},
+ {{3,1,1,1,2,1,4,4}, {4,1,1,1,2,1,2,5}, {2,1,1,1,2,1,6,3}},
+ {{4,1,1,1,2,1,5,2}, {5,1,1,1,2,1,3,3}, {6,1,1,1,2,2,1,3}},
+ {{2,1,1,1,2,2,3,5}, {6,1,1,1,2,1,4,1}, {1,1,1,1,2,2,5,4}},
+ {{3,1,1,1,2,2,4,3}, {3,1,1,1,2,2,1,6}, {2,1,1,1,2,2,6,2}},
+ {{4,1,1,1,2,2,5,1}, {4,1,1,1,2,2,2,4}, {6,1,1,1,2,3,1,2}},
+ {{1,1,1,1,2,3,2,6}, {5,1,1,1,2,2,3,2}, {1,1,1,1,2,3,5,3}},
+ {{2,1,1,1,2,3,3,4}, {3,1,1,1,2,3,1,5}, {2,1,1,1,2,3,6,1}},
+ {{1,1,1,1,2,4,2,5}, {4,1,1,1,2,3,2,3}, {6,1,1,1,2,4,1,1}},
+ {{1,1,1,1,3,1,3,6}, {5,1,1,1,2,3,3,1}, {1,1,1,1,2,4,5,2}},
+ {{2,1,1,1,3,1,4,4}, {3,1,1,1,2,4,1,4}, {5,1,1,1,3,1,1,4}},
+ {{3,1,1,1,3,1,5,2}, {4,1,1,1,2,4,2,2}, {6,1,1,1,3,1,2,2}},
+ {{1,1,1,1,3,2,3,5}, {3,1,1,1,2,5,1,3}, {1,1,1,1,3,1,6,3}},
+ {{2,1,1,1,3,2,4,3}, {4,1,1,1,2,5,2,1}, {5,1,1,1,3,2,1,3}},
+ {{3,1,1,1,3,2,5,1}, {3,1,1,1,2,6,1,2}, {6,1,1,1,3,2,2,1}},
+ {{1,1,1,1,3,3,3,4}, {3,1,1,1,3,1,2,5}, {1,1,1,1,3,2,6,2}},
+ {{2,1,1,1,3,3,4,2}, {4,1,1,1,3,1,3,3}, {5,1,1,1,3,3,1,2}},
+ {{1,1,1,1,4,1,4,4}, {5,1,1,1,3,1,4,1}, {1,1,1,1,3,3,6,1}},
+ {{2,1,1,1,4,1,5,2}, {2,1,1,1,3,2,1,6}, {5,1,1,1,3,4,1,1}},
+ {{1,1,1,1,4,2,4,3}, {3,1,1,1,3,2,2,4}, {4,1,1,1,4,1,1,4}},
+ {{2,1,1,1,4,2,5,1}, {4,1,1,1,3,2,3,2}, {5,1,1,1,4,1,2,2}},
+ {{1,1,1,1,5,1,5,2}, {2,1,1,1,3,3,1,5}, {4,1,1,1,4,2,1,3}},
+ {{5,1,1,1,6,1,1,1}, {3,1,1,1,3,3,2,3}, {5,1,1,1,4,2,2,1}},
+ {{3,1,1,2,1,1,3,5}, {4,1,1,1,3,3,3,1}, {4,1,1,1,4,3,1,2}},
+ {{4,1,1,2,1,1,4,3}, {2,1,1,1,3,4,1,4}, {4,1,1,1,4,4,1,1}},
+ {{5,1,1,2,1,1,5,1}, {3,1,1,1,3,4,2,2}, {3,1,1,1,5,1,1,4}},
+ {{2,1,1,2,1,2,2,6}, {2,1,1,1,3,5,1,3}, {4,1,1,1,5,1,2,2}},
+ {{3,1,1,2,1,2,3,4}, {3,1,1,1,3,5,2,1}, {3,1,1,1,5,2,1,3}},
+ {{4,1,1,2,1,2,4,2}, {2,1,1,1,3,6,1,2}, {4,1,1,1,5,2,2,1}},
+ {{2,1,1,2,1,3,2,5}, {2,1,1,1,4,1,2,5}, {3,1,1,1,5,3,1,2}},
+ {{3,1,1,2,1,3,3,3}, {3,1,1,1,4,1,3,3}, {3,1,1,1,5,4,1,1}},
+ {{1,1,1,2,1,4,1,6}, {4,1,1,1,4,1,4,1}, {2,1,1,1,6,1,1,4}},
+ {{2,1,1,2,1,4,2,4}, {1,1,1,1,4,2,1,6}, {3,1,1,1,6,1,2,2}},
+ {{3,1,1,2,1,4,3,2}, {2,1,1,1,4,2,2,4}, {2,1,1,1,6,2,1,3}},
+ {{1,1,1,2,1,5,1,5}, {3,1,1,1,4,2,3,2}, {3,1,1,1,6,2,2,1}},
+ {{2,1,1,2,1,5,2,3}, {1,1,1,1,4,3,1,5}, {2,1,1,1,6,3,1,2}},
+ {{1,1,1,2,1,6,1,4}, {2,1,1,1,4,3,2,3}, {1,1,1,2,1,1,4,6}},
+ {{2,1,1,2,2,1,3,5}, {3,1,1,1,4,3,3,1}, {2,1,1,2,1,1,5,4}},
+ {{3,1,1,2,2,1,4,3}, {1,1,1,1,4,4,1,4}, {3,1,1,2,1,1,6,2}},
+ {{4,1,1,2,2,1,5,1}, {2,1,1,1,4,4,2,2}, {1,1,1,2,1,2,4,5}},
+ {{1,1,1,2,2,2,2,6}, {1,1,1,1,4,5,1,3}, {2,1,1,2,1,2,5,3}},
+ {{2,1,1,2,2,2,3,4}, {2,1,1,1,4,5,2,1}, {3,1,1,2,1,2,6,1}},
+ {{3,1,1,2,2,2,4,2}, {1,1,1,1,5,1,2,5}, {1,1,1,2,1,3,4,4}},
+ {{1,1,1,2,2,3,2,5}, {2,1,1,1,5,1,3,3}, {2,1,1,2,1,3,5,2}},
+ {{2,1,1,2,2,3,3,3}, {3,1,1,1,5,1,4,1}, {1,1,1,2,1,4,4,3}},
+ {{3,1,1,2,2,3,4,1}, {1,1,1,1,5,2,2,4}, {2,1,1,2,1,4,5,1}},
+ {{1,1,1,2,2,4,2,4}, {2,1,1,1,5,2,3,2}, {1,1,1,2,1,5,4,2}},
+ {{2,1,1,2,2,4,3,2}, {1,1,1,1,5,3,2,3}, {6,1,1,2,2,1,1,3}},
+ {{1,1,1,2,3,1,3,5}, {2,1,1,1,5,3,3,1}, {1,1,1,2,2,1,5,4}},
+ {{2,1,1,2,3,1,4,3}, {1,1,1,1,5,4,2,2}, {2,1,1,2,2,1,6,2}},
+ {{3,1,1,2,3,1,5,1}, {1,1,1,1,6,1,3,3}, {6,1,1,2,2,2,1,2}},
+ {{1,1,1,2,3,2,3,4}, {2,1,1,1,6,1,4,1}, {1,1,1,2,2,2,5,3}},
+ {{2,1,1,2,3,2,4,2}, {1,1,1,1,6,2,3,2}, {2,1,1,2,2,2,6,1}},
+ {{1,1,1,2,3,3,3,3}, {1,1,1,1,6,3,3,1}, {6,1,1,2,2,3,1,1}},
+ {{2,1,1,2,3,3,4,1}, {4,1,1,2,1,1,1,6}, {1,1,1,2,2,3,5,2}},
+ {{1,1,1,2,4,1,4,3}, {5,1,1,2,1,1,2,4}, {1,1,1,2,2,4,5,1}},
+ {{2,1,1,2,4,1,5,1}, {6,1,1,2,1,1,3,2}, {5,1,1,2,3,1,1,3}},
+ {{1,1,1,2,4,2,4,2}, {4,1,1,2,1,2,1,5}, {6,1,1,2,3,1,2,1}},
+ {{1,1,1,2,4,3,4,1}, {5,1,1,2,1,2,2,3}, {1,1,1,2,3,1,6,2}},
+ {{2,1,1,3,1,1,2,6}, {6,1,1,2,1,2,3,1}, {5,1,1,2,3,2,1,2}},
+ {{3,1,1,3,1,1,3,4}, {4,1,1,2,1,3,1,4}, {1,1,1,2,3,2,6,1}},
+ {{4,1,1,3,1,1,4,2}, {5,1,1,2,1,3,2,2}, {5,1,1,2,3,3,1,1}},
+ {{2,1,1,3,1,2,2,5}, {4,1,1,2,1,4,1,3}, {4,1,1,2,4,1,1,3}},
+ {{3,1,1,3,1,2,3,3}, {5,1,1,2,1,4,2,1}, {5,1,1,2,4,1,2,1}},
+ {{4,1,1,3,1,2,4,1}, {4,1,1,2,1,5,1,2}, {4,1,1,2,4,2,1,2}},
+ {{1,1,1,3,1,3,1,6}, {4,1,1,2,1,6,1,1}, {4,1,1,2,4,3,1,1}},
+ {{2,1,1,3,1,3,2,4}, {3,1,1,2,2,1,1,6}, {3,1,1,2,5,1,1,3}},
+ {{3,1,1,3,1,3,3,2}, {4,1,1,2,2,1,2,4}, {4,1,1,2,5,1,2,1}},
+ {{1,1,1,3,1,4,1,5}, {5,1,1,2,2,1,3,2}, {3,1,1,2,5,2,1,2}},
+ {{2,1,1,3,1,4,2,3}, {3,1,1,2,2,2,1,5}, {3,1,1,2,5,3,1,1}},
+ {{1,1,1,3,1,5,1,4}, {4,1,1,2,2,2,2,3}, {2,1,1,2,6,1,1,3}},
+ {{1,1,1,3,1,6,1,3}, {5,1,1,2,2,2,3,1}, {3,1,1,2,6,1,2,1}},
+ {{1,1,1,3,2,1,2,6}, {3,1,1,2,2,3,1,4}, {2,1,1,2,6,2,1,2}},
+ {{2,1,1,3,2,1,3,4}, {4,1,1,2,2,3,2,2}, {2,1,1,2,6,3,1,1}},
+ {{3,1,1,3,2,1,4,2}, {3,1,1,2,2,4,1,3}, {1,1,1,3,1,1,4,5}},
+ {{1,1,1,3,2,2,2,5}, {4,1,1,2,2,4,2,1}, {2,1,1,3,1,1,5,3}},
+ {{2,1,1,3,2,2,3,3}, {3,1,1,2,2,5,1,2}, {3,1,1,3,1,1,6,1}},
+ {{3,1,1,3,2,2,4,1}, {3,1,1,2,2,6,1,1}, {1,1,1,3,1,2,4,4}},
+ {{1,1,1,3,2,3,2,4}, {2,1,1,2,3,1,1,6}, {2,1,1,3,1,2,5,2}},
+ {{2,1,1,3,2,3,3,2}, {3,1,1,2,3,1,2,4}, {1,1,1,3,1,3,4,3}},
+ {{1,1,1,3,2,4,2,3}, {4,1,1,2,3,1,3,2}, {2,1,1,3,1,3,5,1}},
+ {{1,1,1,3,2,5,2,2}, {2,1,1,2,3,2,1,5}, {1,1,1,3,1,4,4,2}},
+ {{1,1,1,3,3,1,3,4}, {3,1,1,2,3,2,2,3}, {1,1,1,3,1,5,4,1}},
+ {{2,1,1,3,3,1,4,2}, {4,1,1,2,3,2,3,1}, {6,1,1,3,2,1,1,2}},
+ {{1,1,1,3,3,2,3,3}, {2,1,1,2,3,3,1,4}, {1,1,1,3,2,1,5,3}},
+ {{2,1,1,3,3,2,4,1}, {3,1,1,2,3,3,2,2}, {2,1,1,3,2,1,6,1}},
+ {{1,1,1,3,3,3,3,2}, {2,1,1,2,3,4,1,3}, {6,1,1,3,2,2,1,1}},
+ {{1,1,1,3,4,1,4,2}, {3,1,1,2,3,4,2,1}, {1,1,1,3,2,2,5,2}},
+ {{2,1,1,4,1,1,2,5}, {2,1,1,2,3,5,1,2}, {1,1,1,3,2,3,5,1}},
+ {{3,1,1,4,1,1,3,3}, {2,1,1,2,3,6,1,1}, {5,1,1,3,3,1,1,2}},
+ {{4,1,1,4,1,1,4,1}, {1,1,1,2,4,1,1,6}, {1,1,1,3,3,1,6,1}},
+ {{1,1,1,4,1,2,1,6}, {2,1,1,2,4,1,2,4}, {5,1,1,3,3,2,1,1}},
+ {{2,1,1,4,1,2,2,4}, {3,1,1,2,4,1,3,2}, {4,1,1,3,4,1,1,2}},
+ {{3,1,1,4,1,2,3,2}, {1,1,1,2,4,2,1,5}, {4,1,1,3,4,2,1,1}},
+ {{1,1,1,4,1,3,1,5}, {2,1,1,2,4,2,2,3}, {3,1,1,3,5,1,1,2}},
+ {{2,1,1,4,1,3,2,3}, {3,1,1,2,4,2,3,1}, {3,1,1,3,5,2,1,1}},
+ {{3,1,1,4,1,3,3,1}, {1,1,1,2,4,3,1,4}, {2,1,1,3,6,1,1,2}},
+ {{1,1,1,4,1,4,1,4}, {2,1,1,2,4,3,2,2}, {2,1,1,3,6,2,1,1}},
+ {{2,1,1,4,1,4,2,2}, {1,1,1,2,4,4,1,3}, {1,1,1,4,1,1,4,4}},
+ {{1,1,1,4,1,5,1,3}, {2,1,1,2,4,4,2,1}, {2,1,1,4,1,1,5,2}},
+ {{2,1,1,4,1,5,2,1}, {1,1,1,2,4,5,1,2}, {1,1,1,4,1,2,4,3}},
+ {{1,1,1,4,2,1,2,5}, {1,1,1,2,5,1,2,4}, {2,1,1,4,1,2,5,1}},
+ {{2,1,1,4,2,1,3,3}, {2,1,1,2,5,1,3,2}, {1,1,1,4,1,3,4,2}},
+ {{3,1,1,4,2,1,4,1}, {1,1,1,2,5,2,2,3}, {1,1,1,4,1,4,4,1}},
+ {{1,1,1,4,2,2,2,4}, {2,1,1,2,5,2,3,1}, {6,1,1,4,2,1,1,1}},
+ {{2,1,1,4,2,2,3,2}, {1,1,1,2,5,3,2,2}, {1,1,1,4,2,1,5,2}},
+ {{1,1,1,4,2,3,2,3}, {1,1,1,2,5,4,2,1}, {1,1,1,4,2,2,5,1}},
+ {{2,1,1,4,2,3,3,1}, {1,1,1,2,6,1,3,2}, {5,1,1,4,3,1,1,1}},
+ {{1,1,1,4,2,4,2,2}, {1,1,1,2,6,2,3,1}, {4,1,1,4,4,1,1,1}},
+ {{1,1,1,4,2,5,2,1}, {4,1,1,3,1,1,1,5}, {3,1,1,4,5,1,1,1}},
+ {{2,1,1,4,3,1,4,1}, {5,1,1,3,1,1,2,3}, {1,1,1,5,1,1,4,3}},
+ {{1,1,1,4,3,3,3,1}, {6,1,1,3,1,1,3,1}, {2,1,1,5,1,1,5,1}},
+ {{1,1,1,5,1,1,1,6}, {4,1,1,3,1,2,1,4}, {1,1,1,5,1,2,4,2}},
+ {{2,1,1,5,1,1,2,4}, {5,1,1,3,1,2,2,2}, {1,1,1,5,1,3,4,1}},
+ {{3,1,1,5,1,1,3,2}, {4,1,1,3,1,3,1,3}, {1,1,1,5,2,1,5,1}},
+ {{1,1,1,5,1,2,1,5}, {5,1,1,3,1,3,2,1}, {1,1,1,6,1,1,4,2}},
+ {{2,1,1,5,1,2,2,3}, {4,1,1,3,1,4,1,2}, {1,1,1,6,1,2,4,1}},
+ {{3,1,1,5,1,2,3,1}, {4,1,1,3,1,5,1,1}, {1,2,1,1,1,1,4,6}},
+ {{1,1,1,5,1,3,1,4}, {3,1,1,3,2,1,1,5}, {2,2,1,1,1,1,5,4}},
+ {{2,1,1,5,1,3,2,2}, {4,1,1,3,2,1,2,3}, {3,2,1,1,1,1,6,2}},
+ {{1,1,1,5,1,4,1,3}, {5,1,1,3,2,1,3,1}, {1,2,1,1,1,2,4,5}},
+ {{2,1,1,5,1,4,2,1}, {3,1,1,3,2,2,1,4}, {2,2,1,1,1,2,5,3}},
+ {{1,1,1,5,1,5,1,2}, {4,1,1,3,2,2,2,2}, {3,2,1,1,1,2,6,1}},
+ {{1,1,1,5,2,1,2,4}, {3,1,1,3,2,3,1,3}, {1,2,1,1,1,3,4,4}},
+ {{1,1,1,5,2,2,2,3}, {4,1,1,3,2,3,2,1}, {2,2,1,1,1,3,5,2}},
+ {{1,1,1,5,2,3,2,2}, {3,1,1,3,2,4,1,2}, {1,2,1,1,1,4,4,3}},
+ {{1,1,1,6,1,1,1,5}, {3,1,1,3,2,5,1,1}, {2,2,1,1,1,4,5,1}},
+ {{3,1,1,6,1,1,3,1}, {2,1,1,3,3,1,1,5}, {1,2,1,1,1,5,4,2}},
+ {{2,1,1,6,1,2,2,2}, {3,1,1,3,3,1,2,3}, {6,2,1,1,2,1,1,3}},
+ {{2,1,1,6,1,3,2,1}, {4,1,1,3,3,1,3,1}, {1,2,1,1,2,1,5,4}},
+ {{1,1,1,6,1,5,1,1}, {2,1,1,3,3,2,1,4}, {2,2,1,1,2,1,6,2}},
+ {{3,2,1,1,1,1,3,5}, {3,1,1,3,3,2,2,2}, {6,2,1,1,2,2,1,2}},
+ {{4,2,1,1,1,1,4,3}, {2,1,1,3,3,3,1,3}, {1,2,1,1,2,2,5,3}},
+ {{5,2,1,1,1,1,5,1}, {3,1,1,3,3,3,2,1}, {2,2,1,1,2,2,6,1}},
+ {{2,2,1,1,1,2,2,6}, {2,1,1,3,3,4,1,2}, {6,2,1,1,2,3,1,1}},
+ {{3,2,1,1,1,2,3,4}, {2,1,1,3,3,5,1,1}, {1,2,1,1,2,3,5,2}},
+ {{4,2,1,1,1,2,4,2}, {1,1,1,3,4,1,1,5}, {1,2,1,1,2,4,5,1}},
+ {{2,2,1,1,1,3,2,5}, {2,1,1,3,4,1,2,3}, {5,2,1,1,3,1,1,3}},
+ {{3,2,1,1,1,3,3,3}, {3,1,1,3,4,1,3,1}, {6,2,1,1,3,1,2,1}},
+ {{4,2,1,1,1,3,4,1}, {1,1,1,3,4,2,1,4}, {1,2,1,1,3,1,6,2}},
+ {{1,2,1,1,1,4,1,6}, {2,1,1,3,4,2,2,2}, {5,2,1,1,3,2,1,2}},
+ {{2,2,1,1,1,4,2,4}, {1,1,1,3,4,3,1,3}, {1,2,1,1,3,2,6,1}},
+ {{1,2,1,1,1,5,1,5}, {2,1,1,3,4,3,2,1}, {5,2,1,1,3,3,1,1}},
+ {{2,2,1,1,2,1,3,5}, {1,1,1,3,4,4,1,2}, {4,2,1,1,4,1,1,3}},
+ {{3,2,1,1,2,1,4,3}, {1,1,1,3,4,5,1,1}, {5,2,1,1,4,1,2,1}},
+ {{4,2,1,1,2,1,5,1}, {1,1,1,3,5,1,2,3}, {4,2,1,1,4,2,1,2}},
+ {{1,2,1,1,2,2,2,6}, {2,1,1,3,5,1,3,1}, {4,2,1,1,4,3,1,1}},
+ {{2,2,1,1,2,2,3,4}, {1,1,1,3,5,2,2,2}, {3,2,1,1,5,1,1,3}},
+ {{3,2,1,1,2,2,4,2}, {1,1,1,3,5,3,2,1}, {4,2,1,1,5,1,2,1}},
+ {{1,2,1,1,2,3,2,5}, {1,1,1,3,6,1,3,1}, {3,2,1,1,5,2,1,2}},
+ {{2,2,1,1,2,3,3,3}, {4,1,1,4,1,1,1,4}, {3,2,1,1,5,3,1,1}},
+ {{1,2,1,1,2,4,2,4}, {5,1,1,4,1,1,2,2}, {2,2,1,1,6,1,1,3}},
+ {{1,2,1,1,2,5,2,3}, {4,1,1,4,1,2,1,3}, {3,2,1,1,6,1,2,1}},
+ {{1,2,1,1,3,1,3,5}, {5,1,1,4,1,2,2,1}, {2,2,1,1,6,2,1,2}},
+ {{2,2,1,1,3,1,4,3}, {4,1,1,4,1,3,1,2}, {2,2,1,1,6,3,1,1}},
+ {{3,2,1,1,3,1,5,1}, {4,1,1,4,1,4,1,1}, {2,1,2,1,1,1,4,5}},
+ {{1,2,1,1,3,2,3,4}, {3,1,1,4,2,1,1,4}, {3,1,2,1,1,1,5,3}},
+ {{2,2,1,1,3,2,4,2}, {4,1,1,4,2,1,2,2}, {4,1,2,1,1,1,6,1}},
+ {{1,2,1,1,3,3,3,3}, {3,1,1,4,2,2,1,3}, {1,1,2,1,1,2,3,6}},
+ {{1,2,1,1,3,4,3,2}, {4,1,1,4,2,2,2,1}, {2,1,2,1,1,2,4,4}},
+ {{1,2,1,1,4,1,4,3}, {3,1,1,4,2,3,1,2}, {3,1,2,1,1,2,5,2}},
+ {{2,2,1,1,4,1,5,1}, {3,1,1,4,2,4,1,1}, {1,1,2,1,1,3,3,5}},
+ {{1,2,1,1,4,2,4,2}, {2,1,1,4,3,1,1,4}, {2,1,2,1,1,3,4,3}},
+ {{1,2,1,1,5,1,5,1}, {3,1,1,4,3,1,2,2}, {3,1,2,1,1,3,5,1}},
+ {{3,1,2,1,1,1,2,6}, {2,1,1,4,3,2,1,3}, {1,1,2,1,1,4,3,4}},
+ {{4,1,2,1,1,1,3,4}, {3,1,1,4,3,2,2,1}, {2,1,2,1,1,4,4,2}},
+ {{5,1,2,1,1,1,4,2}, {2,1,1,4,3,3,1,2}, {1,1,2,1,1,5,3,3}},
+ {{3,1,2,1,1,2,2,5}, {2,1,1,4,3,4,1,1}, {2,1,2,1,1,5,4,1}},
+ {{4,1,2,1,1,2,3,3}, {1,1,1,4,4,1,1,4}, {1,1,2,1,1,6,3,2}},
+ {{5,1,2,1,1,2,4,1}, {2,1,1,4,4,1,2,2}, {1,2,1,2,1,1,4,5}},
+ {{2,1,2,1,1,3,1,6}, {1,1,1,4,4,2,1,3}, {2,2,1,2,1,1,5,3}},
+ {{3,1,2,1,1,3,2,4}, {2,1,1,4,4,2,2,1}, {3,2,1,2,1,1,6,1}},
+ {{4,1,2,1,1,3,3,2}, {1,1,1,4,4,3,1,2}, {1,1,2,1,2,1,4,5}},
+ {{2,1,2,1,1,4,1,5}, {1,1,1,4,4,4,1,1}, {1,2,1,2,1,2,4,4}},
+ {{3,1,2,1,1,4,2,3}, {1,1,1,4,5,1,2,2}, {2,2,1,2,1,2,5,2}},
+ {{4,1,2,1,1,4,3,1}, {1,1,1,4,5,2,2,1}, {1,1,2,1,2,2,4,4}},
+ {{2,1,2,1,1,5,1,4}, {4,1,1,5,1,1,1,3}, {2,1,2,1,2,2,5,2}},
+ {{3,1,2,1,1,5,2,2}, {5,1,1,5,1,1,2,1}, {2,2,1,2,1,3,5,1}},
+ {{2,2,1,2,1,1,2,6}, {4,1,1,5,1,2,1,2}, {1,1,2,1,2,3,4,3}},
+ {{3,2,1,2,1,1,3,4}, {4,1,1,5,1,3,1,1}, {1,2,1,2,1,4,4,2}},
+ {{4,2,1,2,1,1,4,2}, {3,1,1,5,2,1,1,3}, {1,1,2,1,2,4,4,2}},
+ {{2,1,2,1,2,1,2,6}, {4,1,1,5,2,1,2,1}, {1,2,1,2,1,5,4,1}},
+ {{2,2,1,2,1,2,2,5}, {3,1,1,5,2,2,1,2}, {1,1,2,1,2,5,4,1}},
+ {{3,2,1,2,1,2,3,3}, {3,1,1,5,2,3,1,1}, {6,2,1,2,2,1,1,2}},
+ {{4,2,1,2,1,2,4,1}, {2,1,1,5,3,1,1,3}, {1,2,1,2,2,1,5,3}},
+ {{2,1,2,1,2,2,2,5}, {3,1,1,5,3,1,2,1}, {2,2,1,2,2,1,6,1}},
+ {{3,1,2,1,2,2,3,3}, {2,1,1,5,3,2,1,2}, {6,1,2,1,3,1,1,2}},
+ {{4,1,2,1,2,2,4,1}, {2,1,1,5,3,3,1,1}, {6,2,1,2,2,2,1,1}},
+ {{1,1,2,1,2,3,1,6}, {1,1,1,5,4,1,1,3}, {1,1,2,1,3,1,5,3}},
+ {{1,2,1,2,1,4,1,5}, {2,1,1,5,4,1,2,1}, {1,2,1,2,2,2,5,2}},
+ {{2,2,1,2,1,4,2,3}, {1,1,1,5,4,2,1,2}, {6,1,2,1,3,2,1,1}},
+ {{3,2,1,2,1,4,3,1}, {1,1,1,5,4,3,1,1}, {1,1,2,1,3,2,5,2}},
+ {{1,1,2,1,2,4,1,5}, {4,1,1,6,1,1,1,2}, {1,2,1,2,2,3,5,1}},
+ {{2,1,2,1,2,4,2,3}, {4,1,1,6,1,2,1,1}, {1,1,2,1,3,3,5,1}},
+ {{1,1,2,1,2,5,1,4}, {3,1,1,6,2,1,1,2}, {5,2,1,2,3,1,1,2}},
+ {{1,2,1,2,2,1,2,6}, {3,1,1,6,2,2,1,1}, {1,2,1,2,3,1,6,1}},
+ {{2,2,1,2,2,1,3,4}, {2,1,1,6,3,1,1,2}, {5,1,2,1,4,1,1,2}},
+ {{3,2,1,2,2,1,4,2}, {2,1,1,6,3,2,1,1}, {5,2,1,2,3,2,1,1}},
+ {{1,1,2,1,3,1,2,6}, {4,2,1,1,1,1,1,6}, {1,1,2,1,4,1,6,1}},
+ {{1,2,1,2,2,2,2,5}, {5,2,1,1,1,1,2,4}, {5,1,2,1,4,2,1,1}},
+ {{2,2,1,2,2,2,3,3}, {6,2,1,1,1,1,3,2}, {4,2,1,2,4,1,1,2}},
+ {{3,2,1,2,2,2,4,1}, {4,2,1,1,1,2,1,5}, {4,1,2,1,5,1,1,2}},
+ {{1,1,2,1,3,2,2,5}, {5,2,1,1,1,2,2,3}, {4,2,1,2,4,2,1,1}},
+ {{2,1,2,1,3,2,3,3}, {6,2,1,1,1,2,3,1}, {4,1,2,1,5,2,1,1}},
+ {{3,1,2,1,3,2,4,1}, {4,2,1,1,1,3,1,4}, {3,2,1,2,5,1,1,2}},
+ {{1,1,2,1,3,3,2,4}, {5,2,1,1,1,3,2,2}, {3,1,2,1,6,1,1,2}},
+ {{1,2,1,2,2,4,2,3}, {4,2,1,1,1,4,1,3}, {3,2,1,2,5,2,1,1}},
+ {{1,1,2,1,3,4,2,3}, {5,2,1,1,1,4,2,1}, {3,1,2,1,6,2,1,1}},
+ {{1,2,1,2,3,1,3,4}, {4,2,1,1,1,5,1,2}, {2,2,1,2,6,1,1,2}},
+ {{2,2,1,2,3,1,4,2}, {4,2,1,1,1,6,1,1}, {2,2,1,2,6,2,1,1}},
+ {{1,1,2,1,4,1,3,4}, {3,2,1,1,2,1,1,6}, {1,1,2,2,1,1,3,6}},
+ {{1,2,1,2,3,2,3,3}, {4,2,1,1,2,1,2,4}, {2,1,2,2,1,1,4,4}},
+ {{2,2,1,2,3,2,4,1}, {5,2,1,1,2,1,3,2}, {3,1,2,2,1,1,5,2}},
+ {{1,1,2,1,4,2,3,3}, {3,2,1,1,2,2,1,5}, {1,1,2,2,1,2,3,5}},
+ {{2,1,2,1,4,2,4,1}, {4,2,1,1,2,2,2,3}, {2,1,2,2,1,2,4,3}},
+ {{1,1,2,1,4,3,3,2}, {5,2,1,1,2,2,3,1}, {3,1,2,2,1,2,5,1}},
+ {{1,2,1,2,4,1,4,2}, {3,2,1,1,2,3,1,4}, {1,1,2,2,1,3,3,4}},
+ {{1,1,2,1,5,1,4,2}, {4,2,1,1,2,3,2,2}, {2,1,2,2,1,3,4,2}},
+ {{1,2,1,2,4,2,4,1}, {3,2,1,1,2,4,1,3}, {1,1,2,2,1,4,3,3}},
+ {{1,1,2,1,5,2,4,1}, {4,2,1,1,2,4,2,1}, {2,1,2,2,1,4,4,1}},
+ {{3,1,2,2,1,1,2,5}, {3,2,1,1,2,5,1,2}, {1,1,2,2,1,5,3,2}},
+ {{4,1,2,2,1,1,3,3}, {3,2,1,1,2,6,1,1}, {1,1,2,2,1,6,3,1}},
+ {{5,1,2,2,1,1,4,1}, {2,2,1,1,3,1,1,6}, {1,2,1,3,1,1,4,4}},
+ {{2,1,2,2,1,2,1,6}, {3,2,1,1,3,1,2,4}, {2,2,1,3,1,1,5,2}},
+ {{3,1,2,2,1,2,2,4}, {4,2,1,1,3,1,3,2}, {1,1,2,2,2,1,4,4}},
+ {{4,1,2,2,1,2,3,2}, {2,2,1,1,3,2,1,5}, {1,2,1,3,1,2,4,3}},
+ {{2,1,2,2,1,3,1,5}, {3,2,1,1,3,2,2,3}, {2,2,1,3,1,2,5,1}},
+ {{3,1,2,2,1,3,2,3}, {4,2,1,1,3,2,3,1}, {1,1,2,2,2,2,4,3}},
+ {{4,1,2,2,1,3,3,1}, {2,2,1,1,3,3,1,4}, {2,1,2,2,2,2,5,1}},
+ {{2,1,2,2,1,4,1,4}, {3,2,1,1,3,3,2,2}, {1,1,2,2,2,3,4,2}},
+ {{3,1,2,2,1,4,2,2}, {2,2,1,1,3,4,1,3}, {1,2,1,3,1,4,4,1}},
+ {{2,1,2,2,1,5,1,3}, {3,2,1,1,3,4,2,1}, {1,1,2,2,2,4,4,1}},
+ {{2,1,2,2,1,6,1,2}, {2,2,1,1,3,5,1,2}, {6,2,1,3,2,1,1,1}},
+ {{2,2,1,3,1,1,2,5}, {2,2,1,1,3,6,1,1}, {1,2,1,3,2,1,5,2}},
+ {{3,2,1,3,1,1,3,3}, {1,2,1,1,4,1,1,6}, {6,1,2,2,3,1,1,1}},
+ {{4,2,1,3,1,1,4,1}, {2,2,1,1,4,1,2,4}, {1,1,2,2,3,1,5,2}},
+ {{2,1,2,2,2,1,2,5}, {3,2,1,1,4,1,3,2}, {1,2,1,3,2,2,5,1}},
+ {{2,2,1,3,1,2,2,4}, {1,2,1,1,4,2,1,5}, {1,1,2,2,3,2,5,1}},
+ {{3,2,1,3,1,2,3,2}, {2,2,1,1,4,2,2,3}, {5,2,1,3,3,1,1,1}},
+ {{1,1,2,2,2,2,1,6}, {3,2,1,1,4,2,3,1}, {5,1,2,2,4,1,1,1}},
+ {{1,2,1,3,1,3,1,5}, {1,2,1,1,4,3,1,4}, {4,2,1,3,4,1,1,1}},
+ {{3,1,2,2,2,2,3,2}, {2,2,1,1,4,3,2,2}, {4,1,2,2,5,1,1,1}},
+ {{3,2,1,3,1,3,3,1}, {1,2,1,1,4,4,1,3}, {3,2,1,3,5,1,1,1}},
+ {{1,1,2,2,2,3,1,5}, {2,2,1,1,4,4,2,1}, {3,1,2,2,6,1,1,1}},
+ {{1,2,1,3,1,4,1,4}, {1,2,1,1,4,5,1,2}, {2,2,1,3,6,1,1,1}},
+ {{2,2,1,3,1,4,2,2}, {1,2,1,1,5,1,2,4}, {1,1,2,3,1,1,3,5}},
+ {{1,1,2,2,2,4,1,4}, {2,2,1,1,5,1,3,2}, {2,1,2,3,1,1,4,3}},
+ {{2,1,2,2,2,4,2,2}, {1,2,1,1,5,2,2,3}, {3,1,2,3,1,1,5,1}},
+ {{2,2,1,3,1,5,2,1}, {2,2,1,1,5,2,3,1}, {1,1,2,3,1,2,3,4}},
+ {{1,2,1,3,1,6,1,2}, {1,2,1,1,5,3,2,2}, {2,1,2,3,1,2,4,2}},
+ {{1,2,1,3,2,1,2,5}, {1,2,1,1,5,4,2,1}, {1,1,2,3,1,3,3,3}},
+ {{2,2,1,3,2,1,3,3}, {1,2,1,1,6,1,3,2}, {2,1,2,3,1,3,4,1}},
+ {{3,2,1,3,2,1,4,1}, {1,2,1,1,6,2,3,1}, {1,1,2,3,1,4,3,2}},
+ {{1,1,2,2,3,1,2,5}, {5,1,2,1,1,1,1,5}, {1,1,2,3,1,5,3,1}},
+ {{1,2,1,3,2,2,2,4}, {6,1,2,1,1,1,2,3}, {1,2,1,4,1,1,4,3}},
+ {{2,2,1,3,2,2,3,2}, {1,1,2,1,1,1,6,4}, {2,2,1,4,1,1,5,1}},
+ {{1,1,2,2,3,2,2,4}, {5,1,2,1,1,2,1,4}, {1,1,2,3,2,1,4,3}},
+ {{2,1,2,2,3,2,3,2}, {6,1,2,1,1,2,2,2}, {1,2,1,4,1,2,4,2}},
+ {{2,2,1,3,2,3,3,1}, {1,1,2,1,1,2,6,3}, {1,1,2,3,2,2,4,2}},
+ {{1,1,2,2,3,3,2,3}, {5,1,2,1,1,3,1,3}, {1,2,1,4,1,3,4,1}},
+ {{1,2,1,3,2,4,2,2}, {6,1,2,1,1,3,2,1}, {1,1,2,3,2,3,4,1}},
+ {{1,2,1,3,2,5,2,1}, {1,1,2,1,1,3,6,2}, {1,2,1,4,2,1,5,1}},
+ {{1,2,1,3,3,1,3,3}, {5,1,2,1,1,4,1,2}, {1,1,2,3,3,1,5,1}},
+ {{2,2,1,3,3,1,4,1}, {5,1,2,1,1,5,1,1}, {1,1,2,4,1,1,3,4}},
+ {{1,1,2,2,4,1,3,3}, {4,2,1,2,1,1,1,5}, {2,1,2,4,1,1,4,2}},
+ {{1,2,1,3,3,2,3,2}, {5,2,1,2,1,1,2,3}, {1,1,2,4,1,2,3,3}},
+ {{1,1,2,2,4,2,3,2}, {6,2,1,2,1,1,3,1}, {2,1,2,4,1,2,4,1}},
+ {{1,2,1,3,3,3,3,1}, {4,1,2,1,2,1,1,5}, {1,1,2,4,1,3,3,2}},
+ {{1,1,2,2,4,3,3,1}, {4,2,1,2,1,2,1,4}, {1,1,2,4,1,4,3,1}},
+ {{1,1,2,2,5,1,4,1}, {6,1,2,1,2,1,3,1}, {1,2,1,5,1,1,4,2}},
+ {{2,1,2,3,1,1,1,6}, {4,1,2,1,2,2,1,4}, {1,1,2,4,2,1,4,2}},
+ {{3,1,2,3,1,1,2,4}, {5,1,2,1,2,2,2,2}, {1,2,1,5,1,2,4,1}},
+ {{4,1,2,3,1,1,3,2}, {5,2,1,2,1,3,2,1}, {1,1,2,4,2,2,4,1}},
+ {{2,1,2,3,1,2,1,5}, {4,1,2,1,2,3,1,3}, {1,1,2,5,1,1,3,3}},
+ {{3,1,2,3,1,2,2,3}, {4,2,1,2,1,4,1,2}, {2,1,2,5,1,1,4,1}},
+ {{4,1,2,3,1,2,3,1}, {4,1,2,1,2,4,1,2}, {1,1,2,5,1,2,3,2}},
+ {{2,1,2,3,1,3,1,4}, {4,2,1,2,1,5,1,1}, {1,1,2,5,1,3,3,1}},
+ {{3,1,2,3,1,3,2,2}, {4,1,2,1,2,5,1,1}, {1,2,1,6,1,1,4,1}},
+ {{2,1,2,3,1,4,1,3}, {3,2,1,2,2,1,1,5}, {1,1,2,5,2,1,4,1}},
+ {{3,1,2,3,1,4,2,1}, {4,2,1,2,2,1,2,3}, {1,1,2,6,1,1,3,2}},
+ {{2,1,2,3,1,5,1,2}, {5,2,1,2,2,1,3,1}, {1,1,2,6,1,2,3,1}},
+ {{2,1,2,3,1,6,1,1}, {3,1,2,1,3,1,1,5}, {1,3,1,1,1,1,4,5}},
+ {{1,2,1,4,1,1,1,6}, {3,2,1,2,2,2,1,4}, {2,3,1,1,1,1,5,3}},
+ {{2,2,1,4,1,1,2,4}, {4,2,1,2,2,2,2,2}, {3,3,1,1,1,1,6,1}},
+ {{3,2,1,4,1,1,3,2}, {3,1,2,1,3,2,1,4}, {1,3,1,1,1,2,4,4}},
+ {{1,1,2,3,2,1,1,6}, {4,1,2,1,3,2,2,2}, {2,3,1,1,1,2,5,2}},
+ {{1,2,1,4,1,2,1,5}, {4,2,1,2,2,3,2,1}, {1,3,1,1,1,3,4,3}},
+ {{2,2,1,4,1,2,2,3}, {3,1,2,1,3,3,1,3}, {2,3,1,1,1,3,5,1}},
+ {{3,2,1,4,1,2,3,1}, {3,2,1,2,2,4,1,2}, {1,3,1,1,1,4,4,2}},
+ {{1,1,2,3,2,2,1,5}, {3,1,2,1,3,4,1,2}, {1,3,1,1,1,5,4,1}},
+ {{2,1,2,3,2,2,2,3}, {3,2,1,2,2,5,1,1}, {6,3,1,1,2,1,1,2}},
+ {{3,1,2,3,2,2,3,1}, {3,1,2,1,3,5,1,1}, {1,3,1,1,2,1,5,3}},
+ {{1,1,2,3,2,3,1,4}, {2,2,1,2,3,1,1,5}, {2,3,1,1,2,1,6,1}},
+ {{1,2,1,4,1,4,1,3}, {3,2,1,2,3,1,2,3}, {6,3,1,1,2,2,1,1}},
+ {{2,2,1,4,1,4,2,1}, {4,2,1,2,3,1,3,1}, {1,3,1,1,2,2,5,2}},
+ {{1,1,2,3,2,4,1,3}, {2,1,2,1,4,1,1,5}, {1,3,1,1,2,3,5,1}},
+ {{2,1,2,3,2,4,2,1}, {2,2,1,2,3,2,1,4}, {5,3,1,1,3,1,1,2}},
+ {{1,1,2,3,2,5,1,2}, {3,2,1,2,3,2,2,2}, {1,3,1,1,3,1,6,1}},
+ {{1,2,1,4,2,1,2,4}, {2,1,2,1,4,2,1,4}, {5,3,1,1,3,2,1,1}},
+ {{2,2,1,4,2,1,3,2}, {3,1,2,1,4,2,2,2}, {4,3,1,1,4,1,1,2}},
+ {{1,1,2,3,3,1,2,4}, {3,2,1,2,3,3,2,1}, {4,3,1,1,4,2,1,1}},
+ {{1,2,1,4,2,2,2,3}, {2,1,2,1,4,3,1,3}, {3,3,1,1,5,1,1,2}},
+ {{2,2,1,4,2,2,3,1}, {2,2,1,2,3,4,1,2}, {3,3,1,1,5,2,1,1}},
+ {{1,1,2,3,3,2,2,3}, {2,1,2,1,4,4,1,2}, {2,3,1,1,6,1,1,2}},
+ {{2,1,2,3,3,2,3,1}, {2,2,1,2,3,5,1,1}, {2,3,1,1,6,2,1,1}},
+ {{1,1,2,3,3,3,2,2}, {2,1,2,1,4,5,1,1}, {1,2,2,1,1,1,3,6}},
+ {{1,2,1,4,2,4,2,1}, {1,2,1,2,4,1,1,5}, {2,2,2,1,1,1,4,4}},
+ {{1,1,2,3,3,4,2,1}, {2,2,1,2,4,1,2,3}, {3,2,2,1,1,1,5,2}},
+ {{1,1,2,3,4,1,3,2}, {3,2,1,2,4,1,3,1}, {1,2,2,1,1,2,3,5}},
+ {{1,1,2,3,4,2,3,1}, {1,1,2,1,5,1,1,5}, {2,2,2,1,1,2,4,3}},
+ {{2,1,2,4,1,1,1,5}, {1,2,1,2,4,2,1,4}, {3,2,2,1,1,2,5,1}},
+ {{3,1,2,4,1,1,2,3}, {2,2,1,2,4,2,2,2}, {1,2,2,1,1,3,3,4}},
+ {{4,1,2,4,1,1,3,1}, {1,1,2,1,5,2,1,4}, {2,2,2,1,1,3,4,2}},
+ {{2,1,2,4,1,2,1,4}, {2,1,2,1,5,2,2,2}, {1,2,2,1,1,4,3,3}},
+ {{3,1,2,4,1,2,2,2}, {2,2,1,2,4,3,2,1}, {2,2,2,1,1,4,4,1}},
+ {{2,1,2,4,1,3,1,3}, {1,1,2,1,5,3,1,3}, {1,2,2,1,1,5,3,2}},
+ {{3,1,2,4,1,3,2,1}, {1,2,1,2,4,4,1,2}, {1,2,2,1,1,6,3,1}},
+ {{2,1,2,4,1,4,1,2}, {1,1,2,1,5,4,1,2}, {1,3,1,2,1,1,4,4}},
+ {{2,1,2,4,1,5,1,1}, {1,2,1,2,4,5,1,1}, {2,3,1,2,1,1,5,2}},
+ {{1,2,1,5,1,1,1,5}, {1,2,1,2,5,1,2,3}, {1,2,2,1,2,1,4,4}},
+ {{2,2,1,5,1,1,2,3}, {2,2,1,2,5,1,3,1}, {1,3,1,2,1,2,4,3}},
+ {{3,2,1,5,1,1,3,1}, {1,1,2,1,6,1,2,3}, {2,3,1,2,1,2,5,1}},
+ {{1,1,2,4,2,1,1,5}, {1,2,1,2,5,2,2,2}, {1,2,2,1,2,2,4,3}},
+ {{1,2,1,5,1,2,1,4}, {1,1,2,1,6,2,2,2}, {2,2,2,1,2,2,5,1}},
+ {{2,2,1,5,1,2,2,2}, {1,2,1,2,5,3,2,1}, {1,2,2,1,2,3,4,2}},
+ {{1,1,2,4,2,2,1,4}, {1,1,2,1,6,3,2,1}, {1,3,1,2,1,4,4,1}},
+ {{2,1,2,4,2,2,2,2}, {1,2,1,2,6,1,3,1}, {1,2,2,1,2,4,4,1}},
+ {{2,2,1,5,1,3,2,1}, {5,1,2,2,1,1,1,4}, {6,3,1,2,2,1,1,1}},
+ {{1,1,2,4,2,3,1,3}, {6,1,2,2,1,1,2,2}, {1,3,1,2,2,1,5,2}},
+ {{1,2,1,5,1,4,1,2}, {1,1,2,2,1,1,6,3}, {6,2,2,1,3,1,1,1}},
+ {{1,1,2,4,2,4,1,2}, {5,1,2,2,1,2,1,3}, {1,2,2,1,3,1,5,2}},
+ {{1,2,1,5,1,5,1,1}, {6,1,2,2,1,2,2,1}, {1,3,1,2,2,2,5,1}},
+ {{1,2,1,5,2,1,2,3}, {1,1,2,2,1,2,6,2}, {1,2,2,1,3,2,5,1}},
+ {{1,1,2,4,3,1,2,3}, {5,1,2,2,1,3,1,2}, {5,3,1,2,3,1,1,1}},
+ {{1,1,2,4,3,2,2,2}, {1,1,2,2,1,3,6,1}, {5,2,2,1,4,1,1,1}},
+ {{1,1,2,4,3,3,2,1}, {5,1,2,2,1,4,1,1}, {4,3,1,2,4,1,1,1}},
+ {{3,1,2,5,1,1,2,2}, {4,2,1,3,1,1,1,4}, {4,2,2,1,5,1,1,1}},
+ {{3,1,2,5,1,2,2,1}, {5,2,1,3,1,1,2,2}, {3,3,1,2,5,1,1,1}},
+ {{2,1,2,5,1,4,1,1}, {4,1,2,2,2,1,1,4}, {3,2,2,1,6,1,1,1}},
+ {{2,2,1,6,1,1,2,2}, {4,2,1,3,1,2,1,3}, {2,3,1,2,6,1,1,1}},
+ {{1,2,1,6,1,2,1,3}, {5,2,1,3,1,2,2,1}, {2,1,3,1,1,1,3,5}},
+ {{1,1,2,5,2,2,1,3}, {4,1,2,2,2,2,1,3}, {3,1,3,1,1,1,4,3}},
+ {{1,1,2,5,2,3,1,2}, {5,1,2,2,2,2,2,1}, {4,1,3,1,1,1,5,1}},
+ {{1,1,2,5,2,4,1,1}, {4,1,2,2,2,3,1,2}, {1,1,3,1,1,2,2,6}},
+ {{2,3,1,1,1,1,2,6}, {4,2,1,3,1,4,1,1}, {2,1,3,1,1,2,3,4}},
+ {{3,3,1,1,1,1,3,4}, {4,1,2,2,2,4,1,1}, {3,1,3,1,1,2,4,2}},
+ {{4,3,1,1,1,1,4,2}, {3,2,1,3,2,1,1,4}, {1,1,3,1,1,3,2,5}},
+ {{2,3,1,1,1,2,2,5}, {4,2,1,3,2,1,2,2}, {2,1,3,1,1,3,3,3}},
+ {{3,3,1,1,1,2,3,3}, {3,1,2,2,3,1,1,4}, {3,1,3,1,1,3,4,1}},
+ {{1,3,1,1,1,3,1,6}, {3,2,1,3,2,2,1,3}, {1,1,3,1,1,4,2,4}},
+ {{2,3,1,1,1,3,2,4}, {4,2,1,3,2,2,2,1}, {2,1,3,1,1,4,3,2}},
+ {{3,3,1,1,1,3,3,2}, {3,1,2,2,3,2,1,3}, {1,1,3,1,1,5,2,3}},
+ {{1,3,1,1,1,4,1,5}, {4,1,2,2,3,2,2,1}, {2,1,3,1,1,5,3,1}},
+ {{2,3,1,1,1,4,2,3}, {3,1,2,2,3,3,1,2}, {1,1,3,1,1,6,2,2}},
+ {{1,3,1,1,1,5,1,4}, {3,2,1,3,2,4,1,1}, {1,2,2,2,1,1,3,5}},
+ {{1,3,1,1,1,6,1,3}, {3,1,2,2,3,4,1,1}, {2,2,2,2,1,1,4,3}},
+ {{1,3,1,1,2,1,2,6}, {2,2,1,3,3,1,1,4}, {3,2,2,2,1,1,5,1}},
+ {{2,3,1,1,2,1,3,4}, {3,2,1,3,3,1,2,2}, {1,1,3,1,2,1,3,5}},
+ {{3,3,1,1,2,1,4,2}, {2,1,2,2,4,1,1,4}, {1,2,2,2,1,2,3,4}},
+ {{1,3,1,1,2,2,2,5}, {2,2,1,3,3,2,1,3}, {2,2,2,2,1,2,4,2}},
+ {{2,3,1,1,2,2,3,3}, {3,2,1,3,3,2,2,1}, {1,1,3,1,2,2,3,4}},
+ {{3,3,1,1,2,2,4,1}, {2,1,2,2,4,2,1,3}, {2,1,3,1,2,2,4,2}},
+ {{1,3,1,1,2,3,2,4}, {3,1,2,2,4,2,2,1}, {2,2,2,2,1,3,4,1}},
+ {{2,3,1,1,2,3,3,2}, {2,1,2,2,4,3,1,2}, {1,1,3,1,2,3,3,3}},
+ {{1,3,1,1,2,4,2,3}, {2,2,1,3,3,4,1,1}, {1,2,2,2,1,4,3,2}},
+ {{1,3,1,1,2,5,2,2}, {2,1,2,2,4,4,1,1}, {1,1,3,1,2,4,3,2}},
+ {{1,3,1,1,3,1,3,4}, {1,2,1,3,4,1,1,4}, {1,2,2,2,1,5,3,1}},
+ {{2,3,1,1,3,1,4,2}, {2,2,1,3,4,1,2,2}, {1,1,3,1,2,5,3,1}},
+ {{1,3,1,1,3,2,3,3}, {1,1,2,2,5,1,1,4}, {1,3,1,3,1,1,4,3}},
+ {{2,3,1,1,3,2,4,1}, {1,2,1,3,4,2,1,3}, {2,3,1,3,1,1,5,1}},
+ {{1,3,1,1,3,3,3,2}, {2,2,1,3,4,2,2,1}, {1,2,2,2,2,1,4,3}},
+ {{1,3,1,1,4,1,4,2}, {1,1,2,2,5,2,1,3}, {1,3,1,3,1,2,4,2}},
+ {{1,3,1,1,4,2,4,1}, {2,1,2,2,5,2,2,1}, {1,1,3,1,3,1,4,3}},
+ {{3,2,2,1,1,1,2,5}, {1,1,2,2,5,3,1,2}, {1,2,2,2,2,2,4,2}},
+ {{4,2,2,1,1,1,3,3}, {1,2,1,3,4,4,1,1}, {1,3,1,3,1,3,4,1}},
+ {{5,2,2,1,1,1,4,1}, {1,1,2,2,5,4,1,1}, {1,1,3,1,3,2,4,2}},
+ {{2,2,2,1,1,2,1,6}, {1,2,1,3,5,1,2,2}, {1,2,2,2,2,3,4,1}},
+ {{3,2,2,1,1,2,2,4}, {1,1,2,2,6,1,2,2}, {1,1,3,1,3,3,4,1}},
+ {{4,2,2,1,1,2,3,2}, {1,2,1,3,5,2,2,1}, {1,3,1,3,2,1,5,1}},
+ {{2,2,2,1,1,3,1,5}, {1,1,2,2,6,2,2,1}, {1,2,2,2,3,1,5,1}},
+ {{3,2,2,1,1,3,2,3}, {5,1,2,3,1,1,1,3}, {1,1,3,1,4,1,5,1}},
+ {{4,2,2,1,1,3,3,1}, {6,1,2,3,1,1,2,1}, {1,1,3,2,1,1,2,6}},
+ {{2,2,2,1,1,4,1,4}, {1,1,2,3,1,1,6,2}, {2,1,3,2,1,1,3,4}},
+ {{3,2,2,1,1,4,2,2}, {5,1,2,3,1,2,1,2}, {3,1,3,2,1,1,4,2}},
+ {{2,2,2,1,1,5,1,3}, {1,1,2,3,1,2,6,1}, {1,1,3,2,1,2,2,5}},
+ {{3,2,2,1,1,5,2,1}, {5,1,2,3,1,3,1,1}, {2,1,3,2,1,2,3,3}},
+ {{2,3,1,2,1,1,2,5}, {4,2,1,4,1,1,1,3}, {3,1,3,2,1,2,4,1}},
+ {{3,3,1,2,1,1,3,3}, {5,2,1,4,1,1,2,1}, {1,1,3,2,1,3,2,4}},
+ {{4,3,1,2,1,1,4,1}, {4,1,2,3,2,1,1,3}, {2,1,3,2,1,3,3,2}},
+ {{2,2,2,1,2,1,2,5}, {5,1,2,3,2,1,2,1}, {1,1,3,2,1,4,2,3}},
+ {{2,3,1,2,1,2,2,4}, {4,1,2,3,2,2,1,2}, {2,1,3,2,1,4,3,1}},
+ {{3,3,1,2,1,2,3,2}, {4,2,1,4,1,3,1,1}, {1,1,3,2,1,5,2,2}},
+ {{1,2,2,1,2,2,1,6}, {4,1,2,3,2,3,1,1}, {1,1,3,2,1,6,2,1}},
+ {{1,3,1,2,1,3,1,5}, {3,2,1,4,2,1,1,3}, {1,2,2,3,1,1,3,4}},
+ {{3,2,2,1,2,2,3,2}, {4,2,1,4,2,1,2,1}, {2,2,2,3,1,1,4,2}},
+ {{3,3,1,2,1,3,3,1}, {3,1,2,3,3,1,1,3}, {1,1,3,2,2,1,3,4}},
+ {{1,2,2,1,2,3,1,5}, {3,2,1,4,2,2,1,2}, {1,2,2,3,1,2,3,3}},
+ {{2,2,2,1,2,3,2,3}, {3,1,2,3,3,2,1,2}, {2,2,2,3,1,2,4,1}},
+ {{2,3,1,2,1,4,2,2}, {3,2,1,4,2,3,1,1}, {1,1,3,2,2,2,3,3}},
+ {{1,2,2,1,2,4,1,4}, {3,1,2,3,3,3,1,1}, {2,1,3,2,2,2,4,1}},
+ {{1,3,1,2,1,5,1,3}, {2,2,1,4,3,1,1,3}, {1,1,3,2,2,3,3,2}},
+ {{1,2,2,1,2,5,1,3}, {3,2,1,4,3,1,2,1}, {1,2,2,3,1,4,3,1}},
+ {{1,3,1,2,2,1,2,5}, {2,1,2,3,4,1,1,3}, {1,1,3,2,2,4,3,1}},
+ {{2,3,1,2,2,1,3,3}, {3,1,2,3,4,1,2,1}, {1,3,1,4,1,1,4,2}},
+ {{3,3,1,2,2,1,4,1}, {2,1,2,3,4,2,1,2}, {1,2,2,3,2,1,4,2}},
+ {{1,2,2,1,3,1,2,5}, {2,2,1,4,3,3,1,1}, {1,3,1,4,1,2,4,1}},
+ {{1,3,1,2,2,2,2,4}, {2,1,2,3,4,3,1,1}, {1,1,3,2,3,1,4,2}},
+ {{3,2,2,1,3,1,4,1}, {1,2,1,4,4,1,1,3}, {1,2,2,3,2,2,4,1}},
+ {{1,2,2,1,3,2,2,4}, {2,2,1,4,4,1,2,1}, {1,1,3,2,3,2,4,1}},
+ {{2,2,2,1,3,2,3,2}, {1,1,2,3,5,1,1,3}, {1,1,3,3,1,1,2,5}},
+ {{2,3,1,2,2,3,3,1}, {1,2,1,4,4,2,1,2}, {2,1,3,3,1,1,3,3}},
+ {{1,2,2,1,3,3,2,3}, {1,1,2,3,5,2,1,2}, {3,1,3,3,1,1,4,1}},
+ {{1,3,1,2,2,4,2,2}, {1,2,1,4,4,3,1,1}, {1,1,3,3,1,2,2,4}},
+ {{1,2,2,1,3,4,2,2}, {1,1,2,3,5,3,1,1}, {2,1,3,3,1,2,3,2}},
+ {{1,3,1,2,3,1,3,3}, {1,2,1,4,5,1,2,1}, {1,1,3,3,1,3,2,3}},
+ {{2,3,1,2,3,1,4,1}, {1,1,2,3,6,1,2,1}, {2,1,3,3,1,3,3,1}},
+ {{1,2,2,1,4,1,3,3}, {5,1,2,4,1,1,1,2}, {1,1,3,3,1,4,2,2}},
+ {{1,3,1,2,3,2,3,2}, {1,1,2,4,1,1,6,1}, {1,1,3,3,1,5,2,1}},
+ {{1,2,2,1,4,2,3,2}, {5,1,2,4,1,2,1,1}, {1,2,2,4,1,1,3,3}},
+ {{1,3,1,2,3,3,3,1}, {4,2,1,5,1,1,1,2}, {2,2,2,4,1,1,4,1}},
+ {{1,3,1,2,4,1,4,1}, {4,1,2,4,2,1,1,2}, {1,1,3,3,2,1,3,3}},
+ {{1,2,2,1,5,1,4,1}, {4,2,1,5,1,2,1,1}, {1,2,2,4,1,2,3,2}},
+ {{3,1,3,1,1,1,1,6}, {4,1,2,4,2,2,1,1}, {1,1,3,3,2,2,3,2}},
+ {{4,1,3,1,1,1,2,4}, {3,2,1,5,2,1,1,2}, {1,2,2,4,1,3,3,1}},
+ {{5,1,3,1,1,1,3,2}, {3,1,2,4,3,1,1,2}, {1,1,3,3,2,3,3,1}},
+ {{3,1,3,1,1,2,1,5}, {3,2,1,5,2,2,1,1}, {1,3,1,5,1,1,4,1}},
+ {{4,1,3,1,1,2,2,3}, {3,1,2,4,3,2,1,1}, {1,2,2,4,2,1,4,1}},
+ {{5,1,3,1,1,2,3,1}, {2,2,1,5,3,1,1,2}, {1,1,3,3,3,1,4,1}},
+ {{3,1,3,1,1,3,1,4}, {2,1,2,4,4,1,1,2}, {1,1,3,4,1,1,2,4}},
+ {{4,1,3,1,1,3,2,2}, {2,2,1,5,3,2,1,1}, {2,1,3,4,1,1,3,2}},
+ {{3,1,3,1,1,4,1,3}, {2,1,2,4,4,2,1,1}, {1,1,3,4,1,2,2,3}},
+ {{4,1,3,1,1,4,2,1}, {1,2,1,5,4,1,1,2}, {2,1,3,4,1,2,3,1}},
+ {{3,1,3,1,1,5,1,2}, {1,1,2,4,5,1,1,2}, {1,1,3,4,1,3,2,2}},
+ {{2,2,2,2,1,1,1,6}, {1,2,1,5,4,2,1,1}, {1,1,3,4,1,4,2,1}},
+ {{3,2,2,2,1,1,2,4}, {1,1,2,4,5,2,1,1}, {1,2,2,5,1,1,3,2}},
+ {{4,2,2,2,1,1,3,2}, {5,1,2,5,1,1,1,1}, {1,1,3,4,2,1,3,2}},
+ {{2,1,3,1,2,1,1,6}, {4,2,1,6,1,1,1,1}, {1,2,2,5,1,2,3,1}},
+ {{2,2,2,2,1,2,1,5}, {4,1,2,5,2,1,1,1}, {1,1,3,4,2,2,3,1}},
+ {{4,1,3,1,2,1,3,2}, {3,2,1,6,2,1,1,1}, {1,1,3,5,1,1,2,3}},
+ {{4,2,2,2,1,2,3,1}, {3,1,2,5,3,1,1,1}, {2,1,3,5,1,1,3,1}},
+ {{2,1,3,1,2,2,1,5}, {2,2,1,6,3,1,1,1}, {1,1,3,5,1,2,2,2}},
+ {{3,1,3,1,2,2,2,3}, {2,1,2,5,4,1,1,1}, {1,1,3,5,1,3,2,1}},
+ {{4,1,3,1,2,2,3,1}, {4,3,1,1,1,1,1,5}, {1,2,2,6,1,1,3,1}},
+ {{2,1,3,1,2,3,1,4}, {5,3,1,1,1,1,2,3}, {1,1,3,5,2,1,3,1}},
+ {{2,2,2,2,1,4,1,3}, {6,3,1,1,1,1,3,1}, {1,1,3,6,1,1,2,2}},
+ {{3,2,2,2,1,4,2,1}, {4,3,1,1,1,2,1,4}, {1,1,3,6,1,2,2,1}},
+ {{2,1,3,1,2,4,1,3}, {5,3,1,1,1,2,2,2}, {1,4,1,1,1,1,4,4}},
+ {{3,1,3,1,2,4,2,1}, {4,3,1,1,1,3,1,3}, {2,4,1,1,1,1,5,2}},
+ {{2,2,2,2,1,6,1,1}, {5,3,1,1,1,3,2,1}, {1,4,1,1,1,2,4,3}},
+ {{1,3,1,3,1,1,1,6}, {4,3,1,1,1,4,1,2}, {2,4,1,1,1,2,5,1}},
+ {{2,3,1,3,1,1,2,4}, {4,3,1,1,1,5,1,1}, {1,4,1,1,1,3,4,2}},
+ {{3,3,1,3,1,1,3,2}, {3,3,1,1,2,1,1,5}, {1,4,1,1,1,4,4,1}},
+ {{1,2,2,2,2,1,1,6}, {4,3,1,1,2,1,2,3}, {1,4,1,1,2,1,5,2}},
+ {{1,3,1,3,1,2,1,5}, {5,3,1,1,2,1,3,1}, {1,4,1,1,2,2,5,1}},
+ {{2,3,1,3,1,2,2,3}, {3,3,1,1,2,2,1,4}, {5,4,1,1,3,1,1,1}},
+ {{3,3,1,3,1,2,3,1}, {4,3,1,1,2,2,2,2}, {4,4,1,1,4,1,1,1}},
+ {{1,1,3,1,3,1,1,6}, {3,3,1,1,2,3,1,3}, {3,4,1,1,5,1,1,1}},
+ {{1,2,2,2,2,2,1,5}, {4,3,1,1,2,3,2,1}, {2,4,1,1,6,1,1,1}},
+ {{2,2,2,2,2,2,2,3}, {3,3,1,1,2,4,1,2}, {1,3,2,1,1,1,3,5}},
+ {{3,2,2,2,2,2,3,1}, {3,3,1,1,2,5,1,1}, {2,3,2,1,1,1,4,3}},
+ {{1,1,3,1,3,2,1,5}, {2,3,1,1,3,1,1,5}, {3,3,2,1,1,1,5,1}},
+ {{2,1,3,1,3,2,2,3}, {3,3,1,1,3,1,2,3}, {1,3,2,1,1,2,3,4}},
+ {{3,1,3,1,3,2,3,1}, {4,3,1,1,3,1,3,1}, {2,3,2,1,1,2,4,2}},
+ {{2,3,1,3,1,4,2,1}, {2,3,1,1,3,2,1,4}, {1,3,2,1,1,3,3,3}},
+ {{1,1,3,1,3,3,1,4}, {3,3,1,1,3,2,2,2}, {2,3,2,1,1,3,4,1}},
+ {{1,2,2,2,2,4,1,3}, {2,3,1,1,3,3,1,3}, {1,3,2,1,1,4,3,2}},
+ {{2,2,2,2,2,4,2,1}, {3,3,1,1,3,3,2,1}, {1,3,2,1,1,5,3,1}},
+ {{1,1,3,1,3,4,1,3}, {2,3,1,1,3,4,1,2}, {1,4,1,2,1,1,4,3}},
+ {{1,3,1,3,1,6,1,1}, {2,3,1,1,3,5,1,1}, {2,4,1,2,1,1,5,1}},
+ {{1,3,1,3,2,1,2,4}, {1,3,1,1,4,1,1,5}, {1,3,2,1,2,1,4,3}},
+ {{2,3,1,3,2,1,3,2}, {2,3,1,1,4,1,2,3}, {1,4,1,2,1,2,4,2}},
+ {{1,2,2,2,3,1,2,4}, {3,3,1,1,4,1,3,1}, {1,3,2,1,2,2,4,2}},
+ {{1,3,1,3,2,2,2,3}, {1,3,1,1,4,2,1,4}, {1,4,1,2,1,3,4,1}},
+ {{2,3,1,3,2,2,3,1}, {2,3,1,1,4,2,2,2}, {1,3,2,1,2,3,4,1}},
+ {{1,1,3,1,4,1,2,4}, {1,3,1,1,4,3,1,3}, {1,4,1,2,2,1,5,1}},
+ {{1,2,2,2,3,2,2,3}, {2,3,1,1,4,3,2,1}, {1,3,2,1,3,1,5,1}},
+ {{2,2,2,2,3,2,3,1}, {1,3,1,1,4,4,1,2}, {1,2,3,1,1,1,2,6}},
+ {{1,1,3,1,4,2,2,3}, {1,3,1,1,4,5,1,1}, {2,2,3,1,1,1,3,4}},
+ {{2,1,3,1,4,2,3,1}, {1,3,1,1,5,1,2,3}, {3,2,3,1,1,1,4,2}},
+ {{1,3,1,3,2,4,2,1}, {2,3,1,1,5,1,3,1}, {1,2,3,1,1,2,2,5}},
+ {{1,2,2,2,3,4,2,1}, {1,3,1,1,5,2,2,2}, {2,2,3,1,1,2,3,3}},
+ {{1,3,1,3,3,1,3,2}, {1,3,1,1,5,3,2,1}, {3,2,3,1,1,2,4,1}},
+ {{1,2,2,2,4,1,3,2}, {1,3,1,1,6,1,3,1}, {1,2,3,1,1,3,2,4}},
+ {{1,3,1,3,3,2,3,1}, {5,2,2,1,1,1,1,4}, {2,2,3,1,1,3,3,2}},
+ {{1,1,3,1,5,1,3,2}, {6,2,2,1,1,1,2,2}, {1,2,3,1,1,4,2,3}},
+ {{1,2,2,2,4,2,3,1}, {1,2,2,1,1,1,6,3}, {2,2,3,1,1,4,3,1}},
+ {{3,1,3,2,1,1,1,5}, {5,2,2,1,1,2,1,3}, {1,2,3,1,1,5,2,2}},
+ {{4,1,3,2,1,1,2,3}, {6,2,2,1,1,2,2,1}, {1,2,3,1,1,6,2,1}},
+ {{5,1,3,2,1,1,3,1}, {1,2,2,1,1,2,6,2}, {1,3,2,2,1,1,3,4}},
+ {{3,1,3,2,1,2,1,4}, {5,2,2,1,1,3,1,2}, {2,3,2,2,1,1,4,2}},
+ {{4,1,3,2,1,2,2,2}, {1,2,2,1,1,3,6,1}, {1,2,3,1,2,1,3,4}},
+ {{3,1,3,2,1,3,1,3}, {5,2,2,1,1,4,1,1}, {1,3,2,2,1,2,3,3}},
+ {{4,1,3,2,1,3,2,1}, {4,3,1,2,1,1,1,4}, {2,3,2,2,1,2,4,1}},
+ {{3,1,3,2,1,4,1,2}, {5,3,1,2,1,1,2,2}, {1,2,3,1,2,2,3,3}},
+ {{3,1,3,2,1,5,1,1}, {4,2,2,1,2,1,1,4}, {1,3,2,2,1,3,3,2}},
+ {{2,2,2,3,1,1,1,5}, {4,3,1,2,1,2,1,3}, {1,2,3,1,2,3,3,2}},
+ {{3,2,2,3,1,1,2,3}, {5,3,1,2,1,2,2,1}, {1,3,2,2,1,4,3,1}},
+ {{4,2,2,3,1,1,3,1}, {4,2,2,1,2,2,1,3}, {1,2,3,1,2,4,3,1}},
+ {{2,1,3,2,2,1,1,5}, {5,2,2,1,2,2,2,1}, {1,4,1,3,1,1,4,2}},
+ {{2,2,2,3,1,2,1,4}, {4,2,2,1,2,3,1,2}, {1,3,2,2,2,1,4,2}},
+ {{4,1,3,2,2,1,3,1}, {4,3,1,2,1,4,1,1}, {1,4,1,3,1,2,4,1}},
+ {{2,1,3,2,2,2,1,4}, {4,2,2,1,2,4,1,1}, {1,2,3,1,3,1,4,2}},
+ {{3,1,3,2,2,2,2,2}, {3,3,1,2,2,1,1,4}, {1,3,2,2,2,2,4,1}},
+ {{3,2,2,3,1,3,2,1}, {4,3,1,2,2,1,2,2}, {1,2,3,1,3,2,4,1}},
+ {{2,1,3,2,2,3,1,3}, {3,2,2,1,3,1,1,4}, {2,1,4,1,1,1,2,5}},
+ {{2,2,2,3,1,4,1,2}, {3,3,1,2,2,2,1,3}, {3,1,4,1,1,1,3,3}},
+ {{2,1,3,2,2,4,1,2}, {4,3,1,2,2,2,2,1}, {4,1,4,1,1,1,4,1}},
+ {{2,2,2,3,1,5,1,1}, {3,2,2,1,3,2,1,3}, {1,1,4,1,1,2,1,6}},
+ {{2,1,3,2,2,5,1,1}, {4,2,2,1,3,2,2,1}, {2,1,4,1,1,2,2,4}},
+ {{1,3,1,4,1,1,1,5}, {3,2,2,1,3,3,1,2}, {3,1,4,1,1,2,3,2}},
+ {{2,3,1,4,1,1,2,3}, {3,3,1,2,2,4,1,1}, {1,1,4,1,1,3,1,5}},
+ {{3,3,1,4,1,1,3,1}, {3,2,2,1,3,4,1,1}, {2,1,4,1,1,3,2,3}},
+ {{1,2,2,3,2,1,1,5}, {2,3,1,2,3,1,1,4}, {3,1,4,1,1,3,3,1}},
+ {{1,3,1,4,1,2,1,4}, {3,3,1,2,3,1,2,2}, {1,1,4,1,1,4,1,4}},
+ {{2,3,1,4,1,2,2,2}, {2,2,2,1,4,1,1,4}, {2,1,4,1,1,4,2,2}},
+ {{1,1,3,2,3,1,1,5}, {2,3,1,2,3,2,1,3}, {1,1,4,1,1,5,1,3}},
+ {{1,2,2,3,2,2,1,4}, {3,3,1,2,3,2,2,1}, {2,1,4,1,1,5,2,1}},
+ {{2,2,2,3,2,2,2,2}, {2,2,2,1,4,2,1,3}, {1,1,4,1,1,6,1,2}},
+ {{2,3,1,4,1,3,2,1}, {3,2,2,1,4,2,2,1}, {1,2,3,2,1,1,2,5}},
+ {{1,1,3,2,3,2,1,4}, {2,2,2,1,4,3,1,2}, {2,2,3,2,1,1,3,3}},
+ {{2,1,3,2,3,2,2,2}, {2,3,1,2,3,4,1,1}, {3,2,3,2,1,1,4,1}},
+ {{1,3,1,4,1,4,1,2}, {2,2,2,1,4,4,1,1}, {1,1,4,1,2,1,2,5}},
+ {{1,1,3,2,3,3,1,3}, {1,3,1,2,4,1,1,4}, {1,2,3,2,1,2,2,4}},
+ {{1,2,2,3,2,4,1,2}, {2,3,1,2,4,1,2,2}, {2,2,3,2,1,2,3,2}},
+ {{1,3,1,4,1,5,1,1}, {1,2,2,1,5,1,1,4}, {1,1,4,1,2,2,2,4}},
+ {{1,2,2,3,2,5,1,1}, {1,3,1,2,4,2,1,3}, {2,1,4,1,2,2,3,2}},
+ {{1,3,1,4,2,1,2,3}, {2,3,1,2,4,2,2,1}, {2,2,3,2,1,3,3,1}},
+ {{2,3,1,4,2,1,3,1}, {1,2,2,1,5,2,1,3}, {1,1,4,1,2,3,2,3}},
+ {{1,2,2,3,3,1,2,3}, {2,2,2,1,5,2,2,1}, {1,2,3,2,1,4,2,2}},
+ {{1,3,1,4,2,2,2,2}, {1,2,2,1,5,3,1,2}, {1,1,4,1,2,4,2,2}},
+ {{1,1,3,2,4,1,2,3}, {1,3,1,2,4,4,1,1}, {1,2,3,2,1,5,2,1}},
+ {{1,2,2,3,3,2,2,2}, {1,2,2,1,5,4,1,1}, {1,1,4,1,2,5,2,1}},
+ {{1,3,1,4,2,3,2,1}, {1,3,1,2,5,1,2,2}, {1,3,2,3,1,1,3,3}},
+ {{1,1,3,2,4,2,2,2}, {1,2,2,1,6,1,2,2}, {2,3,2,3,1,1,4,1}},
+ {{1,2,2,3,3,3,2,1}, {1,3,1,2,5,2,2,1}, {1,2,3,2,2,1,3,3}},
+ {{1,3,1,4,3,1,3,1}, {1,2,2,1,6,2,2,1}, {1,3,2,3,1,2,3,2}},
+ {{1,1,3,2,5,1,3,1}, {6,1,3,1,1,1,1,3}, {1,1,4,1,3,1,3,3}},
+ {{3,1,3,3,1,1,1,4}, {1,1,3,1,1,1,5,4}, {1,2,3,2,2,2,3,2}},
+ {{4,1,3,3,1,1,2,2}, {2,1,3,1,1,1,6,2}, {1,3,2,3,1,3,3,1}},
+ {{3,1,3,3,1,2,1,3}, {6,1,3,1,1,2,1,2}, {1,1,4,1,3,2,3,2}},
+ {{4,1,3,3,1,2,2,1}, {1,1,3,1,1,2,5,3}, {1,2,3,2,2,3,3,1}},
+ {{3,1,3,3,1,3,1,2}, {2,1,3,1,1,2,6,1}, {1,1,4,1,3,3,3,1}},
+ {{3,1,3,3,1,4,1,1}, {6,1,3,1,1,3,1,1}, {1,4,1,4,1,1,4,1}},
+ {{2,2,2,4,1,1,1,4}, {1,1,3,1,1,3,5,2}, {1,3,2,3,2,1,4,1}},
+ {{3,2,2,4,1,1,2,2}, {1,1,3,1,1,4,5,1}, {1,2,3,2,3,1,4,1}},
+ {{2,1,3,3,2,1,1,4}, {5,2,2,2,1,1,1,3}, {1,1,4,1,4,1,4,1}},
+ {{2,2,2,4,1,2,1,3}, {6,2,2,2,1,1,2,1}, {1,1,4,2,1,1,1,6}},
+ {{3,2,2,4,1,2,2,1}, {1,2,2,2,1,1,6,2}, {2,1,4,2,1,1,2,4}},
+ {{2,1,3,3,2,2,1,3}, {5,1,3,1,2,1,1,3}, {3,1,4,2,1,1,3,2}},
+ {{3,1,3,3,2,2,2,1}, {6,1,3,1,2,1,2,1}, {1,1,4,2,1,2,1,5}},
+ {{2,1,3,3,2,3,1,2}, {1,1,3,1,2,1,6,2}, {2,1,4,2,1,2,2,3}},
+ {{2,2,2,4,1,4,1,1}, {1,2,2,2,1,2,6,1}, {3,1,4,2,1,2,3,1}},
+ {{2,1,3,3,2,4,1,1}, {5,1,3,1,2,2,1,2}, {1,1,4,2,1,3,1,4}},
+ {{1,3,1,5,1,1,1,4}, {5,2,2,2,1,3,1,1}, {2,1,4,2,1,3,2,2}},
+ {{2,3,1,5,1,1,2,2}, {1,1,3,1,2,2,6,1}, {1,1,4,2,1,4,1,3}},
+ {{1,2,2,4,2,1,1,4}, {5,1,3,1,2,3,1,1}, {2,1,4,2,1,4,2,1}},
+ {{1,3,1,5,1,2,1,3}, {4,3,1,3,1,1,1,3}, {1,1,4,2,1,5,1,2}},
+ {{2,3,1,5,1,2,2,1}, {5,3,1,3,1,1,2,1}, {1,1,4,2,1,6,1,1}},
+ {{1,1,3,3,3,1,1,4}, {4,2,2,2,2,1,1,3}, {1,2,3,3,1,1,2,4}},
+ {{1,2,2,4,2,2,1,3}, {4,3,1,3,1,2,1,2}, {2,2,3,3,1,1,3,2}},
+ {{2,2,2,4,2,2,2,1}, {4,1,3,1,3,1,1,3}, {1,1,4,2,2,1,2,4}},
+ {{1,1,3,3,3,2,1,3}, {5,1,3,1,3,1,2,1}, {1,2,3,3,1,2,2,3}},
+ {{2,1,3,3,3,2,2,1}, {4,3,1,3,1,3,1,1}, {2,2,3,3,1,2,3,1}},
+ {{1,3,1,5,1,4,1,1}, {4,1,3,1,3,2,1,2}, {1,1,4,2,2,2,2,3}},
+ {{1,1,3,3,3,3,1,2}, {4,2,2,2,2,3,1,1}, {2,1,4,2,2,2,3,1}},
+ {{1,2,2,4,2,4,1,1}, {4,1,3,1,3,3,1,1}, {1,1,4,2,2,3,2,2}},
+ {{1,1,3,3,3,4,1,1}, {3,3,1,3,2,1,1,3}, {1,2,3,3,1,4,2,1}},
+ {{1,2,2,4,3,1,2,2}, {4,3,1,3,2,1,2,1}, {1,1,4,2,2,4,2,1}},
+ {{1,1,3,3,4,1,2,2}, {3,2,2,2,3,1,1,3}, {1,3,2,4,1,1,3,2}},
+ {{1,1,3,3,4,2,2,1}, {3,3,1,3,2,2,1,2}, {1,2,3,3,2,1,3,2}},
+ {{4,1,3,4,1,1,2,1}, {3,1,3,1,4,1,1,3}, {1,3,2,4,1,2,3,1}},
+ {{3,1,3,4,1,3,1,1}, {3,2,2,2,3,2,1,2}, {1,1,4,2,3,1,3,2}},
+ {{3,2,2,5,1,1,2,1}, {3,3,1,3,2,3,1,1}, {1,2,3,3,2,2,3,1}},
+ {{2,2,2,5,1,2,1,2}, {3,1,3,1,4,2,1,2}, {1,1,4,2,3,2,3,1}},
+ {{2,2,2,5,1,3,1,1}, {3,2,2,2,3,3,1,1}, {1,1,4,3,1,1,1,5}},
+ {{1,3,1,6,1,1,1,3}, {3,1,3,1,4,3,1,1}, {2,1,4,3,1,1,2,3}},
+ {{1,2,2,5,2,1,1,3}, {2,3,1,3,3,1,1,3}, {3,1,4,3,1,1,3,1}},
+ {{1,1,3,4,3,1,1,3}, {3,3,1,3,3,1,2,1}, {1,1,4,3,1,2,1,4}},
+ {{1,3,1,6,1,3,1,1}, {2,2,2,2,4,1,1,3}, {2,1,4,3,1,2,2,2}},
+ {{1,2,2,5,2,3,1,1}, {2,3,1,3,3,2,1,2}, {1,1,4,3,1,3,1,3}},
+ {{2,4,1,1,1,1,2,5}, {2,1,3,1,5,1,1,3}, {2,1,4,3,1,3,2,1}},
+ {{1,4,1,1,1,2,1,6}, {2,2,2,2,4,2,1,2}, {1,1,4,3,1,4,1,2}},
+ {{2,4,1,1,1,2,2,4}, {2,3,1,3,3,3,1,1}, {1,1,4,3,1,5,1,1}},
+ {{1,4,1,1,1,3,1,5}, {2,1,3,1,5,2,1,2}, {1,2,3,4,1,1,2,3}},
+ {{2,4,1,1,1,3,2,3}, {2,2,2,2,4,3,1,1}, {2,2,3,4,1,1,3,1}},
+ {{3,4,1,1,1,3,3,1}, {2,1,3,1,5,3,1,1}, {1,1,4,3,2,1,2,3}},
+ {{1,4,1,1,1,4,1,4}, {1,3,1,3,4,1,1,3}, {1,2,3,4,1,2,2,2}},
+ {{2,4,1,1,1,4,2,2}, {2,3,1,3,4,1,2,1}, {1,1,4,3,2,2,2,2}},
+ {{1,4,1,1,1,5,1,3}, {1,2,2,2,5,1,1,3}, {1,2,3,4,1,3,2,1}},
+ {{2,4,1,1,1,5,2,1}, {1,3,1,3,4,2,1,2}, {1,1,4,3,2,3,2,1}},
+ {{1,4,1,1,2,1,2,5}, {1,1,3,1,6,1,1,3}, {1,3,2,5,1,1,3,1}},
+ {{2,4,1,1,2,1,3,3}, {1,2,2,2,5,2,1,2}, {1,2,3,4,2,1,3,1}},
+ {{3,4,1,1,2,1,4,1}, {1,3,1,3,4,3,1,1}, {1,1,4,3,3,1,3,1}},
+ {{1,4,1,1,2,2,2,4}, {1,1,3,1,6,2,1,2}, {1,1,4,4,1,1,1,4}},
+ {{2,4,1,1,2,2,3,2}, {1,2,2,2,5,3,1,1}, {2,1,4,4,1,1,2,2}},
+ {{1,4,1,1,2,3,2,3}, {1,1,3,1,6,3,1,1}, {1,1,4,4,1,2,1,3}},
+ {{2,4,1,1,2,3,3,1}, {1,3,1,3,5,1,2,1}, {2,1,4,4,1,2,2,1}},
+ {{1,4,1,1,2,4,2,2}, {1,2,2,2,6,1,2,1}, {1,1,4,4,1,3,1,2}},
+ {{1,4,1,1,2,5,2,1}, {6,1,3,2,1,1,1,2}, {1,1,4,4,1,4,1,1}},
+ {{1,4,1,1,3,1,3,3}, {1,1,3,2,1,1,5,3}, {1,2,3,5,1,1,2,2}},
+ {{2,4,1,1,3,1,4,1}, {2,1,3,2,1,1,6,1}, {1,1,4,4,2,1,2,2}},
+ {{1,4,1,1,3,2,3,2}, {6,1,3,2,1,2,1,1}, {1,2,3,5,1,2,2,1}},
+ {{1,4,1,1,3,3,3,1}, {1,1,3,2,1,2,5,2}, {1,1,4,4,2,2,2,1}},
+ {{1,4,1,1,4,1,4,1}, {1,1,3,2,1,3,5,1}, {1,1,4,5,1,1,1,3}},
+ {{2,3,2,1,1,1,1,6}, {5,2,2,3,1,1,1,2}, {2,1,4,5,1,1,2,1}},
+ {{3,3,2,1,1,1,2,4}, {1,2,2,3,1,1,6,1}, {1,1,4,5,1,2,1,2}},
+ {{4,3,2,1,1,1,3,2}, {5,1,3,2,2,1,1,2}, {1,1,4,5,1,3,1,1}},
+ {{2,3,2,1,1,2,1,5}, {5,2,2,3,1,2,1,1}, {1,2,3,6,1,1,2,1}},
+ {{3,3,2,1,1,2,2,3}, {1,1,3,2,2,1,6,1}, {1,1,4,5,2,1,2,1}},
+ {{2,3,2,1,1,3,1,4}, {5,1,3,2,2,2,1,1}, {1,5,1,1,1,1,4,3}},
+ {{3,3,2,1,1,3,2,2}, {4,3,1,4,1,1,1,2}, {2,5,1,1,1,1,5,1}},
+ {{2,3,2,1,1,4,1,3}, {4,2,2,3,2,1,1,2}, {1,5,1,1,1,2,4,2}},
+ {{3,3,2,1,1,4,2,1}, {4,3,1,4,1,2,1,1}, {1,5,1,1,1,3,4,1}},
+ {{2,3,2,1,1,5,1,2}, {4,1,3,2,3,1,1,2}, {1,5,1,1,2,1,5,1}},
+ {{1,4,1,2,1,1,1,6}, {4,2,2,3,2,2,1,1}, {1,4,2,1,1,1,3,4}},
+ {{2,4,1,2,1,1,2,4}, {4,1,3,2,3,2,1,1}, {2,4,2,1,1,1,4,2}},
+ {{3,4,1,2,1,1,3,2}, {3,3,1,4,2,1,1,2}, {1,4,2,1,1,2,3,3}},
+ {{1,3,2,1,2,1,1,6}, {3,2,2,3,3,1,1,2}, {2,4,2,1,1,2,4,1}},
+ {{1,4,1,2,1,2,1,5}, {3,3,1,4,2,2,1,1}, {1,4,2,1,1,3,3,2}},
+ {{3,3,2,1,2,1,3,2}, {3,1,3,2,4,1,1,2}, {1,4,2,1,1,4,3,1}},
+ {{3,4,1,2,1,2,3,1}, {3,2,2,3,3,2,1,1}, {1,5,1,2,1,1,4,2}},
+ {{1,3,2,1,2,2,1,5}, {3,1,3,2,4,2,1,1}, {1,4,2,1,2,1,4,2}},
+ {{2,3,2,1,2,2,2,3}, {2,3,1,4,3,1,1,2}, {1,5,1,2,1,2,4,1}},
+ {{3,3,2,1,2,2,3,1}, {2,2,2,3,4,1,1,2}, {1,4,2,1,2,2,4,1}},
+ {{1,3,2,1,2,3,1,4}, {2,3,1,4,3,2,1,1}, {1,3,3,1,1,1,2,5}},
+ {{1,4,1,2,1,4,1,3}, {2,1,3,2,5,1,1,2}, {2,3,3,1,1,1,3,3}},
+ {{2,4,1,2,1,4,2,1}, {2,2,2,3,4,2,1,1}, {3,3,3,1,1,1,4,1}},
+ {{1,3,2,1,2,4,1,3}, {2,1,3,2,5,2,1,1}, {1,3,3,1,1,2,2,4}},
+ {{2,3,2,1,2,4,2,1}, {1,3,1,4,4,1,1,2}, {2,3,3,1,1,2,3,2}},
+ {{1,4,1,2,1,6,1,1}, {1,2,2,3,5,1,1,2}, {1,3,3,1,1,3,2,3}},
+ {{1,4,1,2,2,1,2,4}, {1,3,1,4,4,2,1,1}, {2,3,3,1,1,3,3,1}},
+ {{2,4,1,2,2,1,3,2}, {1,1,3,2,6,1,1,2}, {1,3,3,1,1,4,2,2}},
+ {{1,3,2,1,3,1,2,4}, {1,2,2,3,5,2,1,1}, {1,3,3,1,1,5,2,1}},
+ {{1,4,1,2,2,2,2,3}, {1,1,3,2,6,2,1,1}, {1,4,2,2,1,1,3,3}},
+ {{2,4,1,2,2,2,3,1}, {6,1,3,3,1,1,1,1}, {2,4,2,2,1,1,4,1}},
+ {{1,3,2,1,3,2,2,3}, {1,1,3,3,1,1,5,2}, {1,3,3,1,2,1,3,3}},
+ {{2,3,2,1,3,2,3,1}, {1,1,3,3,1,2,5,1}, {1,4,2,2,1,2,3,2}},
+ {{1,3,2,1,3,3,2,2}, {5,2,2,4,1,1,1,1}, {1,3,3,1,2,2,3,2}},
+ {{1,4,1,2,2,4,2,1}, {5,1,3,3,2,1,1,1}, {1,4,2,2,1,3,3,1}},
+ {{1,4,1,2,3,1,3,2}, {4,3,1,5,1,1,1,1}, {1,3,3,1,2,3,3,1}},
+ {{1,3,2,1,4,1,3,2}, {4,2,2,4,2,1,1,1}, {1,5,1,3,1,1,4,1}},
+ {{1,4,1,2,3,2,3,1}, {4,1,3,3,3,1,1,1}, {1,4,2,2,2,1,4,1}},
+ {{1,3,2,1,4,2,3,1}, {3,3,1,5,2,1,1,1}, {1,3,3,1,3,1,4,1}},
+ {{3,2,3,1,1,1,1,5}, {3,2,2,4,3,1,1,1}, {1,2,4,1,1,1,1,6}},
+ {{4,2,3,1,1,1,2,3}, {3,1,3,3,4,1,1,1}, {2,2,4,1,1,1,2,4}},
+ {{5,2,3,1,1,1,3,1}, {2,3,1,5,3,1,1,1}, {3,2,4,1,1,1,3,2}},
+ {{3,2,3,1,1,2,1,4}, {2,2,2,4,4,1,1,1}, {1,2,4,1,1,2,1,5}},
+ {{4,2,3,1,1,2,2,2}, {2,1,3,3,5,1,1,1}, {2,2,4,1,1,2,2,3}},
+ {{3,2,3,1,1,3,1,3}, {1,3,1,5,4,1,1,1}, {3,2,4,1,1,2,3,1}},
+ {{4,2,3,1,1,3,2,1}, {1,2,2,4,5,1,1,1}, {1,2,4,1,1,3,1,4}},
+ {{3,2,3,1,1,4,1,2}, {1,1,3,3,6,1,1,1}, {2,2,4,1,1,3,2,2}},
+ {{3,2,3,1,1,5,1,1}, {1,1,3,4,1,1,5,1}, {1,2,4,1,1,4,1,3}},
+ {{2,3,2,2,1,1,1,5}, {4,4,1,1,1,1,1,4}, {2,2,4,1,1,4,2,1}},
+ {{3,3,2,2,1,1,2,3}, {5,4,1,1,1,1,2,2}, {1,2,4,1,1,5,1,2}},
+ {{2,2,3,1,2,1,1,5}, {4,4,1,1,1,2,1,3}, {1,2,4,1,1,6,1,1}},
+ {{2,3,2,2,1,2,1,4}, {5,4,1,1,1,2,2,1}, {1,3,3,2,1,1,2,4}},
+ {{3,3,2,2,1,2,2,2}, {4,4,1,1,1,3,1,2}, {2,3,3,2,1,1,3,2}},
+ {{2,2,3,1,2,2,1,4}, {4,4,1,1,1,4,1,1}, {1,2,4,1,2,1,2,4}},
+ {{3,2,3,1,2,2,2,2}, {3,4,1,1,2,1,1,4}, {1,3,3,2,1,2,2,3}},
+ {{3,3,2,2,1,3,2,1}, {4,4,1,1,2,1,2,2}, {2,3,3,2,1,2,3,1}},
+ {{2,2,3,1,2,3,1,3}, {3,4,1,1,2,2,1,3}, {1,2,4,1,2,2,2,3}},
+ {{2,3,2,2,1,4,1,2}, {4,4,1,1,2,2,2,1}, {2,2,4,1,2,2,3,1}},
+ {{2,2,3,1,2,4,1,2}, {3,4,1,1,2,3,1,2}, {1,2,4,1,2,3,2,2}},
+ {{2,3,2,2,1,5,1,1}, {3,4,1,1,2,4,1,1}, {1,3,3,2,1,4,2,1}},
+ {{2,2,3,1,2,5,1,1}, {2,4,1,1,3,1,1,4}, {1,2,4,1,2,4,2,1}},
+ {{1,4,1,3,1,1,1,5}, {3,4,1,1,3,1,2,2}, {1,4,2,3,1,1,3,2}},
+ {{2,4,1,3,1,1,2,3}, {2,4,1,1,3,2,1,3}, {1,3,3,2,2,1,3,2}},
+ {{1,3,2,2,2,1,1,5}, {3,4,1,1,3,2,2,1}, {1,4,2,3,1,2,3,1}},
+ {{1,4,1,3,1,2,1,4}, {2,4,1,1,3,3,1,2}, {1,2,4,1,3,1,3,2}},
+ {{3,3,2,2,2,1,3,1}, {2,4,1,1,3,4,1,1}, {1,3,3,2,2,2,3,1}},
+ {{1,2,3,1,3,1,1,5}, {1,4,1,1,4,1,1,4}, {1,2,4,1,3,2,3,1}},
+ {{1,3,2,2,2,2,1,4}, {2,4,1,1,4,1,2,2}, {2,1,5,1,1,1,1,5}},
+ {{2,3,2,2,2,2,2,2}, {1,4,1,1,4,2,1,3}, {3,1,5,1,1,1,2,3}},
+ {{2,4,1,3,1,3,2,1}, {2,4,1,1,4,2,2,1}, {4,1,5,1,1,1,3,1}},
+ {{1,2,3,1,3,2,1,4}, {1,4,1,1,4,3,1,2}, {2,1,5,1,1,2,1,4}},
+ {{2,2,3,1,3,2,2,2}, {1,4,1,1,4,4,1,1}, {3,1,5,1,1,2,2,2}},
+ {{1,4,1,3,1,4,1,2}, {1,4,1,1,5,1,2,2}, {2,1,5,1,1,3,1,3}},
+ {{1,2,3,1,3,3,1,3}, {1,4,1,1,5,2,2,1}, {3,1,5,1,1,3,2,1}},
+ {{1,3,2,2,2,4,1,2}, {5,3,2,1,1,1,1,3}, {2,1,5,1,1,4,1,2}},
+ {{1,4,1,3,1,5,1,1}, {6,3,2,1,1,1,2,1}, {2,1,5,1,1,5,1,1}},
+ {{1,3,2,2,2,5,1,1}, {1,3,2,1,1,1,6,2}, {1,2,4,2,1,1,1,5}},
+ {{1,4,1,3,2,1,2,3}, {5,3,2,1,1,2,1,2}, {2,2,4,2,1,1,2,3}},
+ {{2,4,1,3,2,1,3,1}, {1,3,2,1,1,2,6,1}, {3,2,4,2,1,1,3,1}},
+ {{1,3,2,2,3,1,2,3}, {5,3,2,1,1,3,1,1}, {1,1,5,1,2,1,1,5}},
+ {{1,4,1,3,2,2,2,2}, {4,4,1,2,1,1,1,3}, {1,2,4,2,1,2,1,4}},
+ {{1,2,3,1,4,1,2,3}, {5,4,1,2,1,1,2,1}, {2,2,4,2,1,2,2,2}},
+ {{1,3,2,2,3,2,2,2}, {4,3,2,1,2,1,1,3}, {1,1,5,1,2,2,1,4}},
+ {{1,4,1,3,2,3,2,1}, {4,4,1,2,1,2,1,2}, {2,1,5,1,2,2,2,2}},
+ {{1,2,3,1,4,2,2,2}, {4,3,2,1,2,2,1,2}, {2,2,4,2,1,3,2,1}},
+ {{1,3,2,2,3,3,2,1}, {4,4,1,2,1,3,1,1}, {1,1,5,1,2,3,1,3}},
+ {{1,4,1,3,3,1,3,1}, {4,3,2,1,2,3,1,1}, {1,2,4,2,1,4,1,2}},
+ {{1,3,2,2,4,1,3,1}, {3,4,1,2,2,1,1,3}, {1,1,5,1,2,4,1,2}},
+ {{1,2,3,1,5,1,3,1}, {4,4,1,2,2,1,2,1}, {1,2,4,2,1,5,1,1}},
+ {{4,1,4,1,1,1,1,4}, {3,3,2,1,3,1,1,3}, {1,1,5,1,2,5,1,1}},
+ {{5,1,4,1,1,1,2,2}, {3,4,1,2,2,2,1,2}, {1,3,3,3,1,1,2,3}},
+ {{4,1,4,1,1,2,1,3}, {3,3,2,1,3,2,1,2}, {2,3,3,3,1,1,3,1}},
+ {{5,1,4,1,1,2,2,1}, {3,4,1,2,2,3,1,1}, {1,2,4,2,2,1,2,3}},
+ {{4,1,4,1,1,3,1,2}, {3,3,2,1,3,3,1,1}, {1,3,3,3,1,2,2,2}},
+ {{4,1,4,1,1,4,1,1}, {2,4,1,2,3,1,1,3}, {1,1,5,1,3,1,2,3}},
+ {{3,2,3,2,1,1,1,4}, {3,4,1,2,3,1,2,1}, {1,2,4,2,2,2,2,2}},
+ {{4,2,3,2,1,1,2,2}, {2,3,2,1,4,1,1,3}, {1,3,3,3,1,3,2,1}},
+ {{3,1,4,1,2,1,1,4}, {2,4,1,2,3,2,1,2}, {1,1,5,1,3,2,2,2}},
+ {{4,1,4,1,2,1,2,2}, {2,3,2,1,4,2,1,2}, {1,2,4,2,2,3,2,1}},
+ {{4,2,3,2,1,2,2,1}, {2,4,1,2,3,3,1,1}, {1,1,5,1,3,3,2,1}},
+ {{3,1,4,1,2,2,1,3}, {2,3,2,1,4,3,1,1}, {1,4,2,4,1,1,3,1}},
+ {{4,1,4,1,2,2,2,1}, {1,4,1,2,4,1,1,3}, {1,3,3,3,2,1,3,1}},
+ {{3,1,4,1,2,3,1,2}, {2,4,1,2,4,1,2,1}, {1,2,4,2,3,1,3,1}},
+ {{3,2,3,2,1,4,1,1}, {1,3,2,1,5,1,1,3}, {1,1,5,1,4,1,3,1}},
+ {{3,1,4,1,2,4,1,1}, {1,4,1,2,4,2,1,2}, {2,1,5,2,1,1,1,4}},
+ {{2,3,2,3,1,1,1,4}, {1,3,2,1,5,2,1,2}, {3,1,5,2,1,1,2,2}},
+ {{3,3,2,3,1,1,2,2}, {1,4,1,2,4,3,1,1}, {2,1,5,2,1,2,1,3}},
+ {{2,2,3,2,2,1,1,4}, {1,3,2,1,5,3,1,1}, {3,1,5,2,1,2,2,1}},
+ {{2,3,2,3,1,2,1,3}, {1,4,1,2,5,1,2,1}, {2,1,5,2,1,3,1,2}},
+ {{3,3,2,3,1,2,2,1}, {1,3,2,1,6,1,2,1}, {2,1,5,2,1,4,1,1}},
+ {{2,1,4,1,3,1,1,4}, {6,2,3,1,1,1,1,2}, {1,2,4,3,1,1,1,4}},
+ {{2,2,3,2,2,2,1,3}, {1,2,3,1,1,1,5,3}, {2,2,4,3,1,1,2,2}},
+ {{3,2,3,2,2,2,2,1}, {2,2,3,1,1,1,6,1}, {1,1,5,2,2,1,1,4}},
+ {{2,1,4,1,3,2,1,3}, {6,2,3,1,1,2,1,1}, {1,2,4,3,1,2,1,3}},
+ {{3,1,4,1,3,2,2,1}, {1,2,3,1,1,2,5,2}, {2,2,4,3,1,2,2,1}},
+ {{2,3,2,3,1,4,1,1}, {1,2,3,1,1,3,5,1}, {1,1,5,2,2,2,1,3}},
+ {{2,1,4,1,3,3,1,2}, {5,3,2,2,1,1,1,2}, {2,1,5,2,2,2,2,1}},
+ {{2,2,3,2,2,4,1,1}, {1,3,2,2,1,1,6,1}, {1,1,5,2,2,3,1,2}},
+ {{2,1,4,1,3,4,1,1}, {5,2,3,1,2,1,1,2}, {1,2,4,3,1,4,1,1}},
+ {{1,4,1,4,1,1,1,4}, {5,3,2,2,1,2,1,1}, {1,1,5,2,2,4,1,1}},
+ {{2,4,1,4,1,1,2,2}, {1,2,3,1,2,1,6,1}, {1,3,3,4,1,1,2,2}},
+ {{1,3,2,3,2,1,1,4}, {5,2,3,1,2,2,1,1}, {1,2,4,3,2,1,2,2}},
+ {{1,4,1,4,1,2,1,3}, {4,4,1,3,1,1,1,2}, {1,3,3,4,1,2,2,1}},
+ {{2,4,1,4,1,2,2,1}, {4,3,2,2,2,1,1,2}, {1,1,5,2,3,1,2,2}},
+ {{1,2,3,2,3,1,1,4}, {4,4,1,3,1,2,1,1}, {1,2,4,3,2,2,2,1}},
+ {{1,3,2,3,2,2,1,3}, {4,2,3,1,3,1,1,2}, {1,1,5,2,3,2,2,1}},
+ {{2,3,2,3,2,2,2,1}, {4,3,2,2,2,2,1,1}, {2,1,5,3,1,1,1,3}},
+ {{1,1,4,1,4,1,1,4}, {4,2,3,1,3,2,1,1}, {3,1,5,3,1,1,2,1}},
+ {{1,2,3,2,3,2,1,3}, {3,4,1,3,2,1,1,2}, {2,1,5,3,1,2,1,2}},
+ {{2,2,3,2,3,2,2,1}, {3,3,2,2,3,1,1,2}, {2,1,5,3,1,3,1,1}},
+ {{1,4,1,4,1,4,1,1}, {3,4,1,3,2,2,1,1}, {1,2,4,4,1,1,1,3}},
+ {{1,1,4,1,4,2,1,3}, {3,2,3,1,4,1,1,2}, {2,2,4,4,1,1,2,1}},
+ {{2,1,4,1,4,2,2,1}, {3,3,2,2,3,2,1,1}, {1,1,5,3,2,1,1,3}},
+ {{1,3,2,3,2,4,1,1}, {3,2,3,1,4,2,1,1}, {1,2,4,4,1,2,1,2}},
+ {{1,1,4,1,4,3,1,2}, {2,4,1,3,3,1,1,2}, {1,1,5,3,2,2,1,2}},
+ {{1,4,1,4,2,1,2,2}, {2,3,2,2,4,1,1,2}, {1,2,4,4,1,3,1,1}},
+ {{1,3,2,3,3,1,2,2}, {2,4,1,3,3,2,1,1}, {1,1,5,3,2,3,1,1}},
+ {{1,4,1,4,2,2,2,1}, {2,2,3,1,5,1,1,2}, {1,3,3,5,1,1,2,1}},
+ {{1,2,3,2,4,1,2,2}, {2,3,2,2,4,2,1,1}, {1,2,4,4,2,1,2,1}},
+ {{1,3,2,3,3,2,2,1}, {2,2,3,1,5,2,1,1}, {1,1,5,3,3,1,2,1}},
+ {{1,1,4,1,5,1,2,2}, {1,4,1,3,4,1,1,2}, {2,1,5,4,1,1,1,2}},
+ {{1,2,3,2,4,2,2,1}, {1,3,2,2,5,1,1,2}, {2,1,5,4,1,2,1,1}},
+ {{1,1,4,1,5,2,2,1}, {1,4,1,3,4,2,1,1}, {1,2,4,5,1,1,1,2}},
+ {{4,1,4,2,1,1,1,3}, {1,2,3,1,6,1,1,2}, {1,1,5,4,2,1,1,2}},
+ {{5,1,4,2,1,1,2,1}, {1,3,2,2,5,2,1,1}, {1,2,4,5,1,2,1,1}},
+ {{4,1,4,2,1,2,1,2}, {1,2,3,1,6,2,1,1}, {1,1,5,4,2,2,1,1}},
+ {{4,1,4,2,1,3,1,1}, {1,1,4,1,1,1,4,4}, {1,6,1,1,1,1,4,2}},
+ {{3,2,3,3,1,1,1,3}, {2,1,4,1,1,1,5,2}, {1,6,1,1,1,2,4,1}},
+ {{4,2,3,3,1,1,2,1}, {1,1,4,1,1,2,4,3}, {1,5,2,1,1,1,3,3}},
+ {{3,1,4,2,2,1,1,3}, {2,1,4,1,1,2,5,1}, {2,5,2,1,1,1,4,1}},
+ {{4,1,4,2,2,1,2,1}, {1,1,4,1,1,3,4,2}, {1,5,2,1,1,2,3,2}},
+ {{3,1,4,2,2,2,1,2}, {1,1,4,1,1,4,4,1}, {1,5,2,1,1,3,3,1}},
+ {{3,2,3,3,1,3,1,1}, {6,2,3,2,1,1,1,1}, {1,6,1,2,1,1,4,1}},
+ {{3,1,4,2,2,3,1,1}, {1,2,3,2,1,1,5,2}, {1,5,2,1,2,1,4,1}},
+ {{2,3,2,4,1,1,1,3}, {6,1,4,1,2,1,1,1}, {1,4,3,1,1,1,2,4}},
+ {{3,3,2,4,1,1,2,1}, {1,1,4,1,2,1,5,2}, {2,4,3,1,1,1,3,2}},
+ {{2,2,3,3,2,1,1,3}, {1,2,3,2,1,2,5,1}, {1,4,3,1,1,2,2,3}},
+ {{2,3,2,4,1,2,1,2}, {1,1,4,1,2,2,5,1}, {2,4,3,1,1,2,3,1}},
+ {{2,1,4,2,3,1,1,3}, {5,3,2,3,1,1,1,1}, {1,4,3,1,1,3,2,2}},
+ {{2,2,3,3,2,2,1,2}, {5,2,3,2,2,1,1,1}, {1,4,3,1,1,4,2,1}},
+ {{2,3,2,4,1,3,1,1}, {5,1,4,1,3,1,1,1}, {1,5,2,2,1,1,3,2}},
+ {{2,1,4,2,3,2,1,2}, {4,4,1,4,1,1,1,1}, {1,4,3,1,2,1,3,2}},
+ {{2,2,3,3,2,3,1,1}, {4,3,2,3,2,1,1,1}, {1,5,2,2,1,2,3,1}},
+ {{2,1,4,2,3,3,1,1}, {4,2,3,2,3,1,1,1}, {1,4,3,1,2,2,3,1}},
+ {{1,4,1,5,1,1,1,3}, {4,1,4,1,4,1,1,1}, {1,3,4,1,1,1,1,5}},
+ {{2,4,1,5,1,1,2,1}, {3,4,1,4,2,1,1,1}, {2,3,4,1,1,1,2,3}},
+ {{1,3,2,4,2,1,1,3}, {3,3,2,3,3,1,1,1}, {3,3,4,1,1,1,3,1}},
+ {{2,3,2,4,2,1,2,1}, {3,2,3,2,4,1,1,1}, {1,3,4,1,1,2,1,4}},
+ {{1,2,3,3,3,1,1,3}, {3,1,4,1,5,1,1,1}, {2,3,4,1,1,2,2,2}},
+ {{1,3,2,4,2,2,1,2}, {2,4,1,4,3,1,1,1}, {1,3,4,1,1,3,1,3}},
+ {{1,4,1,5,1,3,1,1}, {2,3,2,3,4,1,1,1}, {2,3,4,1,1,3,2,1}},
+ {{1,1,4,2,4,1,1,3}, {2,2,3,2,5,1,1,1}, {1,3,4,1,1,4,1,2}},
+ {{1,2,3,3,3,2,1,2}, {2,1,4,1,6,1,1,1}, {1,3,4,1,1,5,1,1}},
+ {{1,3,2,4,2,3,1,1}, {1,4,1,4,4,1,1,1}, {1,4,3,2,1,1,2,3}},
+ {{1,1,4,2,4,2,1,2}, {1,3,2,3,5,1,1,1}, {2,4,3,2,1,1,3,1}},
+ {{1,2,3,3,3,3,1,1}, {1,2,3,2,6,1,1,1}, {1,3,4,1,2,1,2,3}},
+ {{1,1,4,2,4,3,1,1}, {1,1,4,2,1,1,4,3}, {2,3,4,1,2,1,3,1}},
+ {{1,3,2,4,3,1,2,1}, {2,1,4,2,1,1,5,1}, {1,3,4,1,2,2,2,2}},
+ {{1,1,4,2,5,1,2,1}, {1,1,4,2,1,2,4,2}, {1,4,3,2,1,3,2,1}},
+ {{4,1,4,3,1,2,1,1}, {1,1,4,2,1,3,4,1}, {1,3,4,1,2,3,2,1}},
+ {{3,1,4,3,2,1,1,2}, {1,2,3,3,1,1,5,1}, {1,5,2,3,1,1,3,1}},
+ {{3,1,4,3,2,2,1,1}, {1,1,4,2,2,1,5,1}, {1,4,3,2,2,1,3,1}},
+ {{2,2,3,4,2,1,1,2}, {1,1,4,3,1,1,4,2}, {1,3,4,1,3,1,3,1}},
+ {{2,1,4,3,3,1,1,2}, {1,1,4,3,1,2,4,1}, {2,2,5,1,1,1,1,4}},
+ {{2,1,4,3,3,2,1,1}, {1,1,4,4,1,1,4,1}, {3,2,5,1,1,1,2,2}},
+ {{1,3,2,5,2,1,1,2}, {4,5,1,1,1,1,1,3}, {2,2,5,1,1,2,1,3}},
+ {{1,2,3,4,3,1,1,2}, {4,5,1,1,1,2,1,2}, {3,2,5,1,1,2,2,1}},
+ {{1,1,4,3,4,1,1,2}, {4,5,1,1,1,3,1,1}, {2,2,5,1,1,3,1,2}},
+ {{1,1,4,3,4,2,1,1}, {3,5,1,1,2,1,1,3}, {2,2,5,1,1,4,1,1}},
+ {{1,5,1,1,1,1,1,6}, {4,5,1,1,2,1,2,1}, {1,3,4,2,1,1,1,4}},
+ {{1,5,1,1,1,2,1,5}, {3,5,1,1,2,2,1,2}, {2,3,4,2,1,1,2,2}},
+ {{2,5,1,1,1,2,2,3}, {3,5,1,1,2,3,1,1}, {1,2,5,1,2,1,1,4}},
+ {{1,5,1,1,1,3,1,4}, {2,5,1,1,3,1,1,3}, {2,2,5,1,2,1,2,2}},
+ {{1,5,1,1,1,4,1,3}, {3,5,1,1,3,1,2,1}, {2,3,4,2,1,2,2,1}},
+ {{1,5,1,1,1,5,1,2}, {2,5,1,1,3,2,1,2}, {1,2,5,1,2,2,1,3}},
+ {{1,5,1,1,2,1,2,4}, {2,5,1,1,3,3,1,1}, {1,3,4,2,1,3,1,2}},
+ {{1,5,1,1,2,2,2,3}, {1,5,1,1,4,1,1,3}, {1,2,5,1,2,3,1,2}},
+ {{1,5,1,1,2,3,2,2}, {2,5,1,1,4,1,2,1}, {1,3,4,2,1,4,1,1}},
+ {{1,5,1,1,2,4,2,1}, {1,5,1,1,4,2,1,2}, {1,2,5,1,2,4,1,1}},
+ {{1,5,1,1,3,1,3,2}, {1,5,1,1,4,3,1,1}, {1,4,3,3,1,1,2,2}},
+ {{1,5,1,1,3,2,3,1}, {1,5,1,1,5,1,2,1}, {1,3,4,2,2,1,2,2}},
+ {{2,4,2,1,1,1,1,5}, {5,4,2,1,1,1,1,2}, {1,4,3,3,1,2,2,1}},
+ {{2,4,2,1,1,2,1,4}, {1,4,2,1,1,1,6,1}, {1,2,5,1,3,1,2,2}},
+ {{3,4,2,1,1,2,2,2}, {5,4,2,1,1,2,1,1}, {1,3,4,2,2,2,2,1}},
+ {{2,4,2,1,1,3,1,3}, {4,5,1,2,1,1,1,2}, {1,2,5,1,3,2,2,1}},
+ {{3,4,2,1,1,3,2,1}, {4,4,2,1,2,1,1,2}, {3,1,6,1,1,1,1,3}},
+ {{2,4,2,1,1,4,1,2}, {4,5,1,2,1,2,1,1}, {4,1,6,1,1,1,2,1}},
+ {{2,4,2,1,1,5,1,1}, {4,4,2,1,2,2,1,1}, {3,1,6,1,1,2,1,2}},
+ {{1,5,1,2,1,1,1,5}, {3,5,1,2,2,1,1,2}, {3,1,6,1,1,3,1,1}},
+ {{2,5,1,2,1,1,2,3}, {3,4,2,1,3,1,1,2}, {2,2,5,2,1,1,1,3}},
+ {{1,4,2,1,2,1,1,5}, {3,5,1,2,2,2,1,1}, {3,2,5,2,1,1,2,1}},
+ {{2,4,2,1,2,1,2,3}, {3,4,2,1,3,2,1,1}, {2,1,6,1,2,1,1,3}},
+ {{2,5,1,2,1,2,2,2}, {2,5,1,2,3,1,1,2}, {2,2,5,2,1,2,1,2}},
+ {{1,4,2,1,2,2,1,4}, {2,4,2,1,4,1,1,2}, {2,1,6,1,2,2,1,2}},
+ {{2,4,2,1,2,2,2,2}, {2,5,1,2,3,2,1,1}, {2,2,5,2,1,3,1,1}},
+ {{1,4,2,1,2,3,1,3}, {2,4,2,1,4,2,1,1}, {2,1,6,1,2,3,1,1}},
+ {{2,4,2,1,2,3,2,1}, {1,5,1,2,4,1,1,2}, {1,3,4,3,1,1,1,3}},
+ {{1,4,2,1,2,4,1,2}, {1,4,2,1,5,1,1,2}, {2,3,4,3,1,1,2,1}},
+ {{1,5,1,2,1,5,1,1}, {1,5,1,2,4,2,1,1}, {1,2,5,2,2,1,1,3}},
+ {{1,4,2,1,2,5,1,1}, {1,4,2,1,5,2,1,1}, {1,3,4,3,1,2,1,2}},
+ {{1,5,1,2,2,1,2,3}, {6,3,3,1,1,1,1,1}, {1,1,6,1,3,1,1,3}},
+ {{2,5,1,2,2,1,3,1}, {1,3,3,1,1,1,5,2}, {1,2,5,2,2,2,1,2}},
+ {{1,4,2,1,3,1,2,3}, {1,3,3,1,1,2,5,1}, {1,3,4,3,1,3,1,1}},
+ {{2,4,2,1,3,1,3,1}, {5,4,2,2,1,1,1,1}, {1,1,6,1,3,2,1,2}},
+ {{1,4,2,1,3,2,2,2}, {5,3,3,1,2,1,1,1}, {1,2,5,2,2,3,1,1}},
+ {{1,5,1,2,2,3,2,1}, {4,5,1,3,1,1,1,1}, {1,1,6,1,3,3,1,1}},
+ {{1,4,2,1,3,3,2,1}, {4,4,2,2,2,1,1,1}, {1,4,3,4,1,1,2,1}},
+ {{1,5,1,2,3,1,3,1}, {4,3,3,1,3,1,1,1}, {1,3,4,3,2,1,2,1}},
+ {{1,4,2,1,4,1,3,1}, {3,5,1,3,2,1,1,1}, {1,2,5,2,3,1,2,1}},
+ {{3,3,3,1,1,1,1,4}, {3,4,2,2,3,1,1,1}, {1,1,6,1,4,1,2,1}},
+ {{3,3,3,1,1,2,1,3}, {3,3,3,1,4,1,1,1}, {3,1,6,2,1,1,1,2}},
+ {{3,3,3,1,1,3,1,2}, {2,5,1,3,3,1,1,1}, {3,1,6,2,1,2,1,1}},
+ {{3,3,3,1,1,4,1,1}, {2,4,2,2,4,1,1,1}, {2,2,5,3,1,1,1,2}},
+ {{2,4,2,2,1,1,1,4}, {2,3,3,1,5,1,1,1}, {2,1,6,2,2,1,1,2}},
+ {{2,3,3,1,2,1,1,4}, {1,5,1,3,4,1,1,1}, {2,2,5,3,1,2,1,1}},
+ {{3,3,3,1,2,1,2,2}, {1,4,2,2,5,1,1,1}, {2,1,6,2,2,2,1,1}},
+ {{3,4,2,2,1,2,2,1}, {1,3,3,1,6,1,1,1}, {1,3,4,4,1,1,1,2}},
+ {{2,3,3,1,2,2,1,3}, {1,2,4,1,1,1,4,3}, {1,2,5,3,2,1,1,2}},
+ {{3,3,3,1,2,2,2,1}, {2,2,4,1,1,1,5,1}, {1,3,4,4,1,2,1,1}},
+ {{2,3,3,1,2,3,1,2}, {1,2,4,1,1,2,4,2}, {1,1,6,2,3,1,1,2}},
+ {{2,4,2,2,1,4,1,1}, {1,2,4,1,1,3,4,1}, {1,2,5,3,2,2,1,1}},
+ {{2,3,3,1,2,4,1,1}, {1,3,3,2,1,1,5,1}, {1,1,6,2,3,2,1,1}},
+ {{1,5,1,3,1,1,1,4}, {1,2,4,1,2,1,5,1}, {3,1,6,3,1,1,1,1}},
+ {{1,4,2,2,2,1,1,4}, {1,1,5,1,1,1,3,4}, {2,2,5,4,1,1,1,1}},
+ {{1,5,1,3,1,2,1,3}, {2,1,5,1,1,1,4,2}, {2,1,6,3,2,1,1,1}},
+ {{2,5,1,3,1,2,2,1}, {1,1,5,1,1,2,3,3}, {1,3,4,5,1,1,1,1}},
+ {{1,3,3,1,3,1,1,4}, {2,1,5,1,1,2,4,1}, {1,2,5,4,2,1,1,1}},
+ {{1,4,2,2,2,2,1,3}, {1,1,5,1,1,3,3,2}, {1,1,6,3,3,1,1,1}},
+ {{1,5,1,3,1,3,1,2}, {1,1,5,1,1,4,3,1}, {1,6,2,1,1,1,3,2}},
+ {{1,3,3,1,3,2,1,3}, {1,2,4,2,1,1,4,2}, {1,6,2,1,1,2,3,1}},
+ {{1,4,2,2,2,3,1,2}, {1,1,5,1,2,1,4,2}, {1,5,3,1,1,1,2,3}},
+ {{1,5,1,3,1,4,1,1}, {1,2,4,2,1,2,4,1}, {2,5,3,1,1,1,3,1}},
+ {{1,3,3,1,3,3,1,2}, {1,1,5,1,2,2,4,1}, {1,5,3,1,1,2,2,2}},
+ {{1,4,2,2,2,4,1,1}, {1,1,5,2,1,1,3,3}, {1,5,3,1,1,3,2,1}},
+ {{1,5,1,3,2,1,2,2}, {2,1,5,2,1,1,4,1}, {1,6,2,2,1,1,3,1}},
+ {{1,4,2,2,3,1,2,2}, {1,1,5,2,1,2,3,2}, {1,5,3,1,2,1,3,1}},
+ {{1,5,1,3,2,2,2,1}, {1,1,5,2,1,3,3,1}, {1,4,4,1,1,1,1,4}},
+ {{1,3,3,1,4,1,2,2}, {1,2,4,3,1,1,4,1}, {2,4,4,1,1,1,2,2}},
+ {{1,4,2,2,3,2,2,1}, {1,1,5,2,2,1,4,1}, {1,4,4,1,1,2,1,3}},
+ {{1,3,3,1,4,2,2,1}, {1,1,5,3,1,1,3,2}, {2,4,4,1,1,2,2,1}},
+ {{4,2,4,1,1,1,1,3}, {1,1,5,3,1,2,3,1}, {1,4,4,1,1,3,1,2}},
+ {{4,2,4,1,1,2,1,2}, {1,1,5,4,1,1,3,1}, {1,4,4,1,1,4,1,1}},
+ {{4,2,4,1,1,3,1,1}, {3,6,1,1,2,1,1,2}, {1,5,3,2,1,1,2,2}},
+ {{3,3,3,2,1,1,1,3}, {3,6,1,1,2,2,1,1}, {1,4,4,1,2,1,2,2}},
+ {{3,2,4,1,2,1,1,3}, {2,6,1,1,3,1,1,2}, {1,5,3,2,1,2,2,1}},
+ {{4,2,4,1,2,1,2,1}, {2,6,1,1,3,2,1,1}, {1,4,4,1,2,2,2,1}},
+ {{3,2,4,1,2,2,1,2}, {1,6,1,1,4,1,1,2}, {2,3,5,1,1,1,1,3}},
+ {{3,3,3,2,1,3,1,1}, {1,6,1,1,4,2,1,1}, {3,3,5,1,1,1,2,1}},
+ {{3,2,4,1,2,3,1,1}, {4,5,2,1,2,1,1,1}, {2,3,5,1,1,2,1,2}},
+ {{2,4,2,3,1,1,1,3}, {3,6,1,2,2,1,1,1}, {2,3,5,1,1,3,1,1}},
+ {{3,4,2,3,1,1,2,1}, {3,5,2,1,3,1,1,1}, {1,4,4,2,1,1,1,3}},
+ {{2,3,3,2,2,1,1,3}, {2,6,1,2,3,1,1,1}, {2,4,4,2,1,1,2,1}},
+ {{3,3,3,2,2,1,2,1}, {2,5,2,1,4,1,1,1}, {1,3,5,1,2,1,1,3}},
+ {{2,2,4,1,3,1,1,3}, {1,6,1,2,4,1,1,1}, {2,3,5,1,2,1,2,1}},
+ {{2,3,3,2,2,2,1,2}, {1,5,2,1,5,1,1,1}, {1,3,5,1,2,2,1,2}},
+ {{2,4,2,3,1,3,1,1}, {1,4,3,1,1,1,5,1}, {1,4,4,2,1,3,1,1}},
+ {{2,2,4,1,3,2,1,2}, {1,3,4,1,1,1,4,2}, {1,3,5,1,2,3,1,1}},
+ {{2,3,3,2,2,3,1,1}, {1,3,4,1,1,2,4,1}, {1,5,3,3,1,1,2,1}},
+ {{2,2,4,1,3,3,1,1}, {1,2,5,1,1,1,3,3}, {1,4,4,2,2,1,2,1}},
+ {{1,5,1,4,1,1,1,3}, {2,2,5,1,1,1,4,1}, {1,3,5,1,3,1,2,1}},
+ {{2,5,1,4,1,1,2,1}, {1,2,5,1,1,2,3,2}, {3,2,6,1,1,1,1,2}},
+ {{1,4,2,3,2,1,1,3}, {1,2,5,1,1,3,3,1}, {3,2,6,1,1,2,1,1}},
+ {{2,4,2,3,2,1,2,1}, {1,3,4,2,1,1,4,1}, {2,3,5,2,1,1,1,2}},
+ {{1,3,3,2,3,1,1,3}, {1,2,5,1,2,1,4,1}, {2,2,6,1,2,1,1,2}},
+ {{1,4,2,3,2,2,1,2}, {1,1,6,1,1,1,2,4}, {2,3,5,2,1,2,1,1}},
+ {{1,5,1,4,1,3,1,1}, {2,1,6,1,1,1,3,2}, {2,2,6,1,2,2,1,1}},
+ {{1,2,4,1,4,1,1,3}, {1,1,6,1,1,2,2,3}, {1,4,4,3,1,1,1,2}},
+ {{1,3,3,2,3,2,1,2}, {2,1,6,1,1,2,3,1}, {1,3,5,2,2,1,1,2}},
+ {{1,4,2,3,2,3,1,1}, {1,1,6,1,1,3,2,2}, {1,4,4,3,1,2,1,1}},
+ {{1,2,4,1,4,2,1,2}, {1,1,6,1,1,4,2,1}, {1,2,6,1,3,1,1,2}},
+ {{1,3,3,2,3,3,1,1}, {1,2,5,2,1,1,3,2}, {1,3,5,2,2,2,1,1}},
+ {{1,5,1,4,2,1,2,1}, {1,1,6,1,2,1,3,2}, {1,2,6,1,3,2,1,1}},
+ {{1,4,2,3,3,1,2,1}, {1,2,5,2,1,2,3,1}, {3,2,6,2,1,1,1,1}},
+ {{1,3,3,2,4,1,2,1}, {1,1,6,1,2,2,3,1}, {2,3,5,3,1,1,1,1}},
+ {{1,2,4,1,5,1,2,1}, {1,1,6,2,1,1,2,3}, {2,2,6,2,2,1,1,1}},
+ {{5,1,5,1,1,1,1,2}, {2,1,6,2,1,1,3,1}, {1,4,4,4,1,1,1,1}},
+ {{5,1,5,1,1,2,1,1}, {1,1,6,2,1,2,2,2}, {1,3,5,3,2,1,1,1}},
+ {{4,2,4,2,1,1,1,2}, {1,1,6,2,1,3,2,1}, {1,2,6,2,3,1,1,1}},
+ {{4,1,5,1,2,1,1,2}, {1,2,5,3,1,1,3,1}, {1,6,3,1,1,1,2,2}},
+ {{4,2,4,2,1,2,1,1}, {1,1,6,2,2,1,3,1}, {1,6,3,1,1,2,2,1}},
+ {{4,1,5,1,2,2,1,1}, {1,1,6,3,1,1,2,2}, {1,5,4,1,1,1,1,3}},
+ {{3,3,3,3,1,1,1,2}, {1,1,6,3,1,2,2,1}, {2,5,4,1,1,1,2,1}},
+ {{3,2,4,2,2,1,1,2}, {1,4,4,1,1,1,4,1}, {1,5,4,1,1,2,1,2}},
+ {{3,3,3,3,1,2,1,1}, {1,3,5,1,1,1,3,2}, {1,5,4,1,1,3,1,1}},
+ {{3,1,5,1,3,1,1,2}, {1,3,5,1,1,2,3,1}, {1,6,3,2,1,1,2,1}},
+ {{3,2,4,2,2,2,1,1}, {1,2,6,1,1,1,2,3}, {1,5,4,1,2,1,2,1}},
+ {{3,1,5,1,3,2,1,1}, {2,2,6,1,1,1,3,1}, {2,4,5,1,1,1,1,2}},
+ {{2,4,2,4,1,1,1,2}, {1,2,6,1,1,2,2,2}, {2,4,5,1,1,2,1,1}},
+ {{2,3,3,3,2,1,1,2}, {1,2,6,1,1,3,2,1}, {1,5,4,2,1,1,1,2}},
+ {{2,4,2,4,1,2,1,1}, {1,3,5,2,1,1,3,1}, {1,4,5,1,2,1,1,2}},
+ {{2,2,4,2,3,1,1,2}, {1,2,6,1,2,1,3,1}, {1,5,4,2,1,2,1,1}},
+ {{2,3,3,3,2,2,1,1}, {1,2,6,2,1,1,2,2}, {1,4,5,1,2,2,1,1}},
+ {{2,1,5,1,4,1,1,2}, {1,2,6,2,1,2,2,1}, {3,3,6,1,1,1,1,1}}
+};
+
+//------------------------------------------------------------------------
+
+static int makeDataCodewords(GString *value, int *codewords);
+static int countNumeric(GString *value, int start);
+static int countText(GString *value, int start);
+static int countBinary(GString *value, int start);
+static GBool makeNumericCodewords(GString *value, int start, int n,
+ int *codewords, int &codewordIdx);
+static int divMod900(int *d, int n);
+static GBool makeByteCodewords(GString *value, int start, int n,
+ int *codewords, int &codewordIdx);
+static GBool makeTextCodewords(GString *value, int start, int n,
+ int *codewords, int &codewordIdx);
+static GBool appendTextHalfSymbol(int *codewords, int &codewordIdx,
+ int halfSymbol, int &prevHalfSymbol);
+static GBool appendDataCodeword(int *codewords, int &codewordIdx,
+ int codeword);
+static void makeErrorCorrectionCodewords(int errorCorrectionLevel,
+ int *codewords, int length);
+static void drawBarcode(int *codewords, int totalLength,
+ int nRows, int nCols,
+ int errorCorrectionLevel,
+ double moduleWidth, double moduleHeight,
+ double fieldWidth, double fieldHeight,
+ GString *appearBuf);
+static void drawPattern(int &x, int y, char *pattern, int length,
+ GString *appearBuf);
+
+//------------------------------------------------------------------------
+
+GBool drawPDF417Barcode(double fieldWidth, double fieldHeight,
+ double moduleWidth, double moduleHeight,
+ int errorCorrectionLevel, GString *value,
+ GString *appearBuf) {
+ int codewords[maxDataCodewords + maxErrorCorrectionCodewords];
+
+ if (moduleWidth <= 0 || moduleHeight <= 0) {
+ error(errSyntaxError, -1, "Invalid module size in PDF417 barcode");
+ return gFalse;
+ }
+
+ // each row contains:
+ // - left quiet zone (2 modules, minimum)
+ // - start code (17 modules)
+ // - left row indicator (17 modules)
+ // - nCols codewords (17 modules each)
+ // - right row indicator (17 modules)
+ // - stop code (18 modules)
+ // - right quiet zone (2 modules, minimum)
+ // total = 73 + nCols * 17
+ int nCols = (int)floor((fieldWidth / moduleWidth - 73) / 17);
+ if (nCols < 1) {
+ error(errSyntaxError, -1, "Invalid field size for PDF417 barcode");
+ return gFalse;
+ }
+ if (nCols > 30) {
+ nCols = 30;
+ }
+
+ int nDataCodewords = makeDataCodewords(value, codewords);
+ if (nDataCodewords == maxDataCodewords) {
+ error(errSyntaxError, -1, "Too many codewords in PDF417 barcode");
+ return gFalse;
+ }
+
+ int nErrorCorrectionCodewords = 1 << (errorCorrectionLevel + 1);
+
+ // total codewords = 1 + nDataCodewords + nPadCodewords
+ // + nErrorCorrectionCodewords
+ int nRows = (1 + nDataCodewords + nErrorCorrectionCodewords + (nCols - 1))
+ / nCols;
+ if (nRows < 3) {
+ nRows = 3;
+ }
+ if (nRows > 90) {
+ error(errSyntaxError, -1, "Too many rows in PDF417 barcode");
+ return gFalse;
+ }
+ int nPadCodewords = nRows * nCols
+ - (1 + nDataCodewords + nErrorCorrectionCodewords);
+ int length = 1 + nDataCodewords + nPadCodewords;
+ if (length > maxDataCodewords) {
+ error(errSyntaxError, -1, "Too many codewords in PDF417 barcode");
+ return gFalse;
+ }
+ codewords[0] = length;
+ for (int i = 1 + nDataCodewords; i < length; ++i) {
+ codewords[i] = 900;
+ }
+
+ makeErrorCorrectionCodewords(errorCorrectionLevel, codewords, length);
+
+ // we need at least 3 modules of vertical quiet space above and below
+ // (the spec says 2, but Adobe seems to use 3)
+ if (nRows * moduleHeight + 6 * moduleWidth > fieldHeight) {
+ error(errSyntaxError, -1, "Too many rows in PDF417 barcode");
+ return gFalse;
+ }
+
+ // Adobe adjusts the module height to fill the available space
+ double moduleHeight2 = (fieldHeight - 6 * moduleWidth) / nRows;
+
+ drawBarcode(codewords, length + nErrorCorrectionCodewords,
+ nRows, nCols, errorCorrectionLevel,
+ moduleWidth, moduleHeight2, fieldWidth, fieldHeight,
+ appearBuf);
+
+ return gTrue;
+}
+
+// Generate up to maxDataCodewords-1 codewords in codewords[1 .. m],
+// leaving codewords[0] unused. Returns the number of generated
+// codewords (m), or maxDataCodewords if there are too many to fit.
+static int makeDataCodewords(GString *value, int *codewords) {
+ GBool textMode = gTrue;
+ int codewordIdx = 1;
+ int valueIdx = 0;
+ while (valueIdx < value->getLength()) {
+
+ int nNumeric = countNumeric(value, valueIdx);
+ if (nNumeric >= 13) {
+ if (!appendDataCodeword(codewords, codewordIdx, numericModeLatch) ||
+ !makeNumericCodewords(value, valueIdx, nNumeric,
+ codewords, codewordIdx)) {
+ return maxDataCodewords;
+ }
+ valueIdx += nNumeric;
+ textMode = gFalse;
+
+ } else {
+ int nText = countText(value, valueIdx);
+ if (nText >= 5) {
+ if (!textMode) {
+ if (!appendDataCodeword(codewords, codewordIdx, textModeLatch)) {
+ return maxDataCodewords;
+ }
+ }
+ if (!makeTextCodewords(value, valueIdx, nText,
+ codewords, codewordIdx)) {
+ return maxDataCodewords;
+ }
+ valueIdx += nText;
+ textMode = gTrue;
+
+ } else {
+ int nBinary = countBinary(value, valueIdx);
+ //~ this doesn't currently use byte mode shift when nBinary==1
+ //~ because we don't track the text submode (which is needed
+ //~ because we'll return to that submode after the byte shift)
+ int code = (nBinary % 6 == 0) ? byteModeLatchMult6
+ : byteModeLatchNonMult6;
+ if (!appendDataCodeword(codewords, codewordIdx, code) ||
+ !makeByteCodewords(value, valueIdx, nBinary,
+ codewords, codewordIdx)) {
+ return maxDataCodewords;
+ }
+ valueIdx += nBinary;
+ textMode = gFalse;
+ }
+ }
+ }
+
+ return codewordIdx - 1;
+}
+
+static int countNumeric(GString *value, int start) {
+ int n = 0;
+ for (int i = start; i < value->getLength(); ++i) {
+ char c = value->getChar(i);
+ if (!(c >= '0' && c <= '9')) {
+ break;
+ }
+ ++n;
+ }
+ return n;
+}
+
+static int countText(GString *value, int start) {
+ int n = 0;
+ for (int i = start; i < value->getLength(); ++i) {
+ char c = value->getChar(i);
+ if ((c & 0x80) ||
+ (textCompaction[(int)c][0] == 30 &&
+ textCompaction[(int)c][1] == 30 &&
+ textCompaction[(int)c][2] == 30 &&
+ textCompaction[(int)c][3] == 30) ||
+ countNumeric(value, i) >= 13) {
+ break;
+ }
+ ++n;
+ }
+ return n;
+}
+
+static int countBinary(GString *value, int start) {
+ int n = 0;
+ for (int i = start; i < value->getLength(); ++i) {
+ if (countNumeric(value, i) >= 13 ||
+ countText(value, i) >= 5) {
+ break;
+ }
+ ++n;
+ }
+ return n;
+}
+
+static GBool makeNumericCodewords(GString *value, int start, int n,
+ int *codewords, int &codewordIdx) {
+ for (int i = 0; i < n; i += 44) {
+ int nInDigits = n - i;
+ if (nInDigits > 44) {
+ nInDigits = 44;
+ }
+ int inDigits[45];
+ inDigits[0] = 1;
+ for (int j = 0; j < nInDigits; ++j) {
+ inDigits[j+1] = value->getChar(start + i + j) - '0';
+ }
+ int nOutDigits = 1 + nInDigits / 3;
+ int outDigits[15];
+ for (int j = 0; j < nOutDigits; ++j) {
+ outDigits[nOutDigits - 1 - j] = divMod900(inDigits, nInDigits + 1);
+ }
+ for (int j = 0; j < nOutDigits; ++j) {
+ if (!appendDataCodeword(codewords, codewordIdx, outDigits[j])) {
+ return gFalse;
+ }
+ }
+ }
+ return gTrue;
+}
+
+// Given x = sum(i = 0..n-1, d[i] * 10^(n-1-i))
+// Compute x' = x / 900 and return x % 900.
+static int divMod900(int *d, int n) {
+ int r = 0;
+ for (int i = 0; i < n; ++i) {
+ int dd = r * 10 + d[i];
+ d[i] = dd / 900;
+ r = dd % 900;
+ }
+ return r;
+}
+
+static GBool makeByteCodewords(GString *value, int start, int n,
+ int *codewords, int &codewordIdx) {
+ int i;
+ for (i = 0; i <= n - 6; i += 6) {
+ long long in = 0;
+ for (int j = 0; j < 6; ++j) {
+ in = (in << 8) | (value->getChar(start + i + j) & 0xff);
+ }
+ int outCodes[5];
+ for (int j = 4; j >= 0; --j) {
+ outCodes[j] = (int)(in % 900);
+ in /= 900;
+ }
+ for (int j = 0; j < 4; ++j) {
+ if (!appendDataCodeword(codewords, codewordIdx, outCodes[j])) {
+ return gFalse;
+ }
+ }
+ }
+ for (; i < n; ++i) {
+ if (!appendDataCodeword(codewords, codewordIdx,
+ value->getChar(start + i) & 0xff)) {
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
+static GBool makeTextCodewords(GString *value, int start, int n,
+ int *codewords, int &codewordIdx) {
+ int submode = textSubmodeAlpha;
+ int prevSubmode = textSubmodeAlpha;
+ int nextSubmode;
+ int prevHalfSymbol = 30;
+ int valueIdx = start;
+ while (valueIdx < start + n) {
+ int c = value->getChar(valueIdx) & 0x7f;
+ nextSubmode = submode;
+ switch (submode) {
+ case textSubmodeAlpha:
+ if (textCompaction[c][textSubmodeAlpha] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textCompaction[c][textSubmodeAlpha],
+ prevHalfSymbol)) {
+ return gFalse;
+ }
+ ++valueIdx;
+ } else if (textCompaction[c][textSubmodeLower] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textAlphaLowerLatch, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodeLower;
+ } else if (textCompaction[c][textSubmodeMixed] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textAlphaMixedLatch, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodeMixed;
+ } else if (textCompaction[c][textSubmodePunc] < 30) {
+ if (valueIdx < start + n - 2 &&
+ textCompaction[value->getChar(valueIdx + 1) & 0x7f]
+ [textSubmodePunc] < 30 &&
+ textCompaction[value->getChar(valueIdx + 2) & 0x7f]
+ [textSubmodePunc] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textAlphaMixedLatch, prevHalfSymbol) ||
+ !appendTextHalfSymbol(codewords, codewordIdx,
+ textMixedPuncLatch, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodePunc;
+ } else {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textAlphaPuncShift, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodePuncShift;
+ }
+ } else {
+ // this shouldn't happen
+ ++valueIdx;
+ }
+ break;
+ case textSubmodeAlphaShift:
+ if (textCompaction[c][textSubmodeAlpha] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textCompaction[c][textSubmodeAlpha],
+ prevHalfSymbol)) {
+ return gFalse;
+ }
+ ++valueIdx;
+ } else {
+ // this shouldn't happen
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ 0, prevHalfSymbol)) {
+ return gFalse;
+ }
+ }
+ nextSubmode = prevSubmode;
+ break;
+ case textSubmodeLower:
+ if (textCompaction[c][textSubmodeAlpha] < 30) {
+ if (valueIdx < start + n - 2 &&
+ textCompaction[value->getChar(valueIdx + 1) & 0x7f]
+ [textSubmodeAlpha] < 30 &&
+ textCompaction[value->getChar(valueIdx + 2) & 0x7f]
+ [textSubmodeAlpha] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textLowerMixedLatch, prevHalfSymbol) ||
+ !appendTextHalfSymbol(codewords, codewordIdx,
+ textMixedAlphaLatch, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodeAlpha;
+ } else {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textLowerAlphaShift, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodeAlphaShift;
+ }
+ } else if (textCompaction[c][textSubmodeLower] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textCompaction[c][textSubmodeLower],
+ prevHalfSymbol)) {
+ return gFalse;
+ }
+ ++valueIdx;
+ } else if (textCompaction[c][textSubmodeMixed] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textLowerMixedLatch, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodeMixed;
+ } else if (textCompaction[c][textSubmodePunc] < 30) {
+ if (valueIdx < start + n - 2 &&
+ textCompaction[value->getChar(valueIdx + 1) & 0x7f]
+ [textSubmodePunc] < 30 &&
+ textCompaction[value->getChar(valueIdx + 2) & 0x7f]
+ [textSubmodePunc] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textLowerMixedLatch, prevHalfSymbol) ||
+ !appendTextHalfSymbol(codewords, codewordIdx,
+ textMixedPuncLatch, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodePunc;
+ } else {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textLowerPuncShift, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodePuncShift;
+ }
+ } else {
+ // this shouldn't happen
+ ++valueIdx;
+ }
+ break;
+ case textSubmodeMixed:
+ if (textCompaction[c][textSubmodeAlpha] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textMixedAlphaLatch, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodeAlpha;
+ } else if (textCompaction[c][textSubmodeLower] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textMixedLowerLatch, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodeLower;
+ } else if (textCompaction[c][textSubmodeMixed] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textCompaction[c][textSubmodeMixed],
+ prevHalfSymbol)) {
+ return gFalse;
+ }
+ ++valueIdx;
+ } else if (textCompaction[c][textSubmodePunc] < 30) {
+ if (valueIdx < start + n - 1 &&
+ textCompaction[value->getChar(valueIdx + 1) & 0x7f]
+ [textSubmodePunc] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textMixedPuncLatch, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodePunc;
+ } else {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textMixedPuncShift, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodePuncShift;
+ }
+ } else {
+ // this shouldn't happen
+ ++valueIdx;
+ }
+ break;
+ case textSubmodePunc:
+ if (textCompaction[c][textSubmodeAlpha] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textPuncAlphaLatch, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodeAlpha;
+ } else if (textCompaction[c][textSubmodeLower] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textPuncAlphaLatch, prevHalfSymbol) ||
+ !appendTextHalfSymbol(codewords, codewordIdx,
+ textAlphaLowerLatch, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodeLower;
+ } else if (textCompaction[c][textSubmodeMixed] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textPuncAlphaLatch, prevHalfSymbol) ||
+ !appendTextHalfSymbol(codewords, codewordIdx,
+ textAlphaMixedLatch, prevHalfSymbol)) {
+ return gFalse;
+ }
+ nextSubmode = textSubmodeMixed;
+ } else if (textCompaction[c][textSubmodePunc] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textCompaction[c][textSubmodePunc],
+ prevHalfSymbol)) {
+ return gFalse;
+ }
+ ++valueIdx;
+ } else {
+ // this shouldn't happen
+ ++valueIdx;
+ }
+ break;
+ case textSubmodePuncShift:
+ if (textCompaction[c][textSubmodePunc] < 30) {
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ textCompaction[c][textSubmodePunc],
+ prevHalfSymbol)) {
+ return gFalse;
+ }
+ ++valueIdx;
+ } else {
+ // this shouldn't happen
+ if (!appendTextHalfSymbol(codewords, codewordIdx,
+ 0, prevHalfSymbol)) {
+ return gFalse;
+ }
+ }
+ nextSubmode = prevSubmode;
+ break;
+ }
+ prevSubmode = submode;
+ submode = nextSubmode;
+ }
+
+ // padding
+ if (prevHalfSymbol < 30) {
+ appendTextHalfSymbol(codewords, codewordIdx, 29, prevHalfSymbol);
+ }
+
+ return gTrue;
+}
+
+static GBool appendTextHalfSymbol(int *codewords, int &codewordIdx,
+ int halfSymbol, int &prevHalfSymbol) {
+ if (prevHalfSymbol == 30) {
+ prevHalfSymbol = halfSymbol;
+ } else {
+ int codeword = prevHalfSymbol * 30 + halfSymbol;
+ if (!appendDataCodeword(codewords, codewordIdx, codeword)) {
+ return gFalse;
+ }
+ prevHalfSymbol = 30;
+ }
+ return gTrue;
+}
+
+static GBool appendDataCodeword(int *codewords, int &codewordIdx,
+ int codeword) {
+ if (codewordIdx >= maxDataCodewords) {
+ return gFalse;
+ }
+ codewords[codewordIdx++] = codeword;
+ return gTrue;
+}
+
+// Compute the error correction codewords and add to codewords[].
+static void makeErrorCorrectionCodewords(int errorCorrectionLevel,
+ int *codewords, int length) {
+ int e[1 << (maxErrorCorrectionLevel + 1)];
+ int k = 1 << (errorCorrectionLevel + 1);
+ for (int i = 0; i < k; ++i) {
+ e[i] = 0;
+ }
+ for (int i = 0; i < length; ++i) {
+ int t1 = (codewords[i] + e[k-1]) % 929;
+ int t2, t3;
+ for (int j = k-1; j >= 1; --j) {
+ t2 = (t1 * errorCorrectionCoeff[errorCorrectionLevel][j]) % 929;
+ t3 = 929 - t2;
+ e[j] = (e[j-1] + t3) % 929;
+ }
+ t2 = (t1 * errorCorrectionCoeff[errorCorrectionLevel][0]) % 929;
+ t3 = 929 - t2;
+ e[0] = t3 % 929;
+ }
+ int j = length;
+ for (int i = k-1; i >= 0; --i) {
+ int ee = e[i] ? (929 - e[i]) : 0;
+ codewords[j++] = ee;
+ }
+}
+
+// Draw the barcode in the rectangle (0, 0, fieldWidth, fieldHeight).
+static void drawBarcode(int *codewords, int totalLength,
+ int nRows, int nCols,
+ int errorCorrectionLevel,
+ double moduleWidth, double moduleHeight,
+ double fieldWidth, double fieldHeight,
+ GString *appearBuf) {
+ double w = (73 + nCols * 17) * moduleWidth;
+ double h = nRows * moduleHeight;
+ double x0 = 0.5 * (fieldWidth - w);
+ double y0 = 0.5 * (fieldHeight - h);
+
+ appearBuf->append("0 g q\n");
+ appearBuf->appendf("{0:.4f} 0 0 {1:.4f} {2:.4f} {3:.4f} cm\n",
+ moduleWidth, moduleHeight, x0, y0);
+
+ int i = 0;
+ for (int row = 0; row < nRows; ++row) {
+ int cluster = row % 3;
+ int y = nRows - 1 - row;
+ int left = 0;
+ int right = 0;
+ switch (cluster) {
+ case 0:
+ left = 30 * (row / 3) + (nRows - 1) / 3;
+ right = 30 * (row / 3) + (nCols - 1);
+ break;
+ case 1:
+ left = 30 * (row / 3) + (errorCorrectionLevel * 3) + (nRows - 1) % 3;
+ right = 30 * (row / 3) + (nRows - 1) / 3;
+ break;
+ case 2:
+ left = 30 * (row / 3) + (nCols - 1);
+ right = 30 * (row / 3) + (errorCorrectionLevel * 3) + (nRows - 1) % 3;
+ break;
+ }
+ int x = 0;
+ drawPattern(x, y, startPattern, startPatternLength, appearBuf);
+ drawPattern(x, y, patterns[left][cluster], patternLength, appearBuf);
+ for (int col = 0; col < nCols; ++col) {
+ drawPattern(x, y, patterns[codewords[i]][cluster], patternLength,
+ appearBuf);
+ ++i;
+ }
+ drawPattern(x, y, patterns[right][cluster], patternLength, appearBuf);
+ drawPattern(x, y, stopPattern, stopPatternLength, appearBuf);
+ }
+
+ appearBuf->append("Q\n");
+}
+
+static void drawPattern(int &x, int y, char *pattern, int length,
+ GString *appearBuf) {
+ for (int i = 0; i < length; ++i) {
+ if (!(i & 1)) {
+ appearBuf->appendf("{0:d} {1:d} {2:d} 1 re f\n", x, y, pattern[i]);
+ }
+ x += pattern[i];
+ }
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDF417Barcode.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PDF417Barcode.h
new file mode 100644
index 00000000000..8166865280b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDF417Barcode.h
@@ -0,0 +1,32 @@
+//========================================================================
+//
+// PDF417Barcode.h
+//
+// Copyright 2018 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PDF417BARCODE_H
+#define PDF417BARCODE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class GString;
+
+// Draw a PDF417 barcode:
+// fieldWidth, fieldHeight = field size (in points)
+// moduleWidth, moduleHeight = requested module size (in points)
+// errorCorrectionLevel = 0 .. 8
+// value = byte string
+// output is appended to appearBuf
+// Returns true on success, false on error.
+extern GBool drawPDF417Barcode(double fieldWidth, double fieldHeight,
+ double moduleWidth, double moduleHeight,
+ int errorCorrectionLevel, GString *value,
+ GString *appearBuf);
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc
new file mode 100644
index 00000000000..877bcb88255
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.cc
@@ -0,0 +1,2009 @@
+//========================================================================
+//
+// PDFCore.cc
+//
+// Copyright 2004-2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <math.h>
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "GlobalParams.h"
+#include "Splash.h"
+#include "SplashBitmap.h"
+#include "SplashPattern.h"
+#include "SplashPath.h"
+#include "Error.h"
+#include "ErrorCodes.h"
+#include "PDFDoc.h"
+#include "Link.h"
+#include "Annot.h"
+#include "AcroForm.h"
+#include "OptionalContent.h"
+#include "TileMap.h"
+#include "TileCache.h"
+#include "TileCompositor.h"
+#include "PDFCore.h"
+
+//------------------------------------------------------------------------
+// PDFCore
+//------------------------------------------------------------------------
+
+PDFCore::PDFCore(SplashColorMode colorMode, int bitmapRowPad,
+ GBool reverseVideo, SplashColorPtr paperColor) {
+ GString *initialZoom, *initialDisplayMode;
+ int z, i;
+
+ doc = NULL;
+
+ linksPage = 0;
+ links = NULL;
+
+ annotsPage = 0;
+ annots = NULL;
+
+ textPage = 0;
+ textDPI = 0;
+ textRotate = 0;
+ textOutCtrl.mode = textOutPhysLayout;
+ text = NULL;
+
+ state = new DisplayState(globalParams->getMaxTileWidth(),
+ globalParams->getMaxTileHeight(),
+ globalParams->getTileCacheSize(),
+ globalParams->getWorkerThreads(),
+ colorMode, bitmapRowPad);
+ tileMap = new TileMap(state);
+ tileCache = new TileCache(state);
+ tileCompositor = new TileCompositor(state, tileMap, tileCache);
+ bitmapFinished = gTrue;
+
+ state->setReverseVideo(reverseVideo);
+ state->setPaperColor(paperColor);
+ initialZoom = globalParams->getInitialZoom();
+ if (!initialZoom->cmp("page")) {
+ state->setZoom(zoomPage);
+ } else if (!initialZoom->cmp("width")) {
+ state->setZoom(zoomWidth);
+ } else {
+ z = atoi(initialZoom->getCString());
+ if (z <= 0) {
+ z = zoomWidth;
+ }
+ state->setZoom(z);
+ }
+ delete initialZoom;
+ initialDisplayMode = globalParams->getInitialDisplayMode();
+ if (!initialDisplayMode->cmp("single")) {
+ state->setDisplayMode(displaySingle);
+ } else if (!initialDisplayMode->cmp("sideBySideSingle")) {
+ state->setDisplayMode(displaySideBySideSingle);
+ } else if (!initialDisplayMode->cmp("sideBySideContinuous")) {
+ state->setDisplayMode(displaySideBySideContinuous);
+ } else if (!initialDisplayMode->cmp("horizontalContinuous")) {
+ state->setDisplayMode(displayHorizontalContinuous);
+ } else {
+ state->setDisplayMode(displayContinuous);
+ }
+ delete initialDisplayMode;
+
+ selectMode = selectModeBlock;
+ selectPage = 0;
+ selectStartX = selectStartY = 0;
+
+ historyCur = pdfHistorySize - 1;
+ historyBLen = historyFLen = 0;
+ for (i = 0; i < pdfHistorySize; ++i) {
+ history[i].fileName = NULL;
+ history[i].page = 0;
+ }
+}
+
+PDFCore::~PDFCore() {
+ int i;
+
+ delete tileCompositor;
+ delete tileCache;
+ delete tileMap;
+ delete state;
+ clearPage();
+ if (doc) {
+ delete doc;
+ }
+ for (i = 0; i < pdfHistorySize; ++i) {
+ if (history[i].fileName) {
+#ifdef _WIN32
+ delete[] history[i].fileName;
+#else
+ delete history[i].fileName;
+#endif
+ }
+ }
+}
+
+int PDFCore::loadFile(GString *fileName, GString *ownerPassword,
+ GString *userPassword) {
+ int err;
+
+ setBusyCursor(gTrue);
+ err = loadFile2(new PDFDoc(fileName->copy(), ownerPassword, userPassword,
+ this));
+ setBusyCursor(gFalse);
+ return err;
+}
+
+#ifdef _WIN32
+int PDFCore::loadFile(wchar_t *fileName, int fileNameLen,
+ GString *ownerPassword, GString *userPassword) {
+ int err;
+
+ setBusyCursor(gTrue);
+ err = loadFile2(new PDFDoc(fileName, fileNameLen,
+ ownerPassword, userPassword, this));
+ setBusyCursor(gFalse);
+ return err;
+}
+#endif
+
+int PDFCore::loadFile(BaseStream *stream, GString *ownerPassword,
+ GString *userPassword) {
+ int err;
+
+ setBusyCursor(gTrue);
+ err = loadFile2(new PDFDoc(stream, ownerPassword, userPassword, this));
+ setBusyCursor(gFalse);
+ return err;
+}
+
+void PDFCore::loadDoc(PDFDoc *docA) {
+ setBusyCursor(gTrue);
+ loadFile2(docA);
+ setBusyCursor(gFalse);
+}
+
+int PDFCore::reload() {
+ int err;
+
+ if (!doc->getFileName()) {
+ return errOpenFile;
+ }
+ setBusyCursor(gTrue);
+ err = loadFile2(new PDFDoc(doc->getFileName()->copy(), NULL, NULL, this));
+ setBusyCursor(gFalse);
+ startUpdate();
+ finishUpdate(gTrue, gFalse);
+ return err;
+}
+
+int PDFCore::loadFile2(PDFDoc *newDoc) {
+ int err;
+
+ clearSelection();
+
+ // open the PDF file
+ if (!newDoc->isOk()) {
+ err = newDoc->getErrorCode();
+ delete newDoc;
+ return err;
+ }
+
+ preLoad();
+
+ // replace old document
+ // NB: do not delete doc until after DisplayState::setDoc() returns
+ state->setDoc(newDoc);
+ if (doc) {
+ delete doc;
+ }
+ doc = newDoc;
+ clearPage();
+
+ postLoad();
+
+ return errNone;
+}
+
+void PDFCore::clear() {
+ if (!doc) {
+ return;
+ }
+
+ // no document
+ // NB: do not delete doc until after DisplayState::setDoc() returns
+ state->setDoc(NULL);
+ delete doc;
+ doc = NULL;
+ clearPage();
+
+ // redraw
+ state->setScrollPosition(1, 0, 0);
+ invalidateWholeWindow();
+ updateScrollbars();
+}
+
+PDFDoc *PDFCore::takeDoc(GBool redraw) {
+ PDFDoc *docA;
+
+ if (!doc) {
+ return NULL;
+ }
+
+ // no document
+ // NB: do not delete doc until after DisplayState::setDoc() returns
+ state->setDoc(NULL);
+ docA = doc;
+ doc = NULL;
+ clearPage();
+
+ // redraw
+ state->setScrollPosition(1, 0, 0);
+ if (redraw) {
+ invalidateWholeWindow();
+ updateScrollbars();
+ }
+
+ return docA;
+}
+
+void PDFCore::displayPage(int page, GBool scrollToTop,
+ GBool scrollToBottom, GBool addToHist) {
+ int scrollX, scrollY;
+
+ if (page <= 0 || page > doc->getNumPages()) {
+ return;
+ }
+ 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);
+ } else {
+ scrollY = state->getScrollY();
+ }
+ if (state->getDisplayMode() == displayHorizontalContinuous) {
+ scrollX = tileMap->getPageLeftX(page);
+ } else {
+ scrollX = state->getScrollX();
+ }
+ startUpdate();
+ state->setScrollPosition(page, scrollX, scrollY);
+ finishUpdate(addToHist, gTrue);
+}
+
+void PDFCore::displayDest(LinkDest *dest) {
+ Ref pageRef;
+ int page;
+ int dx, dy, scrollX, scrollY;
+
+ if (dest->isPageRef()) {
+ pageRef = dest->getPageRef();
+ page = doc->findPage(pageRef.num, pageRef.gen);
+ } else {
+ page = dest->getPageNum();
+ }
+ if (page <= 0 || page > doc->getNumPages()) {
+ page = 1;
+ }
+
+ switch (dest->getKind()) {
+ case destXYZ:
+ cvtUserToDev(page, dest->getLeft(), dest->getTop(), &dx, &dy);
+ scrollX = tileMap->getPageLeftX(page);
+ if (dest->getChangeLeft()) {
+ scrollX += dx;
+ }
+ scrollY = tileMap->getPageTopY(page);
+ if (dest->getChangeTop()) {
+ scrollY += dy;
+ }
+ startUpdate();
+ state->setScrollPosition(page, scrollX, scrollY);
+ finishUpdate(gTrue, gTrue);
+ break;
+ case destFit:
+ case destFitB:
+ state->setZoom(zoomPage);
+ scrollX = tileMap->getPageLeftX(page);
+ scrollY = tileMap->getPageTopY(page);
+ startUpdate();
+ state->setScrollPosition(page, scrollX, scrollY);
+ finishUpdate(gTrue, gTrue);
+ break;
+ case destFitH:
+ case destFitBH:
+ state->setZoom(zoomWidth);
+ scrollX = tileMap->getPageLeftX(page);
+ scrollY = tileMap->getPageTopY(page);
+ if (dest->getChangeTop()) {
+ cvtUserToDev(page, 0, dest->getTop(), &dx, &dy);
+ scrollY += dy;
+ }
+ startUpdate();
+ state->setScrollPosition(page, scrollX, scrollY);
+ finishUpdate(gTrue, gTrue);
+ break;
+ case destFitV:
+ case destFitBV:
+ state->setZoom(zoomHeight);
+ scrollX = tileMap->getPageLeftX(page);
+ scrollY = tileMap->getPageTopY(page);
+ if (dest->getChangeTop()) {
+ cvtUserToDev(page, dest->getLeft(), 0, &dx, &dy);
+ scrollX += dx;
+ }
+ startUpdate();
+ state->setScrollPosition(page, scrollX, scrollY);
+ finishUpdate(gTrue, gTrue);
+ break;
+ case destFitR:
+ zoomToRect(page, dest->getLeft(), dest->getTop(),
+ dest->getRight(), dest->getBottom());
+ break;
+ }
+}
+
+void PDFCore::startUpdate() {
+}
+
+void PDFCore::finishUpdate(GBool addToHist, GBool checkForChangedFile) {
+ int scrollPage, scrollX, scrollY, maxScrollX, maxScrollY;
+
+ if (!doc) {
+ invalidateWholeWindow();
+ updateScrollbars();
+ return;
+ }
+
+ // check for changes to the PDF file
+ if (checkForChangedFile &&
+ doc->getFileName() &&
+ checkForNewFile()) {
+ loadFile(doc->getFileName());
+ }
+
+ // zero-page documents are a special case
+ // (check for this *after* checking for changes to the file)
+ if (!doc->getNumPages()) {
+ invalidateWholeWindow();
+ updateScrollbars();
+ return;
+ }
+
+ // check the scroll position
+ scrollPage = state->getScrollPage();
+ if (state->getDisplayMode() == displaySideBySideSingle &&
+ !(scrollPage & 1)) {
+ --scrollPage;
+ }
+ if (state->displayModeIsContinuous()) {
+ scrollPage = 0;
+ } else if (scrollPage <= 0 || scrollPage > doc->getNumPages()) {
+ scrollPage = 1;
+ }
+ scrollX = state->getScrollX();
+ scrollY = state->getScrollY();
+ // we need to set scrollPage before calling getScrollLimits()
+ state->setScrollPosition(scrollPage, scrollX, scrollY);
+ tileMap->getScrollLimits(&maxScrollX, &maxScrollY);
+ maxScrollX -= state->getWinW();
+ maxScrollY -= state->getWinH();
+ if (scrollX > maxScrollX) {
+ scrollX = maxScrollX;
+ }
+ if (scrollX < 0) {
+ scrollX = 0;
+ }
+ if (scrollY > maxScrollY) {
+ scrollY = maxScrollY;
+ }
+ if (scrollY < 0) {
+ scrollY = 0;
+ }
+ if (scrollPage != state->getScrollPage() ||
+ scrollX != state->getScrollX() ||
+ scrollY != state->getScrollY()) {
+ state->setScrollPosition(scrollPage, scrollX, scrollY);
+ }
+
+ // redraw
+ invalidateWholeWindow();
+ updateScrollbars();
+
+ // add to history
+ if (addToHist) {
+ addToHistory();
+ }
+}
+
+void PDFCore::addToHistory() {
+ PDFHistory h;
+ PDFHistory *cur;
+
+ cur = &history[historyCur];
+ h.page = tileMap->getMidPage();
+#ifdef _WIN32
+ if (doc->getFileNameU()) {
+ h.fileName = (wchar_t *)gmallocn(MAX_PATH + 1, sizeof(wchar_t));
+ if (GetFullPathNameW(doc->getFileNameU(), MAX_PATH + 1,
+ h.fileName, NULL) == 0) {
+ h.fileName = NULL;
+ }
+ } else {
+ h.fileName = NULL;
+ }
+#else
+ if (doc->getFileName()) {
+ h.fileName = doc->getFileName()->copy();
+ } else {
+ h.fileName = NULL;
+ }
+#endif
+ if (historyBLen > 0 && h.page == cur->page) {
+ if (!h.fileName && !cur->fileName) {
+ return;
+ }
+#ifdef _WIN32
+ if (h.fileName && cur->fileName && !wcscmp(h.fileName, cur->fileName)) {
+ gfree(h.fileName);
+ return;
+ }
+#else
+ if (h.fileName && cur->fileName && !h.fileName->cmp(cur->fileName)) {
+ delete h.fileName;
+ return;
+ }
+#endif
+ }
+ if (++historyCur == pdfHistorySize) {
+ historyCur = 0;
+ }
+ if (history[historyCur].fileName) {
+#ifdef _WIN32
+ gfree(history[historyCur].fileName);
+#else
+ delete history[historyCur].fileName;
+#endif
+ }
+ history[historyCur] = h;
+ if (historyBLen < pdfHistorySize) {
+ ++historyBLen;
+ }
+ historyFLen = 0;
+}
+
+GBool PDFCore::gotoNextPage(int inc, GBool top) {
+ GBool sideBySide;
+ int pg;
+
+ if (!doc || !doc->getNumPages()) {
+ return gFalse;
+ }
+ pg = tileMap->getFirstPage();
+ sideBySide = state->displayModeIsSideBySide();
+ if (pg + (sideBySide ? 2 : 1) > doc->getNumPages()) {
+ return gFalse;
+ }
+ if (sideBySide && inc < 2) {
+ inc = 2;
+ }
+ if ((pg += inc) > doc->getNumPages()) {
+ pg = doc->getNumPages();
+ }
+ displayPage(pg, top, gFalse);
+ return gTrue;
+}
+
+GBool PDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
+ int pg;
+
+ if (!doc || !doc->getNumPages()) {
+ return gFalse;
+ }
+ pg = tileMap->getFirstPage();
+ if (state->getDisplayMode() == displayContinuous &&
+ state->getScrollY() > tileMap->getPageTopY(pg)) {
+ ++pg;
+ } else if (state->getDisplayMode() == displaySideBySideContinuous &&
+ state->getScrollY() > tileMap->getPageTopY(pg)) {
+ pg += 2;
+ } else if (state->getDisplayMode() == displayHorizontalContinuous &&
+ state->getScrollX() > tileMap->getPageLeftX(pg)) {
+ ++pg;
+ }
+ if (pg <= 1) {
+ return gFalse;
+ }
+ if (state->displayModeIsSideBySide() && dec < 2) {
+ dec = 2;
+ }
+ if ((pg -= dec) < 1) {
+ pg = 1;
+ }
+ displayPage(pg, top, bottom);
+ return gTrue;
+}
+
+GBool PDFCore::gotoNamedDestination(GString *dest) {
+ LinkDest *d;
+
+ if (!doc) {
+ return gFalse;
+ }
+ if (!(d = doc->findDest(dest))) {
+ return gFalse;
+ }
+ displayDest(d);
+ delete d;
+ return gTrue;
+}
+
+GBool PDFCore::goForward() {
+ int pg;
+
+ if (historyFLen == 0) {
+ return gFalse;
+ }
+ if (++historyCur == pdfHistorySize) {
+ historyCur = 0;
+ }
+ --historyFLen;
+ ++historyBLen;
+ if (!history[historyCur].fileName) {
+ return gFalse;
+ }
+#ifdef _WIN32
+ if (!doc ||
+ !doc->getFileNameU() ||
+ wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
+ if (loadFile(history[historyCur].fileName,
+ (int)wcslen(history[historyCur].fileName)) != errNone) {
+ return gFalse;
+ }
+ }
+#else
+ if (!doc ||
+ !doc->getFileName() ||
+ history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+ if (loadFile(history[historyCur].fileName) != errNone) {
+ return gFalse;
+ }
+ }
+#endif
+ pg = history[historyCur].page;
+ displayPage(pg, gFalse, gFalse, gFalse);
+ return gTrue;
+}
+
+GBool PDFCore::goBackward() {
+ int pg;
+
+ if (historyBLen <= 1) {
+ return gFalse;
+ }
+ if (--historyCur < 0) {
+ historyCur = pdfHistorySize - 1;
+ }
+ --historyBLen;
+ ++historyFLen;
+ if (!history[historyCur].fileName) {
+ return gFalse;
+ }
+#ifdef _WIN32
+ if (!doc ||
+ !doc->getFileNameU() ||
+ wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
+ if (loadFile(history[historyCur].fileName,
+ (int)wcslen(history[historyCur].fileName)) != errNone) {
+ return gFalse;
+ }
+ }
+#else
+ if (!doc ||
+ !doc->getFileName() ||
+ history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+ if (loadFile(history[historyCur].fileName) != errNone) {
+ return gFalse;
+ }
+ }
+#endif
+ pg = history[historyCur].page;
+ displayPage(pg, gFalse, gFalse, gFalse);
+ return gTrue;
+}
+
+void PDFCore::scrollLeft(int nCols) {
+ scrollTo(state->getScrollX() - nCols, state->getScrollY());
+}
+
+void PDFCore::scrollRight(int nCols) {
+ scrollTo(state->getScrollX() + nCols, state->getScrollY());
+}
+
+void PDFCore::scrollUp(int nLines, GBool snapToPage) {
+ scrollTo(state->getScrollX(), state->getScrollY() - nLines, snapToPage);
+}
+
+void PDFCore::scrollUpPrevPage(int nLines) {
+ if (!state->displayModeIsContinuous() &&
+ state->getScrollY() == 0) {
+ gotoPrevPage(1, gFalse, gTrue);
+ } else {
+ scrollUp(nLines, gTrue);
+ }
+}
+
+void PDFCore::scrollDown(int nLines, GBool snapToPage) {
+ scrollTo(state->getScrollX(), state->getScrollY() + nLines, snapToPage);
+}
+
+void PDFCore::scrollDownNextPage(int nLines) {
+ int horizMax, vertMax;
+
+ if (!state->displayModeIsContinuous()) {
+ tileMap->getScrollLimits(&horizMax, &vertMax);
+ if (state->getScrollY() >= vertMax - state->getWinH()) {
+ gotoNextPage(1, gTrue);
+ } else {
+ scrollDown(nLines);
+ }
+ } else {
+ scrollDown(nLines, gTrue);
+ }
+}
+
+void PDFCore::scrollPageUp() {
+ scrollUpPrevPage(state->getWinH());
+}
+
+void PDFCore::scrollPageDown() {
+ scrollDownNextPage(state->getWinH());
+}
+
+void PDFCore::scrollTo(int x, int y, GBool snapToPage) {
+ int next, topPage, topPageY, sy, dy;
+
+ startUpdate();
+ state->setScrollPosition(state->getScrollPage(), x, y);
+
+ if (snapToPage) {
+ if (state->getDisplayMode() == displayContinuous ||
+ state->getDisplayMode() == displaySideBySideContinuous) {
+ next = state->getDisplayMode() == displaySideBySideContinuous ? 2 : 1;
+ topPage = tileMap->getFirstPage();
+ // 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;
+ // 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);
+ }
+ }
+ }
+ }
+ }
+
+ finishUpdate(gTrue, gTrue);
+}
+
+void PDFCore::scrollToLeftEdge() {
+ scrollTo(0, state->getScrollY());
+}
+
+void PDFCore::scrollToRightEdge() {
+ int horizMax, vertMax;
+
+ tileMap->getScrollLimits(&horizMax, &vertMax);
+ scrollTo(horizMax - state->getWinW(), state->getScrollY());
+}
+
+void PDFCore::scrollToTopEdge() {
+ scrollTo(state->getScrollX(),
+ tileMap->getPageTopY(tileMap->getFirstPage()));
+}
+
+void PDFCore::scrollToBottomEdge() {
+ scrollTo(state->getScrollX(),
+ tileMap->getPageBottomY(tileMap->getLastPage()));
+}
+
+void PDFCore::scrollToTopLeft() {
+ scrollTo(tileMap->getPageLeftX(tileMap->getFirstPage()),
+ tileMap->getPageTopY(tileMap->getFirstPage()));
+}
+
+void PDFCore::scrollToBottomRight() {
+ scrollTo(tileMap->getPageRightX(tileMap->getLastPage()),
+ tileMap->getPageBottomY(tileMap->getLastPage()));
+}
+
+void PDFCore::scrollToCentered(int page, double x, double y) {
+ int wx, wy, sx, sy;
+
+ startUpdate();
+
+ // scroll to the requested page
+ state->setScrollPosition(page, tileMap->getPageLeftX(page),
+ tileMap->getPageTopY(page));
+
+ // scroll the requested point to the center of the window
+ cvtUserToWindow(page, x, y, &wx, &wy);
+ sx = state->getScrollX() + wx - state->getWinW() / 2;
+ sy = state->getScrollY() + wy - state->getWinH() / 2;
+ state->setScrollPosition(page, sx, sy);
+
+ finishUpdate(gTrue, gFalse);
+}
+
+void PDFCore::setZoom(double zoom) {
+ int page;
+
+ if (state->getZoom() == zoom) {
+ return;
+ }
+ if (!doc || !doc->getNumPages()) {
+ state->setZoom(zoom);
+ return;
+ }
+ startUpdate();
+ page = tileMap->getFirstPage();
+ state->setZoom(zoom);
+ state->setScrollPosition(page, tileMap->getPageLeftX(page),
+ tileMap->getPageTopY(page));
+ finishUpdate(gTrue, gTrue);
+}
+
+void PDFCore::zoomToRect(int page, double ulx, double uly,
+ double lrx, double lry) {
+ int x0, y0, x1, y1, sx, sy, t;
+ double dpi, rx, ry, zoom;
+
+ startUpdate();
+
+ // set the new zoom level
+ cvtUserToDev(page, ulx, uly, &x0, &y0);
+ cvtUserToDev(page, lrx, lry, &x1, &y1);
+ if (x0 > x1) {
+ t = x0; x0 = x1; x1 = t;
+ }
+ if (y0 > y1) {
+ t = y0; y0 = y1; y1 = t;
+ }
+ rx = (double)state->getWinW() / (double)(x1 - x0);
+ ry = (double)state->getWinH() / (double)(y1 - y0);
+ dpi = tileMap->getDPI(page);
+ if (rx < ry) {
+ zoom = rx * (dpi / (0.01 * 72));
+ } else {
+ zoom = ry * (dpi / (0.01 * 72));
+ }
+ state->setZoom(zoom);
+
+ // scroll to the requested page
+ state->setScrollPosition(page, tileMap->getPageLeftX(page),
+ tileMap->getPageTopY(page));
+
+ // scroll the requested rectangle to the center of the window
+ cvtUserToWindow(page, 0.5 * (ulx + lrx), 0.5 * (uly + lry), &x0, &y0);
+ sx = state->getScrollX() + x0 - state->getWinW() / 2;
+ sy = state->getScrollY() + y0 - state->getWinH() / 2;
+ state->setScrollPosition(page, sx, sy);
+
+ finishUpdate(gTrue, gFalse);
+}
+
+void PDFCore::zoomCentered(double zoom) {
+ int page, wx, wy, sx, sy;
+ double cx, cy;
+
+ if (state->getZoom() == zoom) {
+ return;
+ }
+
+ startUpdate();
+
+ // get the center of the window, in user coords
+ cvtWindowToUser(state->getWinW() / 2, state->getWinH() / 2,
+ &page, &cx, &cy);
+
+ // set the new zoom level
+ state->setZoom(zoom);
+
+ // scroll to re-center
+ cvtUserToWindow(page, cx, cy, &wx, &wy);
+ sx = state->getScrollX() + wx - state->getWinW() / 2;
+ sy = state->getScrollY() + wy - state->getWinH() / 2;
+ state->setScrollPosition(page, sx, sy);
+
+ finishUpdate(gTrue, gFalse);
+}
+
+// Zoom so that the current page(s) fill the window width. Maintain
+// the vertical center.
+void PDFCore::zoomToCurrentWidth() {
+ int page0, page1, page, gap;
+ double w, w1, zoom;
+
+ startUpdate();
+
+ // get first and last pages
+ page0 = tileMap->getFirstPage();
+ page1 = tileMap->getLastPage();
+
+ // compute the desired width (in points)
+ gap = 0;
+ switch (state->getDisplayMode()) {
+ case displaySingle:
+ default:
+ w = tileMap->getPageBoxWidth(page0);
+ break;
+ case displayContinuous:
+ w = 0;
+ for (page = page0; page <= page1; ++page) {
+ w1 = tileMap->getPageBoxWidth(page);
+ if (w1 > w) {
+ w = w1;
+ }
+ }
+ break;
+ case displaySideBySideSingle:
+ w = tileMap->getPageBoxWidth(page0);
+ if (page1 != page0) {
+ w += tileMap->getPageBoxWidth(page1);
+ gap = tileMap->getSideBySidePageSpacing();
+ }
+ break;
+ case displaySideBySideContinuous:
+ w = 0;
+ for (page = page0; w <= page1; w += 2) {
+ w1 = tileMap->getPageBoxWidth(page);
+ if (page + 1 <= doc->getNumPages()) {
+ w1 += tileMap->getPageBoxWidth(page + 1);
+ }
+ if (w1 > w) {
+ w = w1;
+ }
+ }
+ gap = tileMap->getSideBySidePageSpacing();
+ break;
+ case displayHorizontalContinuous:
+ w = 0;
+ gap = 0;
+ for (page = page0; page <= page1; ++page) {
+ w += tileMap->getPageBoxWidth(page);
+ if (page != page0) {
+ gap += tileMap->getHorizContinuousPageSpacing();
+ }
+ }
+ break;
+ }
+
+ // set the new zoom level
+ zoom = 100.0 * (state->getWinW() - gap) / w;
+ state->setZoom(zoom);
+
+ // scroll so that the first page is at the left edge of the window
+ state->setScrollPosition(page0, tileMap->getPageLeftX(page0),
+ tileMap->getPageTopY(page0));
+
+ finishUpdate(gTrue, gFalse);
+}
+
+void PDFCore::setRotate(int rotate) {
+ int page;
+
+ if (state->getRotate() == rotate) {
+ return;
+ }
+ if (!doc || !doc->getNumPages()) {
+ state->setRotate(rotate);
+ return;
+ }
+ startUpdate();
+ page = tileMap->getFirstPage();
+ state->setRotate(rotate);
+ state->setScrollPosition(page, tileMap->getPageLeftX(page),
+ tileMap->getPageTopY(page));
+ finishUpdate(gTrue, gTrue);
+}
+
+void PDFCore::setDisplayMode(DisplayMode mode) {
+ int page;
+
+ if (state->getDisplayMode() == mode) {
+ return;
+ }
+ if (!doc || !doc->getNumPages()) {
+ state->setDisplayMode(mode);
+ return;
+ }
+ startUpdate();
+ page = tileMap->getFirstPage();
+ state->setDisplayMode(mode);
+ state->setScrollPosition(page, tileMap->getPageLeftX(page),
+ tileMap->getPageTopY(page));
+ finishUpdate(gTrue, gTrue);
+}
+
+void PDFCore::setOCGState(OptionalContentGroup *ocg, GBool ocgState) {
+ if (ocgState != ocg->getState()) {
+ ocg->setState(ocgState);
+ state->optionalContentChanged();
+ invalidateWholeWindow();
+ }
+}
+
+void PDFCore::setSelectMode(SelectMode mode) {
+ if (mode != selectMode) {
+ selectMode = mode;
+ clearSelection();
+ }
+}
+
+SplashColorPtr PDFCore::getSelectionColor() {
+ return state->getSelectColor();
+}
+
+void PDFCore::setSelectionColor(SplashColor color) {
+ int wx0, wy0, wx1, wy1;
+
+ state->setSelectColor(color);
+ if (state->hasSelection()) {
+ getSelectionBBox(&wx0, &wy0, &wx1, &wy1);
+ checkInvalidate(wx0, wy0, wx1 - wx0, wy1 - wy0);
+ }
+}
+
+void PDFCore::setSelection(int page, int x0, int y0, int x1, int y1) {
+ SelectRect *rect;
+ GBool moveLeft, moveTop, moveRight, moveBottom, needScroll;
+ double selectX0, selectY0, selectX1, selectY1;
+ int oldWx0, oldWy0, oldWx1, oldWy1, ix0, iy0, ix1, iy1;
+ int wx0, wy0, wx1, wy1, sx, sy, t;
+
+
+ // if selection rectangle is empty, clear the selection
+ if (x0 == x1 || y0 == y1) {
+ clearSelection();
+ return;
+ }
+
+ // x0 = left, x1 = right
+ // y0 = top, y1 = bottom
+ if (x0 > x1) {
+ t = x0; x0 = x1; x1 = t;
+ }
+ if (y0 > y1) {
+ t = y0; y0 = y1; y1 = t;
+ }
+
+ // convert new selection coords to user space and window space
+ tileMap->cvtDevToUser(page, x0, y0, &selectX0, &selectY0);
+ tileMap->cvtDevToUser(page, x1, y1, &selectX1, &selectY1);
+ cvtUserToWindow(page, selectX0, selectY0, &wx0, &wy0);
+ cvtUserToWindow(page, selectX1, selectY1, &wx1, &wy1);
+ if (wx0 > wx1) {
+ t = wx0; wx0 = wx1; wx1 = t;
+ }
+ if (wy0 > wy1) {
+ t = wy0; wy0 = wy1; wy1 = t;
+ }
+
+ // convert current selection coords to window space;
+ // check which edges moved
+ if (state->hasSelection()) {
+ rect = state->getSelectRect(0);
+ tileMap->cvtUserToWindow(rect->page, rect->x0, rect->y0, &oldWx0, &oldWy0);
+ tileMap->cvtUserToWindow(rect->page, rect->x1, rect->y1, &oldWx1, &oldWy1);
+ if (oldWx0 > oldWx1) {
+ t = oldWx0; oldWx0 = oldWx1; oldWx1 = t;
+ }
+ if (oldWy0 > oldWy1) {
+ t = oldWy0; oldWy0 = oldWy1; oldWy1 = t;
+ }
+ moveLeft = wx0 != oldWx0;
+ moveTop = wy0 != oldWy0;
+ moveRight = wx1 != oldWx1;
+ moveBottom = wy1 != oldWy1;
+ } else {
+ oldWx0 = wx0;
+ oldWy0 = wy0;
+ oldWx1 = wx1;
+ oldWy1 = wy1;
+ moveLeft = moveTop = moveRight = moveBottom = gTrue;
+ }
+
+ // set the new selection
+ state->setSelection(page, selectX0, selectY0, selectX1, selectY1);
+
+ // scroll if necessary
+ needScroll = gFalse;
+ sx = state->getScrollX();
+ sy = state->getScrollY();
+ if (moveLeft && wx0 < 0) {
+ sx += wx0;
+ needScroll = gTrue;
+ } else if (moveRight && wx1 >= state->getWinW()) {
+ sx += wx1 - state->getWinW();
+ needScroll = gTrue;
+ } else if (moveLeft && wx0 >= state->getWinW()) {
+ sx += wx0 - state->getWinW();
+ needScroll = gTrue;
+ } else if (moveRight && wx1 < 0) {
+ sx += wx1;
+ needScroll = gTrue;
+ }
+ if (moveTop && wy0 < 0) {
+ sy += wy0;
+ needScroll = gTrue;
+ } else if (moveBottom && wy1 >= state->getWinH()) {
+ sy += wy1 - state->getWinH();
+ needScroll = gTrue;
+ } else if (moveTop && wy0 >= state->getWinH()) {
+ sy += wy0 - state->getWinH();
+ needScroll = gTrue;
+ } else if (moveBottom && wy1 < 0) {
+ sy += wy1;
+ needScroll = gTrue;
+ }
+ if (needScroll) {
+ scrollTo(sx, sy);
+ } else {
+ ix0 = (wx0 < oldWx0) ? wx0 : oldWx0;
+ iy0 = (wy0 < oldWy0) ? wy0 : oldWy0;
+ ix1 = (wx1 > oldWx1) ? wx1 : oldWx1;
+ iy1 = (wy1 > oldWy1) ? wy1 : oldWy1;
+ checkInvalidate(ix0, iy0, ix1 - ix0, iy1 - iy0);
+ }
+}
+
+void PDFCore::setLinearSelection(int page, TextPosition *pos0,
+ TextPosition *pos1) {
+ TextPosition begin, end;
+ GList *rects;
+ GBool moveLeft, moveTop, moveRight, moveBottom, needScroll;
+ double x0, y0, x1, y1, x2, y2, x3, y3;
+ double ux0, uy0, ux1, uy1;
+ int oldWx0, oldWy0, oldWx1, oldWy1, ix0, iy0, ix1, iy1;
+ int wx0, wy0, wx1, wy1;
+ int sx, sy, colIdx;
+
+
+ // if selection rectangle is empty, clear the selection
+ if (*pos0 == *pos1) {
+ clearSelection();
+ return;
+ }
+
+ // swap into correct order
+ if (*pos0 < *pos1) {
+ begin = *pos0;
+ end = *pos1;
+ } else {
+ begin = *pos1;
+ end = *pos0;
+ }
+
+ // build the list of rectangles
+ //~ this doesn't handle RtL, vertical, or rotated text
+ loadText(page);
+ rects = new GList();
+ if (begin.colIdx == end.colIdx &&
+ begin.parIdx == end.parIdx &&
+ begin.lineIdx == end.lineIdx) {
+ // same line
+ text->convertPosToPointUpper(&begin, &x0, &y0);
+ text->convertPosToPointLower(&end, &x1, &y1);
+ cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
+ cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
+ rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
+ } else if (begin.colIdx == end.colIdx) {
+ // same column
+ text->convertPosToPointUpper(&begin, &x0, &y0);
+ text->convertPosToPointRightEdge(&begin, &x1, &y1);
+ text->convertPosToPointLeftEdge(&end, &x2, &y2);
+ text->convertPosToPointLower(&end, &x3, &y3);
+ cvtDevToUser(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(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(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(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;
+ // check which edges moved
+ if (state->hasSelection()) {
+ getSelectionBBox(&oldWx0, &oldWy0, &oldWx1, &oldWy1);
+ getSelectRectListBBox(rects, &wx0, &wy0, &wx1, &wy1);
+ moveLeft = wx0 != oldWx0;
+ moveTop = wy0 != oldWy0;
+ moveRight = wx1 != oldWx1;
+ moveBottom = wy1 != oldWy1;
+ } else {
+ getSelectRectListBBox(rects, &wx0, &wy0, &wx1, &wy1);
+ oldWx0 = wx0;
+ oldWy0 = wy0;
+ oldWx1 = wx1;
+ oldWy1 = wy1;
+ moveLeft = moveTop = moveRight = moveBottom = gTrue;
+ }
+
+ // set the new selection
+ state->setSelection(rects);
+
+ // scroll if necessary
+ needScroll = gFalse;
+ sx = state->getScrollX();
+ sy = state->getScrollY();
+ if (moveLeft && wx0 < 0) {
+ sx += wx0;
+ needScroll = gTrue;
+ } else if (moveRight && wx1 >= state->getWinW()) {
+ sx += wx1 - state->getWinW();
+ needScroll = gTrue;
+ } else if (moveLeft && wx0 >= state->getWinW()) {
+ sx += wx0 - state->getWinW();
+ needScroll = gTrue;
+ } else if (moveRight && wx1 < 0) {
+ sx += wx1;
+ needScroll = gTrue;
+ }
+ if (moveTop && wy0 < 0) {
+ sy += wy0;
+ needScroll = gTrue;
+ } else if (moveBottom && wy1 >= state->getWinH()) {
+ sy += wy1 - state->getWinH();
+ needScroll = gTrue;
+ } else if (moveTop && wy0 >= state->getWinH()) {
+ sy += wy0 - state->getWinH();
+ needScroll = gTrue;
+ } else if (moveBottom && wy1 < 0) {
+ sy += wy1;
+ needScroll = gTrue;
+ }
+ if (needScroll) {
+ scrollTo(sx, sy);
+ } else {
+ ix0 = (wx0 < oldWx0) ? wx0 : oldWx0;
+ iy0 = (wy0 < oldWy0) ? wy0 : oldWy0;
+ ix1 = (wx1 > oldWx1) ? wx1 : oldWx1;
+ iy1 = (wy1 > oldWy1) ? wy1 : oldWy1;
+ checkInvalidate(ix0, iy0, ix1 - ix0, iy1 - iy0);
+ }
+}
+
+void PDFCore::clearSelection() {
+ int wx0, wy0, wx1, wy1;
+
+ if (state->hasSelection()) {
+ getSelectionBBox(&wx0, &wy0, &wx1, &wy1);
+ state->clearSelection();
+ checkInvalidate(wx0, wy0, wx1 - wx0, wy1 - wy0);
+ }
+}
+
+void PDFCore::startSelectionDrag(int pg, int x, int y) {
+ clearSelection();
+ if (selectMode == selectModeBlock) {
+ selectPage = pg;
+ selectStartX = x;
+ selectStartY = y;
+ } else { // selectModeLinear
+ loadText(pg);
+ if (text->findPointInside(x, y, &selectStartPos)) {
+ selectPage = pg;
+ } else {
+ selectPage = 0;
+ }
+ }
+}
+
+void PDFCore::moveSelectionDrag(int pg, int x, int y) {
+ TextPosition pos;
+
+ // don't allow selections to span multiple pages
+ // -- this also handles the case where a linear selection was started
+ // outside any text column, in which case selectPage = 0
+ if (pg != selectPage) {
+ return;
+ }
+
+ if (selectMode == selectModeBlock) {
+ setSelection(pg, selectStartX, selectStartY, x, y);
+ } else { // selectModeLinear
+ loadText(pg);
+ if (text->findPointNear(x, y, &pos)) {
+ setLinearSelection(pg, &selectStartPos, &pos);
+ }
+ }
+}
+
+void PDFCore::finishSelectionDrag() {
+ // 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;
+ double xMin, yMin, xMax, yMax;
+ int page, i;
+
+ if (!state->hasSelection()) {
+ return gFalse;
+ }
+ page = state->getSelectRect(0)->page;
+ xMin = yMin = xMax = yMax = 0;
+ for (i = 0; i < state->getNumSelectRects(); ++i) {
+ rect = state->getSelectRect(i);
+ if (rect->page != page) {
+ continue;
+ }
+ if (i == 0) {
+ xMin = xMax = rect->x0;
+ yMin = yMax = rect->y0;
+ } else {
+ if (rect->x0 < xMin) {
+ xMin = rect->x0;
+ } else if (rect->x0 > xMax) {
+ xMax = rect->x0;
+ }
+ if (rect->y0 < yMin) {
+ yMin = rect->y0;
+ } else if (rect->y0 > yMax) {
+ yMax = rect->y0;
+ }
+ }
+ if (rect->x1 < xMin) {
+ xMin = rect->x1;
+ } else if (rect->x1 > xMax) {
+ xMax = rect->x1;
+ }
+ if (rect->y1 < yMin) {
+ yMin = rect->y1;
+ } else if (rect->y1 > yMax) {
+ yMax = rect->y1;
+ }
+ }
+ *pg = page;
+ *ulx = xMin;
+ *uly = yMax;
+ *lrx = xMax;
+ *lry = yMin;
+ return gTrue;
+}
+
+GBool PDFCore::hasSelection() {
+ return state->hasSelection();
+}
+
+void PDFCore::setTextExtractionMode(TextOutputMode mode) {
+ if (textOutCtrl.mode != mode) {
+ textOutCtrl.mode = mode;
+ if (text) {
+ delete text;
+ text = NULL;
+ }
+ textPage = 0;
+ textDPI = 0;
+ textRotate = 0;
+ }
+}
+
+GBool PDFCore::getDiscardDiagonalText() {
+ return textOutCtrl.discardDiagonalText;
+}
+
+void PDFCore::setDiscardDiagonalText(GBool discard) {
+ if (textOutCtrl.discardDiagonalText != discard) {
+ textOutCtrl.discardDiagonalText = discard;
+ if (text) {
+ delete text;
+ text = NULL;
+ }
+ textPage = 0;
+ textDPI = 0;
+ textRotate = 0;
+ }
+}
+
+GString *PDFCore::extractText(int pg, double xMin, double yMin,
+ double xMax, double yMax) {
+ int x0, y0, x1, y1, t;
+
+ loadText(pg);
+ cvtUserToDev(pg, xMin, yMin, &x0, &y0);
+ cvtUserToDev(pg, xMax, yMax, &x1, &y1);
+ if (x0 > x1) {
+ t = x0; x0 = x1; x1 = t;
+ }
+ if (y0 > y1) {
+ t = y0; y0 = y1; y1 = t;
+ }
+ return text->getText(x0, y0, x1, y1);
+}
+
+GString *PDFCore::getSelectedText() {
+ SelectRect *rect;
+ GString *ret, *s;
+ int x0, y0, x1, y1, t, i;
+
+ if (!state->hasSelection()) {
+ return NULL;
+ }
+ ret = new GString();
+ for (i = 0; i < state->getNumSelectRects(); ++i) {
+ rect = state->getSelectRect(i);
+ loadText(rect->page);
+ cvtUserToDev(rect->page, rect->x0, rect->y0, &x0, &y0);
+ cvtUserToDev(rect->page, rect->x1, rect->y1, &x1, &y1);
+ if (x0 > x1) {
+ t = x0; x0 = x1; x1 = t;
+ }
+ if (y0 > y1) {
+ t = y0; y0 = y1; y1 = t;
+ }
+ s = text->getText(x0, y0, x1, y1, state->getNumSelectRects() > 1);
+ ret->append(s);
+ delete s;
+ }
+ return ret;
+}
+
+GBool PDFCore::find(char *s, GBool caseSensitive, GBool next, GBool backward,
+ GBool wholeWord, GBool onePageOnly) {
+ Unicode *u;
+ int len, i;
+ GBool ret;
+
+ // convert to Unicode
+ len = (int)strlen(s);
+ u = (Unicode *)gmallocn(len, sizeof(Unicode));
+ for (i = 0; i < len; ++i) {
+ u[i] = (Unicode)(s[i] & 0xff);
+ }
+
+ ret = findU(u, len, caseSensitive, next, backward, wholeWord, onePageOnly);
+
+ gfree(u);
+ return ret;
+}
+
+GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
+ GBool next, GBool backward, GBool wholeWord,
+ GBool onePageOnly) {
+ TextOutputDev *textOut;
+ SelectRect *rect;
+ double xMin, yMin, xMax, yMax;
+ int topPage, pg, x, y, x2, y2;
+ GBool startAtTop, startAtLast, stopAtLast;
+
+ // check for zero-length string
+ if (len == 0) {
+ return gFalse;
+ }
+
+ setBusyCursor(gTrue);
+
+ // search current page starting at previous result, current
+ // selection, or top/bottom of page
+ startAtTop = startAtLast = gFalse;
+ rect = NULL;
+ xMin = yMin = xMax = yMax = 0;
+ topPage = tileMap->getFirstPage();
+ pg = topPage;
+ if (next) {
+ if (textPage >= 1 && textPage <= doc->getNumPages()) {
+ startAtLast = gTrue;
+ pg = textPage;
+ }
+ } else if (state->hasSelection()) {
+ rect = state->getSelectRect(0);
+ pg = rect->page;
+ cvtUserToDev(pg, rect->x0, rect->y0, &x, &y);
+ cvtUserToDev(pg, rect->x1, rect->y1, &x2, &y2);
+ if (x2 < x) {
+ x = x2;
+ }
+ if (y2 < y) {
+ y = y2;
+ }
+ if (backward) {
+ xMin = x - 1;
+ yMin = y - 1;
+ } else {
+ xMin = x + 1;
+ yMin = y + 1;
+ }
+ } else {
+ startAtTop = gTrue;
+ }
+ loadText(pg);
+ if (text->findText(u, len, startAtTop, gTrue, startAtLast, gFalse,
+ caseSensitive, backward, wholeWord,
+ &xMin, &yMin, &xMax, &yMax)) {
+ goto found;
+ }
+
+ if (!onePageOnly) {
+
+ // search following/previous pages
+ textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
+ if (!textOut->isOk()) {
+ delete textOut;
+ goto notFound;
+ }
+ for (pg = backward ? pg - 1 : pg + 1;
+ backward ? pg >= 1 : pg <= doc->getNumPages();
+ pg += backward ? -1 : 1) {
+ doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
+ if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
+ caseSensitive, backward, wholeWord,
+ &xMin, &yMin, &xMax, &yMax)) {
+ delete textOut;
+ goto foundPage;
+ }
+ }
+
+ // search previous/following pages
+ for (pg = backward ? doc->getNumPages() : 1;
+ backward ? pg > topPage : pg < topPage;
+ pg += backward ? -1 : 1) {
+ doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
+ if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
+ caseSensitive, backward, wholeWord,
+ &xMin, &yMin, &xMax, &yMax)) {
+ delete textOut;
+ goto foundPage;
+ }
+ }
+ delete textOut;
+
+ }
+
+ // search current page ending at previous result, current selection,
+ // or bottom/top of page
+ if (!startAtTop) {
+ xMin = yMin = xMax = yMax = 0;
+ if (next) {
+ stopAtLast = gTrue;
+ } else {
+ stopAtLast = gFalse;
+ cvtUserToDev(pg, rect->x1, rect->y1, &x, &y);
+ xMax = x;
+ yMax = y;
+ }
+ if (text->findText(u, len, gTrue, gFalse, gFalse, stopAtLast,
+ caseSensitive, backward, wholeWord,
+ &xMin, &yMin, &xMax, &yMax)) {
+ goto found;
+ }
+ }
+
+ // not found
+ notFound:
+ setBusyCursor(gFalse);
+ return gFalse;
+
+ // found on a different page
+ foundPage:
+ displayPage(pg, gTrue, gFalse);
+ loadText(pg);
+ if (!text->findText(u, len, gTrue, gTrue, gFalse, gFalse,
+ caseSensitive, backward, wholeWord,
+ &xMin, &yMin, &xMax, &yMax)) {
+ // this can happen if coalescing is bad
+ goto notFound;
+ }
+
+ // found: change the selection
+ found:
+ setSelection(pg, (int)floor(xMin), (int)floor(yMin),
+ (int)ceil(xMax), (int)ceil(yMax));
+
+ setBusyCursor(gFalse);
+ 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);
+}
+
+GBool PDFCore::cvtWindowToDev(int xw, int yw, int *pg, int *xd, int *yd) {
+ return tileMap->cvtWindowToDev(xw, yw, pg, xd, yd);
+}
+
+GBool PDFCore::cvtUserToWindow(int pg, double xu, double yu, int *xw, int *yw) {
+ return tileMap->cvtUserToWindow(pg, xu, yu, xw, yw);
+}
+
+void PDFCore::cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd) {
+ tileMap->cvtUserToDev(pg, xu, yu, xd, yd);
+}
+
+GBool PDFCore::cvtDevToWindow(int pg, int xd, int yd, int *xw, int *yw) {
+ return tileMap->cvtDevToWindow(pg, xd, yd, xw, yw);
+}
+
+void PDFCore::cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu) {
+ tileMap->cvtDevToUser(pg, xd, yd, xu, yu);
+}
+
+void PDFCore::getWindowPageRange(int x, int y, int w, int h,
+ int *firstPage, int *lastPage) {
+ tileMap->getWindowPageRange(x, y, w, h, firstPage, lastPage);
+}
+
+int PDFCore::getPageNum() {
+ if (!doc || !doc->getNumPages()) {
+ return 0;
+ }
+ return tileMap->getFirstPage();
+}
+
+int PDFCore::getMidPageNum() {
+ if (!doc || !doc->getNumPages()) {
+ return 0;
+ }
+ return tileMap->getMidPage();
+}
+
+double PDFCore::getZoom() {
+ return state->getZoom();
+}
+
+double PDFCore::getZoomDPI(int page) {
+ if (!doc) {
+ return 0;
+ }
+ return tileMap->getDPI(page);
+}
+
+int PDFCore::getRotate() {
+ return state->getRotate();
+}
+
+DisplayMode PDFCore::getDisplayMode() {
+ return state->getDisplayMode();
+}
+
+int PDFCore::getScrollX() {
+ return state->getScrollX();
+}
+
+int PDFCore::getScrollY() {
+ return state->getScrollY();
+}
+
+int PDFCore::getWindowWidth() {
+ return state->getWinW();
+}
+
+int PDFCore::getWindowHeight() {
+ return state->getWinH();
+}
+
+void PDFCore::setPaperColor(SplashColorPtr paperColor) {
+ state->setPaperColor(paperColor);
+ invalidateWholeWindow();
+}
+
+void PDFCore::setMatteColor(SplashColorPtr matteColor) {
+ state->setMatteColor(matteColor);
+ invalidateWholeWindow();
+}
+
+void PDFCore::setReverseVideo(GBool reverseVideo) {
+ SplashColorPtr oldPaperColor;
+ SplashColor newPaperColor;
+ int i;
+
+ if (reverseVideo != state->getReverseVideo()) {
+ state->setReverseVideo(reverseVideo);
+ oldPaperColor = state->getPaperColor();
+ for (i = 0; i < splashColorModeNComps[state->getColorMode()]; ++i) {
+ newPaperColor[i] = oldPaperColor[i] ^ 0xff;
+ }
+ state->setPaperColor(newPaperColor);
+ invalidateWholeWindow();
+ }
+}
+
+
+
+LinkAction *PDFCore::findLink(int pg, double x, double y) {
+ loadLinks(pg);
+ return links->find(x, y);
+}
+
+Annot *PDFCore::findAnnot(int pg, double x, double y) {
+ loadAnnots(pg);
+ return annots->find(x, y);
+}
+
+int PDFCore::findAnnotIdx(int pg, double x, double y) {
+ loadAnnots(pg);
+ return annots->findIdx(x, y);
+}
+
+Annot *PDFCore::getAnnot(int idx) {
+ if (!annots) {
+ return NULL;
+ }
+ if (idx < 0 || idx >= annots->getNumAnnots()) {
+ return NULL;
+ }
+ return annots->getAnnot(idx);
+}
+
+AcroFormField *PDFCore::findFormField(int pg, double x, double y) {
+ if (!doc->getCatalog()->getForm()) {
+ return NULL;
+ }
+ return doc->getCatalog()->getForm()->findField(pg, x, y);
+}
+
+int PDFCore::findFormFieldIdx(int pg, double x, double y) {
+ if (!doc->getCatalog()->getForm()) {
+ return -1;
+ }
+ return doc->getCatalog()->getForm()->findFieldIdx(pg, x, y);
+}
+
+AcroFormField *PDFCore::getFormField(int idx) {
+ if (!doc->getCatalog()->getForm()) {
+ return NULL;
+ }
+ if (idx < 0 || idx >= doc->getCatalog()->getForm()->getNumFields()) {
+ return NULL;
+ }
+ return doc->getCatalog()->getForm()->getField(idx);
+}
+
+GBool PDFCore::overText(int pg, double x, double y) {
+ loadText(pg);
+ return text->checkPointInside(x, y);
+}
+
+void PDFCore::forceRedraw() {
+ startUpdate();
+ state->forceRedraw();
+ finishUpdate(gFalse, gFalse);
+}
+
+void PDFCore::setTileDoneCbk(void (*cbk)(void *data), void *data) {
+ tileCache->setTileDoneCbk(cbk, data);
+}
+
+void PDFCore::setWindowSize(int winWidth, int winHeight) {
+ GBool doScroll;
+ int page, wx0, wy0, wx, wy, sx, sy;
+ double ux, uy;
+
+ startUpdate();
+
+ wx0 = wy0 = 0; // make gcc happy
+ doScroll = gFalse;
+ if (state->getZoom() < 0 && state->displayModeIsContinuous()) {
+ // save the user coordinates of the appropriate edge of the window
+ if (state->getDisplayMode() == displayHorizontalContinuous) {
+ wx0 = 0;
+ wy0 = state->getWinH() / 2;
+ } else {
+ wx0 = state->getWinW() / 2;
+ wy0 = 0;
+ }
+ if (!(doScroll = cvtWindowToUser(wx0, wy0, &page, &ux, &uy))) {
+ // tweak the save position if it happens to fall in a gutter
+ if (state->getDisplayMode() == displayContinuous) {
+ wy0 += tileMap->getContinuousPageSpacing();
+ } else if (state->getDisplayMode() == displaySideBySideContinuous) {
+ wx0 += tileMap->getSideBySidePageSpacing();
+ wy0 += tileMap->getContinuousPageSpacing();
+ } else { // state->getDisplayMode() == displayHorizontalContinuous
+ wx0 += tileMap->getHorizContinuousPageSpacing();
+ }
+ doScroll = cvtWindowToUser(wx0, wy0, &page, &ux, &uy);
+ }
+ }
+
+ state->setWindowSize(winWidth, winHeight);
+
+ if (doScroll) {
+ // restore the saved scroll position
+ cvtUserToWindow(page, ux, uy, &wx, &wy);
+ sx = state->getScrollX();
+ sy = state->getScrollY();
+ if (state->getDisplayMode() == displayHorizontalContinuous) {
+ sx += wx - wx0;
+ } else {
+ sy += wy - wy0;
+ }
+ state->setScrollPosition(page, sx, sy);
+ }
+
+ finishUpdate(gTrue, gFalse);
+}
+
+SplashBitmap *PDFCore::getWindowBitmap(GBool wholeWindow) {
+ GBool dummy;
+
+ return tileCompositor->getBitmap(wholeWindow ? &bitmapFinished : &dummy);
+}
+
+void PDFCore::tick() {
+ if (!bitmapFinished) {
+ invalidateWholeWindow();
+ }
+}
+
+// Clear cached info (links, text) that's tied to a PDFDoc.
+void PDFCore::clearPage() {
+ if (links) {
+ delete links;
+ }
+ links = NULL;
+ linksPage = 0;
+
+ if (annots) {
+ delete annots;
+ }
+ annots = NULL;
+ annotsPage = 0;
+
+ if (text) {
+ delete text;
+ }
+ text = NULL;
+ textPage = 0;
+ textDPI = 0;
+ textRotate = 0;
+}
+
+// Load the links for <pg>.
+void PDFCore::loadLinks(int pg) {
+ if (links && linksPage == pg) {
+ return;
+ }
+ if (links) {
+ delete links;
+ }
+ links = doc->getLinks(pg);
+ linksPage = pg;
+}
+
+// Load the annotations for <pg>.
+void PDFCore::loadAnnots(int pg) {
+ Object annotsObj;
+
+ if (annots && annotsPage == pg) {
+ return;
+ }
+ if (annots) {
+ delete annots;
+ }
+ doc->getCatalog()->getPage(pg)->getAnnots(&annotsObj);
+ annots = new Annots(doc, &annotsObj);
+ annotsObj.free();
+ annotsPage = pg;
+}
+
+// Extract text from <pg>.
+void PDFCore::loadText(int pg) {
+ TextOutputDev *textOut;
+ double dpi;
+ int rotate;
+
+ dpi = tileMap->getDPI(pg);
+ rotate = state->getRotate();
+ if (text && textPage == pg && textDPI == dpi && textRotate == rotate) {
+ return;
+ }
+ if (text) {
+ delete text;
+ }
+ textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
+ if (!textOut->isOk()) {
+ text = new TextPage(&textOutCtrl);
+ } else {
+ doc->displayPage(textOut, pg, dpi, dpi, rotate, gFalse, gTrue, gFalse);
+ text = textOut->takeText();
+ }
+ delete textOut;
+ textPage = pg;
+ textDPI = dpi;
+ textRotate = rotate;
+}
+
+void PDFCore::getSelectionBBox(int *wxMin, int *wyMin, int *wxMax, int *wyMax) {
+ *wxMin = *wyMin = *wxMax = *wyMax = 0;
+ if (!state->hasSelection()) {
+ return;
+ }
+ getSelectRectListBBox(state->getSelectRects(), wxMin, wyMin, wxMax, wyMax);
+}
+
+void PDFCore::getSelectRectListBBox(GList *rects, int *wxMin, int *wyMin,
+ int *wxMax, int *wyMax) {
+ SelectRect *rect;
+ int x, y, i;
+
+ *wxMin = *wyMin = *wxMax = *wyMax = 0;
+ for (i = 0; i < rects->getLength(); ++i) {
+ rect = (SelectRect *)rects->get(i);
+ tileMap->cvtUserToWindow(rect->page, rect->x0, rect->y0, &x, &y);
+ if (i == 0) {
+ *wxMin = *wxMax = x;
+ *wyMin = *wyMax = y;
+ } else {
+ if (x < *wxMin) {
+ *wxMin = x;
+ } else if (x > *wxMax) {
+ *wxMax = x;
+ }
+ if (y < *wyMin) {
+ *wyMin = y;
+ } else if (y > *wyMax) {
+ *wyMax = y;
+ }
+ }
+ tileMap->cvtUserToWindow(rect->page, rect->x1, rect->y1, &x, &y);
+ if (x < *wxMin) {
+ *wxMin = x;
+ } else if (x > *wxMax) {
+ *wxMax = x;
+ }
+ if (y < *wyMin) {
+ *wyMin = y;
+ } else if (y > *wyMax) {
+ *wyMax = y;
+ }
+ }
+}
+
+void PDFCore::checkInvalidate(int x, int y, int w, int h) {
+ if (x < 0) {
+ w += x;
+ x = 0;
+ }
+ if (x + w > state->getWinW()) {
+ w = state->getWinW() - x;
+ }
+ if (w <= 0) {
+ return;
+ }
+ if (y < 0) {
+ h += y;
+ y = 0;
+ }
+ if (y + h > state->getWinH()) {
+ h = state->getWinH() - y;
+ }
+ if (h <= 0) {
+ return;
+ }
+ invalidate(x, y, w, h);
+}
+
+void PDFCore::invalidateWholeWindow() {
+ invalidate(0, 0, state->getWinW(), state->getWinH());
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h
new file mode 100644
index 00000000000..accf19d82ef
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFCore.h
@@ -0,0 +1,357 @@
+//========================================================================
+//
+// PDFCore.h
+//
+// Copyright 2004-2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PDFCORE_H
+#define PDFCORE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdlib.h>
+#include "SplashTypes.h"
+#include "CharTypes.h"
+#include "DisplayState.h"
+#include "TextOutputDev.h"
+
+class GString;
+class GList;
+class SplashBitmap;
+class SplashPattern;
+class BaseStream;
+class PDFDoc;
+class Links;
+class LinkDest;
+class LinkAction;
+class Annot;
+class Annots;
+class AcroFormField;
+class TextPage;
+class HighlightFile;
+class OptionalContentGroup;
+class TileMap;
+class TileCache;
+class TileCompositor;
+class PDFCore;
+
+//------------------------------------------------------------------------
+// PDFHistory
+//------------------------------------------------------------------------
+
+struct PDFHistory {
+#ifdef _WIN32
+ wchar_t *fileName;
+#else
+ GString *fileName;
+#endif
+ int page;
+};
+
+#define pdfHistorySize 50
+
+//------------------------------------------------------------------------
+// SelectMode
+//------------------------------------------------------------------------
+
+enum SelectMode {
+ selectModeBlock,
+ selectModeLinear
+};
+
+//------------------------------------------------------------------------
+// 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
+//------------------------------------------------------------------------
+
+class PDFCore {
+public:
+
+ PDFCore(SplashColorMode colorMode, int bitmapRowPad,
+ GBool reverseVideo, SplashColorPtr paperColor);
+ virtual ~PDFCore();
+
+ //----- loadFile / displayPage / displayDest
+
+ // Load a new file. Returns pdfOk or error code.
+ virtual int loadFile(GString *fileName, GString *ownerPassword = NULL,
+ GString *userPassword = NULL);
+
+#ifdef _WIN32
+ // Load a new file. Returns pdfOk or error code.
+ virtual int loadFile(wchar_t *fileName, int fileNameLen,
+ GString *ownerPassword = NULL,
+ GString *userPassword = NULL);
+#endif
+
+ // Load a new file, via a Stream instead of a file name. Returns
+ // pdfOk or error code.
+ virtual int loadFile(BaseStream *stream, GString *ownerPassword = NULL,
+ GString *userPassword = NULL);
+
+ // Load an already-created PDFDoc object.
+ virtual void loadDoc(PDFDoc *docA);
+
+ // Reload the current file. This only works if the PDF was loaded
+ // via a file. Returns pdfOk or error code.
+ virtual int reload();
+
+ // Clear out the current document, if any.
+ virtual void clear();
+
+ // Same as clear(), but returns the PDFDoc object instead of
+ // deleting it.
+ virtual PDFDoc *takeDoc(GBool redraw);
+
+ // Display (or redisplay) the specified page. If <scrollToTop> is
+ // set, the window is vertically scrolled to the top; if
+ // <scrollToBottom> is set, the window is vertically scrolled to the
+ // bottom; otherwise, no scrolling is done. If <addToHist> is set,
+ // this page change is added to the history list.
+ virtual void displayPage(int page, GBool scrollToTop,
+ GBool scrollToBottom, GBool addToHist = gTrue);
+
+ // Display a link destination.
+ virtual void displayDest(LinkDest *dest);
+
+ // Called before any update is started.
+ virtual void startUpdate();
+
+ // Called after any update is complete. Subclasses can check for
+ // changes in the display parameters here.
+ virtual void finishUpdate(GBool addToHist, GBool checkForChangedFile);
+
+ //----- page/position changes
+
+ virtual GBool gotoNextPage(int inc, GBool top);
+ virtual GBool gotoPrevPage(int dec, GBool top, GBool bottom);
+ virtual GBool gotoNamedDestination(GString *dest);
+ virtual GBool goForward();
+ virtual GBool goBackward();
+ virtual void scrollLeft(int nCols = 16);
+ virtual void scrollRight(int nCols = 16);
+ virtual void scrollUp(int nLines = 16, GBool snapToPage = gFalse);
+ virtual void scrollUpPrevPage(int nLines = 16);
+ virtual void scrollDown(int nLines = 16, GBool snapToPage = gFalse);
+ virtual void scrollDownNextPage(int nLines = 16);
+ virtual void scrollPageUp();
+ virtual void scrollPageDown();
+ virtual void scrollTo(int x, int y, GBool snapToPage = gFalse);
+ virtual void scrollToLeftEdge();
+ virtual void scrollToRightEdge();
+ virtual void scrollToTopEdge();
+ virtual void scrollToBottomEdge();
+ virtual void scrollToTopLeft();
+ virtual void scrollToBottomRight();
+ // Scroll so that (page, x, y) is centered in the window.
+ virtual void scrollToCentered(int page, double x, double y);
+ virtual void setZoom(double zoom);
+ virtual void zoomToRect(int page, double ulx, double uly,
+ double lrx, double lry);
+ virtual void zoomCentered(double zoom);
+ virtual void zoomToCurrentWidth();
+ virtual void setRotate(int rotate);
+ virtual void setDisplayMode(DisplayMode mode);
+ virtual void setOCGState(OptionalContentGroup *ocg, GBool ocgState);
+
+ //----- selection
+
+ // Selection mode.
+ SelectMode getSelectMode() { return selectMode; }
+ void setSelectMode(SelectMode mode);
+
+ // Selection color.
+ SplashColorPtr getSelectionColor();
+ void setSelectionColor(SplashColor color);
+
+ // Modify the selection. These functions use device coordinates.
+ void setSelection(int page, int x0, int y0, int x1, int y1);
+ void setLinearSelection(int page, TextPosition *pos0, TextPosition *pos1);
+ void clearSelection();
+ 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.
+ GBool getSelection(int *pg, double *ulx, double *uly,
+ double *lrx, double *lry);
+ GBool hasSelection();
+
+ // Text extraction.
+ void setTextExtractionMode(TextOutputMode mode);
+ GBool getDiscardDiagonalText();
+ void setDiscardDiagonalText(GBool discard);
+ GString *extractText(int pg, double xMin, double yMin,
+ double xMax, double yMax);
+ GString *getSelectedText();
+
+ //----- find
+
+ virtual GBool find(char *s, GBool caseSensitive, GBool next, GBool backward,
+ GBool wholeWord, GBool onePageOnly);
+ 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
+
+ // user space: per-page, as defined by PDF file; unit = point
+ // device space: (0,0) is upper-left corner of a page; unit = pixel
+ // window space: (0,0) is upper-left corner of drawing area; unit = pixel
+
+ GBool cvtWindowToUser(int xw, int yw, int *pg, double *xu, double *yu);
+ GBool cvtWindowToDev(int xw, int yw, int *pg, int *xd, int *yd);
+ GBool cvtUserToWindow(int pg, double xy, double yu, int *xw, int *yw);
+ void cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd);
+ GBool cvtDevToWindow(int pg, int xd, int yd, int *xw, int *yw);
+ void cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu);
+ void getWindowPageRange(int x, int y, int w, int h,
+ int *firstPage, int *lastPage);
+
+ //----- password dialog
+
+ virtual GString *getPassword() { return NULL; }
+
+ //----- misc access
+
+ PDFDoc *getDoc() { return doc; }
+ int getPageNum();
+ int getMidPageNum();
+ double getZoom();
+ double getZoomDPI(int page);
+ int getRotate();
+ DisplayMode getDisplayMode();
+ virtual void setPaperColor(SplashColorPtr paperColor);
+ virtual void setMatteColor(SplashColorPtr matteColor);
+ virtual void setReverseVideo(GBool reverseVideo);
+ GBool canGoBack() { return historyBLen > 1; }
+ GBool canGoForward() { return historyFLen > 0; }
+ int getScrollX();
+ int getScrollY();
+ int getWindowWidth();
+ int getWindowHeight();
+ virtual void setBusyCursor(GBool busy) = 0;
+ LinkAction *findLink(int pg, double x, double y);
+ Annot *findAnnot(int pg, double x, double y);
+ int findAnnotIdx(int pg, double x, double y);
+ Annot *getAnnot(int idx);
+ AcroFormField *findFormField(int pg, double x, double y);
+ int findFormFieldIdx(int pg, double x, double y);
+ AcroFormField *getFormField(int idx);
+ GBool overText(int pg, double x, double y);
+ void forceRedraw();
+ void setTileDoneCbk(void (*cbk)(void *data), void *data);
+
+protected:
+
+ //--- calls from PDFCore subclass
+
+ // Set the window size (when the window is resized).
+ void setWindowSize(int winWidthA, int winHeightA);
+
+ // Get the current window bitmap. If <wholeWindow> is true, the
+ // full window is being redrawn -- this is used to end incremental
+ // updates when the rasterization is done.
+ SplashBitmap *getWindowBitmap(GBool wholeWindow);
+
+ // Returns true if the last call to getWindowBitmap() returned a
+ // finished bitmap; or false if the bitmap was still being
+ // rasterized.
+ GBool isBitmapFinished() { return bitmapFinished; }
+
+ // This should be called periodically (typically every ~0.1 seconds)
+ // to do incremental updates. If an update is required, it will
+ // trigger a call to invalidate().
+ virtual void tick();
+
+ //--- callbacks to PDFCore subclass
+
+ // Invalidate the specified rectangle (in window coordinates).
+ virtual void invalidate(int x, int y, int w, int h) = 0;
+
+ // Update the scrollbars.
+ virtual void updateScrollbars() = 0;
+
+ // This returns true if the PDF file has changed on disk (if it can
+ // be checked).
+ virtual GBool checkForNewFile() { return gFalse; }
+
+ // This is called just before a PDF file is loaded.
+ virtual void preLoad() {}
+
+ // This is called just after a PDF file is loaded.
+ virtual void postLoad() {}
+
+ //--- internal
+
+ int loadFile2(PDFDoc *newDoc);
+ void addToHistory();
+ void clearPage();
+ void loadLinks(int pg);
+ void loadAnnots(int pg);
+ void loadText(int pg);
+ void getSelectionBBox(int *wxMin, int *wyMin, int *wxMax, int *wyMax);
+ void getSelectRectListBBox(GList *rects, int *wxMin, int *wyMin,
+ int *wxMax, int *wyMax);
+ void checkInvalidate(int x, int y, int w, int h);
+ void invalidateWholeWindow();
+
+ PDFDoc *doc;
+
+ int linksPage; // cached links for one page
+ Links *links;
+
+ int annotsPage; // cached annotations for one page
+ Annots *annots;
+
+ int textPage; // cached extracted text for one page
+ double textDPI;
+ int textRotate;
+ TextOutputControl textOutCtrl;
+ TextPage *text;
+
+ DisplayState *state;
+ TileMap *tileMap;
+ TileCache *tileCache;
+ TileCompositor *tileCompositor;
+ GBool bitmapFinished;
+
+ SelectMode selectMode;
+ int selectPage; // page of current selection
+ int selectStartX, // for block mode: start point of current
+ selectStartY; // selection, in device coords
+ TextPosition selectStartPos; // for linear mode: start position of
+ // current selection
+
+ PDFHistory // page history queue
+ history[pdfHistorySize];
+ int historyCur; // currently displayed page
+ int historyBLen; // number of valid entries backward from
+ // current entry
+ int historyFLen; // number of valid entries forward from
+ // current entry
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc
new file mode 100644
index 00000000000..6efd4aac6c4
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.cc
@@ -0,0 +1,688 @@
+//========================================================================
+//
+// PDFDoc.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#ifdef _WIN32
+# include <windows.h>
+#endif
+#include "gmempp.h"
+#include "GString.h"
+#include "gfile.h"
+#include "config.h"
+#include "GlobalParams.h"
+#include "Page.h"
+#include "Catalog.h"
+#include "Stream.h"
+#include "XRef.h"
+#include "Link.h"
+#include "OutputDev.h"
+#include "Error.h"
+#include "ErrorCodes.h"
+#include "Lexer.h"
+#include "Parser.h"
+#include "SecurityHandler.h"
+#include "UTF8.h"
+#ifndef DISABLE_OUTLINE
+#include "Outline.h"
+#endif
+#include "OptionalContent.h"
+#include "PDFDoc.h"
+
+//------------------------------------------------------------------------
+
+#define headerSearchSize 1024 // read this many bytes at beginning of
+ // file to look for '%PDF'
+
+// Avoid sharing files with child processes on Windows, where sharing
+// can cause problems.
+#ifdef _WIN32
+# define fopenReadMode "rbN"
+# define wfopenReadMode L"rbN"
+#else
+# define fopenReadMode "rb"
+#endif
+
+//------------------------------------------------------------------------
+// PDFDoc
+//------------------------------------------------------------------------
+
+PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
+ GString *userPassword, PDFCore *coreA) {
+ Object obj;
+ GString *fileName1, *fileName2;
+#ifdef _WIN32
+ int n, i;
+#endif
+
+ init(coreA);
+
+ fileName = fileNameA;
+#ifdef _WIN32
+ n = fileName->getLength();
+ fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t));
+ for (i = 0; i < n; ++i) {
+ fileNameU[i] = (wchar_t)(fileName->getChar(i) & 0xff);
+ }
+ fileNameU[n] = L'\0';
+#endif
+
+ fileName1 = fileName;
+
+ // try to open file
+ fileName2 = NULL;
+#ifdef VMS
+ if (!(file = fopen(fileName1->getCString(), fopenReadMode, "ctx=stm"))) {
+ error(errIO, -1, "Couldn't open file '{0:t}'", fileName1);
+ errCode = errOpenFile;
+ return;
+ }
+#else
+ if (!(file = fopen(fileName1->getCString(), fopenReadMode))) {
+ fileName2 = fileName->copy();
+ fileName2->lowerCase();
+ if (!(file = fopen(fileName2->getCString(), fopenReadMode))) {
+ fileName2->upperCase();
+ if (!(file = fopen(fileName2->getCString(), fopenReadMode))) {
+ error(errIO, -1, "Couldn't open file '{0:t}'", fileName);
+ delete fileName2;
+ errCode = errOpenFile;
+ return;
+ }
+ }
+ delete fileName2;
+ }
+#endif
+
+ // create stream
+ obj.initNull();
+ str = new FileStream(file, 0, gFalse, 0, &obj);
+
+ ok = setup(ownerPassword, userPassword);
+}
+
+#if 0
+#ifdef _WIN32
+PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
+ GString *userPassword, PDFCore *coreA) {
+ OSVERSIONINFO version;
+ Object obj;
+ int i;
+
+ 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(wPathLen + 1, sizeof(wchar_t));
+ memcpy(fileNameU, wPath, (wPathLen + 1) * sizeof(wchar_t));
+ for (i = 0; i < wPathLen; ++i) {
+ fileName->append((char)fileNameA[i]);
+ }
+
+ // try to open file
+ // NB: _wfopen is only available in NT
+ version.dwOSVersionInfoSize = sizeof(version);
+ GetVersionEx(&version);
+ if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ file = _wfopen(fileNameU, wfopenReadMode);
+ } else {
+ file = fopen(fileName->getCString(), fopenReadMode);
+ }
+ if (!file) {
+ error(errIO, -1, "Couldn't open file '{0:t}'", fileName);
+ errCode = errOpenFile;
+ return;
+ }
+
+ // create stream
+ obj.initNull();
+ str = new FileStream(file, 0, gFalse, 0, &obj);
+
+ ok = setup(ownerPassword, userPassword);
+}
+#endif
+#endif /* 0 */
+
+PDFDoc::PDFDoc(char *fileNameA, GString *ownerPassword,
+ GString *userPassword, PDFCore *coreA) {
+/*
+#ifdef _WIN32
+ OSVERSIONINFO version;
+#endif
+*/
+ Object obj;
+/*
+#ifdef _WIN32
+ Unicode u;
+ int i, j;
+#endif
+*/
+
+ init(coreA);
+
+ fileName = new GString(fileNameA);
+
+#if defined(_WIN32)
+#if 0
+ wchar_t wPath[MAX_PATH + 1];
+ i = 0;
+ j = 0;
+ while (j < MAX_PATH && getUTF8(fileName, &i, &u)) {
+ wPath[j++] = (wchar_t)u;
+ }
+ 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);
+ if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ file = _wfopen(fileNameU, wfopenReadMode);
+ } else {
+#endif /* 0 */
+ file = fopen(fileName->getCString(), fopenReadMode);
+#if 0
+ }
+#endif /* 0 */
+#elif defined(VMS)
+ file = fopen(fileName->getCString(), fopenReadMode, "ctx=stm");
+#else
+ file = fopen(fileName->getCString(), fopenReadMode);
+#endif
+
+ if (!file) {
+ error(errIO, -1, "Couldn't open file '{0:t}'", fileName);
+ errCode = errOpenFile;
+ return;
+ }
+
+ // create stream
+ obj.initNull();
+ str = new FileStream(file, 0, gFalse, 0, &obj);
+
+ ok = setup(ownerPassword, userPassword);
+}
+
+PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
+ GString *userPassword, PDFCore *coreA) {
+#ifdef _WIN32
+ int n, i;
+#endif
+
+ init(coreA);
+
+ if (strA->getFileName()) {
+ fileName = strA->getFileName()->copy();
+#ifdef _WIN32
+ n = fileName->getLength();
+ fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t));
+ for (i = 0; i < n; ++i) {
+ fileNameU[i] = (wchar_t)(fileName->getChar(i) & 0xff);
+ }
+ fileNameU[n] = L'\0';
+#endif
+ } else {
+ fileName = NULL;
+#ifdef _WIN32
+ fileNameU = NULL;
+#endif
+ }
+ str = strA;
+ ok = setup(ownerPassword, userPassword);
+}
+
+void PDFDoc::init(PDFCore *coreA) {
+ ok = gFalse;
+ errCode = errNone;
+ core = coreA;
+ file = NULL;
+ str = NULL;
+ xref = NULL;
+ catalog = NULL;
+#ifndef DISABLE_OUTLINE
+ outline = NULL;
+#endif
+ optContent = NULL;
+}
+
+GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
+
+ str->reset();
+
+ // check header
+ checkHeader();
+
+ // read the xref and catalog
+ if (!PDFDoc::setup2(ownerPassword, userPassword, gFalse)) {
+ if (errCode == errDamaged || errCode == errBadCatalog) {
+ // try repairing the xref table
+ error(errSyntaxWarning, -1,
+ "PDF file is damaged - attempting to reconstruct xref table...");
+ if (!PDFDoc::setup2(ownerPassword, userPassword, gTrue)) {
+ return gFalse;
+ }
+ } else {
+ return gFalse;
+ }
+ }
+
+#ifndef DISABLE_OUTLINE
+ // read outline
+ outline = new Outline(catalog->getOutline(), xref);
+#endif
+
+ // read the optional content info
+ optContent = new OptionalContent(this);
+
+
+ // done
+ return gTrue;
+}
+
+GBool PDFDoc::setup2(GString *ownerPassword, GString *userPassword,
+ GBool repairXRef) {
+ // read xref table
+ xref = new XRef(str, repairXRef);
+ if (!xref->isOk()) {
+ error(errSyntaxError, -1, "Couldn't read xref table");
+ errCode = xref->getErrorCode();
+ delete xref;
+ xref = NULL;
+ return gFalse;
+ }
+
+ // check for encryption
+ if (!checkEncryption(ownerPassword, userPassword)) {
+ errCode = errEncrypted;
+ delete xref;
+ xref = NULL;
+ return gFalse;
+ }
+
+ // read catalog
+ catalog = new Catalog(this);
+ if (!catalog->isOk()) {
+ error(errSyntaxError, -1, "Couldn't read page catalog");
+ errCode = errBadCatalog;
+ delete catalog;
+ catalog = NULL;
+ delete xref;
+ xref = NULL;
+ return gFalse;
+ }
+
+ return gTrue;
+}
+
+PDFDoc::~PDFDoc() {
+ if (optContent) {
+ delete optContent;
+ }
+#ifndef DISABLE_OUTLINE
+ if (outline) {
+ delete outline;
+ }
+#endif
+ if (catalog) {
+ delete catalog;
+ }
+ if (xref) {
+ delete xref;
+ }
+ if (str) {
+ delete str;
+ }
+ if (file) {
+ fclose(file);
+ }
+ if (fileName) {
+ delete fileName;
+ }
+#ifdef _WIN32
+ if (fileNameU) {
+ gfree(fileNameU);
+ }
+#endif
+}
+
+// Check for a PDF header on this stream. Skip past some garbage
+// if necessary.
+void PDFDoc::checkHeader() {
+ char hdrBuf[headerSearchSize+1];
+ char *p;
+ int i;
+
+ pdfVersion = 0;
+ memset(hdrBuf, 0, headerSearchSize + 1);
+ str->getBlock(hdrBuf, headerSearchSize);
+ for (i = 0; i < headerSearchSize - 5; ++i) {
+ if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
+ break;
+ }
+ }
+ if (i >= headerSearchSize - 5) {
+ error(errSyntaxWarning, -1, "May not be a PDF file (continuing anyway)");
+ return;
+ }
+ str->moveStart(i);
+ if (!(p = strtok(&hdrBuf[i+5], " \t\n\r"))) {
+ error(errSyntaxWarning, -1, "May not be a PDF file (continuing anyway)");
+ return;
+ }
+ pdfVersion = atof(p);
+ if (!(hdrBuf[i+5] >= '0' && hdrBuf[i+5] <= '9') ||
+ pdfVersion > supportedPDFVersionNum + 0.0001) {
+ error(errSyntaxWarning, -1,
+ "PDF version {0:s} -- xpdf supports version {1:s} (continuing anyway)",
+ p, supportedPDFVersionStr);
+ }
+}
+
+GBool PDFDoc::checkEncryption(GString *ownerPassword, GString *userPassword) {
+ Object encrypt;
+ GBool encrypted;
+ SecurityHandler *secHdlr;
+ GBool ret;
+
+ xref->getTrailerDict()->dictLookup("Encrypt", &encrypt);
+ if ((encrypted = encrypt.isDict())) {
+ if ((secHdlr = SecurityHandler::make(this, &encrypt))) {
+ if (secHdlr->isUnencrypted()) {
+ // no encryption
+ ret = gTrue;
+ } else if (secHdlr->checkEncryption(ownerPassword, userPassword)) {
+ // authorization succeeded
+ xref->setEncryption(secHdlr->getPermissionFlags(),
+ secHdlr->getOwnerPasswordOk(),
+ secHdlr->getFileKey(),
+ secHdlr->getFileKeyLength(),
+ secHdlr->getEncVersion(),
+ secHdlr->getEncAlgorithm());
+ ret = gTrue;
+ } else {
+ // authorization failed
+ ret = gFalse;
+ }
+ delete secHdlr;
+ } else {
+ // couldn't find the matching security handler
+ ret = gFalse;
+ }
+ } else {
+ // document is not encrypted
+ ret = gTrue;
+ }
+ encrypt.free();
+ return ret;
+}
+
+void PDFDoc::displayPage(OutputDev *out, int page,
+ double hDPI, double vDPI, int rotate,
+ GBool useMediaBox, GBool crop, GBool printing,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+ if (globalParams->getPrintCommands()) {
+ printf("***** page %d *****\n", page);
+ }
+ catalog->getPage(page)->display(out, hDPI, vDPI,
+ rotate, useMediaBox, crop, printing,
+ abortCheckCbk, abortCheckCbkData);
+}
+
+void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
+ double hDPI, double vDPI, int rotate,
+ GBool useMediaBox, GBool crop, GBool printing,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+ int page;
+
+ for (page = firstPage; page <= lastPage; ++page) {
+ displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, printing,
+ abortCheckCbk, abortCheckCbkData);
+ catalog->doneWithPage(page);
+ }
+}
+
+void PDFDoc::displayPageSlice(OutputDev *out, int page,
+ double hDPI, double vDPI, int rotate,
+ GBool useMediaBox, GBool crop, GBool printing,
+ int sliceX, int sliceY, int sliceW, int sliceH,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+ catalog->getPage(page)->displaySlice(out, hDPI, vDPI,
+ rotate, useMediaBox, crop,
+ sliceX, sliceY, sliceW, sliceH,
+ printing,
+ abortCheckCbk, abortCheckCbkData);
+}
+
+Links *PDFDoc::getLinks(int page) {
+ return catalog->getPage(page)->getLinks();
+}
+
+void PDFDoc::processLinks(OutputDev *out, int page) {
+ catalog->getPage(page)->processLinks(out);
+}
+
+#ifndef DISABLE_OUTLINE
+int PDFDoc::getOutlineTargetPage(OutlineItem *outlineItem) {
+ LinkAction *action;
+ LinkActionKind kind;
+ LinkDest *dest;
+ GString *namedDest;
+ Ref pageRef;
+ int pg;
+
+ if (outlineItem->pageNum >= 0) {
+ return outlineItem->pageNum;
+ }
+ if (!(action = outlineItem->getAction())) {
+ outlineItem->pageNum = 0;
+ return 0;
+ }
+ kind = action->getKind();
+ if (kind != actionGoTo) {
+ outlineItem->pageNum = 0;
+ return 0;
+ }
+ if ((dest = ((LinkGoTo *)action)->getDest())) {
+ dest = dest->copy();
+ } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
+ dest = findDest(namedDest);
+ }
+ pg = 0;
+ if (dest) {
+ if (dest->isPageRef()) {
+ pageRef = dest->getPageRef();
+ pg = findPage(pageRef.num, pageRef.gen);
+ } else {
+ pg = dest->getPageNum();
+ }
+ delete dest;
+ }
+ outlineItem->pageNum = pg;
+ return pg;
+}
+#endif
+
+GBool PDFDoc::isLinearized() {
+ Parser *parser;
+ Object obj1, obj2, obj3, obj4, obj5;
+ GBool lin;
+
+ lin = gFalse;
+ obj1.initNull();
+ parser = new Parser(xref,
+ new Lexer(xref,
+ str->makeSubStream(str->getStart(), gFalse, 0, &obj1)),
+ gTrue);
+ parser->getObj(&obj1);
+ parser->getObj(&obj2);
+ parser->getObj(&obj3);
+ parser->getObj(&obj4);
+ if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") &&
+ obj4.isDict()) {
+ obj4.dictLookup("Linearized", &obj5);
+ if (obj5.isNum() && obj5.getNum() > 0) {
+ lin = gTrue;
+ }
+ obj5.free();
+ }
+ obj4.free();
+ obj3.free();
+ obj2.free();
+ obj1.free();
+ delete parser;
+ return lin;
+}
+
+GBool PDFDoc::saveAs(GString *name) {
+ FILE *f;
+ char buf[4096];
+ int n;
+
+ if (!(f = fopen(name->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open file '{0:t}'", name);
+ return gFalse;
+ }
+ str->reset();
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
+ str->close();
+ fclose(f);
+ return gTrue;
+}
+
+GBool PDFDoc::saveEmbeddedFile(int idx, const char *path) {
+ FILE *f;
+ GBool ret;
+
+ if (!(f = fopen(path, "wb"))) {
+ return gFalse;
+ }
+ ret = saveEmbeddedFile2(idx, f);
+ fclose(f);
+ 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;
+ OSVERSIONINFO version;
+ wchar_t path2w[_MAX_PATH + 1];
+ char path2c[_MAX_PATH + 1];
+ int i;
+ GBool ret;
+
+ // NB: _wfopen is only available in NT
+/*
+ version.dwOSVersionInfoSize = sizeof(version);
+ GetVersionEx(&version);
+ if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ for (i = 0; i < pathLen && i < _MAX_PATH; ++i) {
+ path2w[i] = path[i];
+ }
+ path2w[i] = 0;
+ f = _wfopen(path2w, L"wb");
+ } else {
+*/
+ for (i = 0; i < pathLen && i < _MAX_PATH; ++i) {
+ path2c[i] = (char)path[i];
+ }
+ path2c[i] = 0;
+ f = fopen(path2c, "wb");
+/*
+ }
+*/
+ if (!f) {
+ return gFalse;
+ }
+ ret = saveEmbeddedFile2(idx, f);
+ fclose(f);
+ return ret;
+}
+#endif
+
+GBool PDFDoc::saveEmbeddedFile2(int idx, FILE *f) {
+ Object strObj;
+ char buf[4096];
+ int n;
+
+ if (!catalog->getEmbeddedFileStreamObj(idx, &strObj)) {
+ return gFalse;
+ }
+ strObj.streamReset();
+ while ((n = strObj.streamGetBlock(buf, sizeof(buf))) > 0) {
+ fwrite(buf, 1, n, f);
+ }
+ strObj.streamClose();
+ strObj.free();
+ return gTrue;
+}
+
+char *PDFDoc::getEmbeddedFileMem(int idx, int *size) {
+ Object strObj;
+ char *buf;
+ int bufSize, sizeInc, n;
+
+ if (!catalog->getEmbeddedFileStreamObj(idx, &strObj)) {
+ return NULL;
+ }
+ strObj.streamReset();
+ bufSize = 0;
+ buf = NULL;
+ do {
+ sizeInc = bufSize ? bufSize : 1024;
+ if (bufSize > INT_MAX - sizeInc) {
+ error(errIO, -1, "embedded file is too large");
+ *size = 0;
+ return NULL;
+ }
+ buf = (char *)grealloc(buf, bufSize + sizeInc);
+ n = strObj.streamGetBlock(buf + bufSize, sizeInc);
+ bufSize += n;
+ } while (n == sizeInc);
+ strObj.streamClose();
+ strObj.free();
+ *size = bufSize;
+ return buf;
+}
+
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h
new file mode 100644
index 00000000000..5069cb78689
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDoc.h
@@ -0,0 +1,224 @@
+//========================================================================
+//
+// PDFDoc.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PDFDOC_H
+#define PDFDOC_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include "XRef.h"
+#include "Catalog.h"
+#include "Page.h"
+
+class GString;
+class BaseStream;
+class OutputDev;
+class Links;
+class LinkAction;
+class LinkDest;
+class Outline;
+class OutlineItem;
+class OptionalContent;
+class PDFCore;
+
+//------------------------------------------------------------------------
+// PDFDoc
+//------------------------------------------------------------------------
+
+class PDFDoc {
+public:
+
+ PDFDoc(GString *fileNameA, GString *ownerPassword = NULL,
+ GString *userPassword = NULL, PDFCore *coreA = NULL);
+
+#ifdef _WIN32
+ PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword = NULL,
+ GString *userPassword = NULL, PDFCore *coreA = NULL);
+#endif
+
+ // This version takes a UTF-8 file name (which is only relevant on
+ // Windows).
+ PDFDoc(char *fileNameA, GString *ownerPassword = NULL,
+ GString *userPassword = NULL, PDFCore *coreA = NULL);
+
+ PDFDoc(BaseStream *strA, GString *ownerPassword = NULL,
+ GString *userPassword = NULL, PDFCore *coreA = NULL);
+
+ ~PDFDoc();
+
+ // Was PDF document successfully opened?
+ GBool isOk() { return ok; }
+
+ // Get the error code (if isOk() returns false).
+ int getErrorCode() { return errCode; }
+
+ // Get file name.
+ GString *getFileName() { return fileName; }
+#ifdef _WIN32
+ wchar_t *getFileNameU() { return fileNameU; }
+#endif
+
+ // Get the xref table.
+ XRef *getXRef() { return xref; }
+
+ // Get catalog.
+ Catalog *getCatalog() { return catalog; }
+
+ // Get base stream.
+ BaseStream *getBaseStream() { return str; }
+
+ // Get page parameters.
+ double getPageMediaWidth(int page)
+ { return catalog->getPage(page)->getMediaWidth(); }
+ double getPageMediaHeight(int page)
+ { return catalog->getPage(page)->getMediaHeight(); }
+ double getPageCropWidth(int page)
+ { return catalog->getPage(page)->getCropWidth(); }
+ double getPageCropHeight(int page)
+ { return catalog->getPage(page)->getCropHeight(); }
+ int getPageRotate(int page)
+ { return catalog->getPage(page)->getRotate(); }
+
+ // Get number of pages.
+ int getNumPages() { return catalog->getNumPages(); }
+
+ // Return the contents of the metadata stream, or NULL if there is
+ // no metadata.
+ GString *readMetadata() { return catalog->readMetadata(); }
+
+ // Return the structure tree root object.
+ Object *getStructTreeRoot() { return catalog->getStructTreeRoot(); }
+
+ // Display a page.
+ void displayPage(OutputDev *out, int page,
+ double hDPI, double vDPI, int rotate,
+ GBool useMediaBox, GBool crop, GBool printing,
+ GBool (*abortCheckCbk)(void *data) = NULL,
+ void *abortCheckCbkData = NULL);
+
+ // Display a range of pages.
+ void displayPages(OutputDev *out, int firstPage, int lastPage,
+ double hDPI, double vDPI, int rotate,
+ GBool useMediaBox, GBool crop, GBool printing,
+ GBool (*abortCheckCbk)(void *data) = NULL,
+ void *abortCheckCbkData = NULL);
+
+ // Display part of a page.
+ void displayPageSlice(OutputDev *out, int page,
+ double hDPI, double vDPI, int rotate,
+ GBool useMediaBox, GBool crop, GBool printing,
+ int sliceX, int sliceY, int sliceW, int sliceH,
+ GBool (*abortCheckCbk)(void *data) = NULL,
+ void *abortCheckCbkData = NULL);
+
+ // Find a page, given its object ID. Returns page number, or 0 if
+ // not found.
+ int findPage(int num, int gen) { return catalog->findPage(num, gen); }
+
+ // Returns the links for the current page, transferring ownership to
+ // the caller.
+ Links *getLinks(int page);
+
+ // Find a named destination. Returns the link destination, or
+ // NULL if <name> is not a destination.
+ LinkDest *findDest(GString *name)
+ { return catalog->findDest(name); }
+
+ // Process the links for a page.
+ void processLinks(OutputDev *out, int page);
+
+#ifndef DISABLE_OUTLINE
+ // Return the outline object.
+ Outline *getOutline() { return outline; }
+
+ // Return the target page number for an outline item. Returns 0 if
+ // the item doesn't target a page in this PDF file.
+ int getOutlineTargetPage(OutlineItem *outlineItem);
+#endif
+
+ // Return the OptionalContent object.
+ OptionalContent *getOptionalContent() { return optContent; }
+
+ // Is the file encrypted?
+ GBool isEncrypted() { return xref->isEncrypted(); }
+
+ // Check various permissions.
+ GBool okToPrint(GBool ignoreOwnerPW = gFalse)
+ { return xref->okToPrint(ignoreOwnerPW); }
+ GBool okToChange(GBool ignoreOwnerPW = gFalse)
+ { return xref->okToChange(ignoreOwnerPW); }
+ GBool okToCopy(GBool ignoreOwnerPW = gFalse)
+ { return xref->okToCopy(ignoreOwnerPW); }
+ GBool okToAddNotes(GBool ignoreOwnerPW = gFalse)
+ { return xref->okToAddNotes(ignoreOwnerPW); }
+
+ // Is this document linearized?
+ GBool isLinearized();
+
+ // Return the document's Info dictionary (if any).
+ Object *getDocInfo(Object *obj) { return xref->getDocInfo(obj); }
+ Object *getDocInfoNF(Object *obj) { return xref->getDocInfoNF(obj); }
+
+ // Return the PDF version specified by the file.
+ double getPDFVersion() { return pdfVersion; }
+
+ // Save this file with another name.
+ GBool saveAs(GString *name);
+
+ // Return a pointer to the PDFCore object.
+ PDFCore *getCore() { return core; }
+
+ // Get the list of embedded files.
+ int getNumEmbeddedFiles() { return catalog->getNumEmbeddedFiles(); }
+ Unicode *getEmbeddedFileName(int idx)
+ { return catalog->getEmbeddedFileName(idx); }
+ 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
+ char *getEmbeddedFileMem(int idx, int *size);
+
+
+private:
+
+ void init(PDFCore *coreA);
+ GBool setup(GString *ownerPassword, GString *userPassword);
+ GBool setup2(GString *ownerPassword, GString *userPassword,
+ GBool repairXRef);
+ void checkHeader();
+ GBool checkEncryption(GString *ownerPassword, GString *userPassword);
+ GBool saveEmbeddedFile2(int idx, FILE *f);
+
+ GString *fileName;
+#ifdef _WIN32
+ wchar_t *fileNameU;
+#endif
+ FILE *file;
+ BaseStream *str;
+ PDFCore *core;
+ double pdfVersion;
+ XRef *xref;
+ Catalog *catalog;
+#ifndef DISABLE_OUTLINE
+ Outline *outline;
+#endif
+ OptionalContent *optContent;
+
+ GBool ok;
+ int errCode;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDocEncoding.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDocEncoding.cc
new file mode 100644
index 00000000000..8258395b543
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDocEncoding.cc
@@ -0,0 +1,46 @@
+//========================================================================
+//
+// PDFDocEncoding.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include "gmempp.h"
+#include "PDFDocEncoding.h"
+
+Unicode pdfDocEncoding[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, // 00
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, // 10
+ 0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, // 20
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // 30
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // 40
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // 50
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, // 60
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, // 70
+ 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000,
+ 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044, // 80
+ 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018,
+ 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160, // 90
+ 0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e, 0x0000,
+ 0x20ac, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x0000, 0x00ae, 0x00af,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
+};
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDocEncoding.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDocEncoding.h
new file mode 100644
index 00000000000..3259d3e1064
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PDFDocEncoding.h
@@ -0,0 +1,16 @@
+//========================================================================
+//
+// PDFDocEncoding.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PDFDOCENCODING_H
+#define PDFDOCENCODING_H
+
+#include "CharTypes.h"
+
+extern Unicode pdfDocEncoding[256];
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc
new file mode 100644
index 00000000000..0c39472ba85
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.cc
@@ -0,0 +1,8438 @@
+//========================================================================
+//
+// PSOutputDev.cc
+//
+// Copyright 1996-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <math.h>
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "GHash.h"
+#include "config.h"
+#include "GlobalParams.h"
+#include "Object.h"
+#include "Error.h"
+#include "Function.h"
+#include "Gfx.h"
+#include "GfxState.h"
+#include "GfxFont.h"
+#include "UnicodeMap.h"
+#include "FoFiType1C.h"
+#include "FoFiTrueType.h"
+#include "Catalog.h"
+#include "Page.h"
+#include "Stream.h"
+#include "Annot.h"
+#include "PDFDoc.h"
+#include "XRef.h"
+#include "PreScanOutputDev.h"
+#include "CharCodeToUnicode.h"
+#include "AcroForm.h"
+#include "TextString.h"
+#if HAVE_SPLASH
+# include "Splash.h"
+# include "SplashBitmap.h"
+# include "SplashOutputDev.h"
+#endif
+#include "PSOutputDev.h"
+
+// the MSVC math.h doesn't define this
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+//------------------------------------------------------------------------
+// PostScript prolog and setup
+//------------------------------------------------------------------------
+
+// The '~' escapes mark prolog code that is emitted only in certain
+// levels:
+//
+// ~[123][ngs]
+// ^ ^----- n=psLevel_, g=psLevel_Gray, s=psLevel_Sep
+// +----- 1=psLevel1__, 2=psLevel2__, 3=psLevel3__
+
+static const char *prolog[] = {
+ "/xpdf 75 dict def xpdf begin",
+ "% PDF special state",
+ "/pdfDictSize 15 def",
+ "~1ns",
+ "/pdfStates 64 array def",
+ " 0 1 63 {",
+ " pdfStates exch pdfDictSize dict",
+ " dup /pdfStateIdx 3 index put",
+ " put",
+ " } for",
+ "~123ngs",
+ "/bdef { bind def } bind def",
+ "/pdfSetup {",
+ " /pdfDuplex exch def",
+ " /setpagedevice where {",
+ " pop 2 dict begin",
+ " /Policies 1 dict dup begin /PageSize 6 def end def",
+ " pdfDuplex { /Duplex true def } if",
+ " currentdict end setpagedevice",
+ " } if",
+ " /pdfPageW 0 def",
+ " /pdfPageH 0 def",
+ "} def",
+ "/pdfSetupPaper {",
+ " 2 copy pdfPageH ne exch pdfPageW ne or {",
+ " /pdfPageH exch def",
+ " /pdfPageW exch def",
+ " /setpagedevice where {",
+ " pop 3 dict begin",
+ " /PageSize [pdfPageW pdfPageH] def",
+ " pdfDuplex { /Duplex true def } if",
+ " /ImagingBBox null def",
+ " currentdict end setpagedevice",
+ " } if",
+ " } {",
+ " pop pop",
+ " } ifelse",
+ "} def",
+ "~1ns",
+ "/pdfOpNames [",
+ " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",
+ " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender",
+ " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",
+ "] def",
+ "~123ngs",
+ "/pdfStartPage {",
+ "~1ns",
+ " pdfStates 0 get begin",
+ "~23ngs",
+ " pdfDictSize dict begin",
+ "~23n",
+ " /pdfFillCS [] def",
+ " /pdfFillXform {} def",
+ " /pdfStrokeCS [] def",
+ " /pdfStrokeXform {} def",
+ "~1n",
+ " /pdfFill 0 def",
+ " /pdfStroke 0 def",
+ "~1s",
+ " /pdfFill [0 0 0 1] def",
+ " /pdfStroke [0 0 0 1] def",
+ "~23g",
+ " /pdfFill 0 def",
+ " /pdfStroke 0 def",
+ "~23ns",
+ " /pdfFill [0] def",
+ " /pdfStroke [0] def",
+ " /pdfFillOP false def",
+ " /pdfStrokeOP false def",
+ "~123ngs",
+ " /pdfLastFill false def",
+ " /pdfLastStroke false def",
+ " /pdfTextMat [1 0 0 1 0 0] def",
+ " /pdfFontSize 0 def",
+ " /pdfCharSpacing 0 def",
+ " /pdfTextRender 0 def",
+ " /pdfTextRise 0 def",
+ " /pdfWordSpacing 0 def",
+ " /pdfHorizScaling 1 def",
+ " /pdfTextClipPath [] def",
+ "} def",
+ "/pdfEndPage { end } def",
+ "~23s",
+ "% separation convention operators",
+ "/findcmykcustomcolor where {",
+ " pop",
+ "}{",
+ " /findcmykcustomcolor { 5 array astore } def",
+ "} ifelse",
+ "/setcustomcolor where {",
+ " pop",
+ "}{",
+ " /setcustomcolor {",
+ " exch",
+ " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
+ " 0 4 getinterval cvx",
+ " [ exch /dup load exch { mul exch dup } /forall load",
+ " /pop load dup ] cvx",
+ " ] setcolorspace setcolor",
+ " } def",
+ "} ifelse",
+ "/customcolorimage where {",
+ " pop",
+ "}{",
+ " /customcolorimage {",
+ " gsave",
+ " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch",
+ " 0 4 getinterval",
+ " [ exch /dup load exch { mul exch dup } /forall load",
+ " /pop load dup ] cvx",
+ " ] setcolorspace",
+ " 10 dict begin",
+ " /ImageType 1 def",
+ " /DataSource exch def",
+ " /ImageMatrix exch def",
+ " /BitsPerComponent exch def",
+ " /Height exch def",
+ " /Width exch def",
+ " /Decode [1 0] def",
+ " currentdict end",
+ " image",
+ " grestore",
+ " } def",
+ "} ifelse",
+ "~123ngs",
+ "% PDF color state",
+ "~1n",
+ "/g { dup /pdfFill exch def setgray",
+ " /pdfLastFill true def /pdfLastStroke false def } def",
+ "/G { dup /pdfStroke exch def setgray",
+ " /pdfLastStroke true def /pdfLastFill false def } def",
+ "/fCol {",
+ " pdfLastFill not {",
+ " pdfFill setgray",
+ " /pdfLastFill true def /pdfLastStroke false def",
+ " } if",
+ "} def",
+ "/sCol {",
+ " pdfLastStroke not {",
+ " pdfStroke setgray",
+ " /pdfLastStroke true def /pdfLastFill false def",
+ " } if",
+ "} def",
+ "~1s",
+ "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
+ " /pdfLastFill true def /pdfLastStroke false def } def",
+ "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
+ " /pdfLastStroke true def /pdfLastFill false def } def",
+ "/fCol {",
+ " pdfLastFill not {",
+ " pdfFill aload pop setcmykcolor",
+ " /pdfLastFill true def /pdfLastStroke false def",
+ " } if",
+ "} def",
+ "/sCol {",
+ " pdfLastStroke not {",
+ " pdfStroke aload pop setcmykcolor",
+ " /pdfLastStroke true def /pdfLastFill false def",
+ " } if",
+ "} def",
+ "~23n",
+ "/cs { /pdfFillXform exch def dup /pdfFillCS exch def",
+ " setcolorspace } def",
+ "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def",
+ " setcolorspace } def",
+ "/sc { pdfLastFill not {",
+ " pdfFillCS setcolorspace pdfFillOP setoverprint",
+ " } if",
+ " dup /pdfFill exch def aload pop pdfFillXform setcolor",
+ " /pdfLastFill true def /pdfLastStroke false def } def",
+ "/SC { pdfLastStroke not {",
+ " pdfStrokeCS setcolorspace pdfStrokeOP setoverprint",
+ " } if",
+ " dup /pdfStroke exch def aload pop pdfStrokeXform setcolor",
+ " /pdfLastStroke true def /pdfLastFill false def } def",
+ "/op { /pdfFillOP exch def",
+ " pdfLastFill { pdfFillOP setoverprint } if } def",
+ "/OP { /pdfStrokeOP exch def",
+ " pdfLastStroke { pdfStrokeOP setoverprint } if } def",
+ "/fCol {",
+ " pdfLastFill not {",
+ " pdfFillCS setcolorspace",
+ " pdfFill aload pop pdfFillXform setcolor",
+ " pdfFillOP setoverprint",
+ " /pdfLastFill true def /pdfLastStroke false def",
+ " } if",
+ "} def",
+ "/sCol {",
+ " pdfLastStroke not {",
+ " pdfStrokeCS setcolorspace",
+ " pdfStroke aload pop pdfStrokeXform setcolor",
+ " pdfStrokeOP setoverprint",
+ " /pdfLastStroke true def /pdfLastFill false def",
+ " } if",
+ "} def",
+ "~23g",
+ "/g { dup /pdfFill exch def setgray",
+ " /pdfLastFill true def /pdfLastStroke false def } def",
+ "/G { dup /pdfStroke exch def setgray",
+ " /pdfLastStroke true def /pdfLastFill false def } def",
+ "/fCol {",
+ " pdfLastFill not {",
+ " pdfFill setgray",
+ " /pdfLastFill true def /pdfLastStroke false def",
+ " } if",
+ "} def",
+ "/sCol {",
+ " pdfLastStroke not {",
+ " pdfStroke setgray",
+ " /pdfLastStroke true def /pdfLastFill false def",
+ " } if",
+ "} def",
+ "~23s",
+ "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor",
+ " pdfFillOP setoverprint",
+ " /pdfLastFill true def /pdfLastStroke false def } def",
+ "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor",
+ " pdfStrokeOP setoverprint",
+ " /pdfLastStroke true def /pdfLastFill false def } def",
+ "/ck { 6 copy 6 array astore /pdfFill exch def",
+ " findcmykcustomcolor exch setcustomcolor",
+ " pdfFillOP setoverprint",
+ " /pdfLastFill true def /pdfLastStroke false def } def",
+ "/CK { 6 copy 6 array astore /pdfStroke exch def",
+ " findcmykcustomcolor exch setcustomcolor",
+ " pdfStrokeOP setoverprint",
+ " /pdfLastStroke true def /pdfLastFill false def } def",
+ "/op { /pdfFillOP exch def",
+ " pdfLastFill { pdfFillOP setoverprint } if } def",
+ "/OP { /pdfStrokeOP exch def",
+ " pdfLastStroke { pdfStrokeOP setoverprint } if } def",
+ "/fCol {",
+ " pdfLastFill not {",
+ " pdfFill aload length 4 eq {",
+ " setcmykcolor",
+ " }{",
+ " findcmykcustomcolor exch setcustomcolor",
+ " } ifelse",
+ " pdfFillOP setoverprint",
+ " /pdfLastFill true def /pdfLastStroke false def",
+ " } if",
+ "} def",
+ "/sCol {",
+ " pdfLastStroke not {",
+ " pdfStroke aload length 4 eq {",
+ " setcmykcolor",
+ " }{",
+ " findcmykcustomcolor exch setcustomcolor",
+ " } ifelse",
+ " pdfStrokeOP setoverprint",
+ " /pdfLastStroke true def /pdfLastFill false def",
+ " } if",
+ "} def",
+ "~3ns",
+ "/opm {",
+ " /setoverprintmode where { pop setoverprintmode } { pop } ifelse",
+ "} def",
+ "~123ngs",
+ "% build a font",
+ "/pdfMakeFont {",
+ " 4 3 roll findfont",
+ " 4 2 roll matrix scale makefont",
+ " dup length dict begin",
+ " { 1 index /FID ne { def } { pop pop } ifelse } forall",
+ " /Encoding exch def",
+ " currentdict",
+ " end",
+ " definefont pop",
+ "} def",
+ "/pdfMakeFont16 {",
+ " exch findfont",
+ " dup length dict begin",
+ " { 1 index /FID ne { def } { pop pop } ifelse } forall",
+ " /WMode exch def",
+ " currentdict",
+ " end",
+ " definefont pop",
+ "} def",
+ "~3ngs",
+ "/pdfMakeFont16L3 {",
+ " 1 index /CIDFont resourcestatus {",
+ " pop pop 1 index /CIDFont findresource /CIDFontType known",
+ " } {",
+ " false",
+ " } ifelse",
+ " {",
+ " 0 eq { /Identity-H } { /Identity-V } ifelse",
+ " exch 1 array astore composefont pop",
+ " } {",
+ " pdfMakeFont16",
+ " } ifelse",
+ "} def",
+ "~123ngs",
+ "% graphics state operators",
+ "~1ns",
+ "/q {",
+ " gsave",
+ " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for",
+ " pdfStates pdfStateIdx 1 add get begin",
+ " pdfOpNames { exch def } forall",
+ "} def",
+ "/Q { end grestore } def",
+ "~23ngs",
+ "/q { gsave pdfDictSize dict begin } def",
+ "/Q {",
+ " end grestore",
+ "} def",
+ "~123ngs",
+ "/cm { concat } def",
+ "/d { setdash } def",
+ "/i { setflat } def",
+ "/j { setlinejoin } def",
+ "/J { setlinecap } def",
+ "/M { setmiterlimit } def",
+ "/w { setlinewidth } def",
+ "% path segment operators",
+ "/m { moveto } def",
+ "/l { lineto } def",
+ "/c { curveto } def",
+ "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto",
+ " neg 0 rlineto closepath } def",
+ "/h { closepath } def",
+ "% path painting operators",
+ "/S { sCol stroke } def",
+ "/Sf { fCol stroke } def",
+ "/f { fCol fill } def",
+ "/f* { fCol eofill } def",
+ "% clipping operators",
+ "/W { clip newpath } bdef",
+ "/W* { eoclip newpath } bdef",
+ "/Ws { strokepath clip newpath } bdef",
+ "% text state operators",
+ "/Tc { /pdfCharSpacing exch def } def",
+ "/Tf { dup /pdfFontSize exch def",
+ " dup pdfHorizScaling mul exch matrix scale",
+ " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put",
+ " exch findfont exch makefont setfont } def",
+ "/Tr { /pdfTextRender exch def } def",
+ "/Ts { /pdfTextRise exch def } def",
+ "/Tw { /pdfWordSpacing exch def } def",
+ "/Tz { /pdfHorizScaling exch def } def",
+ "% text positioning operators",
+ "/Td { pdfTextMat transform moveto } def",
+ "/Tm { /pdfTextMat exch def } def",
+ "% text string operators",
+ "/xyshow where {",
+ " pop",
+ " /xyshow2 {",
+ " dup length array",
+ " 0 2 2 index length 1 sub {",
+ " 2 index 1 index 2 copy get 3 1 roll 1 add get",
+ " pdfTextMat dtransform",
+ " 4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put",
+ " } for",
+ " exch pop",
+ " xyshow",
+ " } def",
+ "}{",
+ " /xyshow2 {",
+ " currentfont /FontType get 0 eq {",
+ " 0 2 3 index length 1 sub {",
+ " currentpoint 4 index 3 index 2 getinterval show moveto",
+ " 2 copy get 2 index 3 2 roll 1 add get",
+ " pdfTextMat dtransform rmoveto",
+ " } for",
+ " } {",
+ " 0 1 3 index length 1 sub {",
+ " currentpoint 4 index 3 index 1 getinterval show moveto",
+ " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get",
+ " pdfTextMat dtransform rmoveto",
+ " } for",
+ " } ifelse",
+ " pop pop",
+ " } def",
+ "} ifelse",
+ "/cshow where {",
+ " pop",
+ " /xycp {", // xycharpath
+ " 0 3 2 roll",
+ " {",
+ " pop pop currentpoint 3 2 roll",
+ " 1 string dup 0 4 3 roll put false charpath moveto",
+ " 2 copy get 2 index 2 index 1 add get",
+ " pdfTextMat dtransform rmoveto",
+ " 2 add",
+ " } exch cshow",
+ " pop pop",
+ " } def",
+ "}{",
+ " /xycp {", // xycharpath
+ " currentfont /FontType get 0 eq {",
+ " 0 2 3 index length 1 sub {",
+ " currentpoint 4 index 3 index 2 getinterval false charpath moveto",
+ " 2 copy get 2 index 3 2 roll 1 add get",
+ " pdfTextMat dtransform rmoveto",
+ " } for",
+ " } {",
+ " 0 1 3 index length 1 sub {",
+ " currentpoint 4 index 3 index 1 getinterval false charpath moveto",
+ " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get",
+ " pdfTextMat dtransform rmoveto",
+ " } for",
+ " } ifelse",
+ " pop pop",
+ " } def",
+ "} ifelse",
+ "/Tj {",
+ " fCol", // because stringwidth has to draw Type 3 chars
+ " 0 pdfTextRise pdfTextMat dtransform rmoveto",
+ " currentpoint 4 2 roll",
+ " pdfTextRender 1 and 0 eq {",
+ " 2 copy xyshow2",
+ " } if",
+ " pdfTextRender 3 and dup 1 eq exch 2 eq or {",
+ " 3 index 3 index moveto",
+ " 2 copy",
+ " currentfont /FontType get 3 eq { fCol } { sCol } ifelse",
+ " xycp currentpoint stroke moveto",
+ " } if",
+ " pdfTextRender 4 and 0 ne {",
+ " 4 2 roll moveto xycp",
+ " /pdfTextClipPath [ pdfTextClipPath aload pop",
+ " {/moveto cvx}",
+ " {/lineto cvx}",
+ " {/curveto cvx}",
+ " {/closepath cvx}",
+ " pathforall ] def",
+ " currentpoint newpath moveto",
+ " } {",
+ " pop pop pop pop",
+ " } ifelse",
+ " 0 pdfTextRise neg pdfTextMat dtransform rmoveto",
+ "} def",
+ "/Tj3 {",
+ " pdfTextRender 3 and 3 ne {"
+ " fCol", // because stringwidth has to draw Type 3 chars
+ " 0 pdfTextRise pdfTextMat dtransform rmoveto",
+ " xyshow2",
+ " 0 pdfTextRise neg pdfTextMat dtransform rmoveto",
+ " } {",
+ " pop pop",
+ " } ifelse",
+ "} def",
+ "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0",
+ " pdfTextMat dtransform rmoveto } def",
+ "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch",
+ " pdfTextMat dtransform rmoveto } def",
+ "/Tclip { pdfTextClipPath cvx exec clip newpath",
+ " /pdfTextClipPath [] def } def",
+ "~1ns",
+ "% Level 1 image operators",
+ "~1n",
+ "/pdfIm1 {",
+ " /pdfImBuf1 4 index string def",
+ " { currentfile pdfImBuf1 readhexstring pop } image",
+ "} def",
+ "~1s",
+ "/pdfIm1Sep {",
+ " /pdfImBuf1 4 index string def",
+ " /pdfImBuf2 4 index string def",
+ " /pdfImBuf3 4 index string def",
+ " /pdfImBuf4 4 index string def",
+ " { currentfile pdfImBuf1 readhexstring pop }",
+ " { currentfile pdfImBuf2 readhexstring pop }",
+ " { currentfile pdfImBuf3 readhexstring pop }",
+ " { currentfile pdfImBuf4 readhexstring pop }",
+ " true 4 colorimage",
+ "} def",
+ "~1ns",
+ "/pdfImM1 {",
+ " fCol /pdfImBuf1 4 index 7 add 8 idiv string def",
+ " { currentfile pdfImBuf1 readhexstring pop } imagemask",
+ "} def",
+ "/pdfImStr {",
+ " 2 copy exch length lt {",
+ " 2 copy get exch 1 add exch",
+ " } {",
+ " ()",
+ " } ifelse",
+ "} def",
+ "/pdfImM1a {",
+ " { pdfImStr } imagemask",
+ " pop pop",
+ "} def",
+ "~23ngs",
+ "% Level 2/3 image operators",
+ "/pdfImBuf 100 string def",
+ "/pdfImStr {",
+ " 2 copy exch length lt {",
+ " 2 copy get exch 1 add exch",
+ " } {",
+ " ()",
+ " } ifelse",
+ "} def",
+ "/skipEOD {",
+ " { currentfile pdfImBuf readline",
+ " not { pop exit } if",
+ " (%-EOD-) eq { exit } if } loop",
+ "} def",
+ "/pdfIm { image skipEOD } def",
+ "~3ngs",
+ "/pdfMask {",
+ " /ReusableStreamDecode filter",
+ " skipEOD",
+ " /maskStream exch def",
+ "} def",
+ "/pdfMaskEnd { maskStream closefile } def",
+ "/pdfMaskInit {",
+ " /maskArray exch def",
+ " /maskIdx 0 def",
+ "} def",
+ "/pdfMaskSrc {",
+ " maskIdx maskArray length lt {",
+ " maskArray maskIdx get",
+ " /maskIdx maskIdx 1 add def",
+ " } {",
+ " ()",
+ " } ifelse",
+ "} def",
+ "~23s",
+ "/pdfImSep {",
+ " findcmykcustomcolor exch",
+ " dup /Width get /pdfImBuf1 exch string def",
+ " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def",
+ " /pdfImDecodeLow exch def",
+ " begin Width Height BitsPerComponent ImageMatrix DataSource end",
+ " /pdfImData exch def",
+ " { pdfImData pdfImBuf1 readstring pop",
+ " 0 1 2 index length 1 sub {",
+ " 1 index exch 2 copy get",
+ " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi",
+ " 255 exch sub put",
+ " } for }",
+ " 6 5 roll customcolorimage",
+ " skipEOD",
+ "} def",
+ "~23ngs",
+ "/pdfImM { fCol imagemask skipEOD } def",
+ "/pr {",
+ " 4 2 roll exch 5 index div exch 4 index div moveto",
+ " exch 3 index div dup 0 rlineto",
+ " exch 2 index div 0 exch rlineto",
+ " neg 0 rlineto",
+ " closepath",
+ "} def",
+ "/pdfImClip { gsave clip } def",
+ "/pdfImClipEnd { grestore } def",
+ "~23ns",
+ "% shading operators",
+ "/colordelta {",
+ " false 0 1 3 index length 1 sub {",
+ " dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {",
+ " pop true",
+ " } if",
+ " } for",
+ " exch pop exch pop",
+ "} def",
+ "/funcCol { func n array astore } def",
+ "/funcSH {",
+ " dup 0 eq {",
+ " true",
+ " } {",
+ " dup 6 eq {",
+ " false",
+ " } {",
+ " 4 index 4 index funcCol dup",
+ " 6 index 4 index funcCol dup",
+ " 3 1 roll colordelta 3 1 roll",
+ " 5 index 5 index funcCol dup",
+ " 3 1 roll colordelta 3 1 roll",
+ " 6 index 8 index funcCol dup",
+ " 3 1 roll colordelta 3 1 roll",
+ " colordelta or or or",
+ " } ifelse",
+ " } ifelse",
+ " {",
+ " 1 add",
+ " 4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch",
+ " 6 index 6 index 4 index 4 index 4 index funcSH",
+ " 2 index 6 index 6 index 4 index 4 index funcSH",
+ " 6 index 2 index 4 index 6 index 4 index funcSH",
+ " 5 3 roll 3 2 roll funcSH pop pop",
+ " } {",
+ " pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul",
+ "~23n",
+ " funcCol sc",
+ "~23s",
+ " funcCol aload pop k",
+ "~23ns",
+ " dup 4 index exch mat transform m",
+ " 3 index 3 index mat transform l",
+ " 1 index 3 index mat transform l",
+ " mat transform l pop pop h f*",
+ " } ifelse",
+ "} def",
+ "/axialCol {",
+ " dup 0 lt {",
+ " pop t0",
+ " } {",
+ " dup 1 gt {",
+ " pop t1",
+ " } {",
+ " dt mul t0 add",
+ " } ifelse",
+ " } ifelse",
+ " func n array astore",
+ "} def",
+ "/axialSH {",
+ " dup 2 lt {",
+ " true",
+ " } {",
+ " dup 8 eq {",
+ " false",
+ " } {",
+ " 2 index axialCol 2 index axialCol colordelta",
+ " } ifelse",
+ " } ifelse",
+ " {",
+ " 1 add 3 1 roll 2 copy add 0.5 mul",
+ " dup 4 3 roll exch 4 index axialSH",
+ " exch 3 2 roll axialSH",
+ " } {",
+ " pop 2 copy add 0.5 mul",
+ "~23n",
+ " axialCol sc",
+ "~23s",
+ " axialCol aload pop k",
+ "~23ns",
+ " exch dup dx mul x0 add exch dy mul y0 add",
+ " 3 2 roll dup dx mul x0 add exch dy mul y0 add",
+ " dx abs dy abs ge {",
+ " 2 copy yMin sub dy mul dx div add yMin m",
+ " yMax sub dy mul dx div add yMax l",
+ " 2 copy yMax sub dy mul dx div add yMax l",
+ " yMin sub dy mul dx div add yMin l",
+ " h f*",
+ " } {",
+ " exch 2 copy xMin sub dx mul dy div add xMin exch m",
+ " xMax sub dx mul dy div add xMax exch l",
+ " exch 2 copy xMax sub dx mul dy div add xMax exch l",
+ " xMin sub dx mul dy div add xMin exch l",
+ " h f*",
+ " } ifelse",
+ " } ifelse",
+ "} def",
+ "/radialCol {",
+ " dup t0 lt {",
+ " pop t0",
+ " } {",
+ " dup t1 gt {",
+ " pop t1",
+ " } if",
+ " } ifelse",
+ " func n array astore",
+ "} def",
+ "/radialSH {",
+ " dup 0 eq {",
+ " true",
+ " } {",
+ " dup 8 eq {",
+ " false",
+ " } {",
+ " 2 index dt mul t0 add radialCol",
+ " 2 index dt mul t0 add radialCol colordelta",
+ " } ifelse",
+ " } ifelse",
+ " {",
+ " 1 add 3 1 roll 2 copy add 0.5 mul",
+ " dup 4 3 roll exch 4 index radialSH",
+ " exch 3 2 roll radialSH",
+ " } {",
+ " pop 2 copy add 0.5 mul dt mul t0 add",
+ "~23n",
+ " radialCol sc",
+ "~23s",
+ " radialCol aload pop k",
+ "~23ns",
+ " encl {",
+ " exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
+ " 0 360 arc h",
+ " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
+ " 360 0 arcn h f",
+ " } {",
+ " 2 copy",
+ " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
+ " a1 a2 arcn",
+ " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
+ " a2 a1 arcn h",
+ " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
+ " a1 a2 arc",
+ " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add",
+ " a2 a1 arc h f",
+ " } ifelse",
+ " } ifelse",
+ "} def",
+ "~123ngs",
+ "end",
+ NULL
+};
+
+static const char *minLineWidthProlog[] = {
+ "/pdfDist { dup dtransform dup mul exch dup mul add 0.5 mul sqrt } def",
+ "/pdfIDist { dup idtransform dup mul exch dup mul add 0.5 mul sqrt } def",
+ "/pdfMinLineDist pdfMinLineWidth pdfDist def",
+ "/setlinewidth {",
+ " dup pdfDist pdfMinLineDist lt {",
+ " pop pdfMinLineDist pdfIDist",
+ " } if",
+ " setlinewidth",
+ "} bind def",
+ NULL
+};
+
+static const char *cmapProlog[] = {
+ "/CIDInit /ProcSet findresource begin",
+ "10 dict begin",
+ " begincmap",
+ " /CMapType 1 def",
+ " /CMapName /Identity-H def",
+ " /CIDSystemInfo 3 dict dup begin",
+ " /Registry (Adobe) def",
+ " /Ordering (Identity) def",
+ " /Supplement 0 def",
+ " end def",
+ " 1 begincodespacerange",
+ " <0000> <ffff>",
+ " endcodespacerange",
+ " 0 usefont",
+ " 1 begincidrange",
+ " <0000> <ffff> 0",
+ " endcidrange",
+ " endcmap",
+ " currentdict CMapName exch /CMap defineresource pop",
+ "end",
+ "10 dict begin",
+ " begincmap",
+ " /CMapType 1 def",
+ " /CMapName /Identity-V def",
+ " /CIDSystemInfo 3 dict dup begin",
+ " /Registry (Adobe) def",
+ " /Ordering (Identity) def",
+ " /Supplement 0 def",
+ " end def",
+ " /WMode 1 def",
+ " 1 begincodespacerange",
+ " <0000> <ffff>",
+ " endcodespacerange",
+ " 0 usefont",
+ " 1 begincidrange",
+ " <0000> <ffff> 0",
+ " endcidrange",
+ " endcmap",
+ " currentdict CMapName exch /CMap defineresource pop",
+ "end",
+ "end",
+ NULL
+};
+
+//------------------------------------------------------------------------
+// Fonts
+//------------------------------------------------------------------------
+
+struct PSSubstFont {
+ const char *psName; // PostScript name
+ double mWidth; // width of 'm' character
+};
+
+// NB: must be in same order as base14SubstFonts in GfxFont.cc
+static PSSubstFont psBase14SubstFonts[14] = {
+ {"Courier", 0.600},
+ {"Courier-Oblique", 0.600},
+ {"Courier-Bold", 0.600},
+ {"Courier-BoldOblique", 0.600},
+ {"Helvetica", 0.833},
+ {"Helvetica-Oblique", 0.833},
+ {"Helvetica-Bold", 0.889},
+ {"Helvetica-BoldOblique", 0.889},
+ {"Times-Roman", 0.788},
+ {"Times-Italic", 0.722},
+ {"Times-Bold", 0.833},
+ {"Times-BoldItalic", 0.778},
+ // the last two are never used for substitution
+ {"Symbol", 0},
+ {"ZapfDingbats", 0}
+};
+
+class PSFontInfo {
+public:
+
+ PSFontInfo(Ref fontIDA)
+ { fontID = fontIDA; ff = NULL; }
+
+ Ref fontID;
+ PSFontFileInfo *ff; // pointer to font file info; NULL indicates
+ // font mapping failed
+};
+
+enum PSFontFileLocation {
+ psFontFileResident,
+ psFontFileEmbedded,
+ psFontFileExternal
+};
+
+class PSFontFileInfo {
+public:
+
+ PSFontFileInfo(GString *psNameA, GfxFontType typeA,
+ PSFontFileLocation locA);
+ ~PSFontFileInfo();
+
+ GString *psName; // name under which font is defined
+ GfxFontType type; // font type
+ PSFontFileLocation loc; // font location
+ Ref embFontID; // object ID for the embedded font file
+ // (for all embedded fonts)
+ GString *extFileName; // external font file path
+ // (for all external fonts)
+ GString *encoding; // encoding name (for resident CID fonts)
+ int *codeToGID; // mapping from code/CID to GID
+ // (for TrueType, OpenType-TrueType, and
+ // CID OpenType-CFF fonts)
+ int codeToGIDLen; // length of codeToGID array
+};
+
+PSFontFileInfo::PSFontFileInfo(GString *psNameA, GfxFontType typeA,
+ PSFontFileLocation locA) {
+ psName = psNameA;
+ type = typeA;
+ loc = locA;
+ embFontID.num = embFontID.gen = -1;
+ extFileName = NULL;
+ encoding = NULL;
+ codeToGID = NULL;
+ codeToGIDLen = 0;
+}
+
+PSFontFileInfo::~PSFontFileInfo() {
+ delete psName;
+ if (extFileName) {
+ delete extFileName;
+ }
+ if (encoding) {
+ delete encoding;
+ }
+ if (codeToGID) {
+ gfree(codeToGID);
+ }
+}
+
+//------------------------------------------------------------------------
+// process colors
+//------------------------------------------------------------------------
+
+#define psProcessCyan 1
+#define psProcessMagenta 2
+#define psProcessYellow 4
+#define psProcessBlack 8
+#define psProcessCMYK 15
+
+//------------------------------------------------------------------------
+// PSOutCustomColor
+//------------------------------------------------------------------------
+
+class PSOutCustomColor {
+public:
+
+ PSOutCustomColor(double cA, double mA,
+ double yA, double kA, GString *nameA);
+ ~PSOutCustomColor();
+
+ double c, m, y, k;
+ GString *name;
+ PSOutCustomColor *next;
+};
+
+PSOutCustomColor::PSOutCustomColor(double cA, double mA,
+ double yA, double kA, GString *nameA) {
+ c = cA;
+ m = mA;
+ y = yA;
+ k = kA;
+ name = nameA;
+ next = NULL;
+}
+
+PSOutCustomColor::~PSOutCustomColor() {
+ delete name;
+}
+
+//------------------------------------------------------------------------
+
+struct PSOutImgClipRect {
+ int x0, x1, y0, y1;
+};
+
+//------------------------------------------------------------------------
+
+struct PSOutPaperSize {
+ PSOutPaperSize(int wA, int hA) { w = wA; h = hA; }
+ int w, h;
+};
+
+//------------------------------------------------------------------------
+// DeviceNRecoder
+//------------------------------------------------------------------------
+
+class DeviceNRecoder: public FilterStream {
+public:
+
+ DeviceNRecoder(Stream *strA, int widthA, int heightA,
+ GfxImageColorMap *colorMapA);
+ virtual ~DeviceNRecoder();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual void close();
+ virtual int getChar()
+ { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; }
+ virtual int lookChar()
+ { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; }
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ GBool fillBuf();
+
+ int width, height;
+ GfxImageColorMap *colorMap;
+ Function *func;
+ ImageStream *imgStr;
+ int buf[gfxColorMaxComps];
+ int pixelIdx;
+ int bufIdx;
+ int bufSize;
+};
+
+DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA,
+ GfxImageColorMap *colorMapA):
+ FilterStream(strA) {
+ width = widthA;
+ height = heightA;
+ colorMap = colorMapA;
+ imgStr = NULL;
+ pixelIdx = 0;
+ bufIdx = gfxColorMaxComps;
+ bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
+ getAlt()->getNComps();
+ func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
+ getTintTransformFunc();
+}
+
+DeviceNRecoder::~DeviceNRecoder() {
+ if (str->isEncoder()) {
+ delete str;
+ }
+}
+
+Stream *DeviceNRecoder::copy() {
+ error(errInternal, -1, "Called copy() on DeviceNRecoder");
+ return NULL;
+}
+
+void DeviceNRecoder::reset() {
+ imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgStr->reset();
+}
+
+void DeviceNRecoder::close() {
+ delete imgStr;
+ imgStr = NULL;
+ str->close();
+}
+
+GBool DeviceNRecoder::fillBuf() {
+ Guchar pixBuf[gfxColorMaxComps];
+ GfxColor color;
+ double x[gfxColorMaxComps], y[gfxColorMaxComps];
+ int i;
+
+ if (pixelIdx >= width * height) {
+ return gFalse;
+ }
+ imgStr->getPixel(pixBuf);
+ colorMap->getColor(pixBuf, &color);
+ for (i = 0;
+ i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps();
+ ++i) {
+ x[i] = colToDbl(color.c[i]);
+ }
+ func->transform(x, y);
+ for (i = 0; i < bufSize; ++i) {
+ buf[i] = (int)(y[i] * 255 + 0.5);
+ }
+ bufIdx = 0;
+ ++pixelIdx;
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// GrayRecoder
+//------------------------------------------------------------------------
+
+class GrayRecoder: public FilterStream {
+public:
+
+ GrayRecoder(Stream *strA, int widthA, int heightA,
+ GfxImageColorMap *colorMapA);
+ virtual ~GrayRecoder();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual void close();
+ virtual int getChar()
+ { return (bufIdx >= width && !fillBuf()) ? EOF : buf[bufIdx++]; }
+ virtual int lookChar()
+ { return (bufIdx >= width && !fillBuf()) ? EOF : buf[bufIdx]; }
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ GBool fillBuf();
+
+ int width, height;
+ GfxImageColorMap *colorMap;
+ ImageStream *imgStr;
+ Guchar *buf;
+ int bufIdx;
+};
+
+GrayRecoder::GrayRecoder(Stream *strA, int widthA, int heightA,
+ GfxImageColorMap *colorMapA):
+ FilterStream(strA) {
+ width = widthA;
+ height = heightA;
+ colorMap = colorMapA;
+ imgStr = NULL;
+ buf = (Guchar *)gmalloc(width);
+ bufIdx = width;
+}
+
+GrayRecoder::~GrayRecoder() {
+ gfree(buf);
+ if (str->isEncoder()) {
+ delete str;
+ }
+}
+
+Stream *GrayRecoder::copy() {
+ error(errInternal, -1, "Called copy() on GrayRecoder");
+ return NULL;
+}
+
+void GrayRecoder::reset() {
+ imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgStr->reset();
+}
+
+void GrayRecoder::close() {
+ delete imgStr;
+ imgStr = NULL;
+ str->close();
+}
+
+GBool GrayRecoder::fillBuf() {
+ Guchar *line;
+
+ if (!(line = imgStr->getLine())) {
+ bufIdx = width;
+ return gFalse;
+ }
+ //~ this should probably use the rendering intent from the image
+ //~ dict, or from the content stream
+ colorMap->getGrayByteLine(line, buf, width,
+ gfxRenderingIntentRelativeColorimetric);
+ bufIdx = 0;
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// ColorKeyToMaskEncoder
+//------------------------------------------------------------------------
+
+class ColorKeyToMaskEncoder: public FilterStream {
+public:
+
+ ColorKeyToMaskEncoder(Stream *strA, int widthA, int heightA,
+ GfxImageColorMap *colorMapA, int *maskColorsA);
+ virtual ~ColorKeyToMaskEncoder();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual void close();
+ virtual int getChar()
+ { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; }
+ virtual int lookChar()
+ { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; }
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ GBool fillBuf();
+
+ int width, height;
+ GfxImageColorMap *colorMap;
+ int numComps;
+ int *maskColors;
+ ImageStream *imgStr;
+ Guchar *buf;
+ int bufIdx;
+ int bufSize;
+};
+
+ColorKeyToMaskEncoder::ColorKeyToMaskEncoder(Stream *strA,
+ int widthA, int heightA,
+ GfxImageColorMap *colorMapA,
+ int *maskColorsA):
+ FilterStream(strA)
+{
+ width = widthA;
+ height = heightA;
+ colorMap = colorMapA;
+ numComps = colorMap->getNumPixelComps();
+ maskColors = maskColorsA;
+ imgStr = NULL;
+ bufSize = (width + 7) / 8;
+ buf = (Guchar *)gmalloc(bufSize);
+ bufIdx = width;
+}
+
+ColorKeyToMaskEncoder::~ColorKeyToMaskEncoder() {
+ gfree(buf);
+ if (str->isEncoder()) {
+ delete str;
+ }
+}
+
+Stream *ColorKeyToMaskEncoder::copy() {
+ error(errInternal, -1, "Called copy() on ColorKeyToMaskEncoder");
+ return NULL;
+}
+
+void ColorKeyToMaskEncoder::reset() {
+ imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgStr->reset();
+}
+
+void ColorKeyToMaskEncoder::close() {
+ delete imgStr;
+ imgStr = NULL;
+ str->close();
+}
+
+GBool ColorKeyToMaskEncoder::fillBuf() {
+ Guchar *line, *linePtr, *bufPtr;
+ Guchar byte;
+ int x, xx, i;
+
+ if (!(line = imgStr->getLine())) {
+ bufIdx = width;
+ return gFalse;
+ }
+ linePtr = line;
+ bufPtr = buf;
+ for (x = 0; x < width; x += 8) {
+ byte = 0;
+ for (xx = 0; xx < 8; ++xx) {
+ byte = (Guchar)(byte << 1);
+ if (x + xx < width) {
+ for (i = 0; i < numComps; ++i) {
+ if (linePtr[i] < maskColors[2 * i] ||
+ linePtr[i] > maskColors[2 * i + 1]) {
+ break;
+ }
+ }
+ if (i >= numComps) {
+ byte |= 1;
+ }
+ linePtr += numComps;
+ } else {
+ byte |= 1;
+ }
+ }
+ *bufPtr++ = byte;
+ }
+ bufIdx = 0;
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// PSOutputDev
+//------------------------------------------------------------------------
+
+extern "C" {
+typedef void (*SignalFunc)(int);
+}
+
+static void outputToFile(void *stream, const char *data, int len) {
+ fwrite(data, 1, len, (FILE *)stream);
+}
+
+PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA,
+ int firstPageA, int lastPageA, PSOutMode modeA,
+ int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
+ GBool manualCtrlA,
+ PSOutCustomCodeCbk customCodeCbkA,
+ void *customCodeCbkDataA,
+ GBool honorUserUnitA,
+ GBool fileNameIsUTF8) {
+ FILE *f;
+ PSFileType fileTypeA;
+
+ underlayCbk = NULL;
+ underlayCbkData = NULL;
+ overlayCbk = NULL;
+ overlayCbkData = NULL;
+ customCodeCbk = customCodeCbkA;
+ customCodeCbkData = customCodeCbkDataA;
+
+ rasterizePage = NULL;
+ fontInfo = new GList();
+ fontFileInfo = new GHash();
+ imgIDs = NULL;
+ formIDs = NULL;
+ visitedResources = NULL;
+ saveStack = NULL;
+ paperSizes = NULL;
+ embFontList = NULL;
+ customColors = NULL;
+ haveTextClip = gFalse;
+ t3String = NULL;
+
+ // open file or pipe
+ if (!strcmp(fileName, "-")) {
+ fileTypeA = psStdout;
+ f = stdout;
+ } else if (fileName[0] == '|') {
+ fileTypeA = psPipe;
+#ifdef HAVE_POPEN
+#ifndef _WIN32
+ signal(SIGPIPE, (SignalFunc)SIG_IGN);
+#endif
+ if (!(f = popen(fileName + 1, "w"))) {
+ error(errIO, -1, "Couldn't run print command '{0:s}'", fileName);
+ ok = gFalse;
+ return;
+ }
+#else
+ error(errIO, -1, "Print commands are not supported ('{0:s}')", fileName);
+ ok = gFalse;
+ return;
+#endif
+ } else {
+ fileTypeA = psFile;
+ 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;
+ }
+ }
+
+ init(outputToFile, f, fileTypeA,
+ docA, firstPageA, lastPageA, modeA,
+ imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, honorUserUnitA);
+}
+
+PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
+ PDFDoc *docA,
+ int firstPageA, int lastPageA, PSOutMode modeA,
+ int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
+ GBool manualCtrlA,
+ PSOutCustomCodeCbk customCodeCbkA,
+ void *customCodeCbkDataA,
+ GBool honorUserUnitA) {
+ underlayCbk = NULL;
+ underlayCbkData = NULL;
+ overlayCbk = NULL;
+ overlayCbkData = NULL;
+ customCodeCbk = customCodeCbkA;
+ customCodeCbkData = customCodeCbkDataA;
+
+ rasterizePage = NULL;
+ fontInfo = new GList();
+ fontFileInfo = new GHash();
+ imgIDs = NULL;
+ formIDs = NULL;
+ visitedResources = NULL;
+ saveStack = NULL;
+ paperSizes = NULL;
+ embFontList = NULL;
+ customColors = NULL;
+ haveTextClip = gFalse;
+ t3String = NULL;
+
+ init(outputFuncA, outputStreamA, psGeneric,
+ docA, firstPageA, lastPageA, modeA,
+ imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, honorUserUnitA);
+}
+
+void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,
+ PSFileType fileTypeA, PDFDoc *docA,
+ int firstPageA, int lastPageA, PSOutMode modeA,
+ int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
+ GBool manualCtrlA, GBool honorUserUnitA) {
+ Catalog *catalog;
+ Page *page;
+ PDFRectangle *box;
+ PSOutPaperSize *size;
+ PSFontFileInfo *ff;
+ GList *names;
+ double userUnit;
+ int pg, w, h, i;
+
+ // initialize
+ ok = gTrue;
+ outputFunc = outputFuncA;
+ outputStream = outputStreamA;
+ fileType = fileTypeA;
+ doc = docA;
+ xref = doc->getXRef();
+ catalog = doc->getCatalog();
+ if ((firstPage = firstPageA) < 1) {
+ firstPage = 1;
+ }
+ if ((lastPage = lastPageA) > doc->getNumPages()) {
+ lastPage = doc->getNumPages();
+ }
+ level = globalParams->getPSLevel();
+ mode = modeA;
+ honorUserUnit = honorUserUnitA;
+ paperWidth = globalParams->getPSPaperWidth();
+ paperHeight = globalParams->getPSPaperHeight();
+ imgLLX = imgLLXA;
+ imgLLY = imgLLYA;
+ imgURX = imgURXA;
+ imgURY = imgURYA;
+ if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) {
+ globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY);
+ }
+ if (paperWidth < 0 || paperHeight < 0) {
+ paperMatch = gTrue;
+ paperSizes = new GList();
+ paperWidth = paperHeight = 1; // in case the document has zero pages
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ page = catalog->getPage(pg);
+ if (honorUserUnit) {
+ userUnit = page->getUserUnit();
+ } else {
+ userUnit = 1;
+ }
+ if (globalParams->getPSUseCropBoxAsPage()) {
+ w = (int)ceil(page->getCropWidth() * userUnit);
+ h = (int)ceil(page->getCropHeight() * userUnit);
+ } else {
+ w = (int)ceil(page->getMediaWidth() * userUnit);
+ h = (int)ceil(page->getMediaHeight() * userUnit);
+ }
+ for (i = 0; i < paperSizes->getLength(); ++i) {
+ size = (PSOutPaperSize *)paperSizes->get(i);
+ if (size->w == w && size->h == h) {
+ break;
+ }
+ }
+ if (i == paperSizes->getLength()) {
+ paperSizes->append(new PSOutPaperSize(w, h));
+ }
+ if (w > paperWidth) {
+ paperWidth = w;
+ }
+ if (h > paperHeight) {
+ paperHeight = h;
+ }
+ }
+ // NB: img{LLX,LLY,URX,URY} will be set by startPage()
+ } else {
+ paperMatch = gFalse;
+ }
+ preload = globalParams->getPSPreload();
+ manualCtrl = manualCtrlA;
+ if (mode == psModeForm) {
+ lastPage = firstPage;
+ }
+ processColors = 0;
+ inType3Char = gFalse;
+
+#if OPI_SUPPORT
+ // initialize OPI nesting levels
+ opi13Nest = 0;
+ opi20Nest = 0;
+#endif
+
+ tx0 = ty0 = -1;
+ xScale0 = yScale0 = 0;
+ rotate0 = -1;
+ clipLLX0 = clipLLY0 = 0;
+ clipURX0 = clipURY0 = -1;
+
+ // initialize font lists, etc.
+ for (i = 0; i < 14; ++i) {
+ ff = new PSFontFileInfo(new GString(psBase14SubstFonts[i].psName),
+ fontType1, psFontFileResident);
+ fontFileInfo->add(ff->psName, ff);
+ }
+ names = globalParams->getPSResidentFonts();
+ for (i = 0; i < names->getLength(); ++i) {
+ if (!fontFileInfo->lookup((GString *)names->get(i))) {
+ ff = new PSFontFileInfo((GString *)names->get(i), fontType1,
+ psFontFileResident);
+ fontFileInfo->add(ff->psName, ff);
+ }
+ }
+ delete names;
+ imgIDLen = 0;
+ imgIDSize = 0;
+ formIDLen = 0;
+ formIDSize = 0;
+
+ noStateChanges = gFalse;
+ saveStack = new GList();
+ numTilingPatterns = 0;
+ nextFunc = 0;
+
+ // initialize embedded font resource comment list
+ embFontList = new GString();
+
+ if (!manualCtrl) {
+ // this check is needed in case the document has zero pages
+ if (firstPage <= catalog->getNumPages()) {
+ writeHeader(catalog->getPage(firstPage)->getMediaBox(),
+ catalog->getPage(firstPage)->getCropBox(),
+ catalog->getPage(firstPage)->getRotate());
+ } else {
+ box = new PDFRectangle(0, 0, 1, 1);
+ writeHeader(box, box, 0);
+ delete box;
+ }
+ if (mode != psModeForm) {
+ writePS("%%BeginProlog\n");
+ }
+ writeXpdfProcset();
+ if (mode != psModeForm) {
+ writePS("%%EndProlog\n");
+ writePS("%%BeginSetup\n");
+ }
+ writeDocSetup(catalog);
+ if (mode != psModeForm) {
+ writePS("%%EndSetup\n");
+ }
+ }
+
+ // initialize sequential page number
+ seqPage = 1;
+}
+
+PSOutputDev::~PSOutputDev() {
+ PSOutCustomColor *cc;
+
+ if (ok) {
+ if (!manualCtrl) {
+ writePS("%%Trailer\n");
+ writeTrailer();
+ if (mode != psModeForm) {
+ writePS("%%EOF\n");
+ }
+ }
+ if (fileType == psFile) {
+ fclose((FILE *)outputStream);
+ }
+#ifdef HAVE_POPEN
+ else if (fileType == psPipe) {
+ pclose((FILE *)outputStream);
+#ifndef _WIN32
+ signal(SIGPIPE, (SignalFunc)SIG_DFL);
+#endif
+ }
+#endif
+ }
+ gfree(rasterizePage);
+ if (paperSizes) {
+ deleteGList(paperSizes, PSOutPaperSize);
+ }
+ if (embFontList) {
+ delete embFontList;
+ }
+ deleteGList(fontInfo, PSFontInfo);
+ deleteGHash(fontFileInfo, PSFontFileInfo);
+ gfree(imgIDs);
+ gfree(formIDs);
+ if (saveStack) {
+ delete saveStack;
+ }
+ while (customColors) {
+ cc = customColors;
+ customColors = cc->next;
+ delete cc;
+ }
+}
+
+GBool PSOutputDev::checkIO() {
+ if (fileType == psFile || fileType == psPipe || fileType == psStdout) {
+ if (ferror((FILE *)outputStream)) {
+ error(errIO, -1, "Error writing to PostScript file");
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
+void PSOutputDev::writeHeader(PDFRectangle *mediaBox, PDFRectangle *cropBox,
+ int pageRotate) {
+ Object info, obj1;
+ PSOutPaperSize *size;
+ double x1, y1, x2, y2;
+ int i;
+
+ switch (mode) {
+ case psModePS:
+ writePS("%!PS-Adobe-3.0\n");
+ break;
+ case psModeEPS:
+ writePS("%!PS-Adobe-3.0 EPSF-3.0\n");
+ break;
+ case psModeForm:
+ writePS("%!PS-Adobe-3.0 Resource-Form\n");
+ break;
+ }
+
+ writePSFmt("%XpdfVersion: {0:s}\n", xpdfVersion);
+ xref->getDocInfo(&info);
+ if (info.isDict() && info.dictLookup("Creator", &obj1)->isString()) {
+ writePS("%%Creator: ");
+ writePSTextLine(obj1.getString());
+ }
+ obj1.free();
+ if (info.isDict() && info.dictLookup("Title", &obj1)->isString()) {
+ writePS("%%Title: ");
+ writePSTextLine(obj1.getString());
+ }
+ obj1.free();
+ info.free();
+ writePSFmt("%%LanguageLevel: {0:d}\n",
+ level >= psLevel3 ? 3 : level >= psLevel2 ? 2 : 1);
+ if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {
+ writePS("%%DocumentProcessColors: (atend)\n");
+ writePS("%%DocumentCustomColors: (atend)\n");
+ }
+ writePS("%%DocumentSuppliedResources: (atend)\n");
+
+ switch (mode) {
+ case psModePS:
+ if (paperMatch) {
+ for (i = 0; i < paperSizes->getLength(); ++i) {
+ size = (PSOutPaperSize *)paperSizes->get(i);
+ writePSFmt("%%{0:s} {1:d}x{2:d} {1:d} {2:d} 0 () ()\n",
+ i==0 ? "DocumentMedia:" : "+", size->w, size->h);
+ }
+ } else {
+ writePSFmt("%%DocumentMedia: plain {0:d} {1:d} 0 () ()\n",
+ paperWidth, paperHeight);
+ }
+ writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight);
+ writePSFmt("%%Pages: {0:d}\n", lastPage - firstPage + 1);
+ writePS("%%EndComments\n");
+ if (!paperMatch) {
+ writePS("%%BeginDefaults\n");
+ writePS("%%PageMedia: plain\n");
+ writePS("%%EndDefaults\n");
+ }
+ break;
+ case psModeEPS:
+ epsX1 = cropBox->x1;
+ epsY1 = cropBox->y1;
+ epsX2 = cropBox->x2;
+ epsY2 = cropBox->y2;
+ if (pageRotate == 0 || pageRotate == 180) {
+ x1 = epsX1;
+ y1 = epsY1;
+ x2 = epsX2;
+ y2 = epsY2;
+ } else { // pageRotate == 90 || pageRotate == 270
+ x1 = 0;
+ y1 = 0;
+ x2 = epsY2 - epsY1;
+ y2 = epsX2 - epsX1;
+ }
+ writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n",
+ (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2));
+ if (floor(x1) != ceil(x1) || floor(y1) != ceil(y1) ||
+ floor(x2) != ceil(x2) || floor(y2) != ceil(y2)) {
+ writePSFmt("%%HiResBoundingBox: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n",
+ x1, y1, x2, y2);
+ }
+ writePS("%%EndComments\n");
+ break;
+ case psModeForm:
+ writePS("%%EndComments\n");
+ writePS("32 dict dup begin\n");
+ writePSFmt("/BBox [{0:d} {1:d} {2:d} {3:d}] def\n",
+ (int)floor(mediaBox->x1), (int)floor(mediaBox->y1),
+ (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2));
+ writePS("/FormType 1 def\n");
+ writePS("/Matrix [1 0 0 1 0 0] def\n");
+ break;
+ }
+}
+
+void PSOutputDev::writeXpdfProcset() {
+ GBool lev1, lev2, lev3, nonSep, gray, sep;
+ const char **p;
+ const char *q;
+ double w;
+
+ writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion);
+ writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright);
+ lev1 = lev2 = lev3 = nonSep = gray = sep = gTrue;
+ for (p = prolog; *p; ++p) {
+ if ((*p)[0] == '~') {
+ lev1 = lev2 = lev3 = nonSep = gray = sep = gFalse;
+ for (q = *p + 1; *q; ++q) {
+ switch (*q) {
+ case '1': lev1 = gTrue; break;
+ case '2': lev2 = gTrue; break;
+ case '3': lev3 = gTrue; break;
+ case 'g': gray = gTrue; break;
+ case 'n': nonSep = gTrue; break;
+ case 's': sep = gTrue; break;
+ }
+ }
+ } else if ((level == psLevel1 && lev1 && nonSep) ||
+ (level == psLevel1Sep && lev1 && sep) ||
+ (level == psLevel2 && lev2 && nonSep) ||
+ (level == psLevel2Gray && lev2 && gray) ||
+ (level == psLevel2Sep && lev2 && sep) ||
+ (level == psLevel3 && lev3 && nonSep) ||
+ (level == psLevel3Gray && lev3 && gray) ||
+ (level == psLevel3Sep && lev3 && sep)) {
+ writePSFmt("{0:s}\n", *p);
+ }
+ }
+ if ((w = globalParams->getPSMinLineWidth()) > 0) {
+ writePSFmt("/pdfMinLineWidth {0:.4g} def\n", w);
+ for (p = minLineWidthProlog; *p; ++p) {
+ writePSFmt("{0:s}\n", *p);
+ }
+ }
+ writePS("%%EndResource\n");
+
+ if (level >= psLevel3) {
+ for (p = cmapProlog; *p; ++p) {
+ writePSFmt("{0:s}\n", *p);
+ }
+ }
+}
+
+void PSOutputDev::writeDocSetup(Catalog *catalog) {
+ Page *page;
+ Dict *resDict;
+ Annots *annots;
+ AcroForm *form;
+ Object obj1, obj2, obj3;
+ GString *s;
+ GBool needDefaultFont;
+ int pg, i, j;
+
+ // check to see which pages will be rasterized
+ if (firstPage <= lastPage) {
+ rasterizePage = (char *)gmalloc(lastPage - firstPage + 1);
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ rasterizePage[pg - firstPage] = (char)checkIfPageNeedsToBeRasterized(pg);
+ }
+ } else {
+ rasterizePage = NULL;
+ }
+
+ visitedResources = (char *)gmalloc(xref->getNumObjects());
+ memset(visitedResources, 0, xref->getNumObjects());
+
+ if (mode == psModeForm) {
+ // swap the form and xpdf dicts
+ writePS("xpdf end begin dup begin\n");
+ } else {
+ writePS("xpdf begin\n");
+ }
+ needDefaultFont = gFalse;
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ if (rasterizePage[pg - firstPage]) {
+ continue;
+ }
+ page = catalog->getPage(pg);
+ if ((resDict = page->getResourceDict())) {
+ setupResources(resDict);
+ }
+ annots = new Annots(doc, page->getAnnots(&obj1));
+ obj1.free();
+ if (annots->getNumAnnots()) {
+ needDefaultFont = gTrue;
+ }
+ for (i = 0; i < annots->getNumAnnots(); ++i) {
+ if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
+ obj1.streamGetDict()->lookup("Resources", &obj2);
+ if (obj2.isDict()) {
+ setupResources(obj2.getDict());
+ }
+ obj2.free();
+ }
+ obj1.free();
+ }
+ delete annots;
+ }
+ if ((form = catalog->getForm())) {
+ if (form->getNumFields() > 0) {
+ needDefaultFont = gTrue;
+ }
+ for (i = 0; i < form->getNumFields(); ++i) {
+ form->getField(i)->getResources(&obj1);
+ if (obj1.isArray()) {
+ for (j = 0; j < obj1.arrayGetLength(); ++j) {
+ obj1.arrayGet(j, &obj2);
+ if (obj2.isDict()) {
+ setupResources(obj2.getDict());
+ }
+ obj2.free();
+ }
+ } else if (obj1.isDict()) {
+ setupResources(obj1.getDict());
+ }
+ obj1.free();
+ }
+ }
+ if (needDefaultFont) {
+ setupDefaultFont();
+ }
+ if (mode != psModeForm) {
+ if (mode != psModeEPS && !manualCtrl) {
+ writePSFmt("{0:s} pdfSetup\n",
+ globalParams->getPSDuplex() ? "true" : "false");
+ if (!paperMatch) {
+ writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth, paperHeight);
+ }
+ }
+#if OPI_SUPPORT
+ if (globalParams->getPSOPI()) {
+ writePS("/opiMatrix matrix currentmatrix def\n");
+ }
+#endif
+ }
+ if (customCodeCbk) {
+ if ((s = (*customCodeCbk)(this, psOutCustomDocSetup, 0,
+ customCodeCbkData))) {
+ writePS(s->getCString());
+ delete s;
+ }
+ }
+ if (mode != psModeForm) {
+ writePS("end\n");
+ }
+
+ gfree(visitedResources);
+ visitedResources = NULL;
+}
+
+void PSOutputDev::writePageTrailer() {
+ if (mode != psModeForm) {
+ writePS("pdfEndPage\n");
+ }
+}
+
+void PSOutputDev::writeTrailer() {
+ PSOutCustomColor *cc;
+
+ if (mode == psModeForm) {
+ writePS("/Foo exch /Form defineresource pop\n");
+ } else {
+ writePS("%%DocumentSuppliedResources:\n");
+ writePS(embFontList->getCString());
+ if (level == psLevel1Sep || level == psLevel2Sep ||
+ level == psLevel3Sep) {
+ writePS("%%DocumentProcessColors:");
+ if (processColors & psProcessCyan) {
+ writePS(" Cyan");
+ }
+ if (processColors & psProcessMagenta) {
+ writePS(" Magenta");
+ }
+ if (processColors & psProcessYellow) {
+ writePS(" Yellow");
+ }
+ if (processColors & psProcessBlack) {
+ writePS(" Black");
+ }
+ writePS("\n");
+ writePS("%%DocumentCustomColors:");
+ for (cc = customColors; cc; cc = cc->next) {
+ writePS(" ");
+ writePSString(cc->name);
+ }
+ writePS("\n");
+ writePS("%%CMYKCustomColor:\n");
+ for (cc = customColors; cc; cc = cc->next) {
+ writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ",
+ cc->c, cc->m, cc->y, cc->k);
+ writePSString(cc->name);
+ writePS("\n");
+ }
+ }
+ }
+}
+
+GBool PSOutputDev::checkIfPageNeedsToBeRasterized(int pg) {
+ PreScanOutputDev *scan;
+ GBool rasterize;
+
+ if (globalParams->getPSAlwaysRasterize()) {
+ rasterize = gTrue;
+ } else {
+ scan = new PreScanOutputDev();
+ //~ this could depend on the printing flag, e.g., if an annotation
+ //~ uses transparency --> need to pass the printing flag into
+ //~ constructor, init, writeDocSetup
+ doc->getCatalog()->getPage(pg)->display(scan, 72, 72, 0,
+ gTrue, gTrue, gTrue);
+ rasterize = scan->usesTransparency() || scan->usesPatternImageMask();
+ delete scan;
+ if (rasterize && globalParams->getPSNeverRasterize()) {
+ error(errSyntaxWarning, -1,
+ "PDF page uses transparency and the psNeverRasterize option is "
+ "set - output may not be correct");
+ rasterize = gFalse;
+ }
+ }
+ return rasterize;
+}
+
+void PSOutputDev::setupResources(Dict *resDict) {
+ Object xObjDict, xObjRef, xObj, patDict, patRef, pat;
+ Object gsDict, gsRef, gs, smask, smaskGroup, resObj;
+ Ref ref0;
+ GBool skip;
+ int i;
+
+ setupFonts(resDict);
+ setupImages(resDict);
+
+ //----- recursively scan XObjects
+ resDict->lookup("XObject", &xObjDict);
+ if (xObjDict.isDict()) {
+ for (i = 0; i < xObjDict.dictGetLength(); ++i) {
+
+ // check for an already-visited XObject
+ skip = gFalse;
+ if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) {
+ ref0 = xObjRef.getRef();
+ if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) {
+ skip = gTrue;
+ } else {
+ skip = (GBool)visitedResources[ref0.num];
+ visitedResources[ref0.num] = 1;
+ }
+ }
+ if (!skip) {
+
+ // process the XObject's resource dictionary
+ xObjDict.dictGetVal(i, &xObj);
+ if (xObj.isStream()) {
+ xObj.streamGetDict()->lookup("Resources", &resObj);
+ if (resObj.isDict()) {
+ setupResources(resObj.getDict());
+ }
+ resObj.free();
+ }
+ xObj.free();
+ }
+
+ xObjRef.free();
+ }
+ }
+ xObjDict.free();
+
+ //----- recursively scan Patterns
+ resDict->lookup("Pattern", &patDict);
+ if (patDict.isDict()) {
+ inType3Char = gTrue;
+ for (i = 0; i < patDict.dictGetLength(); ++i) {
+
+ // check for an already-visited Pattern
+ skip = gFalse;
+ if ((patDict.dictGetValNF(i, &patRef)->isRef())) {
+ ref0 = patRef.getRef();
+ if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) {
+ skip = gTrue;
+ } else {
+ skip = (GBool)visitedResources[ref0.num];
+ visitedResources[ref0.num] = 1;
+ }
+ }
+ if (!skip) {
+
+ // process the Pattern's resource dictionary
+ patDict.dictGetVal(i, &pat);
+ if (pat.isStream()) {
+ pat.streamGetDict()->lookup("Resources", &resObj);
+ if (resObj.isDict()) {
+ setupResources(resObj.getDict());
+ }
+ resObj.free();
+ }
+ pat.free();
+ }
+
+ patRef.free();
+ }
+ inType3Char = gFalse;
+ }
+ patDict.free();
+
+
+ //----- recursively scan SMask transparency groups in ExtGState dicts
+ resDict->lookup("ExtGState", &gsDict);
+ if (gsDict.isDict()) {
+ for (i = 0; i < gsDict.dictGetLength(); ++i) {
+
+ // check for an already-visited ExtGState
+ skip = gFalse;
+ if ((gsDict.dictGetValNF(i, &gsRef)->isRef())) {
+ ref0 = gsRef.getRef();
+ if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) {
+ skip = gTrue;
+ } else {
+ skip = (GBool)visitedResources[ref0.num];
+ visitedResources[ref0.num] = 1;
+ }
+ }
+ if (!skip) {
+
+ // process the ExtGState's SMask's transparency group's resource dict
+ if (gsDict.dictGetVal(i, &gs)->isDict()) {
+ if (gs.dictLookup("SMask", &smask)->isDict()) {
+ if (smask.dictLookup("G", &smaskGroup)->isStream()) {
+ smaskGroup.streamGetDict()->lookup("Resources", &resObj);
+ if (resObj.isDict()) {
+ setupResources(resObj.getDict());
+ }
+ resObj.free();
+ }
+ smaskGroup.free();
+ }
+ smask.free();
+ }
+ gs.free();
+ }
+
+ gsRef.free();
+ }
+ }
+ gsDict.free();
+
+ setupForms(resDict);
+}
+
+void PSOutputDev::setupFonts(Dict *resDict) {
+ Object obj1, obj2;
+ Ref r;
+ GfxFontDict *gfxFontDict;
+ GfxFont *font;
+ int i;
+
+ gfxFontDict = NULL;
+ resDict->lookupNF("Font", &obj1);
+ if (obj1.isRef()) {
+ obj1.fetch(xref, &obj2);
+ if (obj2.isDict()) {
+ r = obj1.getRef();
+ gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict());
+ }
+ obj2.free();
+ } else if (obj1.isDict()) {
+ gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict());
+ }
+ if (gfxFontDict) {
+ for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
+ if ((font = gfxFontDict->getFont(i))) {
+ setupFont(font, resDict);
+ }
+ }
+ delete gfxFontDict;
+ }
+ obj1.free();
+}
+
+void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) {
+ PSFontInfo *fi;
+ GfxFontLoc *fontLoc;
+ GBool subst;
+ char buf[16];
+ UnicodeMap *uMap;
+ char *charName;
+ double xs, ys;
+ int code;
+ double w1, w2;
+ int i, j;
+
+ // check if font is already set up
+ for (i = 0; i < fontInfo->getLength(); ++i) {
+ fi = (PSFontInfo *)fontInfo->get(i);
+ if (fi->fontID.num == font->getID()->num &&
+ fi->fontID.gen == font->getID()->gen) {
+ return;
+ }
+ }
+
+ // add fontInfo entry
+ fi = new PSFontInfo(*font->getID());
+ fontInfo->append(fi);
+
+ xs = ys = 1;
+ subst = gFalse;
+
+ if (font->getType() == fontType3) {
+ fi->ff = setupType3Font(font, parentResDict);
+ } else {
+ if ((fontLoc = font->locateFont(xref, gTrue))) {
+ switch (fontLoc->locType) {
+ case gfxFontLocEmbedded:
+ switch (fontLoc->fontType) {
+ case fontType1:
+ fi->ff = setupEmbeddedType1Font(font, &fontLoc->embFontID);
+ break;
+ case fontType1C:
+ fi->ff = setupEmbeddedType1CFont(font, &fontLoc->embFontID);
+ break;
+ case fontType1COT:
+ fi->ff = setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID);
+ break;
+ case fontTrueType:
+ case fontTrueTypeOT:
+ fi->ff = setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID);
+ break;
+ case fontCIDType0C:
+ fi->ff = setupEmbeddedCIDType0Font(font, &fontLoc->embFontID);
+ break;
+ case fontCIDType2:
+ case fontCIDType2OT:
+ //~ should check to see if font actually uses vertical mode
+ fi->ff = setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID,
+ gTrue);
+ break;
+ case fontCIDType0COT:
+ fi->ff = setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID);
+ break;
+ default:
+ break;
+ }
+ break;
+ case gfxFontLocExternal:
+ //~ add cases for other external 16-bit fonts
+ switch (fontLoc->fontType) {
+ case fontType1:
+ fi->ff = setupExternalType1Font(font, fontLoc->path);
+ break;
+ case fontTrueType:
+ case fontTrueTypeOT:
+ fi->ff = setupExternalTrueTypeFont(font, fontLoc->path,
+ fontLoc->fontNum);
+ break;
+ case fontCIDType2:
+ case fontCIDType2OT:
+ //~ should check to see if font actually uses vertical mode
+ fi->ff = setupExternalCIDTrueTypeFont(font, fontLoc->path,
+ fontLoc->fontNum, gTrue);
+ break;
+ case fontCIDType0COT:
+ fi->ff = setupExternalOpenTypeCFFFont(font, fontLoc->path);
+ break;
+ default:
+ break;
+ }
+ break;
+ case gfxFontLocResident:
+ if (!(fi->ff = (PSFontFileInfo *)fontFileInfo->lookup(fontLoc->path))) {
+ // handle psFontPassthrough
+ fi->ff = new PSFontFileInfo(fontLoc->path->copy(), fontLoc->fontType,
+ psFontFileResident);
+ fontFileInfo->add(fi->ff->psName, fi->ff);
+ }
+ break;
+ }
+ }
+
+ if (!fi->ff) {
+ if (font->isCIDFont()) {
+ error(errSyntaxError, -1,
+ "Couldn't find a font for '{0:s}' ('{1:s}' character collection)",
+ font->getName() ? font->getName()->getCString()
+ : "(unnamed)",
+ ((GfxCIDFont *)font)->getCollection()
+ ? ((GfxCIDFont *)font)->getCollection()->getCString()
+ : "(unknown)");
+ } else {
+ error(errSyntaxError, -1,
+ "Couldn't find a font for '{0:s}'",
+ font->getName() ? font->getName()->getCString()
+ : "(unnamed)");
+ }
+ delete fontLoc;
+ return;
+ }
+
+ // scale substituted 8-bit fonts
+ if (fontLoc->locType == gfxFontLocResident &&
+ fontLoc->substIdx >= 0) {
+ subst = gTrue;
+ for (code = 0; code < 256; ++code) {
+ if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) &&
+ charName[0] == 'm' && charName[1] == '\0') {
+ break;
+ }
+ }
+ if (code < 256) {
+ w1 = ((Gfx8BitFont *)font)->getWidth((Guchar)code);
+ } else {
+ w1 = 0;
+ }
+ w2 = psBase14SubstFonts[fontLoc->substIdx].mWidth;
+ xs = w1 / w2;
+ if (xs < 0.1) {
+ xs = 1;
+ }
+ }
+
+ // handle encodings for substituted CID fonts
+ if (fontLoc->locType == gfxFontLocResident &&
+ fontLoc->fontType >= fontCIDType0) {
+ subst = gTrue;
+ 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);
+ }
+ }
+ }
+
+ delete fontLoc;
+ }
+
+ // generate PostScript code to set up the font
+ if (font->isCIDFont()) {
+ if (level >= psLevel3) {
+ writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n",
+ font->getID()->num, font->getID()->gen, fi->ff->psName,
+ font->getWMode());
+ } else {
+ writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n",
+ font->getID()->num, font->getID()->gen, fi->ff->psName,
+ font->getWMode());
+ }
+ } else {
+ writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n",
+ font->getID()->num, font->getID()->gen, fi->ff->psName, xs, ys);
+ for (i = 0; i < 256; i += 8) {
+ writePS((char *)((i == 0) ? "[ " : " "));
+ for (j = 0; j < 8; ++j) {
+ if (font->getType() == fontTrueType &&
+ !subst &&
+ !((Gfx8BitFont *)font)->getHasEncoding()) {
+ snprintf(buf, sizeof(buf), "c%02x", i+j);
+ charName = buf;
+ } else {
+ charName = ((Gfx8BitFont *)font)->getCharName(i+j);
+ }
+ writePS("/");
+ writePSName(charName ? charName : (char *)".notdef");
+ // the empty name is legal in PDF and PostScript, but PostScript
+ // uses a double-slash (//...) for "immediately evaluated names",
+ // so we need to add a space character here
+ if (charName && !charName[0]) {
+ writePS(" ");
+ }
+ }
+ writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n");
+ }
+ writePS("pdfMakeFont\n");
+ }
+}
+
+PSFontFileInfo *PSOutputDev::setupEmbeddedType1Font(GfxFont *font, Ref *id) {
+ GString *psName, *origFont, *cleanFont;
+ PSFontFileInfo *ff;
+ Object refObj, strObj, obj1, obj2;
+ Dict *dict;
+ char buf[4096];
+ GBool rename;
+ int length1, length2, n;
+
+ // check if font is already embedded
+ if (!font->getEmbeddedFontName()) {
+ rename = gTrue;
+ } else if ((ff = (PSFontFileInfo *)
+ fontFileInfo->lookup(font->getEmbeddedFontName()))) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ return ff;
+ }
+ rename = gTrue;
+ } else {
+ rename = gFalse;
+ }
+
+ // generate name
+ // (this assumes that the PS font name matches the PDF font name)
+ if (rename) {
+ psName = makePSFontName(font, id);
+ } else {
+ psName = font->getEmbeddedFontName()->copy();
+ }
+
+ // get the font stream and info
+ refObj.initRef(id->num, id->gen);
+ refObj.fetch(xref, &strObj);
+ refObj.free();
+ if (!strObj.isStream()) {
+ error(errSyntaxError, -1, "Embedded font file object is not a stream");
+ goto err1;
+ }
+ if (!(dict = strObj.streamGetDict())) {
+ error(errSyntaxError, -1,
+ "Embedded font stream is missing its dictionary");
+ goto err1;
+ }
+ dict->lookup("Length1", &obj1);
+ dict->lookup("Length2", &obj2);
+ if (!obj1.isInt() || !obj2.isInt()) {
+ error(errSyntaxError, -1,
+ "Missing length fields in embedded font stream dictionary");
+ obj1.free();
+ obj2.free();
+ goto err1;
+ }
+ length1 = obj1.getInt();
+ length2 = obj2.getInt();
+ obj1.free();
+ obj2.free();
+
+ // read the font file
+ origFont = new GString();
+ strObj.streamReset();
+ while ((n = strObj.streamGetBlock(buf, sizeof(buf))) > 0) {
+ origFont->append(buf, n);
+ }
+ strObj.streamClose();
+ strObj.free();
+
+ // beginning comment
+ writePSFmt("%%BeginResource: font {0:t}\n", psName);
+ embFontList->append("%%+ font ");
+ embFontList->append(psName->getCString());
+ embFontList->append("\n");
+
+ // clean up the font file
+ cleanFont = fixType1Font(origFont, length1, length2);
+ if (rename) {
+ renameType1Font(cleanFont, psName);
+ }
+ writePSBlock(cleanFont->getCString(), cleanFont->getLength());
+ delete cleanFont;
+ delete origFont;
+
+ // ending comment
+ writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+
+ err1:
+ strObj.free();
+ delete psName;
+ return NULL;
+}
+
+PSFontFileInfo *PSOutputDev::setupExternalType1Font(GfxFont *font,
+ GString *fileName) {
+ static char hexChar[17] = "0123456789abcdef";
+ GString *psName;
+ PSFontFileInfo *ff;
+ FILE *fontFile;
+ int buf[6];
+ int c, n, i;
+
+ if (font->getName()) {
+ // check if font is already embedded
+ if ((ff = (PSFontFileInfo *)fontFileInfo->lookup(font->getName()))) {
+ return ff;
+ }
+ // this assumes that the PS font name matches the PDF font name
+ psName = font->getName()->copy();
+ } else {
+ // generate name
+ //~ this won't work -- the PS font name won't match
+ psName = makePSFontName(font, font->getID());
+ }
+
+ // beginning comment
+ writePSFmt("%%BeginResource: font {0:t}\n", psName);
+ embFontList->append("%%+ font ");
+ embFontList->append(psName->getCString());
+ embFontList->append("\n");
+
+ // open the font file
+ if (!(fontFile = fopen(fileName->getCString(), "rb"))) {
+ error(errIO, -1, "Couldn't open external font file");
+ delete psName;
+ return NULL;
+ }
+
+ // check for PFB format
+ buf[0] = fgetc(fontFile);
+ buf[1] = fgetc(fontFile);
+ if (buf[0] == 0x80 && buf[1] == 0x01) {
+ while (1) {
+ for (i = 2; i < 6; ++i) {
+ buf[i] = fgetc(fontFile);
+ }
+ if (buf[2] == EOF || buf[3] == EOF || buf[4] == EOF || buf[5] == EOF) {
+ break;
+ }
+ n = buf[2] + (buf[3] << 8) + (buf[4] << 16) + (buf[5] << 24);
+ if (buf[1] == 0x01) {
+ for (i = 0; i < n; ++i) {
+ if ((c = fgetc(fontFile)) == EOF) {
+ break;
+ }
+ writePSChar((char)c);
+ }
+ } else {
+ for (i = 0; i < n; ++i) {
+ if ((c = fgetc(fontFile)) == EOF) {
+ break;
+ }
+ writePSChar(hexChar[(c >> 4) & 0x0f]);
+ writePSChar(hexChar[c & 0x0f]);
+ if (i % 32 == 31) {
+ writePSChar('\n');
+ }
+ }
+ }
+ buf[0] = fgetc(fontFile);
+ buf[1] = fgetc(fontFile);
+ if (buf[0] == EOF || buf[1] == EOF ||
+ (buf[0] == 0x80 && buf[1] == 0x03)) {
+ break;
+ } else if (!(buf[0] == 0x80 &&
+ (buf[1] == 0x01 || buf[1] == 0x02))) {
+ error(errSyntaxError, -1,
+ "Invalid PFB header in external font file");
+ break;
+ }
+ }
+ writePSChar('\n');
+
+ // plain text (PFA) format
+ } else {
+ writePSChar((char)buf[0]);
+ writePSChar((char)buf[1]);
+ while ((c = fgetc(fontFile)) != EOF) {
+ writePSChar((char)c);
+ }
+ }
+
+ fclose(fontFile);
+
+ // ending comment
+ writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+}
+
+PSFontFileInfo *PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
+ char *fontBuf;
+ int fontLen;
+ FoFiType1C *ffT1C;
+ GHashIter *iter;
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, id);
+
+ // beginning comment
+ writePSFmt("%%BeginResource: font {0:t}\n", psName);
+ embFontList->append("%%+ font ");
+ embFontList->append(psName->getCString());
+ embFontList->append("\n");
+
+ // convert it to a Type 1 font
+ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+ if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
+ ffT1C->convertToType1(psName->getCString(), NULL, gTrue,
+ outputFunc, outputStream);
+ delete ffT1C;
+ }
+ gfree(fontBuf);
+ }
+
+ // ending comment
+ writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+}
+
+PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font,
+ Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
+ char *fontBuf;
+ int fontLen;
+ FoFiTrueType *ffTT;
+ GHashIter *iter;
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, id);
+
+ // beginning comment
+ writePSFmt("%%BeginResource: font {0:t}\n", psName);
+ embFontList->append("%%+ font ");
+ embFontList->append(psName->getCString());
+ embFontList->append("\n");
+
+ // convert it to a Type 1 font
+ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0, gTrue))) {
+ if (ffTT->isOpenTypeCFF()) {
+ ffTT->convertToType1(psName->getCString(), NULL, gTrue,
+ outputFunc, outputStream);
+ }
+ delete ffTT;
+ }
+ gfree(fontBuf);
+ }
+
+ // ending comment
+ writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+}
+
+PSFontFileInfo *PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
+ char *fontBuf;
+ int fontLen;
+ FoFiTrueType *ffTT;
+ int *codeToGID;
+ GHashIter *iter;
+
+ // get the code-to-GID mapping
+ if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+ return NULL;
+ }
+ if (!(ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
+ gfree(fontBuf);
+ return NULL;
+ }
+ codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->type == font->getType() &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen &&
+ ff->codeToGIDLen == 256 &&
+ !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) {
+ fontFileInfo->killIter(&iter);
+ gfree(codeToGID);
+ delete ffTT;
+ gfree(fontBuf);
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, id);
+
+ // beginning comment
+ writePSFmt("%%BeginResource: font {0:t}\n", psName);
+ embFontList->append("%%+ font ");
+ embFontList->append(psName->getCString());
+ embFontList->append("\n");
+
+ // convert it to a Type 42 font
+ ffTT->convertToType42(psName->getCString(),
+ ((Gfx8BitFont *)font)->getHasEncoding()
+ ? ((Gfx8BitFont *)font)->getEncoding()
+ : (char **)NULL,
+ codeToGID, outputFunc, outputStream);
+ delete ffTT;
+ gfree(fontBuf);
+
+ // ending comment
+ writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = 256;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+}
+
+PSFontFileInfo *PSOutputDev::setupExternalTrueTypeFont(GfxFont *font,
+ GString *fileName,
+ int fontNum) {
+ GString *psName;
+ PSFontFileInfo *ff;
+ FoFiTrueType *ffTT;
+ int *codeToGID;
+ GHashIter *iter;
+
+ // get the code-to-GID mapping
+ if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+ return NULL;
+ }
+ codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileExternal &&
+ ff->type == font->getType() &&
+ !ff->extFileName->cmp(fileName) &&
+ ff->codeToGIDLen == 256 &&
+ !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) {
+ fontFileInfo->killIter(&iter);
+ gfree(codeToGID);
+ delete ffTT;
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, font->getID());
+
+ // beginning comment
+ writePSFmt("%%BeginResource: font {0:t}\n", psName);
+ embFontList->append("%%+ font ");
+ embFontList->append(psName->getCString());
+ embFontList->append("\n");
+
+ // convert it to a Type 42 font
+ ffTT->convertToType42(psName->getCString(),
+ ((Gfx8BitFont *)font)->getHasEncoding()
+ ? ((Gfx8BitFont *)font)->getEncoding()
+ : (char **)NULL,
+ codeToGID, outputFunc, outputStream);
+ delete ffTT;
+
+ // ending comment
+ writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = 256;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+}
+
+PSFontFileInfo *PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
+ char *fontBuf;
+ int fontLen;
+ FoFiType1C *ffT1C;
+ GHashIter *iter;
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, id);
+
+ // beginning comment
+ writePSFmt("%%BeginResource: font {0:t}\n", psName);
+ embFontList->append("%%+ font ");
+ embFontList->append(psName->getCString());
+ embFontList->append("\n");
+
+ // convert it to a Type 0 font
+ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+ if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) {
+ if (globalParams->getPSLevel() >= psLevel3) {
+ // Level 3: use a CID font
+ ffT1C->convertToCIDType0(psName->getCString(),
+ ((GfxCIDFont *)font)->getCIDToGID(),
+ ((GfxCIDFont *)font)->getCIDToGIDLen(),
+ outputFunc, outputStream);
+ } else {
+ // otherwise: use a non-CID composite font
+ ffT1C->convertToType0(psName->getCString(),
+ ((GfxCIDFont *)font)->getCIDToGID(),
+ ((GfxCIDFont *)font)->getCIDToGIDLen(),
+ outputFunc, outputStream);
+ }
+ delete ffT1C;
+ }
+ gfree(fontBuf);
+ }
+
+ // ending comment
+ writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+}
+
+PSFontFileInfo *PSOutputDev::setupEmbeddedCIDTrueTypeFont(
+ GfxFont *font, Ref *id,
+ GBool needVerticalMetrics) {
+ GString *psName;
+ PSFontFileInfo *ff;
+ char *fontBuf;
+ int fontLen;
+ FoFiTrueType *ffTT;
+ int *codeToGID;
+ int codeToGIDLen;
+ GHashIter *iter;
+
+ // get the code-to-GID mapping
+ codeToGID = ((GfxCIDFont *)font)->getCIDToGID();
+ codeToGIDLen = ((GfxCIDFont *)font)->getCIDToGIDLen();
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->type == font->getType() &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen &&
+ ff->codeToGIDLen == codeToGIDLen &&
+ ((!ff->codeToGID && !codeToGID) ||
+ (ff->codeToGID && codeToGID &&
+ !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))))) {
+ fontFileInfo->killIter(&iter);
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, id);
+
+ // beginning comment
+ writePSFmt("%%BeginResource: font {0:t}\n", psName);
+ embFontList->append("%%+ font ");
+ embFontList->append(psName->getCString());
+ embFontList->append("\n");
+
+ // convert it to a Type 0 font
+ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) {
+ if (globalParams->getPSLevel() >= psLevel3) {
+ // Level 3: use a CID font
+ ffTT->convertToCIDType2(psName->getCString(),
+ codeToGID, codeToGIDLen,
+ needVerticalMetrics,
+ outputFunc, outputStream);
+ } else {
+ // otherwise: use a non-CID composite font
+ ffTT->convertToType0(psName->getCString(),
+ codeToGID, codeToGIDLen,
+ needVerticalMetrics,
+ outputFunc, outputStream);
+ }
+ delete ffTT;
+ }
+ gfree(fontBuf);
+ }
+
+ // ending comment
+ writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ if (codeToGIDLen) {
+ ff->codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
+ memcpy(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int));
+ ff->codeToGIDLen = codeToGIDLen;
+ }
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+}
+
+PSFontFileInfo *PSOutputDev::setupExternalCIDTrueTypeFont(
+ GfxFont *font,
+ GString *fileName,
+ int fontNum,
+ GBool needVerticalMetrics) {
+ GString *psName;
+ PSFontFileInfo *ff;
+ FoFiTrueType *ffTT;
+ int *codeToGID;
+ int codeToGIDLen;
+ CharCodeToUnicode *ctu;
+ Unicode uBuf[8];
+ int cmap, cmapPlatform, cmapEncoding, code;
+ GHashIter *iter;
+
+ // create a code-to-GID mapping, via Unicode
+ if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+ return NULL;
+ }
+ if (!(ctu = ((GfxCIDFont *)font)->getToUnicode())) {
+ error(errSyntaxError, -1,
+ "Couldn't find a mapping to Unicode for font '{0:s}'",
+ font->getName() ? font->getName()->getCString() : "(unnamed)");
+ delete ffTT;
+ return NULL;
+ }
+ // look for a Unicode cmap
+ for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) {
+ cmapPlatform = ffTT->getCmapPlatform(cmap);
+ cmapEncoding = ffTT->getCmapEncoding(cmap);
+ if ((cmapPlatform == 3 && cmapEncoding == 1) ||
+ (cmapPlatform == 0 && cmapEncoding <= 4)) {
+ break;
+ }
+ }
+ if (cmap >= ffTT->getNumCmaps()) {
+ error(errSyntaxError, -1,
+ "Couldn't find a Unicode cmap in font '{0:s}'",
+ font->getName() ? font->getName()->getCString() : "(unnamed)");
+ ctu->decRefCnt();
+ delete ffTT;
+ return NULL;
+ }
+ // map CID -> Unicode -> GID
+ if (ctu->isIdentity()) {
+ codeToGIDLen = 65536;
+ } else {
+ codeToGIDLen = ctu->getLength();
+ }
+ codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
+ for (code = 0; code < codeToGIDLen; ++code) {
+ if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
+ codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]);
+ } else {
+ codeToGID[code] = 0;
+ }
+ }
+ ctu->decRefCnt();
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileExternal &&
+ ff->type == font->getType() &&
+ !ff->extFileName->cmp(fileName) &&
+ ff->codeToGIDLen == codeToGIDLen &&
+ ff->codeToGID &&
+ !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))) {
+ fontFileInfo->killIter(&iter);
+ gfree(codeToGID);
+ delete ffTT;
+ return ff;
+ }
+ }
+
+ // check for embedding permission
+ if (ffTT->getEmbeddingRights() < 1) {
+ error(errSyntaxError, -1,
+ "TrueType font '{0:s}' does not allow embedding",
+ font->getName() ? font->getName()->getCString() : "(unnamed)");
+ gfree(codeToGID);
+ delete ffTT;
+ return NULL;
+ }
+
+ // generate name
+ psName = makePSFontName(font, font->getID());
+
+ // beginning comment
+ writePSFmt("%%BeginResource: font {0:t}\n", psName);
+ embFontList->append("%%+ font ");
+ embFontList->append(psName->getCString());
+ embFontList->append("\n");
+
+ // convert it to a Type 0 font
+ //~ this should use fontNum to load the correct font
+ if (globalParams->getPSLevel() >= psLevel3) {
+ // Level 3: use a CID font
+ ffTT->convertToCIDType2(psName->getCString(),
+ codeToGID, codeToGIDLen,
+ needVerticalMetrics,
+ outputFunc, outputStream);
+ } else {
+ // otherwise: use a non-CID composite font
+ ffTT->convertToType0(psName->getCString(),
+ codeToGID, codeToGIDLen,
+ needVerticalMetrics,
+ outputFunc, outputStream);
+ }
+ delete ffTT;
+
+ // ending comment
+ writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = codeToGIDLen;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+}
+
+PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font,
+ Ref *id) {
+ GString *psName;
+ PSFontFileInfo *ff;
+ char *fontBuf;
+ int fontLen;
+ FoFiTrueType *ffTT;
+ GHashIter *iter;
+ int n;
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileEmbedded &&
+ ff->embFontID.num == id->num &&
+ ff->embFontID.gen == id->gen) {
+ fontFileInfo->killIter(&iter);
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, id);
+
+ // beginning comment
+ writePSFmt("%%BeginResource: font {0:t}\n", psName);
+ embFontList->append("%%+ font ");
+ embFontList->append(psName->getCString());
+ embFontList->append("\n");
+
+ // convert it to a Type 0 font
+ if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+ if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0, gTrue))) {
+ if (ffTT->isOpenTypeCFF()) {
+ if (globalParams->getPSLevel() >= psLevel3) {
+ // Level 3: use a CID font
+ ffTT->convertToCIDType0(psName->getCString(),
+ ((GfxCIDFont *)font)->getCIDToGID(),
+ ((GfxCIDFont *)font)->getCIDToGIDLen(),
+ outputFunc, outputStream);
+ } else {
+ // otherwise: use a non-CID composite font
+ ffTT->convertToType0(psName->getCString(),
+ ((GfxCIDFont *)font)->getCIDToGID(),
+ ((GfxCIDFont *)font)->getCIDToGIDLen(),
+ outputFunc, outputStream);
+ }
+ }
+ delete ffTT;
+ }
+ gfree(fontBuf);
+ }
+
+ // ending comment
+ writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ ff->embFontID = *id;
+ if ((n = ((GfxCIDFont *)font)->getCIDToGIDLen())) {
+ ff->codeToGID = (int *)gmallocn(n, sizeof(int));
+ memcpy(ff->codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), n * sizeof(int));
+ ff->codeToGIDLen = n;
+ }
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+}
+
+// This assumes an OpenType CFF font that has a Unicode cmap (in the
+// OpenType section), and a CFF blob that uses an identity CID-to-GID
+// mapping.
+PSFontFileInfo *PSOutputDev::setupExternalOpenTypeCFFFont(GfxFont *font,
+ GString *fileName) {
+ GString *psName;
+ PSFontFileInfo *ff;
+ FoFiTrueType *ffTT;
+ GHashIter *iter;
+ CharCodeToUnicode *ctu;
+ Unicode uBuf[8];
+ int *codeToGID;
+ int codeToGIDLen;
+ int cmap, cmapPlatform, cmapEncoding, code;
+
+ // create a code-to-GID mapping, via Unicode
+ if (!(ffTT = FoFiTrueType::load(fileName->getCString(), 0, gTrue))) {
+ return NULL;
+ }
+ if (!ffTT->isOpenTypeCFF()) {
+ delete ffTT;
+ return NULL;
+ }
+ if (!(ctu = ((GfxCIDFont *)font)->getToUnicode())) {
+ error(errSyntaxError, -1,
+ "Couldn't find a mapping to Unicode for font '{0:s}'",
+ font->getName() ? font->getName()->getCString() : "(unnamed)");
+ delete ffTT;
+ return NULL;
+ }
+ // look for a Unicode cmap
+ for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) {
+ cmapPlatform = ffTT->getCmapPlatform(cmap);
+ cmapEncoding = ffTT->getCmapEncoding(cmap);
+ if ((cmapPlatform == 3 && cmapEncoding == 1) ||
+ (cmapPlatform == 0 && cmapEncoding <= 4)) {
+ break;
+ }
+ }
+ if (cmap >= ffTT->getNumCmaps()) {
+ error(errSyntaxError, -1,
+ "Couldn't find a Unicode cmap in font '{0:s}'",
+ font->getName() ? font->getName()->getCString() : "(unnamed)");
+ ctu->decRefCnt();
+ delete ffTT;
+ return NULL;
+ }
+ // map CID -> Unicode -> GID
+ if (ctu->isIdentity()) {
+ codeToGIDLen = 65536;
+ } else {
+ codeToGIDLen = ctu->getLength();
+ }
+ codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int));
+ for (code = 0; code < codeToGIDLen; ++code) {
+ if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
+ codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]);
+ } else {
+ codeToGID[code] = 0;
+ }
+ }
+ ctu->decRefCnt();
+
+ // check if font is already embedded
+ fontFileInfo->startIter(&iter);
+ while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) {
+ if (ff->loc == psFontFileExternal &&
+ ff->type == font->getType() &&
+ !ff->extFileName->cmp(fileName) &&
+ ff->codeToGIDLen == codeToGIDLen &&
+ ff->codeToGID &&
+ !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))) {
+ fontFileInfo->killIter(&iter);
+ gfree(codeToGID);
+ delete ffTT;
+ return ff;
+ }
+ }
+
+ // generate name
+ psName = makePSFontName(font, font->getID());
+
+ // beginning comment
+ writePSFmt("%%BeginResource: font {0:t}\n", psName);
+ embFontList->append("%%+ font ");
+ embFontList->append(psName->getCString());
+ embFontList->append("\n");
+
+ // convert it to a Type 0 font
+ if (globalParams->getPSLevel() >= psLevel3) {
+ // Level 3: use a CID font
+ ffTT->convertToCIDType0(psName->getCString(),
+ codeToGID, codeToGIDLen,
+ outputFunc, outputStream);
+ } else {
+ // otherwise: use a non-CID composite font
+ ffTT->convertToType0(psName->getCString(),
+ codeToGID, codeToGIDLen,
+ outputFunc, outputStream);
+ }
+ delete ffTT;
+
+ // ending comment
+ writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal);
+ ff->extFileName = fileName->copy();
+ ff->codeToGID = codeToGID;
+ ff->codeToGIDLen = codeToGIDLen;
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+}
+
+PSFontFileInfo *PSOutputDev::setupType3Font(GfxFont *font,
+ Dict *parentResDict) {
+ PSFontFileInfo *ff;
+ GString *psName;
+ Dict *resDict;
+ Dict *charProcs;
+ Object charProc;
+ Gfx *gfx;
+ PDFRectangle box;
+ double *m;
+ GString *buf;
+ int i;
+
+ // generate name
+ psName = GString::format("T3_{0:d}_{1:d}",
+ font->getID()->num, font->getID()->gen);
+
+ // set up resources used by font
+ if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
+ inType3Char = gTrue;
+ setupResources(resDict);
+ inType3Char = gFalse;
+ } else {
+ resDict = parentResDict;
+ }
+
+ // beginning comment
+ writePSFmt("%%BeginResource: font {0:t}\n", psName);
+ embFontList->append("%%+ font ");
+ embFontList->append(psName->getCString());
+ embFontList->append("\n");
+
+ // font dictionary
+ writePS("8 dict begin\n");
+ writePS("/FontType 3 def\n");
+ m = font->getFontMatrix();
+ writePSFmt("/FontMatrix [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n",
+ m[0], m[1], m[2], m[3], m[4], m[5]);
+ m = font->getFontBBox();
+ writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n",
+ m[0], m[1], m[2], m[3]);
+ writePS("/Encoding 256 array def\n");
+ writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
+ writePS("/BuildGlyph {\n");
+ writePS(" exch /CharProcs get exch\n");
+ writePS(" 2 copy known not { pop /.notdef } if\n");
+ writePS(" get exec\n");
+ writePS("} bind def\n");
+ writePS("/BuildChar {\n");
+ writePS(" 1 index /Encoding get exch get\n");
+ writePS(" 1 index /BuildGlyph get exec\n");
+ writePS("} bind def\n");
+ if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) {
+ writePSFmt("/CharProcs {0:d} dict def\n", charProcs->getLength());
+ writePS("CharProcs begin\n");
+ box.x1 = m[0];
+ box.y1 = m[1];
+ box.x2 = m[2];
+ box.y2 = m[3];
+ gfx = new Gfx(doc, this, resDict, &box, NULL);
+ inType3Char = gTrue;
+ for (i = 0; i < charProcs->getLength(); ++i) {
+ t3FillColorOnly = gFalse;
+ t3Cacheable = gFalse;
+ t3NeedsRestore = gFalse;
+ writePS("/");
+ writePSName(charProcs->getKey(i));
+ writePS(" {\n");
+ gfx->display(charProcs->getValNF(i, &charProc));
+ charProc.free();
+ if (t3String) {
+ if (t3Cacheable) {
+ buf = GString::format("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} setcachedevice\n",
+ t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);
+ } else {
+ buf = GString::format("{0:.6g} {1:.6g} setcharwidth\n", t3WX, t3WY);
+ }
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ (*outputFunc)(outputStream, t3String->getCString(),
+ t3String->getLength());
+ delete t3String;
+ t3String = NULL;
+ }
+ if (t3NeedsRestore) {
+ (*outputFunc)(outputStream, "Q\n", 2);
+ }
+ writePS("} def\n");
+ }
+ inType3Char = gFalse;
+ delete gfx;
+ writePS("end\n");
+ }
+ writePS("currentdict end\n");
+ writePSFmt("/{0:t} exch definefont pop\n", psName);
+
+ // ending comment
+ writePS("%%EndResource\n");
+
+ ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded);
+ fontFileInfo->add(ff->psName, ff);
+ return ff;
+}
+
+// Make a unique PS font name, based on the names given in the PDF
+// font object, and an object ID (font file object for
+GString *PSOutputDev::makePSFontName(GfxFont *font, Ref *id) {
+ GString *psName, *s;
+
+ if ((s = font->getEmbeddedFontName())) {
+ psName = filterPSName(s);
+ if (!fontFileInfo->lookup(psName)) {
+ return psName;
+ }
+ delete psName;
+ }
+ if ((s = font->getName())) {
+ psName = filterPSName(s);
+ if (!fontFileInfo->lookup(psName)) {
+ return psName;
+ }
+ delete psName;
+ }
+ psName = GString::format("FF{0:d}_{1:d}", id->num, id->gen);
+ if ((s = font->getEmbeddedFontName())) {
+ s = filterPSName(s);
+ psName->append('_')->append(s);
+ delete s;
+ } else if ((s = font->getName())) {
+ s = filterPSName(s);
+ psName->append('_')->append(s);
+ delete s;
+ }
+ return psName;
+}
+
+GString *PSOutputDev::fixType1Font(GString *font, int length1, int length2) {
+ Guchar *fontData;
+ GString *out, *binSection;
+ GBool pfb;
+ int fontSize, i;
+
+ fontData = (Guchar *)font->getCString();
+ fontSize = font->getLength();
+
+ // check for PFB
+ pfb = fontSize >= 6 && fontData[0] == 0x80 && fontData[1] == 0x01;
+ out = new GString();
+ binSection = new GString();
+ if (pfb) {
+ if (!splitType1PFB(fontData, fontSize, out, binSection)) {
+ delete out;
+ delete binSection;
+ return copyType1PFB(fontData, fontSize);
+ }
+ } else {
+ if (!splitType1PFA(fontData, fontSize, length1, length2,
+ out, binSection)) {
+ delete out;
+ delete binSection;
+ return copyType1PFA(fontData, fontSize);
+ }
+ }
+
+ out->append('\n');
+
+ binSection = asciiHexDecodeType1EexecSection(binSection);
+
+ if (!fixType1EexecSection(binSection, out)) {
+ delete out;
+ delete binSection;
+ return pfb ? copyType1PFB(fontData, fontSize)
+ : copyType1PFA(fontData, fontSize);
+ }
+ delete binSection;
+
+ for (i = 0; i < 8; ++i) {
+ out->append("0000000000000000000000000000000000000000000000000000000000000000\n");
+ }
+ out->append("cleartomark\n");
+
+ return out;
+}
+
+// Split a Type 1 font in PFA format into a text section and a binary
+// section.
+GBool PSOutputDev::splitType1PFA(Guchar *font, int fontSize,
+ int length1, int length2,
+ GString *textSection, GString *binSection) {
+ int textLength, binStart, binLength, lastSpace, i;
+
+ //--- extract the text section
+
+ // Length1 is correct, and the text section ends with whitespace
+ if (length1 <= fontSize &&
+ length1 >= 18 &&
+ !memcmp(font + length1 - 18, "currentfile eexec", 17)) {
+ textLength = length1 - 1;
+
+ // Length1 is correct, but the trailing whitespace is missing
+ } else if (length1 <= fontSize &&
+ length1 >= 17 &&
+ !memcmp(font + length1 - 17, "currentfile eexec", 17)) {
+ textLength = length1;
+
+ // Length1 is incorrect
+ } else {
+ for (textLength = 17; textLength <= fontSize; ++textLength) {
+ if (!memcmp(font + textLength - 17, "currentfile eexec", 17)) {
+ break;
+ }
+ }
+ if (textLength > fontSize) {
+ return gFalse;
+ }
+ }
+
+ textSection->append((char *)font, textLength);
+
+ //--- skip whitespace between the text section and the binary section
+
+ for (i = 0, binStart = textLength;
+ i < 8 && binStart < fontSize;
+ ++i, ++binStart) {
+ if (font[binStart] != ' ' && font[binStart] != '\t' &&
+ font[binStart] != '\n' && font[binStart] != '\r') {
+ break;
+ }
+ }
+ if (i == 8) {
+ return gFalse;
+ }
+
+ //--- extract binary section
+
+ // if we see "0000", assume Length2 is correct
+ // (if Length2 is too long, it will be corrected by fixType1EexecSection)
+ if (length2 > 0 && length2 < INT_MAX - 4 &&
+ binStart <= fontSize - length2 - 4 &&
+ !memcmp(font + binStart + length2, "0000", 4)) {
+ binLength = length2;
+
+ } else {
+
+ // look for "0000" near the end of the font (note that there can
+ // be intervening "\n", "\r\n", etc.), then search backward
+ if (fontSize - binStart < 512) {
+ return gFalse;
+ }
+ if (!memcmp(font + fontSize - 256, "0000", 4) ||
+ !memcmp(font + fontSize - 255, "0000", 4) ||
+ !memcmp(font + fontSize - 254, "0000", 4) ||
+ !memcmp(font + fontSize - 253, "0000", 4) ||
+ !memcmp(font + fontSize - 252, "0000", 4) ||
+ !memcmp(font + fontSize - 251, "0000", 4)) {
+ i = fontSize - 252;
+ lastSpace = -1;
+ while (i >= binStart) {
+ if (font[i] == ' ' || font[i] == '\t' ||
+ font[i] == '\n' || font[i] == '\r') {
+ lastSpace = i;
+ --i;
+ } else if (font[i] == '0') {
+ --i;
+ } else {
+ break;
+ }
+ }
+ if (lastSpace < 0) {
+ return gFalse;
+ }
+ // check for the case where the newline/space is missing between
+ // the binary section and the first set of 64 '0' chars
+ if (lastSpace - binStart > 64 &&
+ !memcmp(font + lastSpace - 64,
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ 64)) {
+ binLength = lastSpace - 64 - binStart;
+ } else {
+ binLength = lastSpace - binStart;
+ }
+
+ // couldn't find zeros after binary section -- assume they're
+ // missing and the binary section extends to the end of the file
+ } else {
+ binLength = fontSize - binStart;
+ }
+ }
+
+ binSection->append((char *)(font + binStart), binLength);
+
+ return gTrue;
+}
+
+// Split a Type 1 font in PFB format into a text section and a binary
+// section.
+GBool PSOutputDev::splitType1PFB(Guchar *font, int fontSize,
+ GString *textSection, GString *binSection) {
+ Guchar *p;
+ int state, remain, len, n;
+
+ // states:
+ // 0: text section
+ // 1: binary section
+ // 2: trailer section
+ // 3: eof
+
+ state = 0;
+ p = font;
+ remain = fontSize;
+ while (remain >= 2) {
+ if (p[0] != 0x80) {
+ return gFalse;
+ }
+ switch (state) {
+ case 0:
+ if (p[1] == 0x02) {
+ state = 1;
+ } else if (p[1] != 0x01) {
+ return gFalse;
+ }
+ break;
+ case 1:
+ if (p[1] == 0x01) {
+ state = 2;
+ } else if (p[1] != 0x02) {
+ return gFalse;
+ }
+ break;
+ case 2:
+ if (p[1] == 0x03) {
+ state = 3;
+ } else if (p[1] != 0x01) {
+ return gFalse;
+ }
+ break;
+ default: // shouldn't happen
+ return gFalse;
+ }
+ if (state == 3) {
+ break;
+ }
+
+ if (remain < 6) {
+ break;
+ }
+ len = p[2] + (p[3] << 8) + (p[4] << 16) + (p[5] << 24);
+ if (len < 0 || len > remain - 6) {
+ return gFalse;
+ }
+
+ switch (state) {
+ case 0:
+ textSection->append((char *)(p + 6), len);
+ break;
+ case 1:
+ binSection->append((char *)(p + 6), len);
+ break;
+ case 2:
+ // we don't use the trailer
+ break;
+ default: // shouldn't happen
+ return gFalse;
+ }
+
+ p += len + 6;
+ remain -= len + 6;
+ }
+
+ if (state != 3) {
+ return gFalse;
+ }
+
+ n = textSection->getLength();
+ if (n >= 18 && !memcmp(textSection->getCString() + n - 18,
+ "currentfile eexec", 17)) {
+ // remove the trailing whitespace
+ textSection->del(n - 1, 1);
+ } else if (n >= 17 && !memcmp(textSection->getCString() + n - 17,
+ "currentfile eexec", 17)) {
+ // missing whitespace at end -- leave as-is
+ } else {
+ return gFalse;
+ }
+
+ return gTrue;
+}
+
+// If <in> is ASCIIHex-encoded, decode it, delete <in>, and return the
+// binary version. Else return <in> unchanged.
+GString *PSOutputDev::asciiHexDecodeType1EexecSection(GString *in) {
+ GString *out;
+ char c;
+ Guchar byte;
+ int state, i;
+
+ out = new GString();
+ state = 0;
+ byte = 0;
+ for (i = 0; i < in->getLength(); ++i) {
+ c = in->getChar(i);
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
+ continue;
+ }
+ if (c >= '0' && c <= '9') {
+ byte = (Guchar)(byte + (c - '0'));
+ } else if (c >= 'A' && c <= 'F') {
+ byte = (Guchar)(byte + (c - 'A' + 10));
+ } else if (c >= 'a' && c <= 'f') {
+ byte = (Guchar)(byte + (c - 'a' + 10));
+ } else {
+ delete out;
+ return in;
+ }
+ if (state == 0) {
+ byte = (Guchar)(byte << 4);
+ state = 1;
+ } else {
+ out->append((char)byte);
+ state = 0;
+ byte = 0;
+ }
+ }
+ delete in;
+ return out;
+}
+
+GBool PSOutputDev::fixType1EexecSection(GString *binSection, GString *out) {
+ static char hexChars[17] = "0123456789abcdef";
+ Guchar buf[16], buf2[16];
+ Guchar byte;
+ int r, i, j;
+
+ // eexec-decode the binary section, keeping the last 16 bytes
+ r = 55665;
+ for (i = 0; i < binSection->getLength(); ++i) {
+ byte = (Guchar)binSection->getChar(i);
+ buf[i & 15] = byte ^ (Guchar)(r >> 8);
+ r = ((r + byte) * 52845 + 22719) & 0xffff;
+ }
+ for (j = 0; j < 16; ++j) {
+ buf2[j] = buf[(i + j) & 15];
+ }
+
+ // look for 'closefile'
+ for (i = 0; i <= 16 - 9; ++i) {
+ if (!memcmp(buf2 + i, "closefile", 9)) {
+ break;
+ }
+ }
+ if (i > 16 - 9) {
+ return gFalse;
+ }
+ // three cases:
+ // - short: missing space after "closefile" (i == 16 - 9)
+ // - correct: exactly one space after "closefile" (i == 16 - 10)
+ // - long: extra chars after "closefile" (i < 16 - 10)
+ if (i == 16 - 9) {
+ binSection->append((char)((Guchar)'\n' ^ (Guchar)(r >> 8)));
+ } else if (i < 16 - 10) {
+ binSection->del(binSection->getLength() - (16 - 10 - i), 16 - 10 - i);
+ }
+
+ // ASCIIHex encode
+ for (i = 0; i < binSection->getLength(); i += 32) {
+ for (j = 0; j < 32 && i+j < binSection->getLength(); ++j) {
+ byte = (Guchar)binSection->getChar(i+j);
+ out->append(hexChars[(byte >> 4) & 0x0f]);
+ out->append(hexChars[byte & 0x0f]);
+ }
+ out->append('\n');
+ }
+
+ return gTrue;
+}
+
+// The Type 1 cleanup code failed -- assume it's a valid PFA-format
+// font and copy it to the output.
+GString *PSOutputDev::copyType1PFA(Guchar *font, int fontSize) {
+ GString *out;
+
+ error(errSyntaxWarning, -1, "Couldn't parse embedded Type 1 font");
+
+ out = new GString((char *)font, fontSize);
+ // append a newline to avoid problems where the original font
+ // doesn't end with one
+ out->append('\n');
+ return out;
+}
+
+// The Type 1 cleanup code failed -- assume it's a valid PFB-format
+// font, decode the PFB blocks, and copy them to the output.
+GString *PSOutputDev::copyType1PFB(Guchar *font, int fontSize) {
+ static char hexChars[17] = "0123456789abcdef";
+ GString *out;
+ Guchar *p;
+ int remain, len, i, j;
+
+ error(errSyntaxWarning, -1, "Couldn't parse embedded Type 1 (PFB) font");
+
+ out = new GString();
+ p = font;
+ remain = fontSize;
+ while (remain >= 6 &&
+ p[0] == 0x80 &&
+ (p[1] == 0x01 || p[1] == 0x02)) {
+ len = p[2] + (p[3] << 8) + (p[4] << 16) + (p[5] << 24);
+ if (len < 0 || len > remain - 6) {
+ break;
+ }
+ if (p[1] == 0x01) {
+ out->append((char *)(p + 6), len);
+ } else {
+ for (i = 0; i < len; i += 32) {
+ for (j = 0; j < 32 && i+j < len; ++j) {
+ out->append(hexChars[(p[6+i+j] >> 4) & 0x0f]);
+ out->append(hexChars[p[6+i+j] & 0x0f]);
+ }
+ out->append('\n');
+ }
+ }
+ p += len + 6;
+ remain -= len + 6;
+ }
+ // append a newline to avoid problems where the original font
+ // doesn't end with one
+ out->append('\n');
+ return out;
+}
+
+void PSOutputDev::renameType1Font(GString *font, GString *name) {
+ char *p1, *p2;
+ int i;
+
+ if (!(p1 = strstr(font->getCString(), "\n/FontName")) &&
+ !(p1 = strstr(font->getCString(), "\r/FontName"))) {
+ return;
+ }
+ p1 += 10;
+ while (*p1 == ' ' || *p1 == '\t' || *p1 == '\n' || *p1 == '\r') {
+ ++p1;
+ }
+ if (*p1 != '/') {
+ return;
+ }
+ ++p1;
+ p2 = p1;
+ while (*p2 && *p2 != ' ' && *p2 != '\t' && *p2 != '\n' && *p2 != '\r') {
+ ++p2;
+ }
+ if (!*p2) {
+ return;
+ }
+ i = (int)(p1 - font->getCString());
+ font->del(i, (int)(p2 - p1));
+ font->insert(i, name);
+}
+
+void PSOutputDev::setupDefaultFont() {
+ writePS("/xpdf_default_font /Helvetica 1 1 ISOLatin1Encoding pdfMakeFont\n");
+}
+
+void PSOutputDev::setupImages(Dict *resDict) {
+ Object xObjDict, xObj, xObjRef, subtypeObj, maskObj, maskRef;
+ Ref imgID;
+ int i, j;
+
+ if (!(mode == psModeForm || inType3Char || preload)) {
+ return;
+ }
+
+ resDict->lookup("XObject", &xObjDict);
+ if (xObjDict.isDict()) {
+ for (i = 0; i < xObjDict.dictGetLength(); ++i) {
+ xObjDict.dictGetValNF(i, &xObjRef);
+ xObjDict.dictGetVal(i, &xObj);
+ if (xObj.isStream()) {
+ xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
+ if (subtypeObj.isName("Image")) {
+ if (xObjRef.isRef()) {
+ imgID = xObjRef.getRef();
+ for (j = 0; j < imgIDLen; ++j) {
+ if (imgIDs[j].num == imgID.num && imgIDs[j].gen == imgID.gen) {
+ break;
+ }
+ }
+ if (j == imgIDLen) {
+ if (imgIDLen >= imgIDSize) {
+ if (imgIDSize == 0) {
+ imgIDSize = 64;
+ } else {
+ imgIDSize *= 2;
+ }
+ imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref));
+ }
+ imgIDs[imgIDLen++] = imgID;
+ setupImage(imgID, xObj.getStream(), gFalse, NULL);
+ if (level >= psLevel3) {
+ xObj.streamGetDict()->lookup("Mask", &maskObj);
+ if (maskObj.isStream()) {
+ setupImage(imgID, maskObj.getStream(), gTrue, NULL);
+ } else if (level == psLevel3Gray && maskObj.isArray()) {
+ setupImage(imgID, xObj.getStream(), gFalse,
+ maskObj.getArray());
+ }
+ maskObj.free();
+ }
+ }
+ } else {
+ error(errSyntaxError, -1,
+ "Image in resource dict is not an indirect reference");
+ }
+ }
+ subtypeObj.free();
+ }
+ xObj.free();
+ xObjRef.free();
+ }
+ }
+ xObjDict.free();
+}
+
+void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask,
+ Array *colorKeyMask) {
+ StreamColorSpaceMode csMode;
+ GfxColorSpace *colorSpace;
+ GfxImageColorMap *colorMap;
+ int maskColors[2*gfxColorMaxComps];
+ Object obj1;
+ GBool imageMask, useLZW, useRLE, useCompressed, useASCIIHex;
+ GString *s;
+ int c, width, height, bits, size, line, col, i;
+
+ // check for mask
+ str->getDict()->lookup("ImageMask", &obj1);
+ if (obj1.isBool()) {
+ imageMask = obj1.getBool();
+ } else {
+ imageMask = gFalse;
+ }
+ obj1.free();
+
+ // get image size
+ str->getDict()->lookup("Width", &obj1);
+ if (!obj1.isInt() || obj1.getInt() <= 0) {
+ error(errSyntaxError, -1, "Invalid Width in image");
+ obj1.free();
+ return;
+ }
+ width = obj1.getInt();
+ obj1.free();
+ str->getDict()->lookup("Height", &obj1);
+ if (!obj1.isInt() || obj1.getInt() <= 0) {
+ error(errSyntaxError, -1, "Invalid Height in image");
+ obj1.free();
+ return;
+ }
+ height = obj1.getInt();
+ obj1.free();
+
+ // build the color map
+ if (mask || imageMask) {
+ colorMap = NULL;
+ } else {
+ bits = 0;
+ csMode = streamCSNone;
+ str->getImageParams(&bits, &csMode);
+ if (bits == 0) {
+ str->getDict()->lookup("BitsPerComponent", &obj1);
+ if (!obj1.isInt()) {
+ error(errSyntaxError, -1, "Invalid BitsPerComponent in image");
+ obj1.free();
+ return;
+ }
+ bits = obj1.getInt();
+ obj1.free();
+ }
+ str->getDict()->lookup("ColorSpace", &obj1);
+ if (!obj1.isNull()) {
+ colorSpace = GfxColorSpace::parse(&obj1
+ );
+ } else if (csMode == streamCSDeviceGray) {
+ colorSpace = GfxColorSpace::create(csDeviceGray);
+ } else if (csMode == streamCSDeviceRGB) {
+ colorSpace = GfxColorSpace::create(csDeviceRGB);
+ } else if (csMode == streamCSDeviceCMYK) {
+ colorSpace = GfxColorSpace::create(csDeviceCMYK);
+ } else {
+ colorSpace = NULL;
+ }
+ obj1.free();
+ if (!colorSpace) {
+ error(errSyntaxError, -1, "Invalid ColorSpace in image");
+ return;
+ }
+ str->getDict()->lookup("Decode", &obj1);
+ colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
+ obj1.free();
+ }
+
+ // filters
+ if (level < psLevel2) {
+ useLZW = useRLE = gFalse;
+ useCompressed = gFalse;
+ useASCIIHex = gTrue;
+ } else {
+ if (colorKeyMask) {
+ if (globalParams->getPSUncompressPreloadedImages()) {
+ useLZW = useRLE = gFalse;
+ } else if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
+ useCompressed = gFalse;
+ } else if (colorMap &&
+ (colorMap->getColorSpace()->getMode() == csDeviceN ||
+ level == psLevel2Gray || level == psLevel3Gray)) {
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
+ useCompressed = gFalse;
+ } else if (globalParams->getPSUncompressPreloadedImages()) {
+ useLZW = useRLE = gFalse;
+ useCompressed = gFalse;
+ } else {
+ s = str->getPSFilter(level < psLevel3 ? 2 : 3, "", gTrue);
+ if (s) {
+ useLZW = useRLE = gFalse;
+ useCompressed = gTrue;
+ delete s;
+ } else {
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
+ useCompressed = gFalse;
+ }
+ }
+ useASCIIHex = globalParams->getPSASCIIHex();
+ }
+ if (useCompressed) {
+ str = str->getUndecodedStream();
+ }
+ if (colorKeyMask) {
+ memset(maskColors, 0, sizeof(maskColors));
+ for (i = 0; i < colorKeyMask->getLength() && i < 2*gfxColorMaxComps; ++i) {
+ colorKeyMask->get(i, &obj1);
+ if (obj1.isInt()) {
+ maskColors[i] = obj1.getInt();
+ }
+ obj1.free();
+ }
+ str = new ColorKeyToMaskEncoder(str, width, height, colorMap, maskColors);
+ } else if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) {
+ str = new GrayRecoder(str, width, height, colorMap);
+ } else if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
+ str = new DeviceNRecoder(str, width, height, colorMap);
+ }
+ if (useLZW) {
+ str = new LZWEncoder(str);
+ } else if (useRLE) {
+ str = new RunLengthEncoder(str);
+ }
+ if (useASCIIHex) {
+ str = new ASCIIHexEncoder(str);
+ } else {
+ str = new ASCII85Encoder(str);
+ }
+
+ // compute image data size
+ str->reset();
+ col = size = 0;
+ do {
+ do {
+ c = str->getChar();
+ } while (c == '\n' || c == '\r');
+ if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
+ break;
+ }
+ if (c == 'z') {
+ ++col;
+ } else {
+ ++col;
+ for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
+ do {
+ c = str->getChar();
+ } while (c == '\n' || c == '\r');
+ if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
+ break;
+ }
+ ++col;
+ }
+ }
+ if (col > 225) {
+ ++size;
+ col = 0;
+ }
+ } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
+ // add one entry for the final line of data; add another entry
+ // because the LZWDecode/RunLengthDecode filter may read past the end
+ ++size;
+ if (useLZW || useRLE) {
+ ++size;
+ }
+ writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n",
+ size, (mask || colorKeyMask) ? "Mask" : "Im", id.num, id.gen);
+ str->close();
+
+ // write the data into the array
+ str->reset();
+ line = col = 0;
+ writePS((char *)(useASCIIHex ? "dup 0 <" : "dup 0 <~"));
+ do {
+ do {
+ c = str->getChar();
+ } while (c == '\n' || c == '\r');
+ if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
+ break;
+ }
+ if (c == 'z') {
+ writePSChar((char)c);
+ ++col;
+ } else {
+ writePSChar((char)c);
+ ++col;
+ for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
+ do {
+ c = str->getChar();
+ } while (c == '\n' || c == '\r');
+ if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
+ break;
+ }
+ writePSChar((char)c);
+ ++col;
+ }
+ }
+ // each line is: "dup nnnnn <~...data...~> put<eol>"
+ // so max data length = 255 - 20 = 235
+ // chunks are 1 or 4 bytes each, so we have to stop at 232
+ // but make it 225 just to be safe
+ if (col > 225) {
+ writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
+ ++line;
+ writePSFmt((char *)(useASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line);
+ col = 0;
+ }
+ } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
+ writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n"));
+ if (useLZW || useRLE) {
+ ++line;
+ writePSFmt("{0:d} <> put\n", line);
+ } else {
+ writePS("pop\n");
+ }
+ str->close();
+
+ delete str;
+
+ if (colorMap) {
+ delete colorMap;
+ }
+}
+
+void PSOutputDev::setupForms(Dict *resDict) {
+ Object xObjDict, xObj, xObjRef, subtypeObj;
+ int i;
+
+ if (!preload) {
+ return;
+ }
+
+ resDict->lookup("XObject", &xObjDict);
+ if (xObjDict.isDict()) {
+ for (i = 0; i < xObjDict.dictGetLength(); ++i) {
+ xObjDict.dictGetValNF(i, &xObjRef);
+ xObjDict.dictGetVal(i, &xObj);
+ if (xObj.isStream()) {
+ xObj.streamGetDict()->lookup("Subtype", &subtypeObj);
+ if (subtypeObj.isName("Form")) {
+ if (xObjRef.isRef()) {
+ setupForm(&xObjRef, &xObj);
+ } else {
+ error(errSyntaxError, -1,
+ "Form in resource dict is not an indirect reference");
+ }
+ }
+ subtypeObj.free();
+ }
+ xObj.free();
+ xObjRef.free();
+ }
+ }
+ xObjDict.free();
+}
+
+void PSOutputDev::setupForm(Object *strRef, Object *strObj) {
+ Dict *dict, *resDict;
+ Object matrixObj, bboxObj, resObj, obj1;
+ double m[6], bbox[4];
+ PDFRectangle box;
+ Gfx *gfx;
+ int i;
+
+ // check if form is already defined
+ for (i = 0; i < formIDLen; ++i) {
+ if (formIDs[i].num == strRef->getRefNum() &&
+ formIDs[i].gen == strRef->getRefGen()) {
+ return;
+ }
+ }
+
+ // add entry to formIDs list
+ if (formIDLen >= formIDSize) {
+ if (formIDSize == 0) {
+ formIDSize = 64;
+ } else {
+ formIDSize *= 2;
+ }
+ formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref));
+ }
+ formIDs[formIDLen++] = strRef->getRef();
+
+ dict = strObj->streamGetDict();
+
+ // get bounding box
+ dict->lookup("BBox", &bboxObj);
+ if (!bboxObj.isArray()) {
+ bboxObj.free();
+ error(errSyntaxError, -1, "Bad form bounding box");
+ return;
+ }
+ for (i = 0; i < 4; ++i) {
+ bboxObj.arrayGet(i, &obj1);
+ bbox[i] = obj1.getNum();
+ obj1.free();
+ }
+ bboxObj.free();
+
+ // get matrix
+ dict->lookup("Matrix", &matrixObj);
+ if (matrixObj.isArray()) {
+ for (i = 0; i < 6; ++i) {
+ matrixObj.arrayGet(i, &obj1);
+ m[i] = obj1.getNum();
+ obj1.free();
+ }
+ } else {
+ m[0] = 1; m[1] = 0;
+ m[2] = 0; m[3] = 1;
+ m[4] = 0; m[5] = 0;
+ }
+ matrixObj.free();
+
+ // get resources
+ dict->lookup("Resources", &resObj);
+ resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
+
+ writePSFmt("/f_{0:d}_{1:d} {{\n", strRef->getRefNum(), strRef->getRefGen());
+ writePS("q\n");
+ writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
+ m[0], m[1], m[2], m[3], m[4], m[5]);
+
+ box.x1 = bbox[0];
+ box.y1 = bbox[1];
+ box.x2 = bbox[2];
+ box.y2 = bbox[3];
+ gfx = new Gfx(doc, this, resDict, &box, &box);
+ gfx->display(strRef);
+ delete gfx;
+
+ writePS("Q\n");
+ writePS("} def\n");
+
+ resObj.free();
+}
+
+
+GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI,
+ int rotateA, GBool useMediaBox, GBool crop,
+ int sliceX, int sliceY,
+ int sliceW, int sliceH,
+ GBool printing,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+ int pg;
+#if HAVE_SPLASH
+ GBool mono;
+ GBool useLZW;
+ double dpi;
+ SplashOutputDev *splashOut;
+ SplashColor paperColor;
+ PDFRectangle box;
+ GfxState *state;
+ SplashBitmap *bitmap;
+ Stream *str0, *str;
+ Object obj;
+ Guchar *p;
+ Guchar col[4];
+ char buf[4096];
+ double userUnit, hDPI2, vDPI2;
+ double m0, m1, m2, m3, m4, m5;
+ int nStripes, stripeH, stripeY;
+ int w, h, x, y, comp, i, n;
+#endif
+
+ pg = page->getNum();
+ if (!(pg >= firstPage && pg <= lastPage &&
+ rasterizePage[pg - firstPage])) {
+ return gTrue;
+ }
+
+#if HAVE_SPLASH
+ // get the rasterization parameters
+ dpi = globalParams->getPSRasterResolution();
+ mono = globalParams->getPSRasterMono() ||
+ level == psLevel1 ||
+ level == psLevel2Gray ||
+ level == psLevel3Gray;
+ useLZW = globalParams->getPSLZW();
+
+ // get the UserUnit
+ if (honorUserUnit) {
+ userUnit = page->getUserUnit();
+ } else {
+ userUnit = 1;
+ }
+
+ // start the PS page
+ page->makeBox(userUnit * dpi, userUnit * dpi, rotateA, useMediaBox, gFalse,
+ sliceX, sliceY, sliceW, sliceH, &box, &crop);
+ rotateA += page->getRotate();
+ if (rotateA >= 360) {
+ rotateA -= 360;
+ } else if (rotateA < 0) {
+ rotateA += 360;
+ }
+ state = new GfxState(dpi, dpi, &box, rotateA, gFalse);
+ startPage(page->getNum(), state);
+ delete state;
+
+ // set up the SplashOutputDev
+ if (mono) {
+ paperColor[0] = 0xff;
+ splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse,
+ paperColor, gFalse,
+ globalParams->getAntialiasPrinting());
+#if SPLASH_CMYK
+ } else if (level == psLevel1Sep) {
+ paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0;
+ splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse,
+ paperColor, gFalse,
+ globalParams->getAntialiasPrinting());
+#endif
+ } else {
+ paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
+ splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse,
+ paperColor, gFalse,
+ globalParams->getAntialiasPrinting());
+ }
+ splashOut->startDoc(xref);
+
+ // break the page into stripes
+ // NB: startPage() has already multiplied xScale and yScale by UserUnit
+ hDPI2 = xScale * dpi;
+ vDPI2 = yScale * dpi;
+ if (sliceW < 0 || sliceH < 0) {
+ if (useMediaBox) {
+ box = *page->getMediaBox();
+ } else {
+ box = *page->getCropBox();
+ }
+ sliceX = sliceY = 0;
+ sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0);
+ if (sliceW == 0) {
+ sliceW = 1;
+ }
+ sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0);
+ if (sliceH == 0) {
+ sliceH = 1;
+ }
+ }
+ nStripes = (int)ceil(((double)sliceW * (double)sliceH) /
+ (double)globalParams->getPSRasterSliceSize());
+ stripeH = (sliceH + nStripes - 1) / nStripes;
+
+ // render the stripes
+ for (stripeY = sliceY; stripeY < sliceH; stripeY += stripeH) {
+
+ // rasterize a stripe
+ page->makeBox(hDPI2, vDPI2, 0, useMediaBox, gFalse,
+ sliceX, stripeY, sliceW, stripeH, &box, &crop);
+ m0 = box.x2 - box.x1;
+ m1 = 0;
+ m2 = 0;
+ m3 = box.y2 - box.y1;
+ m4 = box.x1;
+ m5 = box.y1;
+ page->displaySlice(splashOut, hDPI2, vDPI2,
+ (360 - page->getRotate()) % 360, useMediaBox, crop,
+ sliceX, stripeY, sliceW, stripeH,
+ printing, abortCheckCbk, abortCheckCbkData);
+
+ // draw the rasterized image
+ bitmap = splashOut->getBitmap();
+ w = bitmap->getWidth();
+ h = bitmap->getHeight();
+ writePS("gsave\n");
+ writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n",
+ m0, m1, m2, m3, m4, m5);
+ switch (level) {
+ case psLevel1:
+ writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n",
+ w, h, w, -h, h);
+ p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
+ i = 0;
+ for (y = 0; y < h; ++y) {
+ for (x = 0; x < w; ++x) {
+ writePSFmt("{0:02x}", *p++);
+ if (++i == 32) {
+ writePSChar('\n');
+ i = 0;
+ }
+ }
+ }
+ if (i != 0) {
+ writePSChar('\n');
+ }
+ break;
+ case psLevel1Sep:
+ writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n",
+ w, h, w, -h, h);
+ p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
+ i = 0;
+ col[0] = col[1] = col[2] = col[3] = 0;
+ for (y = 0; y < h; ++y) {
+ for (comp = 0; comp < 4; ++comp) {
+ for (x = 0; x < w; ++x) {
+ writePSFmt("{0:02x}", p[4*x + comp]);
+ col[comp] |= p[4*x + comp];
+ if (++i == 32) {
+ writePSChar('\n');
+ i = 0;
+ }
+ }
+ }
+ p -= bitmap->getRowSize();
+ }
+ if (i != 0) {
+ writePSChar('\n');
+ }
+ if (col[0]) {
+ processColors |= psProcessCyan;
+ }
+ if (col[1]) {
+ processColors |= psProcessMagenta;
+ }
+ if (col[2]) {
+ processColors |= psProcessYellow;
+ }
+ if (col[3]) {
+ processColors |= psProcessBlack;
+ }
+ break;
+ case psLevel2:
+ case psLevel2Gray:
+ case psLevel2Sep:
+ case psLevel3:
+ case psLevel3Gray:
+ case psLevel3Sep:
+ if (mono) {
+ writePS("/DeviceGray setcolorspace\n");
+ } else {
+ writePS("/DeviceRGB setcolorspace\n");
+ }
+ writePS("<<\n /ImageType 1\n");
+ writePSFmt(" /Width {0:d}\n", bitmap->getWidth());
+ writePSFmt(" /Height {0:d}\n", bitmap->getHeight());
+ writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h);
+ writePS(" /BitsPerComponent 8\n");
+ if (mono) {
+ writePS(" /Decode [0 1]\n");
+ } else {
+ writePS(" /Decode [0 1 0 1 0 1]\n");
+ }
+ writePS(" /DataSource currentfile\n");
+ if (globalParams->getPSASCIIHex()) {
+ writePS(" /ASCIIHexDecode filter\n");
+ } else {
+ writePS(" /ASCII85Decode filter\n");
+ }
+ if (useLZW) {
+ writePS(" /LZWDecode filter\n");
+ } else {
+ writePS(" /RunLengthDecode filter\n");
+ }
+ writePS(">>\n");
+ writePS("image\n");
+ obj.initNull();
+ p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize();
+ str0 = new MemStream((char *)p, 0, w * h * (mono ? 1 : 3), &obj);
+ if (useLZW) {
+ str = new LZWEncoder(str0);
+ } else {
+ str = new RunLengthEncoder(str0);
+ }
+ if (globalParams->getPSASCIIHex()) {
+ str = new ASCIIHexEncoder(str);
+ } else {
+ str = new ASCII85Encoder(str);
+ }
+ str->reset();
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
+ }
+ str->close();
+ delete str;
+ delete str0;
+ writePSChar('\n');
+ processColors |= mono ? psProcessBlack : psProcessCMYK;
+ break;
+ }
+ writePS("grestore\n");
+ }
+
+ delete splashOut;
+
+ // finish the PS page
+ endPage();
+
+ return gFalse;
+
+#else // HAVE_SPLASH
+
+ error(errSyntaxWarning, -1,
+ "PDF page uses transparency and PSOutputDev was built without"
+ " the Splash rasterizer - output may not be correct");
+ return gTrue;
+#endif // HAVE_SPLASH
+}
+
+void PSOutputDev::startPage(int pageNum, GfxState *state) {
+ Page *page;
+ double userUnit;
+ int x1, y1, x2, y2, width, height, t;
+ int imgWidth, imgHeight, imgWidth2, imgHeight2;
+ GBool landscape;
+ GString *s;
+
+ page = doc->getCatalog()->getPage(pageNum);
+ if (honorUserUnit) {
+ userUnit = page->getUserUnit();
+ } else {
+ userUnit = 1;
+ }
+
+ if (mode == psModePS) {
+ writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage);
+ if (paperMatch) {
+ imgLLX = imgLLY = 0;
+ if (globalParams->getPSUseCropBoxAsPage()) {
+ imgURX = (int)ceil(page->getCropWidth() * userUnit);
+ imgURY = (int)ceil(page->getCropHeight() * userUnit);
+ } else {
+ imgURX = (int)ceil(page->getMediaWidth() * userUnit);
+ imgURY = (int)ceil(page->getMediaHeight() * userUnit);
+ }
+ if (state->getRotate() == 90 || state->getRotate() == 270) {
+ t = imgURX;
+ imgURX = imgURY;
+ imgURY = t;
+ }
+ writePSFmt("%%PageMedia: {0:d}x{1:d}\n", imgURX, imgURY);
+ writePSFmt("%%PageBoundingBox: 0 0 {0:d} {1:d}\n", imgURX, imgURY);
+ }
+ writePS("%%BeginPageSetup\n");
+ }
+ if (mode != psModeForm) {
+ writePS("xpdf begin\n");
+ }
+
+ // set up paper size for paper=match mode
+ // NB: this must be done *before* the saveState() for overlays.
+ if (mode == psModePS && paperMatch) {
+ writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX, imgURY);
+ }
+
+ // underlays
+ if (underlayCbk) {
+ (*underlayCbk)(this, underlayCbkData);
+ }
+ if (overlayCbk) {
+ saveState(NULL);
+ }
+
+ switch (mode) {
+
+ case psModePS:
+ // rotate, translate, and scale page
+ imgWidth = imgURX - imgLLX;
+ imgHeight = imgURY - imgLLY;
+ x1 = (int)floor(state->getX1());
+ y1 = (int)floor(state->getY1());
+ x2 = (int)ceil(state->getX2());
+ y2 = (int)ceil(state->getY2());
+ width = x2 - x1;
+ height = y2 - y1;
+ tx = ty = 0;
+ // rotation and portrait/landscape mode
+ if (paperMatch) {
+ rotate = (360 - state->getRotate()) % 360;
+ landscape = gFalse;
+ } else if (rotate0 >= 0) {
+ rotate = (360 - rotate0) % 360;
+ 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 ((scaledWidth < scaledHeight && imgWidth > imgHeight &&
+ scaledHeight > imgHeight) ||
+ (scaledWidth > scaledHeight && imgWidth < imgHeight &&
+ scaledWidth > imgWidth)) {
+ rotate += 90;
+ landscape = gTrue;
+ } else {
+ landscape = gFalse;
+ }
+ } else { // rotate == 90 || rotate == 270
+ if ((scaledHeight < scaledWidth && imgWidth > imgHeight &&
+ scaledWidth > imgHeight) ||
+ (scaledHeight > scaledWidth && imgWidth < imgHeight &&
+ scaledHeight > imgWidth)) {
+ rotate = 270 - rotate;
+ landscape = gTrue;
+ } else {
+ landscape = gFalse;
+ }
+ }
+ }
+ writePSFmt("%%PageOrientation: {0:s}\n",
+ landscape ? "Landscape" : "Portrait");
+ writePS("pdfStartPage\n");
+ if (rotate == 0) {
+ imgWidth2 = imgWidth;
+ imgHeight2 = imgHeight;
+ } else if (rotate == 90) {
+ writePS("90 rotate\n");
+ ty = -imgWidth;
+ imgWidth2 = imgHeight;
+ imgHeight2 = imgWidth;
+ } else if (rotate == 180) {
+ writePS("180 rotate\n");
+ imgWidth2 = imgWidth;
+ imgHeight2 = imgHeight;
+ tx = -imgWidth;
+ ty = -imgHeight;
+ } else { // rotate == 270
+ writePS("270 rotate\n");
+ tx = -imgHeight;
+ imgWidth2 = imgHeight;
+ imgHeight2 = imgWidth;
+ }
+ // shrink or expand
+ if (xScale0 > 0 && yScale0 > 0) {
+ xScale = xScale0 * userUnit;
+ yScale = yScale0 * userUnit;
+ } else if ((globalParams->getPSShrinkLarger() &&
+ (width * userUnit > imgWidth2 ||
+ height * userUnit > imgHeight2)) ||
+ (globalParams->getPSExpandSmaller() &&
+ (width * userUnit < imgWidth2 &&
+ height * userUnit < imgHeight2))) {
+ xScale = (double)imgWidth2 / (double)width;
+ yScale = (double)imgHeight2 / (double)height;
+ if (yScale < xScale) {
+ xScale = yScale;
+ } else {
+ yScale = xScale;
+ }
+ } else {
+ xScale = yScale = userUnit;
+ }
+ // deal with odd bounding boxes or clipping
+ if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
+ tx -= xScale * clipLLX0;
+ ty -= yScale * clipLLY0;
+ } else {
+ tx -= xScale * x1;
+ ty -= yScale * y1;
+ }
+ // center
+ if (tx0 >= 0 && ty0 >= 0) {
+ tx += (rotate == 0 || rotate == 180) ? tx0 : ty0;
+ ty += (rotate == 0 || rotate == 180) ? ty0 : -tx0;
+ } else if (globalParams->getPSCenter()) {
+ if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
+ tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2;
+ ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2;
+ } else {
+ tx += (imgWidth2 - xScale * width) / 2;
+ ty += (imgHeight2 - yScale * height) / 2;
+ }
+ }
+ tx += (rotate == 0 || rotate == 180) ? imgLLX : imgLLY;
+ ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX;
+ if (tx != 0 || ty != 0) {
+ writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty);
+ }
+ if (xScale != 1 || yScale != 1) {
+ writePSFmt("{0:.4f} {1:.4f} scale\n", xScale, yScale);
+ }
+ if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {
+ writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n",
+ clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0);
+ } else {
+ writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1);
+ }
+
+ ++seqPage;
+ break;
+
+ case psModeEPS:
+ writePS("pdfStartPage\n");
+ tx = ty = 0;
+ rotate = (360 - state->getRotate()) % 360;
+ if (rotate == 0) {
+ } else if (rotate == 90) {
+ writePS("90 rotate\n");
+ tx = -epsX1;
+ ty = -epsY2;
+ } else if (rotate == 180) {
+ writePS("180 rotate\n");
+ tx = -(epsX1 + epsX2);
+ ty = -(epsY1 + epsY2);
+ } else { // rotate == 270
+ writePS("270 rotate\n");
+ tx = -epsX2;
+ ty = -epsY1;
+ }
+ if (tx != 0 || ty != 0) {
+ writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty);
+ }
+ xScale = yScale = 1;
+ break;
+
+ case psModeForm:
+ writePS("/PaintProc {\n");
+ writePS("begin xpdf begin\n");
+ writePS("pdfStartPage\n");
+ tx = ty = 0;
+ xScale = yScale = 1;
+ rotate = 0;
+ break;
+ }
+
+ if (level == psLevel2Gray || level == psLevel3Gray) {
+ writePS("/DeviceGray setcolorspace\n");
+ }
+
+ if (customCodeCbk) {
+ if ((s = (*customCodeCbk)(this, psOutCustomPageSetup, pageNum,
+ customCodeCbkData))) {
+ writePS(s->getCString());
+ delete s;
+ }
+ }
+
+ if (mode == psModePS) {
+ writePS("%%EndPageSetup\n");
+ }
+
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::endPage() {
+ if (overlayCbk) {
+ restoreState(NULL);
+ (*overlayCbk)(this, overlayCbkData);
+ }
+
+ if (mode == psModeForm) {
+ writePS("pdfEndPage\n");
+ writePS("end end\n");
+ writePS("} def\n");
+ writePS("end end\n");
+ } else {
+ if (!manualCtrl) {
+ writePS("showpage\n");
+ }
+ writePS("%%PageTrailer\n");
+ writePageTrailer();
+ writePS("end\n");
+ }
+}
+
+void PSOutputDev::saveState(GfxState *state) {
+ // The noStateChanges and saveStack fields are used to implement an
+ // optimization to reduce gsave/grestore nesting. The idea is to
+ // look for sequences like this:
+ // q q AAA Q BBB Q (where AAA and BBB are sequences of operations)
+ // and transform them to:
+ // q AAA Q q BBB Q
+ if (noStateChanges) {
+ // any non-NULL pointer will work here
+ saveStack->append(this);
+ } else {
+ saveStack->append((PSOutputDev *)NULL);
+ writePS("q\n");
+ noStateChanges = gTrue;
+ }
+}
+
+void PSOutputDev::restoreState(GfxState *state) {
+ if (saveStack->getLength()) {
+ writePS("Q\n");
+ if (saveStack->del(saveStack->getLength() - 1)) {
+ writePS("q\n");
+ noStateChanges = gTrue;
+ } else {
+ noStateChanges = gFalse;
+ }
+ }
+}
+
+void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,
+ double m21, double m22, double m31, double m32) {
+ if (m11 == 1 && m12 == 0 && m21 == 0 && m22 == 1 && m31 == 0 && m32 == 0) {
+ return;
+ }
+ if (fabs(m11 * m22 - m12 * m21) < 1e-10) {
+ // avoid a singular (or close-to-singular) matrix
+ writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] cm\n", m31, m32);
+ } else {
+ writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n",
+ m11, m12, m21, m22, m31, m32);
+ }
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateLineDash(GfxState *state) {
+ double *dash;
+ double start;
+ int length, i;
+
+ state->getLineDash(&dash, &length, &start);
+ writePS("[");
+ for (i = 0; i < length; ++i) {
+ writePSFmt("{0:.6g}{1:w}",
+ dash[i] < 0 ? 0 : dash[i],
+ (i == length-1) ? 0 : 1);
+ }
+ writePSFmt("] {0:.6g} d\n", start);
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateFlatness(GfxState *state) {
+ writePSFmt("{0:.4g} i\n", state->getFlatness());
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateLineJoin(GfxState *state) {
+ writePSFmt("{0:d} j\n", state->getLineJoin());
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateLineCap(GfxState *state) {
+ writePSFmt("{0:d} J\n", state->getLineCap());
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateMiterLimit(GfxState *state) {
+ writePSFmt("{0:.4g} M\n", state->getMiterLimit());
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateLineWidth(GfxState *state) {
+ writePSFmt("{0:.6g} w\n", state->getLineWidth());
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateFillColorSpace(GfxState *state) {
+ switch (level) {
+ case psLevel1:
+ case psLevel1Sep:
+ break;
+ case psLevel2:
+ case psLevel3:
+ if (state->getFillColorSpace()->getMode() != csPattern) {
+ dumpColorSpaceL2(state, state->getFillColorSpace(),
+ gTrue, gFalse, gFalse);
+ writePS(" cs\n");
+ noStateChanges = gFalse;
+ }
+ break;
+ case psLevel2Gray:
+ case psLevel3Gray:
+ case psLevel2Sep:
+ case psLevel3Sep:
+ break;
+ }
+}
+
+void PSOutputDev::updateStrokeColorSpace(GfxState *state) {
+ switch (level) {
+ case psLevel1:
+ case psLevel1Sep:
+ break;
+ case psLevel2:
+ case psLevel3:
+ if (state->getStrokeColorSpace()->getMode() != csPattern) {
+ dumpColorSpaceL2(state, state->getStrokeColorSpace(),
+ gTrue, gFalse, gFalse);
+ writePS(" CS\n");
+ noStateChanges = gFalse;
+ }
+ break;
+ case psLevel2Gray:
+ case psLevel3Gray:
+ case psLevel2Sep:
+ case psLevel3Sep:
+ break;
+ }
+}
+
+void PSOutputDev::updateFillColor(GfxState *state) {
+ GfxColor color;
+ GfxColor *colorPtr;
+ GfxGray gray;
+ GfxCMYK cmyk;
+ GfxSeparationColorSpace *sepCS;
+ double c, m, y, k;
+ int i;
+
+ switch (level) {
+ case psLevel1:
+ case psLevel2Gray:
+ case psLevel3Gray:
+ state->getFillGray(&gray);
+ writePSFmt("{0:.4g} g\n", colToDbl(gray));
+ break;
+ case psLevel1Sep:
+ state->getFillCMYK(&cmyk);
+ c = colToDbl(cmyk.c);
+ m = colToDbl(cmyk.m);
+ y = colToDbl(cmyk.y);
+ k = colToDbl(cmyk.k);
+ writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k);
+ addProcessColor(c, m, y, k);
+ break;
+ case psLevel2:
+ case psLevel3:
+ if (state->getFillColorSpace()->getMode() != csPattern) {
+ colorPtr = state->getFillColor();
+ writePS("[");
+ for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) {
+ if (i > 0) {
+ writePS(" ");
+ }
+ writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i]));
+ }
+ writePS("] sc\n");
+ }
+ break;
+ case psLevel2Sep:
+ case psLevel3Sep:
+ if (state->getFillColorSpace()->getMode() == csSeparation) {
+ sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace();
+ color.c[0] = gfxColorComp1;
+ sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent());
+ writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) ck\n",
+ colToDbl(state->getFillColor()->c[0]),
+ colToDbl(cmyk.c), colToDbl(cmyk.m),
+ colToDbl(cmyk.y), colToDbl(cmyk.k),
+ sepCS->getName());
+ addCustomColor(state, sepCS);
+ } else {
+ state->getFillCMYK(&cmyk);
+ c = colToDbl(cmyk.c);
+ m = colToDbl(cmyk.m);
+ y = colToDbl(cmyk.y);
+ k = colToDbl(cmyk.k);
+ writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k);
+ addProcessColor(c, m, y, k);
+ }
+ break;
+ }
+ t3Cacheable = gFalse;
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateStrokeColor(GfxState *state) {
+ GfxColor color;
+ GfxColor *colorPtr;
+ GfxGray gray;
+ GfxCMYK cmyk;
+ GfxSeparationColorSpace *sepCS;
+ double c, m, y, k;
+ int i;
+
+ switch (level) {
+ case psLevel1:
+ case psLevel2Gray:
+ case psLevel3Gray:
+ state->getStrokeGray(&gray);
+ writePSFmt("{0:.4g} G\n", colToDbl(gray));
+ break;
+ case psLevel1Sep:
+ state->getStrokeCMYK(&cmyk);
+ c = colToDbl(cmyk.c);
+ m = colToDbl(cmyk.m);
+ y = colToDbl(cmyk.y);
+ k = colToDbl(cmyk.k);
+ writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k);
+ addProcessColor(c, m, y, k);
+ break;
+ case psLevel2:
+ case psLevel3:
+ if (state->getStrokeColorSpace()->getMode() != csPattern) {
+ colorPtr = state->getStrokeColor();
+ writePS("[");
+ for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) {
+ if (i > 0) {
+ writePS(" ");
+ }
+ writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i]));
+ }
+ writePS("] SC\n");
+ }
+ break;
+ case psLevel2Sep:
+ case psLevel3Sep:
+ if (state->getStrokeColorSpace()->getMode() == csSeparation) {
+ sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace();
+ color.c[0] = gfxColorComp1;
+ sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent());
+ writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) CK\n",
+ colToDbl(state->getStrokeColor()->c[0]),
+ colToDbl(cmyk.c), colToDbl(cmyk.m),
+ colToDbl(cmyk.y), colToDbl(cmyk.k),
+ sepCS->getName());
+ addCustomColor(state, sepCS);
+ } else {
+ state->getStrokeCMYK(&cmyk);
+ c = colToDbl(cmyk.c);
+ m = colToDbl(cmyk.m);
+ y = colToDbl(cmyk.y);
+ k = colToDbl(cmyk.k);
+ writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k);
+ addProcessColor(c, m, y, k);
+ }
+ break;
+ }
+ t3Cacheable = gFalse;
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::addProcessColor(double c, double m, double y, double k) {
+ if (c > 0) {
+ processColors |= psProcessCyan;
+ }
+ if (m > 0) {
+ processColors |= psProcessMagenta;
+ }
+ if (y > 0) {
+ processColors |= psProcessYellow;
+ }
+ if (k > 0) {
+ processColors |= psProcessBlack;
+ }
+}
+
+void PSOutputDev::addCustomColor(GfxState *state,
+ GfxSeparationColorSpace *sepCS) {
+ PSOutCustomColor *cc;
+ GfxColor color;
+ GfxCMYK cmyk;
+
+ for (cc = customColors; cc; cc = cc->next) {
+ if (!cc->name->cmp(sepCS->getName())) {
+ return;
+ }
+ }
+ color.c[0] = gfxColorComp1;
+ sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent());
+ cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
+ colToDbl(cmyk.y), colToDbl(cmyk.k),
+ sepCS->getName()->copy());
+ cc->next = customColors;
+ customColors = cc;
+}
+
+void PSOutputDev::addCustomColors(GfxState *state,
+ GfxDeviceNColorSpace *devnCS) {
+ PSOutCustomColor *cc;
+ GfxColor color;
+ GfxCMYK cmyk;
+ int i;
+
+ for (i = 0; i < devnCS->getNComps(); ++i) {
+ color.c[i] = 0;
+ }
+ for (i = 0; i < devnCS->getNComps(); ++i) {
+ for (cc = customColors; cc; cc = cc->next) {
+ if (!cc->name->cmp(devnCS->getColorantName(i))) {
+ break;
+ }
+ }
+ if (cc) {
+ continue;
+ }
+ color.c[i] = gfxColorComp1;
+ devnCS->getCMYK(&color, &cmyk, state->getRenderingIntent());
+ color.c[i] = 0;
+ cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
+ colToDbl(cmyk.y), colToDbl(cmyk.k),
+ devnCS->getColorantName(i)->copy());
+ cc->next = customColors;
+ customColors = cc;
+ }
+}
+
+void PSOutputDev::updateFillOverprint(GfxState *state) {
+ if (level == psLevel2 || level == psLevel2Sep ||
+ level == psLevel3 || level == psLevel3Sep) {
+ writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false");
+ noStateChanges = gFalse;
+ }
+}
+
+void PSOutputDev::updateStrokeOverprint(GfxState *state) {
+ if (level == psLevel2 || level == psLevel2Sep ||
+ level == psLevel3 || level == psLevel3Sep) {
+ writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false");
+ noStateChanges = gFalse;
+ }
+}
+
+void PSOutputDev::updateOverprintMode(GfxState *state) {
+ if (level == psLevel3 || level == psLevel3Sep) {
+ writePSFmt("{0:s} opm\n", state->getOverprintMode() ? "true" : "false");
+ noStateChanges = gFalse;
+ }
+}
+
+void PSOutputDev::updateTransfer(GfxState *state) {
+ Function **funcs;
+ int i;
+
+ funcs = state->getTransfer();
+ if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) {
+ if (level == psLevel2 || level == psLevel2Sep ||
+ level == psLevel3 || level == psLevel3Sep) {
+ for (i = 0; i < 4; ++i) {
+ cvtFunction(funcs[i]);
+ }
+ writePS("setcolortransfer\n");
+ } else {
+ cvtFunction(funcs[3]);
+ writePS("settransfer\n");
+ }
+ } else if (funcs[0]) {
+ cvtFunction(funcs[0]);
+ writePS("settransfer\n");
+ } else {
+ writePS("{} settransfer\n");
+ }
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateFont(GfxState *state) {
+ if (state->getFont()) {
+ if (state->getFont()->getTag() &&
+ !state->getFont()->getTag()->cmp("xpdf_default_font")) {
+ writePSFmt("/xpdf_default_font {0:.6g} Tf\n",
+ fabs(state->getFontSize()) < 0.0001 ? 0.0001
+ : state->getFontSize());
+ } else {
+ writePSFmt("/F{0:d}_{1:d} {2:.6g} Tf\n",
+ state->getFont()->getID()->num, state->getFont()->getID()->gen,
+ fabs(state->getFontSize()) < 0.0001 ? 0.0001
+ : state->getFontSize());
+ }
+ noStateChanges = gFalse;
+ }
+}
+
+void PSOutputDev::updateTextMat(GfxState *state) {
+ double *mat;
+
+ mat = state->getTextMat();
+ if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 1e-10) {
+ // avoid a singular (or close-to-singular) matrix
+ writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", mat[4], mat[5]);
+ } else {
+ writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] Tm\n",
+ mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
+ }
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateCharSpace(GfxState *state) {
+ writePSFmt("{0:.6g} Tc\n", state->getCharSpace());
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateRender(GfxState *state) {
+ int rm;
+
+ rm = state->getRender();
+ writePSFmt("{0:d} Tr\n", rm);
+ rm &= 3;
+ if (rm != 0 && rm != 3) {
+ t3Cacheable = gFalse;
+ }
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateRise(GfxState *state) {
+ writePSFmt("{0:.6g} Ts\n", state->getRise());
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateWordSpace(GfxState *state) {
+ writePSFmt("{0:.6g} Tw\n", state->getWordSpace());
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateHorizScaling(GfxState *state) {
+ double h;
+
+ h = state->getHorizScaling();
+ if (fabs(h) < 0.01) {
+ h = 0.01;
+ }
+ writePSFmt("{0:.6g} Tz\n", h);
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateTextPos(GfxState *state) {
+ writePSFmt("{0:.6g} {1:.6g} Td\n", state->getLineX(), state->getLineY());
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::updateTextShift(GfxState *state, double shift) {
+ if (state->getFont()->getWMode()) {
+ writePSFmt("{0:.6g} TJmV\n", shift);
+ } else {
+ writePSFmt("{0:.6g} TJm\n", shift);
+ }
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::saveTextPos(GfxState *state) {
+ writePS("currentpoint\n");
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::restoreTextPos(GfxState *state) {
+ writePS("m\n");
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::stroke(GfxState *state) {
+ doPath(state->getPath());
+ if (inType3Char && t3FillColorOnly) {
+ // if we're constructing a cacheable Type 3 glyph, we need to do
+ // everything in the fill color
+ writePS("Sf\n");
+ } else {
+ writePS("S\n");
+ }
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::fill(GfxState *state) {
+ doPath(state->getPath());
+ writePS("f\n");
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::eoFill(GfxState *state) {
+ doPath(state->getPath());
+ writePS("f*\n");
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
+ int paintType, int tilingType,
+ Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep) {
+ if (level <= psLevel1Sep) {
+ tilingPatternFillL1(state, gfx, strRef, paintType, tilingType,
+ resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
+ } else {
+ tilingPatternFillL2(state, gfx, strRef, paintType, tilingType,
+ resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep);
+ }
+}
+
+void PSOutputDev::tilingPatternFillL1(GfxState *state, Gfx *gfx,
+ Object *strRef,
+ int paintType, int tilingType,
+ Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep) {
+ PDFRectangle box;
+ Gfx *gfx2;
+
+ // define a Type 3 font
+ writePS("8 dict begin\n");
+ writePS("/FontType 3 def\n");
+ writePS("/FontMatrix [1 0 0 1 0 0] def\n");
+ writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n",
+ bbox[0], bbox[1], bbox[2], bbox[3]);
+ writePS("/Encoding 256 array def\n");
+ writePS(" 0 1 255 { Encoding exch /.notdef put } for\n");
+ writePS(" Encoding 120 /x put\n");
+ writePS("/BuildGlyph {\n");
+ writePS(" exch /CharProcs get exch\n");
+ writePS(" 2 copy known not { pop /.notdef } if\n");
+ writePS(" get exec\n");
+ writePS("} bind def\n");
+ writePS("/BuildChar {\n");
+ writePS(" 1 index /Encoding get exch get\n");
+ writePS(" 1 index /BuildGlyph get exec\n");
+ writePS("} bind def\n");
+ writePS("/CharProcs 1 dict def\n");
+ writePS("CharProcs begin\n");
+ box.x1 = bbox[0];
+ box.y1 = bbox[1];
+ box.x2 = bbox[2];
+ box.y2 = bbox[3];
+ gfx2 = new Gfx(doc, this, resDict, &box, NULL);
+ gfx2->takeContentStreamStack(gfx);
+ writePS("/x {\n");
+ if (paintType == 2) {
+ writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n",
+ xStep, bbox[0], bbox[1], bbox[2], bbox[3]);
+ t3FillColorOnly = gTrue;
+ } else {
+ if (x1 - 1 <= x0) {
+ writePS("1 0 setcharwidth\n");
+ } else {
+ writePSFmt("{0:.6g} 0 setcharwidth\n", xStep);
+ }
+ t3FillColorOnly = gFalse;
+ }
+ inType3Char = gTrue;
+ ++numTilingPatterns;
+ gfx2->display(strRef);
+ --numTilingPatterns;
+ inType3Char = gFalse;
+ writePS("} def\n");
+ delete gfx2;
+ writePS("end\n");
+ writePS("currentdict end\n");
+ writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns);
+
+ // draw the tiles
+ writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns);
+ writePS("fCol\n");
+ writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n",
+ mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
+ writePSFmt("{0:d} 1 {1:d} {{ {2:.6g} exch {3:.6g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n",
+ y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1);
+ writePS("grestore\n");
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::tilingPatternFillL2(GfxState *state, Gfx *gfx,
+ Object *strRef,
+ int paintType, int tilingType,
+ Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep) {
+ PDFRectangle box;
+ Gfx *gfx2;
+
+ // switch to pattern space
+ writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n",
+ mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
+
+ // define a pattern
+ writePSFmt("/xpdfTile{0:d}\n", numTilingPatterns);
+ writePS("<<\n");
+ writePS(" /PatternType 1\n");
+ writePSFmt(" /PaintType {0:d}\n", paintType);
+ writePSFmt(" /TilingType {0:d}\n", tilingType);
+ writePSFmt(" /BBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}]\n",
+ bbox[0], bbox[1], bbox[2], bbox[3]);
+ writePSFmt(" /XStep {0:.6g}\n", xStep);
+ writePSFmt(" /YStep {0:.6g}\n", yStep);
+ writePS(" /PaintProc {\n");
+ writePS(" pop\n");
+ box.x1 = bbox[0];
+ box.y1 = bbox[1];
+ box.x2 = bbox[2];
+ box.y2 = bbox[3];
+ gfx2 = new Gfx(doc, this, resDict, &box, NULL);
+ gfx2->takeContentStreamStack(gfx);
+ t3FillColorOnly = paintType == 2;
+ inType3Char = gTrue;
+ ++numTilingPatterns;
+ gfx2->display(strRef);
+ --numTilingPatterns;
+ inType3Char = gFalse;
+ delete gfx2;
+ writePS(" }\n");
+ writePS(">> matrix makepattern def\n");
+
+ // set the pattern
+ if (paintType == 2) {
+ writePS("fCol\n");
+ writePS("currentcolor ");
+ }
+ writePSFmt("xpdfTile{0:d} setpattern\n", numTilingPatterns);
+
+ // fill with the pattern
+ writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} rectfill\n",
+ x0 * xStep + bbox[0],
+ y0 * yStep + bbox[1],
+ (x1 - x0) * xStep + bbox[2],
+ (y1 - y0) * yStep + bbox[3]);
+
+ writePS("grestore\n");
+ 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;
+ double *mat;
+ int i;
+
+ if (level == psLevel2Sep || level == psLevel3Sep) {
+ if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
+ return gFalse;
+ }
+ processColors |= psProcessCMYK;
+ }
+
+ shading->getDomain(&x0, &y0, &x1, &y1);
+ mat = shading->getMatrix();
+ writePSFmt("/mat [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n",
+ mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
+ writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
+ if (shading->getNFuncs() == 1) {
+ writePS("/func ");
+ cvtFunction(shading->getFunc(0));
+ writePS("def\n");
+ } else {
+ writePS("/func {\n");
+ for (i = 0; i < shading->getNFuncs(); ++i) {
+ if (i < shading->getNFuncs() - 1) {
+ writePS("2 copy\n");
+ }
+ cvtFunction(shading->getFunc(i));
+ writePS("exec\n");
+ if (i < shading->getNFuncs() - 1) {
+ writePS("3 1 roll\n");
+ }
+ }
+ writePS("} def\n");
+ }
+ writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} 0 funcSH\n", x0, y0, x1, y1);
+
+ noStateChanges = gFalse;
+ return gTrue;
+}
+
+GBool PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading) {
+ double xMin, yMin, xMax, yMax;
+ double x0, y0, x1, y1, dx, dy, mul;
+ double tMin, tMax, t, t0, t1;
+ int i;
+
+ if (level == psLevel2Sep || level == psLevel3Sep) {
+ if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
+ return gFalse;
+ }
+ processColors |= psProcessCMYK;
+ }
+
+ // get the clip region bbox
+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+
+ // compute min and max t values, based on the four corners of the
+ // clip region bbox
+ shading->getCoords(&x0, &y0, &x1, &y1);
+ dx = x1 - x0;
+ dy = y1 - y0;
+ if (fabs(dx) < 0.01 && fabs(dy) < 0.01) {
+ return gTrue;
+ } else {
+ mul = 1 / (dx * dx + dy * dy);
+ tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
+ t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
+ if (t < tMin) {
+ tMin = t;
+ } else if (t > tMax) {
+ tMax = t;
+ }
+ t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
+ if (t < tMin) {
+ tMin = t;
+ } else if (t > tMax) {
+ tMax = t;
+ }
+ t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
+ if (t < tMin) {
+ tMin = t;
+ } else if (t > tMax) {
+ tMax = t;
+ }
+ if (tMin < 0 && !shading->getExtend0()) {
+ tMin = 0;
+ }
+ if (tMax > 1 && !shading->getExtend1()) {
+ tMax = 1;
+ }
+ }
+
+ // get the function domain
+ t0 = shading->getDomain0();
+ t1 = shading->getDomain1();
+
+ // generate the PS code
+ writePSFmt("/t0 {0:.6g} def\n", t0);
+ writePSFmt("/t1 {0:.6g} def\n", t1);
+ writePSFmt("/dt {0:.6g} def\n", t1 - t0);
+ writePSFmt("/x0 {0:.6g} def\n", x0);
+ writePSFmt("/y0 {0:.6g} def\n", y0);
+ writePSFmt("/dx {0:.6g} def\n", x1 - x0);
+ writePSFmt("/x1 {0:.6g} def\n", x1);
+ writePSFmt("/y1 {0:.6g} def\n", y1);
+ writePSFmt("/dy {0:.6g} def\n", y1 - y0);
+ writePSFmt("/xMin {0:.6g} def\n", xMin);
+ writePSFmt("/yMin {0:.6g} def\n", yMin);
+ writePSFmt("/xMax {0:.6g} def\n", xMax);
+ writePSFmt("/yMax {0:.6g} def\n", yMax);
+ writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
+ if (shading->getNFuncs() == 1) {
+ writePS("/func ");
+ cvtFunction(shading->getFunc(0));
+ writePS("def\n");
+ } else {
+ writePS("/func {\n");
+ for (i = 0; i < shading->getNFuncs(); ++i) {
+ if (i < shading->getNFuncs() - 1) {
+ writePS("dup\n");
+ }
+ cvtFunction(shading->getFunc(i));
+ writePS("exec\n");
+ if (i < shading->getNFuncs() - 1) {
+ writePS("exch\n");
+ }
+ }
+ writePS("} def\n");
+ }
+ writePSFmt("{0:.6g} {1:.6g} 0 axialSH\n", tMin, tMax);
+
+ noStateChanges = gFalse;
+ return gTrue;
+}
+
+GBool PSOutputDev::radialShadedFill(GfxState *state,
+ GfxRadialShading *shading) {
+ double xMin, yMin, xMax, yMax;
+ double x0, y0, r0, x1, y1, r1, t0, t1;
+ double xa, ya, ra;
+ double sMin, sMax, h, ta;
+ double sLeft, sRight, sTop, sBottom, sZero, sDiag;
+ GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero;
+ GBool haveSMin, haveSMax;
+ double theta, alpha, a1, a2;
+ GBool enclosed;
+ int i;
+
+ if (level == psLevel2Sep || level == psLevel3Sep) {
+ if (shading->getColorSpace()->getMode() != csDeviceCMYK) {
+ return gFalse;
+ }
+ processColors |= psProcessCMYK;
+ }
+
+ // get the shading info
+ shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
+ t0 = shading->getDomain0();
+ t1 = shading->getDomain1();
+
+ // Compute the point at which r(s) = 0; check for the enclosed
+ // circles case; and compute the angles for the tangent lines.
+ h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
+ if (h == 0) {
+ enclosed = gTrue;
+ theta = 0; // make gcc happy
+ } else if (r1 - r0 == 0) {
+ enclosed = gFalse;
+ theta = 0;
+ } else if (fabs(r1 - r0) >= h) {
+ enclosed = gTrue;
+ theta = 0; // make gcc happy
+ } else {
+ enclosed = gFalse;
+ theta = asin((r1 - r0) / h);
+ }
+ if (enclosed) {
+ a1 = 0;
+ a2 = 360;
+ } else {
+ alpha = atan2(y1 - y0, x1 - x0);
+ a1 = (180 / M_PI) * (alpha + theta) + 90;
+ a2 = (180 / M_PI) * (alpha - theta) - 90;
+ while (a2 < a1) {
+ a2 += 360;
+ }
+ }
+
+ // compute the (possibly extended) s range
+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+ if (enclosed) {
+ sMin = 0;
+ sMax = 1;
+ } else {
+ // solve x(sLeft) + r(sLeft) = xMin
+ if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) {
+ sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));
+ } else {
+ sLeft = 0; // make gcc happy
+ }
+ // solve x(sRight) - r(sRight) = xMax
+ if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) {
+ sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));
+ } else {
+ sRight = 0; // make gcc happy
+ }
+ // solve y(sBottom) + r(sBottom) = yMin
+ if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) {
+ sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));
+ } else {
+ sBottom = 0; // make gcc happy
+ }
+ // solve y(sTop) - r(sTop) = yMax
+ if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) {
+ sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));
+ } else {
+ sTop = 0; // make gcc happy
+ }
+ // solve r(sZero) = 0
+ if ((haveSZero = fabs(r1 - r0) > 0.000001)) {
+ sZero = -r0 / (r1 - r0);
+ } else {
+ sZero = 0; // make gcc happy
+ }
+ // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2)
+ if (haveSZero) {
+ sDiag = (sqrt((xMax - xMin) * (xMax - xMin) +
+ (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0);
+ } else {
+ sDiag = 0; // make gcc happy
+ }
+ // compute sMin
+ if (shading->getExtend0()) {
+ sMin = 0;
+ haveSMin = gFalse;
+ if (x0 < x1 && haveSLeft && sLeft < 0) {
+ sMin = sLeft;
+ haveSMin = gTrue;
+ } else if (x0 > x1 && haveSRight && sRight < 0) {
+ sMin = sRight;
+ haveSMin = gTrue;
+ }
+ if (y0 < y1 && haveSBottom && sBottom < 0) {
+ if (!haveSMin || sBottom > sMin) {
+ sMin = sBottom;
+ haveSMin = gTrue;
+ }
+ } else if (y0 > y1 && haveSTop && sTop < 0) {
+ if (!haveSMin || sTop > sMin) {
+ sMin = sTop;
+ haveSMin = gTrue;
+ }
+ }
+ if (haveSZero && sZero < 0) {
+ if (!haveSMin || sZero > sMin) {
+ sMin = sZero;
+ }
+ }
+ } else {
+ sMin = 0;
+ }
+ // compute sMax
+ if (shading->getExtend1()) {
+ sMax = 1;
+ haveSMax = gFalse;
+ if (x1 < x0 && haveSLeft && sLeft > 1) {
+ sMax = sLeft;
+ haveSMax = gTrue;
+ } else if (x1 > x0 && haveSRight && sRight > 1) {
+ sMax = sRight;
+ haveSMax = gTrue;
+ }
+ if (y1 < y0 && haveSBottom && sBottom > 1) {
+ if (!haveSMax || sBottom < sMax) {
+ sMax = sBottom;
+ haveSMax = gTrue;
+ }
+ } else if (y1 > y0 && haveSTop && sTop > 1) {
+ if (!haveSMax || sTop < sMax) {
+ sMax = sTop;
+ haveSMax = gTrue;
+ }
+ }
+ if (haveSZero && sDiag > 1) {
+ if (!haveSMax || sDiag < sMax) {
+ sMax = sDiag;
+ }
+ }
+ } else {
+ sMax = 1;
+ }
+ }
+
+ // generate the PS code
+ writePSFmt("/x0 {0:.6g} def\n", x0);
+ writePSFmt("/x1 {0:.6g} def\n", x1);
+ writePSFmt("/dx {0:.6g} def\n", x1 - x0);
+ writePSFmt("/y0 {0:.6g} def\n", y0);
+ writePSFmt("/y1 {0:.6g} def\n", y1);
+ writePSFmt("/dy {0:.6g} def\n", y1 - y0);
+ writePSFmt("/r0 {0:.6g} def\n", r0);
+ writePSFmt("/r1 {0:.6g} def\n", r1);
+ writePSFmt("/dr {0:.6g} def\n", r1 - r0);
+ writePSFmt("/t0 {0:.6g} def\n", t0);
+ writePSFmt("/t1 {0:.6g} def\n", t1);
+ writePSFmt("/dt {0:.6g} def\n", t1 - t0);
+ writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps());
+ writePSFmt("/encl {0:s} def\n", enclosed ? "true" : "false");
+ writePSFmt("/a1 {0:.6g} def\n", a1);
+ writePSFmt("/a2 {0:.6g} def\n", a2);
+ if (shading->getNFuncs() == 1) {
+ writePS("/func ");
+ cvtFunction(shading->getFunc(0));
+ writePS("def\n");
+ } else {
+ writePS("/func {\n");
+ for (i = 0; i < shading->getNFuncs(); ++i) {
+ if (i < shading->getNFuncs() - 1) {
+ writePS("dup\n");
+ }
+ cvtFunction(shading->getFunc(i));
+ writePS("exec\n");
+ if (i < shading->getNFuncs() - 1) {
+ writePS("exch\n");
+ }
+ }
+ writePS("} def\n");
+ }
+ writePSFmt("{0:.6g} {1:.6g} 0 radialSH\n", sMin, sMax);
+
+ // extend the 'enclosed' case
+ if (enclosed) {
+ // extend the smaller circle
+ if ((shading->getExtend0() && r0 <= r1) ||
+ (shading->getExtend1() && r1 < r0)) {
+ if (r0 <= r1) {
+ ta = t0;
+ ra = r0;
+ xa = x0;
+ ya = y0;
+ } else {
+ ta = t1;
+ ra = r1;
+ xa = x1;
+ ya = y1;
+ }
+ if (level == psLevel2Sep || level == psLevel3Sep) {
+ writePSFmt("{0:.6g} radialCol aload pop k\n", ta);
+ } else {
+ writePSFmt("{0:.6g} radialCol sc\n", ta);
+ }
+ writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h f*\n", xa, ya, ra);
+ }
+
+ // extend the larger circle
+ if ((shading->getExtend0() && r0 > r1) ||
+ (shading->getExtend1() && r1 >= r0)) {
+ if (r0 > r1) {
+ ta = t0;
+ ra = r0;
+ xa = x0;
+ ya = y0;
+ } else {
+ ta = t1;
+ ra = r1;
+ xa = x1;
+ ya = y1;
+ }
+ if (level == psLevel2Sep || level == psLevel3Sep) {
+ writePSFmt("{0:.6g} radialCol aload pop k\n", ta);
+ } else {
+ writePSFmt("{0:.6g} radialCol sc\n", ta);
+ }
+ writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h\n", xa, ya, ra);
+ writePSFmt("{0:.6g} {1:.6g} m {2:.6g} {3:.6g} l {4:.6g} {5:.6g} l {6:.6g} {7:.6g} l h f*\n",
+ xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin);
+ }
+ }
+
+ noStateChanges = gFalse;
+ return gTrue;
+}
+
+void PSOutputDev::clip(GfxState *state) {
+ doPath(state->getPath());
+ writePS("W\n");
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::eoClip(GfxState *state) {
+ doPath(state->getPath());
+ writePS("W*\n");
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::clipToStrokePath(GfxState *state) {
+ doPath(state->getPath());
+ writePS("Ws\n");
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::doPath(GfxPath *path) {
+ GfxSubpath *subpath;
+ double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4;
+ int n, m, i, j;
+
+ n = path->getNumSubpaths();
+
+ if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) {
+ subpath = path->getSubpath(0);
+ x0 = subpath->getX(0);
+ y0 = subpath->getY(0);
+ x4 = subpath->getX(4);
+ y4 = subpath->getY(4);
+ if (x4 == x0 && y4 == y0) {
+ x1 = subpath->getX(1);
+ y1 = subpath->getY(1);
+ x2 = subpath->getX(2);
+ y2 = subpath->getY(2);
+ x3 = subpath->getX(3);
+ y3 = subpath->getY(3);
+ if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) {
+ writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n",
+ x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1,
+ fabs(x2 - x0), fabs(y1 - y0));
+ return;
+ } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) {
+ writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n",
+ x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2,
+ fabs(x1 - x0), fabs(y2 - y0));
+ return;
+ }
+ }
+ }
+
+ for (i = 0; i < n; ++i) {
+ subpath = path->getSubpath(i);
+ m = subpath->getNumPoints();
+ writePSFmt("{0:.6g} {1:.6g} m\n", subpath->getX(0), subpath->getY(0));
+ j = 1;
+ while (j < m) {
+ if (subpath->getCurve(j)) {
+ writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} c\n",
+ subpath->getX(j), subpath->getY(j),
+ subpath->getX(j+1), subpath->getY(j+1),
+ subpath->getX(j+2), subpath->getY(j+2));
+ j += 3;
+ } else {
+ writePSFmt("{0:.6g} {1:.6g} l\n", subpath->getX(j), subpath->getY(j));
+ ++j;
+ }
+ }
+ if (subpath->isClosed()) {
+ writePS("h\n");
+ }
+ }
+}
+
+void PSOutputDev::drawString(GfxState *state, GString *s) {
+ GfxFont *font;
+ int wMode;
+ int *codeToGID;
+ GString *s2;
+ double dx, dy, originX, originY, originX0, originY0, tOriginX0, tOriginY0;
+ char *p;
+ PSFontInfo *fi;
+ UnicodeMap *uMap;
+ CharCode code;
+ Unicode u[8];
+ char buf[8];
+ double *dxdy;
+ int dxdySize, len, nChars, uLen, n, m, i, j;
+
+ // check for invisible text -- this is used by Acrobat Capture
+ if (state->getRender() == 3) {
+ return;
+ }
+
+ // ignore empty strings
+ if (s->getLength() == 0) {
+ return;
+ }
+
+ // get the font
+ if (!(font = state->getFont())) {
+ return;
+ }
+ wMode = font->getWMode();
+
+ fi = NULL;
+ for (i = 0; i < fontInfo->getLength(); ++i) {
+ fi = (PSFontInfo *)fontInfo->get(i);
+ if (fi->fontID.num == font->getID()->num &&
+ fi->fontID.gen == font->getID()->gen) {
+ break;
+ }
+ fi = NULL;
+ }
+
+ // check for a subtitute 16-bit font
+ uMap = NULL;
+ codeToGID = NULL;
+ if (font->isCIDFont()) {
+ if (!(fi && fi->ff)) {
+ // font substitution failed, so don't output any text
+ return;
+ }
+ if (fi->ff->encoding) {
+ uMap = globalParams->getUnicodeMap(fi->ff->encoding);
+ }
+
+ // check for an 8-bit code-to-GID map
+ } else {
+ if (fi && fi->ff) {
+ codeToGID = fi->ff->codeToGID;
+ }
+ }
+
+ // compute the positioning (dx, dy) for each char in the string
+ nChars = 0;
+ p = s->getCString();
+ len = s->getLength();
+ s2 = new GString();
+ dxdySize = font->isCIDFont() ? 8 : s->getLength();
+ dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double));
+ originX0 = originY0 = 0; // make gcc happy
+ while (len > 0) {
+ n = font->getNextChar(p, len, &code,
+ u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+ &dx, &dy, &originX, &originY);
+ //~ this doesn't handle the case where the origin offset changes
+ //~ within a string of characters -- which could be fixed by
+ //~ modifying dx,dy as needed for each character
+ if (p == s->getCString()) {
+ originX0 = originX;
+ originY0 = originY;
+ }
+ dx *= state->getFontSize();
+ dy *= state->getFontSize();
+ if (wMode) {
+ dy += state->getCharSpace();
+ if (n == 1 && *p == ' ') {
+ dy += state->getWordSpace();
+ }
+ } else {
+ dx += state->getCharSpace();
+ if (n == 1 && *p == ' ') {
+ dx += state->getWordSpace();
+ }
+ }
+ dx *= state->getHorizScaling();
+ if (font->isCIDFont()) {
+ if (uMap) {
+ if (nChars + uLen > dxdySize) {
+ do {
+ dxdySize *= 2;
+ } while (nChars + uLen > dxdySize);
+ dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double));
+ }
+ for (i = 0; i < uLen; ++i) {
+ m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf));
+ for (j = 0; j < m; ++j) {
+ s2->append(buf[j]);
+ }
+ //~ this really needs to get the number of chars in the target
+ //~ encoding - which may be more than the number of Unicode
+ //~ chars
+ dxdy[2 * nChars] = dx;
+ dxdy[2 * nChars + 1] = dy;
+ ++nChars;
+ }
+ } else {
+ if (nChars + 1 > dxdySize) {
+ dxdySize *= 2;
+ dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double));
+ }
+ s2->append((char)((code >> 8) & 0xff));
+ s2->append((char)(code & 0xff));
+ dxdy[2 * nChars] = dx;
+ dxdy[2 * nChars + 1] = dy;
+ ++nChars;
+ }
+ } else {
+ if (!codeToGID || codeToGID[code] >= 0) {
+ s2->append((char)code);
+ dxdy[2 * nChars] = dx;
+ dxdy[2 * nChars + 1] = dy;
+ ++nChars;
+ }
+ }
+ p += n;
+ len -= n;
+ }
+ if (uMap) {
+ uMap->decRefCnt();
+ }
+ originX0 *= state->getFontSize();
+ originY0 *= state->getFontSize();
+ state->textTransformDelta(originX0, originY0, &tOriginX0, &tOriginY0);
+
+ if (nChars > 0) {
+ if (wMode) {
+ writePSFmt("{0:.6g} {1:.6g} rmoveto\n", -tOriginX0, -tOriginY0);
+ }
+ writePSString(s2);
+ writePS("\n[");
+ for (i = 0; i < 2 * nChars; ++i) {
+ if (i > 0) {
+ writePS("\n");
+ }
+ writePSFmt("{0:.6g}", dxdy[i]);
+ }
+ if (font->getType() == fontType3) {
+ writePS("] Tj3\n");
+ } else {
+ writePS("] Tj\n");
+ }
+ if (wMode) {
+ writePSFmt("{0:.6g} {1:.6g} rmoveto\n", tOriginX0, tOriginY0);
+ }
+ }
+ gfree(dxdy);
+ delete s2;
+
+ if ((state->getRender() & 4) && font->getType() != fontType3) {
+ haveTextClip = gTrue;
+ }
+
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::endTextObject(GfxState *state) {
+ if (haveTextClip) {
+ writePS("Tclip\n");
+ haveTextClip = gFalse;
+ noStateChanges = gFalse;
+ }
+}
+
+void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate) {
+ int len;
+
+ len = height * ((width + 7) / 8);
+ switch (level) {
+ case psLevel1:
+ case psLevel1Sep:
+ doImageL1(ref, state, NULL, invert, inlineImg, str, width, height, len);
+ break;
+ case psLevel2:
+ case psLevel2Gray:
+ case psLevel2Sep:
+ doImageL2(ref, state, NULL, invert, inlineImg, str, width, height, len,
+ NULL, NULL, 0, 0, gFalse);
+ break;
+ case psLevel3:
+ case psLevel3Gray:
+ case psLevel3Sep:
+ doImageL3(ref, state, NULL, invert, inlineImg, str, width, height, len,
+ NULL, NULL, 0, 0, gFalse);
+ break;
+ }
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
+ int len;
+
+ len = height * ((width * colorMap->getNumPixelComps() *
+ colorMap->getBits() + 7) / 8);
+ switch (level) {
+ case psLevel1:
+ doImageL1(ref, state, colorMap, gFalse, inlineImg, str,
+ width, height, len);
+ break;
+ case psLevel1Sep:
+ //~ handle indexed, separation, ... color spaces
+ doImageL1Sep(state, colorMap, gFalse, inlineImg, str, width, height, len);
+ break;
+ case psLevel2:
+ case psLevel2Gray:
+ case psLevel2Sep:
+ doImageL2(ref, state, colorMap, gFalse, inlineImg, str,
+ width, height, len, maskColors, NULL, 0, 0, gFalse);
+ break;
+ case psLevel3:
+ case psLevel3Gray:
+ case psLevel3Sep:
+ doImageL3(ref, state, colorMap, gFalse, inlineImg, str,
+ width, height, len, maskColors, NULL, 0, 0, gFalse);
+ break;
+ }
+ t3Cacheable = gFalse;
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GBool maskInvert, GBool interpolate) {
+ int len;
+
+ len = height * ((width * colorMap->getNumPixelComps() *
+ colorMap->getBits() + 7) / 8);
+ switch (level) {
+ case psLevel1:
+ doImageL1(ref, state, colorMap, gFalse, gFalse, str, width, height, len);
+ break;
+ case psLevel1Sep:
+ //~ handle indexed, separation, ... color spaces
+ doImageL1Sep(state, colorMap, gFalse, gFalse, str, width, height, len);
+ break;
+ case psLevel2:
+ case psLevel2Gray:
+ case psLevel2Sep:
+ doImageL2(ref, state, colorMap, gFalse, gFalse, str, width, height, len,
+ NULL, maskStr, maskWidth, maskHeight, maskInvert);
+ break;
+ case psLevel3:
+ case psLevel3Gray:
+ case psLevel3Sep:
+ doImageL3(ref, state, colorMap, gFalse, gFalse, str, width, height, len,
+ NULL, maskStr, maskWidth, maskHeight, maskInvert);
+ break;
+ }
+ t3Cacheable = gFalse;
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::doImageL1(Object *ref, GfxState *state,
+ GfxImageColorMap *colorMap,
+ GBool invert, GBool inlineImg,
+ Stream *str, int width, int height, int len) {
+ ImageStream *imgStr;
+ Guchar pixBuf[gfxColorMaxComps];
+ GfxGray gray;
+ int col, x, y, c, i;
+
+ if ((inType3Char || preload) && !colorMap) {
+ if (inlineImg) {
+ // create an array
+ str = new FixedLengthEncoder(str, len);
+ str = new ASCIIHexEncoder(str);
+ str->reset();
+ col = 0;
+ writePS("[<");
+ do {
+ do {
+ c = str->getChar();
+ } while (c == '\n' || c == '\r');
+ if (c == '>' || c == EOF) {
+ break;
+ }
+ writePSChar((char)c);
+ ++col;
+ // each line is: "<...data...><eol>"
+ // so max data length = 255 - 4 = 251
+ // but make it 240 just to be safe
+ // chunks are 2 bytes each, so we need to stop on an even col number
+ if (col == 240) {
+ writePS(">\n<");
+ col = 0;
+ }
+ } while (c != '>' && c != EOF);
+ writePS(">]\n");
+ writePS("0\n");
+ str->close();
+ delete str;
+ } else {
+ // set up to use the array already created by setupImages()
+ writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
+ }
+ }
+
+ // image/imagemask command
+ if ((inType3Char || preload) && !colorMap) {
+ writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1a\n",
+ width, height, invert ? "true" : "false",
+ width, -height, height);
+ } else if (colorMap) {
+ writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n",
+ width, height,
+ width, -height, height);
+ } else {
+ writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1\n",
+ width, height, invert ? "true" : "false",
+ width, -height, height);
+ }
+
+ // image data
+ if (!((inType3Char || preload) && !colorMap)) {
+
+ if (colorMap) {
+
+ // set up to process the data stream
+ imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgStr->reset();
+
+ // process the data stream
+ i = 0;
+ for (y = 0; y < height; ++y) {
+
+ // write the line
+ for (x = 0; x < width; ++x) {
+ imgStr->getPixel(pixBuf);
+ colorMap->getGray(pixBuf, &gray, state->getRenderingIntent());
+ writePSFmt("{0:02x}", colToByte(gray));
+ if (++i == 32) {
+ writePSChar('\n');
+ i = 0;
+ }
+ }
+ }
+ if (i != 0) {
+ writePSChar('\n');
+ }
+ str->close();
+ delete imgStr;
+
+ // imagemask
+ } else {
+ str->reset();
+ i = 0;
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; x += 8) {
+ writePSFmt("{0:02x}", str->getChar() & 0xff);
+ if (++i == 32) {
+ writePSChar('\n');
+ i = 0;
+ }
+ }
+ }
+ if (i != 0) {
+ writePSChar('\n');
+ }
+ str->close();
+ }
+ }
+}
+
+void PSOutputDev::doImageL1Sep(GfxState *state, GfxImageColorMap *colorMap,
+ GBool invert, GBool inlineImg,
+ Stream *str, int width, int height, int len) {
+ ImageStream *imgStr;
+ Guchar *lineBuf;
+ Guchar pixBuf[gfxColorMaxComps];
+ GfxCMYK cmyk;
+ int x, y, i, comp;
+
+ // width, height, matrix, bits per component
+ writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n",
+ width, height,
+ width, -height, height);
+
+ // allocate a line buffer
+ lineBuf = (Guchar *)gmallocn(width, 4);
+
+ // set up to process the data stream
+ imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgStr->reset();
+
+ // process the data stream
+ i = 0;
+ for (y = 0; y < height; ++y) {
+
+ // read the line
+ for (x = 0; x < width; ++x) {
+ imgStr->getPixel(pixBuf);
+ colorMap->getCMYK(pixBuf, &cmyk, state->getRenderingIntent());
+ lineBuf[4*x+0] = colToByte(cmyk.c);
+ lineBuf[4*x+1] = colToByte(cmyk.m);
+ lineBuf[4*x+2] = colToByte(cmyk.y);
+ lineBuf[4*x+3] = colToByte(cmyk.k);
+ addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
+ colToDbl(cmyk.y), colToDbl(cmyk.k));
+ }
+
+ // write one line of each color component
+ for (comp = 0; comp < 4; ++comp) {
+ for (x = 0; x < width; ++x) {
+ writePSFmt("{0:02x}", lineBuf[4*x + comp]);
+ if (++i == 32) {
+ writePSChar('\n');
+ i = 0;
+ }
+ }
+ }
+ }
+
+ if (i != 0) {
+ writePSChar('\n');
+ }
+
+ str->close();
+ delete imgStr;
+ gfree(lineBuf);
+}
+
+void PSOutputDev::doImageL2(Object *ref, GfxState *state,
+ GfxImageColorMap *colorMap,
+ GBool invert, GBool inlineImg,
+ Stream *str, int width, int height, int len,
+ int *maskColors, Stream *maskStr,
+ int maskWidth, int maskHeight, GBool maskInvert) {
+ Stream *str2;
+ GString *s;
+ int n, numComps;
+ GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed;
+ GfxSeparationColorSpace *sepCS;
+ GfxColor color;
+ GfxCMYK cmyk;
+ char buf[4096];
+ int c, col, i;
+
+ // color key masking
+ if (maskColors && colorMap && !inlineImg) {
+ // can't read the stream twice for inline images -- but masking
+ // isn't allowed with inline images anyway
+ convertColorKeyMaskToClipRects(colorMap, str, width, height, maskColors);
+
+ // explicit masking
+ } else if (maskStr) {
+ convertExplicitMaskToClipRects(maskStr, maskWidth, maskHeight, maskInvert);
+ }
+
+ // color space
+ if (colorMap && !(level == psLevel2Gray || level == psLevel3Gray)) {
+ dumpColorSpaceL2(state, colorMap->getColorSpace(), gFalse, gTrue, gFalse);
+ writePS(" setcolorspace\n");
+ }
+
+ useASCIIHex = globalParams->getPSASCIIHex();
+
+ // set up the image data
+ if (mode == psModeForm || inType3Char || preload) {
+ if (inlineImg) {
+ // create an array
+ str2 = new FixedLengthEncoder(str, len);
+ if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) {
+ str2 = new GrayRecoder(str2, width, height, colorMap);
+ }
+ if (globalParams->getPSLZW()) {
+ str2 = new LZWEncoder(str2);
+ } else {
+ str2 = new RunLengthEncoder(str2);
+ }
+ if (useASCIIHex) {
+ str2 = new ASCIIHexEncoder(str2);
+ } else {
+ str2 = new ASCII85Encoder(str2);
+ }
+ str2->reset();
+ col = 0;
+ writePS((char *)(useASCIIHex ? "[<" : "[<~"));
+ do {
+ do {
+ c = str2->getChar();
+ } while (c == '\n' || c == '\r');
+ if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
+ break;
+ }
+ if (c == 'z') {
+ writePSChar((char)c);
+ ++col;
+ } else {
+ writePSChar((char)c);
+ ++col;
+ for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
+ do {
+ c = str2->getChar();
+ } while (c == '\n' || c == '\r');
+ if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
+ break;
+ }
+ writePSChar((char)c);
+ ++col;
+ }
+ }
+ // each line is: "<~...data...~><eol>"
+ // so max data length = 255 - 6 = 249
+ // chunks are 1 or 5 bytes each, so we have to stop at 245
+ // but make it 240 just to be safe
+ if (col > 240) {
+ writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
+ col = 0;
+ }
+ } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
+ writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
+ // add an extra entry because the LZWDecode/RunLengthDecode
+ // filter may read past the end
+ writePS("<>]\n");
+ writePS("0\n");
+ str2->close();
+ delete str2;
+ } else {
+ // set up to use the array already created by setupImages()
+ writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
+ }
+ }
+
+ // image dictionary
+ writePS("<<\n /ImageType 1\n");
+
+ // width, height, matrix, bits per component
+ writePSFmt(" /Width {0:d}\n", width);
+ writePSFmt(" /Height {0:d}\n", height);
+ writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
+ width, -height, height);
+ if (colorMap && (colorMap->getColorSpace()->getMode() == csDeviceN ||
+ level == psLevel2Gray || level == psLevel3Gray)) {
+ writePS(" /BitsPerComponent 8\n");
+ } else {
+ writePSFmt(" /BitsPerComponent {0:d}\n",
+ colorMap ? colorMap->getBits() : 1);
+ }
+
+ // decode
+ if (colorMap) {
+ writePS(" /Decode [");
+ if ((level == psLevel2Sep || level == psLevel3Sep) &&
+ colorMap->getColorSpace()->getMode() == csSeparation) {
+ // this matches up with the code in the pdfImSep operator
+ n = (1 << colorMap->getBits()) - 1;
+ writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n,
+ colorMap->getDecodeHigh(0) * n);
+ } else if (level == psLevel2Gray || level == psLevel3Gray) {
+ writePS("0 1");
+ } else if (colorMap->getColorSpace()->getMode() == csDeviceN) {
+ numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->
+ getAlt()->getNComps();
+ for (i = 0; i < numComps; ++i) {
+ if (i > 0) {
+ writePS(" ");
+ }
+ writePS("0 1");
+ }
+ } else {
+ numComps = colorMap->getNumPixelComps();
+ for (i = 0; i < numComps; ++i) {
+ if (i > 0) {
+ writePS(" ");
+ }
+ writePSFmt("{0:.4g} {1:.4g}",
+ colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i));
+ }
+ }
+ writePS("]\n");
+ } else {
+ writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1);
+ }
+
+ // data source
+ if (mode == psModeForm || inType3Char || preload) {
+ writePS(" /DataSource { pdfImStr }\n");
+ } else {
+ writePS(" /DataSource currentfile\n");
+ }
+
+ // filters
+ if ((mode == psModeForm || inType3Char || preload) &&
+ globalParams->getPSUncompressPreloadedImages()) {
+ s = NULL;
+ useLZW = useRLE = gFalse;
+ useCompressed = gFalse;
+ useASCII = gFalse;
+ } else {
+ s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,
+ " ", !inlineImg);
+ if ((colorMap && (colorMap->getColorSpace()->getMode() == csDeviceN ||
+ level == psLevel2Gray || level == psLevel3Gray)) ||
+ inlineImg || !s) {
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
+ useASCII = !(mode == psModeForm || inType3Char || preload);
+ useCompressed = gFalse;
+ } else {
+ useLZW = useRLE = gFalse;
+ useASCII = str->isBinary() &&
+ !(mode == psModeForm || inType3Char || preload);
+ useCompressed = gTrue;
+ }
+ }
+ if (useASCII) {
+ writePSFmt(" /ASCII{0:s}Decode filter\n",
+ useASCIIHex ? "Hex" : "85");
+ }
+ if (useLZW) {
+ writePS(" /LZWDecode filter\n");
+ } else if (useRLE) {
+ writePS(" /RunLengthDecode filter\n");
+ }
+ if (useCompressed) {
+ writePS(s->getCString());
+ }
+ if (s) {
+ delete s;
+ }
+
+ if (mode == psModeForm || inType3Char || preload) {
+
+ // end of image dictionary
+ writePSFmt(">>\n{0:s}\n", colorMap ? "image" : "imagemask");
+
+ // get rid of the array and index
+ writePS("pop pop\n");
+
+ } else {
+
+ // cut off inline image streams at appropriate length
+ if (inlineImg) {
+ str = new FixedLengthEncoder(str, len);
+ } else if (useCompressed) {
+ str = str->getUndecodedStream();
+ }
+
+ // recode to grayscale
+ if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) {
+ str = new GrayRecoder(str, width, height, colorMap);
+
+ // recode DeviceN data
+ } else if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {
+ str = new DeviceNRecoder(str, width, height, colorMap);
+ }
+
+ // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
+ if (useLZW) {
+ str = new LZWEncoder(str);
+ } else if (useRLE) {
+ str = new RunLengthEncoder(str);
+ }
+ if (useASCII) {
+ if (useASCIIHex) {
+ str = new ASCIIHexEncoder(str);
+ } else {
+ str = new ASCII85Encoder(str);
+ }
+ }
+
+ // end of image dictionary
+ writePS(">>\n");
+#if OPI_SUPPORT
+ if (opi13Nest) {
+ if (inlineImg) {
+ // this can't happen -- OPI dictionaries are in XObjects
+ error(errSyntaxError, -1, "OPI in inline image");
+ n = 0;
+ } else {
+ // need to read the stream to count characters -- the length
+ // is data-dependent (because of ASCII and LZW/RunLength
+ // filters)
+ str->reset();
+ n = 0;
+ do {
+ i = str->discardChars(4096);
+ n += i;
+ } while (i == 4096);
+ str->close();
+ }
+ // +6/7 for "pdfIm\n" / "pdfImM\n"
+ // +8 for newline + trailer
+ n += colorMap ? 14 : 15;
+ writePSFmt("%%BeginData: {0:d} Hex Bytes\n", n);
+ }
+#endif
+ if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&
+ colorMap->getColorSpace()->getMode() == csSeparation) {
+ color.c[0] = gfxColorComp1;
+ sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
+ sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent());
+ writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n",
+ colToDbl(cmyk.c), colToDbl(cmyk.m),
+ colToDbl(cmyk.y), colToDbl(cmyk.k),
+ sepCS->getName());
+ } else {
+ writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM");
+ }
+
+ // copy the stream data
+ str->reset();
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
+ }
+ str->close();
+
+ // add newline and trailer to the end
+ writePSChar('\n');
+ writePS("%-EOD-\n");
+#if OPI_SUPPORT
+ if (opi13Nest) {
+ writePS("%%EndData\n");
+ }
+#endif
+
+ // delete encoders
+ if (useLZW || useRLE || useASCII || inlineImg) {
+ delete str;
+ }
+ }
+
+ if ((maskColors && colorMap && !inlineImg) || maskStr) {
+ writePS("pdfImClipEnd\n");
+ }
+}
+
+// Convert color key masking to a clipping region consisting of a
+// sequence of clip rectangles.
+void PSOutputDev::convertColorKeyMaskToClipRects(GfxImageColorMap *colorMap,
+ Stream *str,
+ int width, int height,
+ int *maskColors) {
+ ImageStream *imgStr;
+ Guchar *line;
+ PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut;
+ int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize;
+ GBool emitRect, addRect, extendRect;
+ int numComps, i, j, x0, x1, y;
+
+ numComps = colorMap->getNumPixelComps();
+ imgStr = new ImageStream(str, width, numComps, colorMap->getBits());
+ imgStr->reset();
+ rects0Len = rects1Len = rectsOutLen = 0;
+ rectsSize = rectsOutSize = 64;
+ rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
+ rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
+ rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize,
+ sizeof(PSOutImgClipRect));
+ for (y = 0; y < height; ++y) {
+ if (!(line = imgStr->getLine())) {
+ break;
+ }
+ i = 0;
+ rects1Len = 0;
+ for (x0 = 0; x0 < width; ++x0) {
+ for (j = 0; j < numComps; ++j) {
+ if (line[x0*numComps+j] < maskColors[2*j] ||
+ line[x0*numComps+j] > maskColors[2*j+1]) {
+ break;
+ }
+ }
+ if (j < numComps) {
+ break;
+ }
+ }
+ for (x1 = x0; x1 < width; ++x1) {
+ for (j = 0; j < numComps; ++j) {
+ if (line[x1*numComps+j] < maskColors[2*j] ||
+ line[x1*numComps+j] > maskColors[2*j+1]) {
+ break;
+ }
+ }
+ if (j == numComps) {
+ break;
+ }
+ }
+ while (x0 < width || i < rects0Len) {
+ emitRect = addRect = extendRect = gFalse;
+ if (x0 >= width) {
+ emitRect = gTrue;
+ } else if (i >= rects0Len) {
+ addRect = gTrue;
+ } else if (rects0[i].x0 < x0) {
+ emitRect = gTrue;
+ } else if (x0 < rects0[i].x0) {
+ addRect = gTrue;
+ } else if (rects0[i].x1 == x1) {
+ extendRect = gTrue;
+ } else {
+ emitRect = addRect = gTrue;
+ }
+ if (emitRect) {
+ if (rectsOutLen == rectsOutSize) {
+ rectsOutSize *= 2;
+ rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
+ sizeof(PSOutImgClipRect));
+ }
+ rectsOut[rectsOutLen].x0 = rects0[i].x0;
+ rectsOut[rectsOutLen].x1 = rects0[i].x1;
+ rectsOut[rectsOutLen].y0 = height - y - 1;
+ rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1;
+ ++rectsOutLen;
+ ++i;
+ }
+ if (addRect || extendRect) {
+ if (rects1Len == rectsSize) {
+ rectsSize *= 2;
+ rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize,
+ sizeof(PSOutImgClipRect));
+ rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize,
+ sizeof(PSOutImgClipRect));
+ }
+ rects1[rects1Len].x0 = x0;
+ rects1[rects1Len].x1 = x1;
+ if (addRect) {
+ rects1[rects1Len].y0 = y;
+ }
+ if (extendRect) {
+ rects1[rects1Len].y0 = rects0[i].y0;
+ ++i;
+ }
+ ++rects1Len;
+ for (x0 = x1; x0 < width; ++x0) {
+ for (j = 0; j < numComps; ++j) {
+ if (line[x0*numComps+j] < maskColors[2*j] ||
+ line[x0*numComps+j] > maskColors[2*j+1]) {
+ break;
+ }
+ }
+ if (j < numComps) {
+ break;
+ }
+ }
+ for (x1 = x0; x1 < width; ++x1) {
+ for (j = 0; j < numComps; ++j) {
+ if (line[x1*numComps+j] < maskColors[2*j] ||
+ line[x1*numComps+j] > maskColors[2*j+1]) {
+ break;
+ }
+ }
+ if (j == numComps) {
+ break;
+ }
+ }
+ }
+ }
+ rectsTmp = rects0;
+ rects0 = rects1;
+ rects1 = rectsTmp;
+ i = rects0Len;
+ rects0Len = rects1Len;
+ rects1Len = i;
+ }
+ for (i = 0; i < rects0Len; ++i) {
+ if (rectsOutLen == rectsOutSize) {
+ rectsOutSize *= 2;
+ rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
+ sizeof(PSOutImgClipRect));
+ }
+ rectsOut[rectsOutLen].x0 = rects0[i].x0;
+ rectsOut[rectsOutLen].x1 = rects0[i].x1;
+ rectsOut[rectsOutLen].y0 = height - y - 1;
+ rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1;
+ ++rectsOutLen;
+ }
+ writePSFmt("{0:d} {1:d}\n", width, height);
+ for (i = 0; i < rectsOutLen; ++i) {
+ writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n",
+ rectsOut[i].x0, rectsOut[i].y0,
+ rectsOut[i].x1 - rectsOut[i].x0,
+ rectsOut[i].y1 - rectsOut[i].y0);
+ }
+ writePS("pop pop pdfImClip\n");
+ gfree(rectsOut);
+ gfree(rects0);
+ gfree(rects1);
+ delete imgStr;
+ str->close();
+}
+
+// Convert an explicit mask image to a clipping region consisting of a
+// sequence of clip rectangles.
+void PSOutputDev::convertExplicitMaskToClipRects(Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GBool maskInvert) {
+ ImageStream *imgStr;
+ Guchar *line;
+ PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut;
+ int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize;
+ GBool emitRect, addRect, extendRect;
+ int i, x0, x1, y, maskXor;
+
+ imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
+ imgStr->reset();
+ rects0Len = rects1Len = rectsOutLen = 0;
+ rectsSize = rectsOutSize = 64;
+ rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
+ rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect));
+ rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize,
+ sizeof(PSOutImgClipRect));
+ maskXor = maskInvert ? 1 : 0;
+ for (y = 0; y < maskHeight; ++y) {
+ if (!(line = imgStr->getLine())) {
+ break;
+ }
+ i = 0;
+ rects1Len = 0;
+ for (x0 = 0; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ;
+ for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ;
+ while (x0 < maskWidth || i < rects0Len) {
+ emitRect = addRect = extendRect = gFalse;
+ if (x0 >= maskWidth) {
+ emitRect = gTrue;
+ } else if (i >= rects0Len) {
+ addRect = gTrue;
+ } else if (rects0[i].x0 < x0) {
+ emitRect = gTrue;
+ } else if (x0 < rects0[i].x0) {
+ addRect = gTrue;
+ } else if (rects0[i].x1 == x1) {
+ extendRect = gTrue;
+ } else {
+ emitRect = addRect = gTrue;
+ }
+ if (emitRect) {
+ if (rectsOutLen == rectsOutSize) {
+ rectsOutSize *= 2;
+ rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
+ sizeof(PSOutImgClipRect));
+ }
+ rectsOut[rectsOutLen].x0 = rects0[i].x0;
+ rectsOut[rectsOutLen].x1 = rects0[i].x1;
+ rectsOut[rectsOutLen].y0 = maskHeight - y - 1;
+ rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1;
+ ++rectsOutLen;
+ ++i;
+ }
+ if (addRect || extendRect) {
+ if (rects1Len == rectsSize) {
+ rectsSize *= 2;
+ rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize,
+ sizeof(PSOutImgClipRect));
+ rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize,
+ sizeof(PSOutImgClipRect));
+ }
+ rects1[rects1Len].x0 = x0;
+ rects1[rects1Len].x1 = x1;
+ if (addRect) {
+ rects1[rects1Len].y0 = y;
+ }
+ if (extendRect) {
+ rects1[rects1Len].y0 = rects0[i].y0;
+ ++i;
+ }
+ ++rects1Len;
+ for (x0 = x1; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ;
+ for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ;
+ }
+ }
+ rectsTmp = rects0;
+ rects0 = rects1;
+ rects1 = rectsTmp;
+ i = rects0Len;
+ rects0Len = rects1Len;
+ rects1Len = i;
+ }
+ for (i = 0; i < rects0Len; ++i) {
+ if (rectsOutLen == rectsOutSize) {
+ rectsOutSize *= 2;
+ rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize,
+ sizeof(PSOutImgClipRect));
+ }
+ rectsOut[rectsOutLen].x0 = rects0[i].x0;
+ rectsOut[rectsOutLen].x1 = rects0[i].x1;
+ rectsOut[rectsOutLen].y0 = maskHeight - y - 1;
+ rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1;
+ ++rectsOutLen;
+ }
+ writePSFmt("{0:d} {1:d}\n", maskWidth, maskHeight);
+ for (i = 0; i < rectsOutLen; ++i) {
+ writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n",
+ rectsOut[i].x0, rectsOut[i].y0,
+ rectsOut[i].x1 - rectsOut[i].x0,
+ rectsOut[i].y1 - rectsOut[i].y0);
+ }
+ writePS("pop pop pdfImClip\n");
+ gfree(rectsOut);
+ gfree(rects0);
+ gfree(rects1);
+ delete imgStr;
+ maskStr->close();
+}
+
+//~ this doesn't currently support OPI
+void PSOutputDev::doImageL3(Object *ref, GfxState *state,
+ GfxImageColorMap *colorMap,
+ GBool invert, GBool inlineImg,
+ Stream *str, int width, int height, int len,
+ int *maskColors, Stream *maskStr,
+ int maskWidth, int maskHeight, GBool maskInvert) {
+ Stream *str2;
+ GString *s;
+ int n, numComps;
+ GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed;
+ GBool maskUseLZW, maskUseRLE, maskUseASCII, maskUseCompressed;
+ GString *maskFilters;
+ GfxSeparationColorSpace *sepCS;
+ GfxColor color;
+ GfxCMYK cmyk;
+ char buf[4096];
+ int c;
+ int col, i;
+
+ useASCIIHex = globalParams->getPSASCIIHex();
+ useLZW = useRLE = useASCII = useCompressed = gFalse; // make gcc happy
+ maskUseLZW = maskUseRLE = maskUseASCII = gFalse; // make gcc happy
+ maskUseCompressed = gFalse; // make gcc happy
+ maskFilters = NULL; // make gcc happy
+
+ // explicit masking
+ // -- this also converts color key masking in grayscale mode
+ if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) {
+
+ // mask data source
+ if (maskColors && colorMap && level == psLevel3Gray) {
+ s = NULL;
+ if (mode == psModeForm || inType3Char || preload) {
+ if (globalParams->getPSUncompressPreloadedImages()) {
+ maskUseLZW = maskUseRLE = gFalse;
+ } else if (globalParams->getPSLZW()) {
+ maskUseLZW = gTrue;
+ maskUseRLE = gFalse;
+ } else {
+ maskUseRLE = gTrue;
+ maskUseLZW = gFalse;
+ }
+ maskUseASCII = gFalse;
+ maskUseCompressed = gFalse;
+ } else {
+ if (globalParams->getPSLZW()) {
+ maskUseLZW = gTrue;
+ maskUseRLE = gFalse;
+ } else {
+ maskUseRLE = gTrue;
+ maskUseLZW = gFalse;
+ }
+ maskUseASCII = gTrue;
+ }
+ maskUseCompressed = gFalse;
+ maskWidth = width;
+ maskHeight = height;
+ maskInvert = gFalse;
+ } else if ((mode == psModeForm || inType3Char || preload) &&
+ globalParams->getPSUncompressPreloadedImages()) {
+ s = NULL;
+ maskUseLZW = maskUseRLE = gFalse;
+ maskUseCompressed = gFalse;
+ maskUseASCII = gFalse;
+ } else {
+ s = maskStr->getPSFilter(3, " ", !inlineImg);
+ if (!s) {
+ if (globalParams->getPSLZW()) {
+ maskUseLZW = gTrue;
+ maskUseRLE = gFalse;
+ } else {
+ maskUseRLE = gTrue;
+ maskUseLZW = gFalse;
+ }
+ maskUseASCII = !(mode == psModeForm || inType3Char || preload);
+ maskUseCompressed = gFalse;
+ } else {
+ maskUseLZW = maskUseRLE = gFalse;
+ maskUseASCII = maskStr->isBinary() &&
+ !(mode == psModeForm || inType3Char || preload);
+ maskUseCompressed = gTrue;
+ }
+ }
+ maskFilters = new GString();
+ if (maskUseASCII) {
+ maskFilters->appendf(" /ASCII{0:s}Decode filter\n",
+ useASCIIHex ? "Hex" : "85");
+ }
+ if (maskUseLZW) {
+ maskFilters->append(" /LZWDecode filter\n");
+ } else if (maskUseRLE) {
+ maskFilters->append(" /RunLengthDecode filter\n");
+ }
+ if (maskUseCompressed) {
+ maskFilters->append(s);
+ }
+ if (s) {
+ delete s;
+ }
+ if (mode == psModeForm || inType3Char || preload) {
+ writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n",
+ ref->getRefNum(), ref->getRefGen());
+ } else {
+ writePS("currentfile\n");
+ writePS(maskFilters->getCString());
+ writePS("pdfMask\n");
+
+ // add the ColorKeyToMask filter
+ if (maskColors && colorMap && level == psLevel3Gray) {
+ maskStr = new ColorKeyToMaskEncoder(str, width, height, colorMap,
+ maskColors);
+ }
+
+ // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
+ if (maskUseCompressed) {
+ maskStr = maskStr->getUndecodedStream();
+ }
+ if (maskUseLZW) {
+ maskStr = new LZWEncoder(maskStr);
+ } else if (maskUseRLE) {
+ maskStr = new RunLengthEncoder(maskStr);
+ }
+ if (maskUseASCII) {
+ if (useASCIIHex) {
+ maskStr = new ASCIIHexEncoder(maskStr);
+ } else {
+ maskStr = new ASCII85Encoder(maskStr);
+ }
+ }
+
+ // copy the stream data
+ maskStr->reset();
+ while ((n = maskStr->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
+ }
+ maskStr->close();
+ writePSChar('\n');
+ writePS("%-EOD-\n");
+
+ // delete encoders
+ if (maskUseLZW || maskUseRLE || maskUseASCII) {
+ delete maskStr;
+ }
+ }
+ }
+
+ // color space
+ if (colorMap && level != psLevel3Gray) {
+ dumpColorSpaceL2(state, colorMap->getColorSpace(), gFalse, gTrue, gFalse);
+ writePS(" setcolorspace\n");
+ }
+
+ // set up the image data
+ if (mode == psModeForm || inType3Char || preload) {
+ if (inlineImg) {
+ // create an array
+ str2 = new FixedLengthEncoder(str, len);
+ if (colorMap && level == psLevel3Gray) {
+ str2 = new GrayRecoder(str2, width, height, colorMap);
+ }
+ if (globalParams->getPSLZW()) {
+ str2 = new LZWEncoder(str2);
+ } else {
+ str2 = new RunLengthEncoder(str2);
+ }
+ if (useASCIIHex) {
+ str2 = new ASCIIHexEncoder(str2);
+ } else {
+ str2 = new ASCII85Encoder(str2);
+ }
+ str2->reset();
+ col = 0;
+ writePS((char *)(useASCIIHex ? "[<" : "[<~"));
+ do {
+ do {
+ c = str2->getChar();
+ } while (c == '\n' || c == '\r');
+ if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
+ break;
+ }
+ if (c == 'z') {
+ writePSChar((char)c);
+ ++col;
+ } else {
+ writePSChar((char)c);
+ ++col;
+ for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) {
+ do {
+ c = str2->getChar();
+ } while (c == '\n' || c == '\r');
+ if (c == (useASCIIHex ? '>' : '~') || c == EOF) {
+ break;
+ }
+ writePSChar((char)c);
+ ++col;
+ }
+ }
+ // each line is: "<~...data...~><eol>"
+ // so max data length = 255 - 6 = 249
+ // chunks are 1 or 5 bytes each, so we have to stop at 245
+ // but make it 240 just to be safe
+ if (col > 240) {
+ writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~"));
+ col = 0;
+ }
+ } while (c != (useASCIIHex ? '>' : '~') && c != EOF);
+ writePS((char *)(useASCIIHex ? ">\n" : "~>\n"));
+ // add an extra entry because the LZWDecode/RunLengthDecode
+ // filter may read past the end
+ writePS("<>]\n");
+ writePS("0\n");
+ str2->close();
+ delete str2;
+ } else {
+ // set up to use the array already created by setupImages()
+ writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen());
+ }
+ }
+
+ // explicit masking
+ if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) {
+ writePS("<<\n /ImageType 3\n");
+ writePS(" /InterleaveType 3\n");
+ writePS(" /DataDict\n");
+ }
+
+ // image (data) dictionary
+ writePSFmt("<<\n /ImageType {0:d}\n",
+ (maskColors && colorMap && level != psLevel3Gray) ? 4 : 1);
+
+ // color key masking
+ if (maskColors && colorMap && level != psLevel3Gray) {
+ writePS(" /MaskColor [\n");
+ numComps = colorMap->getNumPixelComps();
+ for (i = 0; i < 2 * numComps; i += 2) {
+ writePSFmt(" {0:d} {1:d}\n", maskColors[i], maskColors[i+1]);
+ }
+ writePS(" ]\n");
+ }
+
+ // width, height, matrix, bits per component
+ writePSFmt(" /Width {0:d}\n", width);
+ writePSFmt(" /Height {0:d}\n", height);
+ writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
+ width, -height, height);
+ if (colorMap && level == psLevel3Gray) {
+ writePS(" /BitsPerComponent 8\n");
+ } else {
+ writePSFmt(" /BitsPerComponent {0:d}\n",
+ colorMap ? colorMap->getBits() : 1);
+ }
+
+ // decode
+ if (colorMap) {
+ writePS(" /Decode [");
+ if (level == psLevel3Sep &&
+ colorMap->getColorSpace()->getMode() == csSeparation) {
+ // this matches up with the code in the pdfImSep operator
+ n = (1 << colorMap->getBits()) - 1;
+ writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n,
+ colorMap->getDecodeHigh(0) * n);
+ } else if (level == psLevel3Gray) {
+ writePS("0 1");
+ } else {
+ numComps = colorMap->getNumPixelComps();
+ for (i = 0; i < numComps; ++i) {
+ if (i > 0) {
+ writePS(" ");
+ }
+ writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i),
+ colorMap->getDecodeHigh(i));
+ }
+ }
+ writePS("]\n");
+ } else {
+ writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1);
+ }
+
+ // data source
+ if (mode == psModeForm || inType3Char || preload) {
+ writePS(" /DataSource { pdfImStr }\n");
+ } else {
+ writePS(" /DataSource currentfile\n");
+ }
+
+ // filters
+ if ((mode == psModeForm || inType3Char || preload) &&
+ globalParams->getPSUncompressPreloadedImages()) {
+ s = NULL;
+ useLZW = useRLE = gFalse;
+ useCompressed = gFalse;
+ useASCII = gFalse;
+ } else {
+ s = str->getPSFilter(3, " ", !inlineImg);
+ if ((colorMap && level == psLevel3Gray) || inlineImg || !s) {
+ if (globalParams->getPSLZW()) {
+ useLZW = gTrue;
+ useRLE = gFalse;
+ } else {
+ useRLE = gTrue;
+ useLZW = gFalse;
+ }
+ useASCII = !(mode == psModeForm || inType3Char || preload);
+ useCompressed = gFalse;
+ } else {
+ useLZW = useRLE = gFalse;
+ useASCII = str->isBinary() &&
+ !(mode == psModeForm || inType3Char || preload);
+ useCompressed = gTrue;
+ }
+ }
+ if (useASCII) {
+ writePSFmt(" /ASCII{0:s}Decode filter\n",
+ useASCIIHex ? "Hex" : "85");
+ }
+ if (useLZW) {
+ writePS(" /LZWDecode filter\n");
+ } else if (useRLE) {
+ writePS(" /RunLengthDecode filter\n");
+ }
+ if (useCompressed) {
+ writePS(s->getCString());
+ }
+ if (s) {
+ delete s;
+ }
+
+ // end of image (data) dictionary
+ writePS(">>\n");
+
+ // explicit masking
+ if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) {
+ writePS(" /MaskDict\n");
+ writePS("<<\n");
+ writePS(" /ImageType 1\n");
+ writePSFmt(" /Width {0:d}\n", maskWidth);
+ writePSFmt(" /Height {0:d}\n", maskHeight);
+ writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n",
+ maskWidth, -maskHeight, maskHeight);
+ writePS(" /BitsPerComponent 1\n");
+ writePSFmt(" /Decode [{0:d} {1:d}]\n",
+ maskInvert ? 1 : 0, maskInvert ? 0 : 1);
+
+ // mask data source
+ if (mode == psModeForm || inType3Char || preload) {
+ writePS(" /DataSource {pdfMaskSrc}\n");
+ writePS(maskFilters->getCString());
+ } else {
+ writePS(" /DataSource maskStream\n");
+ }
+ delete maskFilters;
+
+ writePS(">>\n");
+ writePS(">>\n");
+ }
+
+ if (mode == psModeForm || inType3Char || preload) {
+
+ // image command
+ writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask");
+
+ } else {
+
+ if (level == psLevel3Sep && colorMap &&
+ colorMap->getColorSpace()->getMode() == csSeparation) {
+ color.c[0] = gfxColorComp1;
+ sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();
+ sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent());
+ writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n",
+ colToDbl(cmyk.c), colToDbl(cmyk.m),
+ colToDbl(cmyk.y), colToDbl(cmyk.k),
+ sepCS->getName());
+ } else {
+ writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM");
+ }
+
+ }
+
+ // get rid of the array and index
+ if (mode == psModeForm || inType3Char || preload) {
+ writePS("pop pop\n");
+
+ // image data
+ } else {
+
+ // cut off inline image streams at appropriate length
+ if (inlineImg) {
+ str = new FixedLengthEncoder(str, len);
+ } else if (useCompressed) {
+ str = str->getUndecodedStream();
+ }
+
+ // recode to grayscale
+ if (colorMap && level == psLevel3Gray) {
+ str = new GrayRecoder(str, width, height, colorMap);
+ }
+
+ // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters
+ if (useLZW) {
+ str = new LZWEncoder(str);
+ } else if (useRLE) {
+ str = new RunLengthEncoder(str);
+ }
+ if (useASCII) {
+ if (useASCIIHex) {
+ str = new ASCIIHexEncoder(str);
+ } else {
+ str = new ASCII85Encoder(str);
+ }
+ }
+
+ // copy the stream data
+ str->reset();
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
+ }
+ str->close();
+
+ // add newline and trailer to the end
+ writePSChar('\n');
+ writePS("%-EOD-\n");
+
+ // delete encoders
+ if (useLZW || useRLE || useASCII || inlineImg) {
+ delete str;
+ }
+ }
+
+ // close the mask stream
+ if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) {
+ if (!(mode == psModeForm || inType3Char || preload)) {
+ writePS("pdfMaskEnd\n");
+ }
+ }
+}
+
+void PSOutputDev::dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace,
+ GBool genXform, GBool updateColors,
+ GBool map01) {
+ switch (colorSpace->getMode()) {
+ case csDeviceGray:
+ dumpDeviceGrayColorSpace((GfxDeviceGrayColorSpace *)colorSpace,
+ genXform, updateColors, map01);
+ break;
+ case csCalGray:
+ dumpCalGrayColorSpace((GfxCalGrayColorSpace *)colorSpace,
+ genXform, updateColors, map01);
+ break;
+ case csDeviceRGB:
+ dumpDeviceRGBColorSpace((GfxDeviceRGBColorSpace *)colorSpace,
+ genXform, updateColors, map01);
+ break;
+ case csCalRGB:
+ dumpCalRGBColorSpace((GfxCalRGBColorSpace *)colorSpace,
+ genXform, updateColors, map01);
+ break;
+ case csDeviceCMYK:
+ dumpDeviceCMYKColorSpace((GfxDeviceCMYKColorSpace *)colorSpace,
+ genXform, updateColors, map01);
+ break;
+ case csLab:
+ dumpLabColorSpace((GfxLabColorSpace *)colorSpace,
+ genXform, updateColors, map01);
+ break;
+ case csICCBased:
+ dumpICCBasedColorSpace(state, (GfxICCBasedColorSpace *)colorSpace,
+ genXform, updateColors, map01);
+ break;
+ case csIndexed:
+ dumpIndexedColorSpace(state, (GfxIndexedColorSpace *)colorSpace,
+ genXform, updateColors, map01);
+ break;
+ case csSeparation:
+ dumpSeparationColorSpace(state, (GfxSeparationColorSpace *)colorSpace,
+ genXform, updateColors, map01);
+ break;
+ case csDeviceN:
+ if (level >= psLevel3) {
+ dumpDeviceNColorSpaceL3(state, (GfxDeviceNColorSpace *)colorSpace,
+ genXform, updateColors, map01);
+ } else {
+ dumpDeviceNColorSpaceL2(state, (GfxDeviceNColorSpace *)colorSpace,
+ genXform, updateColors, map01);
+ }
+ break;
+ case csPattern:
+ //~ unimplemented
+ break;
+ }
+}
+
+void PSOutputDev::dumpDeviceGrayColorSpace(GfxDeviceGrayColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01) {
+ writePS("/DeviceGray");
+ if (genXform) {
+ writePS(" {}");
+ }
+ if (updateColors) {
+ processColors |= psProcessBlack;
+ }
+}
+
+void PSOutputDev::dumpCalGrayColorSpace(GfxCalGrayColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01) {
+ writePS("[/CIEBasedA <<\n");
+ writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", cs->getGamma());
+ writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n",
+ cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ());
+ writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
+ cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ());
+ writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
+ cs->getBlackX(), cs->getBlackY(), cs->getBlackZ());
+ writePS(">>]");
+ if (genXform) {
+ writePS(" {}");
+ }
+ if (updateColors) {
+ processColors |= psProcessBlack;
+ }
+}
+
+void PSOutputDev::dumpDeviceRGBColorSpace(GfxDeviceRGBColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01) {
+ writePS("/DeviceRGB");
+ if (genXform) {
+ writePS(" {}");
+ }
+ if (updateColors) {
+ processColors |= psProcessCMYK;
+ }
+}
+
+void PSOutputDev::dumpCalRGBColorSpace(GfxCalRGBColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01) {
+ writePS("[/CIEBasedABC <<\n");
+ writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n",
+ cs->getGammaR(), cs->getGammaG(), cs->getGammaB());
+ writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n",
+ cs->getMatrix()[0], cs->getMatrix()[1], cs->getMatrix()[2],
+ cs->getMatrix()[3], cs->getMatrix()[4], cs->getMatrix()[5],
+ cs->getMatrix()[6], cs->getMatrix()[7], cs->getMatrix()[8]);
+ writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
+ cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ());
+ writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
+ cs->getBlackX(), cs->getBlackY(), cs->getBlackZ());
+ writePS(">>]");
+ if (genXform) {
+ writePS(" {}");
+ }
+ if (updateColors) {
+ processColors |= psProcessCMYK;
+ }
+}
+
+void PSOutputDev::dumpDeviceCMYKColorSpace(GfxDeviceCMYKColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01) {
+ writePS("/DeviceCMYK");
+ if (genXform) {
+ writePS(" {}");
+ }
+ if (updateColors) {
+ processColors |= psProcessCMYK;
+ }
+}
+
+void PSOutputDev::dumpLabColorSpace(GfxLabColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01) {
+ writePS("[/CIEBasedABC <<\n");
+ if (map01) {
+ writePS(" /RangeABC [0 1 0 1 0 1]\n");
+ writePSFmt(" /DecodeABC [{{100 mul 16 add 116 div}} bind {{{0:.4g} mul {1:.4g} add}} bind {{{2:.4g} mul {3:.4g} add}} bind]\n",
+ (cs->getAMax() - cs->getAMin()) / 500.0,
+ cs->getAMin() / 500.0,
+ (cs->getBMax() - cs->getBMin()) / 200.0,
+ cs->getBMin() / 200.0);
+ } else {
+ writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n",
+ cs->getAMin(), cs->getAMax(),
+ cs->getBMin(), cs->getBMax());
+ writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n");
+ }
+ writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n");
+ writePS(" /DecodeLMN\n");
+ writePS(" [{dup 6 29 div ge {dup dup mul mul}\n");
+ writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n",
+ cs->getWhiteX());
+ writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
+ writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n",
+ cs->getWhiteY());
+ writePS(" {dup 6 29 div ge {dup dup mul mul}\n");
+ writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind]\n",
+ cs->getWhiteZ());
+ writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",
+ cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ());
+ writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",
+ cs->getBlackX(), cs->getBlackY(), cs->getBlackZ());
+ writePS(">>]");
+ if (genXform) {
+ writePS(" {}");
+ }
+ if (updateColors) {
+ processColors |= psProcessCMYK;
+ }
+}
+
+void PSOutputDev::dumpICCBasedColorSpace(GfxState *state,
+ GfxICCBasedColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01) {
+ // there is no transform function to the alternate color space, so
+ // we can use it directly
+ dumpColorSpaceL2(state, cs->getAlt(), genXform, updateColors, gFalse);
+}
+
+
+void PSOutputDev::dumpIndexedColorSpace(GfxState *state,
+ GfxIndexedColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01) {
+ GfxColorSpace *baseCS;
+ GfxLabColorSpace *labCS;
+ Guchar *lookup, *p;
+ double x[gfxColorMaxComps], y[gfxColorMaxComps];
+ double low[gfxColorMaxComps], range[gfxColorMaxComps];
+ GfxColor color;
+ GfxCMYK cmyk;
+ Function *func;
+ int n, numComps, numAltComps;
+ int byte;
+ int i, j, k;
+
+ baseCS = cs->getBase();
+ writePS("[/Indexed ");
+ dumpColorSpaceL2(state, baseCS, gFalse, updateColors, gTrue);
+ n = cs->getIndexHigh();
+ numComps = baseCS->getNComps();
+ lookup = cs->getLookup();
+ writePSFmt(" {0:d} <\n", n);
+ if (baseCS->getMode() == csDeviceN && level < psLevel3) {
+ func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();
+ baseCS->getDefaultRanges(low, range, cs->getIndexHigh());
+ if (((GfxDeviceNColorSpace *)baseCS)->getAlt()->getMode() == csLab) {
+ labCS = (GfxLabColorSpace *)((GfxDeviceNColorSpace *)baseCS)->getAlt();
+ } else {
+ labCS = NULL;
+ }
+ numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps();
+ p = lookup;
+ for (i = 0; i <= n; i += 8) {
+ writePS(" ");
+ for (j = i; j < i+8 && j <= n; ++j) {
+ for (k = 0; k < numComps; ++k) {
+ x[k] = low[k] + (*p++ / 255.0) * range[k];
+ }
+ func->transform(x, y);
+ if (labCS) {
+ y[0] /= 100.0;
+ y[1] = (y[1] - labCS->getAMin()) /
+ (labCS->getAMax() - labCS->getAMin());
+ y[2] = (y[2] - labCS->getBMin()) /
+ (labCS->getBMax() - labCS->getBMin());
+ }
+ for (k = 0; k < numAltComps; ++k) {
+ byte = (int)(y[k] * 255 + 0.5);
+ if (byte < 0) {
+ byte = 0;
+ } else if (byte > 255) {
+ byte = 255;
+ }
+ writePSFmt("{0:02x}", byte);
+ }
+ if (updateColors) {
+ color.c[0] = dblToCol(j);
+ cs->getCMYK(&color, &cmyk, state->getRenderingIntent());
+ addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
+ colToDbl(cmyk.y), colToDbl(cmyk.k));
+ }
+ }
+ writePS("\n");
+ }
+ } else {
+ for (i = 0; i <= n; i += 8) {
+ writePS(" ");
+ for (j = i; j < i+8 && j <= n; ++j) {
+ for (k = 0; k < numComps; ++k) {
+ writePSFmt("{0:02x}", lookup[j * numComps + k]);
+ }
+ if (updateColors) {
+ color.c[0] = dblToCol(j);
+ cs->getCMYK(&color, &cmyk, state->getRenderingIntent());
+ addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),
+ colToDbl(cmyk.y), colToDbl(cmyk.k));
+ }
+ }
+ writePS("\n");
+ }
+ }
+ writePS(">]");
+ if (genXform) {
+ writePS(" {}");
+ }
+}
+
+void PSOutputDev::dumpSeparationColorSpace(GfxState *state,
+ GfxSeparationColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01) {
+ writePS("[/Separation ");
+ writePSString(cs->getName());
+ writePS(" ");
+ dumpColorSpaceL2(state, cs->getAlt(), gFalse, gFalse, gFalse);
+ writePS("\n");
+ cvtFunction(cs->getFunc());
+ writePS("]");
+ if (genXform) {
+ writePS(" {}");
+ }
+ if (updateColors) {
+ addCustomColor(state, cs);
+ }
+}
+
+void PSOutputDev::dumpDeviceNColorSpaceL2(GfxState *state,
+ GfxDeviceNColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01) {
+ dumpColorSpaceL2(state, cs->getAlt(), gFalse, updateColors, map01);
+ if (genXform) {
+ writePS(" ");
+ cvtFunction(cs->getTintTransformFunc());
+ }
+}
+
+void PSOutputDev::dumpDeviceNColorSpaceL3(GfxState *state,
+ GfxDeviceNColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01) {
+ GString *tint;
+ int i;
+
+ writePS("[/DeviceN [\n");
+ for (i = 0; i < cs->getNComps(); ++i) {
+ writePSString(cs->getColorantName(i));
+ writePS("\n");
+ }
+ writePS("]\n");
+ if ((tint = createDeviceNTintFunc(cs))) {
+ writePS("/DeviceCMYK\n");
+ writePS(tint->getCString());
+ delete tint;
+ } else {
+ dumpColorSpaceL2(state, cs->getAlt(), gFalse, gFalse, gFalse);
+ writePS("\n");
+ cvtFunction(cs->getTintTransformFunc());
+ }
+ writePS("]");
+ if (genXform) {
+ writePS(" {}");
+ }
+ if (updateColors) {
+ addCustomColors(state, cs);
+ }
+}
+
+// If the DeviceN color space has a Colorants dictionary, and all of
+// the colorants are one of: "None", "Cyan", "Magenta", "Yellow",
+// "Black", or have an entry in the Colorants dict that maps to
+// DeviceCMYK, then build a new tint function; else use the existing
+// tint function.
+GString *PSOutputDev::createDeviceNTintFunc(GfxDeviceNColorSpace *cs) {
+ Object *attrs;
+ Object colorants, sepCSObj, funcObj, obj1;
+ GString *name;
+ Function *func;
+ double sepIn;
+ double cmyk[gfxColorMaxComps][4];
+ GString *tint;
+ GBool first;
+ int i, j;
+
+ attrs = cs->getAttrs();
+ if (!attrs->isDict()) {
+ return NULL;
+ }
+ if (!attrs->dictLookup("Colorants", &colorants)->isDict()) {
+ colorants.free();
+ return NULL;
+ }
+ for (i = 0; i < cs->getNComps(); ++i) {
+ name = cs->getColorantName(i);
+ if (!name->cmp("None")) {
+ cmyk[i][0] = cmyk[i][1] = cmyk[i][2] = cmyk[i][3] = 0;
+ } else if (!name->cmp("Cyan")) {
+ cmyk[i][1] = cmyk[i][2] = cmyk[i][3] = 0;
+ cmyk[i][0] = 1;
+ } else if (!name->cmp("Magenta")) {
+ cmyk[i][0] = cmyk[i][2] = cmyk[i][3] = 0;
+ cmyk[i][1] = 1;
+ } else if (!name->cmp("Yellow")) {
+ cmyk[i][0] = cmyk[i][1] = cmyk[i][3] = 0;
+ cmyk[i][2] = 1;
+ } else if (!name->cmp("Black")) {
+ cmyk[i][0] = cmyk[i][1] = cmyk[i][2] = 0;
+ cmyk[i][3] = 1;
+ } else {
+ colorants.dictLookup(name->getCString(), &sepCSObj);
+ if (!sepCSObj.isArray() || sepCSObj.arrayGetLength() != 4) {
+ sepCSObj.free();
+ colorants.free();
+ return NULL;
+ }
+ if (!sepCSObj.arrayGet(0, &obj1)->isName("Separation")) {
+ obj1.free();
+ sepCSObj.free();
+ colorants.free();
+ return NULL;
+ }
+ obj1.free();
+ if (!sepCSObj.arrayGet(2, &obj1)->isName("DeviceCMYK")) {
+ obj1.free();
+ sepCSObj.free();
+ colorants.free();
+ return NULL;
+ }
+ obj1.free();
+ sepCSObj.arrayGet(3, &funcObj);
+ if (!(func = Function::parse(&funcObj, 1, 4))) {
+ funcObj.free();
+ sepCSObj.free();
+ colorants.free();
+ return NULL;
+ }
+ funcObj.free();
+ sepIn = 1;
+ func->transform(&sepIn, cmyk[i]);
+ delete func;
+ sepCSObj.free();
+ }
+ }
+ colorants.free();
+
+ tint = new GString();
+ tint->append("{\n");
+ for (j = 0; j < 4; ++j) { // C, M, Y, K
+ first = gTrue;
+ for (i = 0; i < cs->getNComps(); ++i) {
+ if (cmyk[i][j] != 0) {
+ tint->appendf("{0:d} index {1:.4f} mul{2:s}\n",
+ j + cs->getNComps() - 1 - i, cmyk[i][j],
+ first ? "" : " add");
+ first = gFalse;
+ }
+ }
+ if (first) {
+ tint->append("0\n");
+ }
+ }
+ tint->appendf("{0:d} 4 roll\n", cs->getNComps() + 4);
+ for (i = 0; i < cs->getNComps(); ++i) {
+ tint->append("pop\n");
+ }
+ tint->append("}\n");
+
+ return tint;
+}
+
+#if OPI_SUPPORT
+void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) {
+ Object dict;
+
+ if (globalParams->getPSOPI()) {
+ opiDict->lookup("2.0", &dict);
+ if (dict.isDict()) {
+ opiBegin20(state, dict.getDict());
+ dict.free();
+ } else {
+ dict.free();
+ opiDict->lookup("1.3", &dict);
+ if (dict.isDict()) {
+ opiBegin13(state, dict.getDict());
+ }
+ dict.free();
+ }
+ }
+}
+
+void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) {
+ Object obj1, obj2, obj3, obj4;
+ double width, height, left, right, top, bottom;
+ int w, h;
+ int i;
+
+ writePS("%%BeginOPI: 2.0\n");
+ writePS("%%Distilled\n");
+
+ dict->lookup("F", &obj1);
+ if (getFileSpec(&obj1, &obj2)) {
+ writePSFmt("%%ImageFileName: {0:t}\n", obj2.getString());
+ obj2.free();
+ }
+ obj1.free();
+
+ dict->lookup("MainImage", &obj1);
+ if (obj1.isString()) {
+ writePSFmt("%%MainImage: {0:t}\n", obj1.getString());
+ }
+ obj1.free();
+
+ //~ ignoring 'Tags' entry
+ //~ need to use writePSString() and deal with >255-char lines
+
+ dict->lookup("Size", &obj1);
+ if (obj1.isArray() && obj1.arrayGetLength() == 2) {
+ obj1.arrayGet(0, &obj2);
+ width = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ height = obj2.getNum();
+ obj2.free();
+ writePSFmt("%%ImageDimensions: {0:.6g} {1:.6g}\n", width, height);
+ }
+ obj1.free();
+
+ dict->lookup("CropRect", &obj1);
+ if (obj1.isArray() && obj1.arrayGetLength() == 4) {
+ obj1.arrayGet(0, &obj2);
+ left = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ top = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2, &obj2);
+ right = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(3, &obj2);
+ bottom = obj2.getNum();
+ obj2.free();
+ writePSFmt("%%ImageCropRect: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n",
+ left, top, right, bottom);
+ }
+ obj1.free();
+
+ dict->lookup("Overprint", &obj1);
+ if (obj1.isBool()) {
+ writePSFmt("%%ImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false");
+ }
+ obj1.free();
+
+ dict->lookup("Inks", &obj1);
+ if (obj1.isName()) {
+ writePSFmt("%%ImageInks: {0:s}\n", obj1.getName());
+ } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) {
+ obj1.arrayGet(0, &obj2);
+ if (obj2.isName()) {
+ writePSFmt("%%ImageInks: {0:s} {1:d}",
+ obj2.getName(), (obj1.arrayGetLength() - 1) / 2);
+ for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) {
+ obj1.arrayGet(i, &obj3);
+ obj1.arrayGet(i+1, &obj4);
+ if (obj3.isString() && obj4.isNum()) {
+ writePS(" ");
+ writePSString(obj3.getString());
+ writePSFmt(" {0:.4g}", obj4.getNum());
+ }
+ obj3.free();
+ obj4.free();
+ }
+ writePS("\n");
+ }
+ obj2.free();
+ }
+ obj1.free();
+
+ writePS("gsave\n");
+
+ writePS("%%BeginIncludedImage\n");
+
+ dict->lookup("IncludedImageDimensions", &obj1);
+ if (obj1.isArray() && obj1.arrayGetLength() == 2) {
+ obj1.arrayGet(0, &obj2);
+ w = obj2.getInt();
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ h = obj2.getInt();
+ obj2.free();
+ writePSFmt("%%IncludedImageDimensions: {0:d} {1:d}\n", w, h);
+ }
+ obj1.free();
+
+ dict->lookup("IncludedImageQuality", &obj1);
+ if (obj1.isNum()) {
+ writePSFmt("%%IncludedImageQuality: {0:.4g}\n", obj1.getNum());
+ }
+ obj1.free();
+
+ ++opi20Nest;
+}
+
+void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) {
+ Object obj1, obj2;
+ int left, right, top, bottom, samples, bits, width, height;
+ double c, m, y, k;
+ double llx, lly, ulx, uly, urx, ury, lrx, lry;
+ double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry;
+ double horiz, vert;
+ int i, j;
+
+ writePS("save\n");
+ writePS("/opiMatrix2 matrix currentmatrix def\n");
+ writePS("opiMatrix setmatrix\n");
+
+ dict->lookup("F", &obj1);
+ if (getFileSpec(&obj1, &obj2)) {
+ writePSFmt("%ALDImageFileName: {0:t}\n", obj2.getString());
+ obj2.free();
+ }
+ obj1.free();
+
+ dict->lookup("CropRect", &obj1);
+ if (obj1.isArray() && obj1.arrayGetLength() == 4) {
+ obj1.arrayGet(0, &obj2);
+ left = obj2.getInt();
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ top = obj2.getInt();
+ obj2.free();
+ obj1.arrayGet(2, &obj2);
+ right = obj2.getInt();
+ obj2.free();
+ obj1.arrayGet(3, &obj2);
+ bottom = obj2.getInt();
+ obj2.free();
+ writePSFmt("%ALDImageCropRect: {0:d} {1:d} {2:d} {3:d}\n",
+ left, top, right, bottom);
+ }
+ obj1.free();
+
+ dict->lookup("Color", &obj1);
+ if (obj1.isArray() && obj1.arrayGetLength() == 5) {
+ obj1.arrayGet(0, &obj2);
+ c = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ m = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2, &obj2);
+ y = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(3, &obj2);
+ k = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(4, &obj2);
+ if (obj2.isString()) {
+ writePSFmt("%ALDImageColor: {0:.4g} {1:.4g} {2:.4g} {3:.4g} ",
+ c, m, y, k);
+ writePSString(obj2.getString());
+ writePS("\n");
+ }
+ obj2.free();
+ }
+ obj1.free();
+
+ dict->lookup("ColorType", &obj1);
+ if (obj1.isName()) {
+ writePSFmt("%ALDImageColorType: {0:s}\n", obj1.getName());
+ }
+ obj1.free();
+
+ //~ ignores 'Comments' entry
+ //~ need to handle multiple lines
+
+ dict->lookup("CropFixed", &obj1);
+ if (obj1.isArray()) {
+ obj1.arrayGet(0, &obj2);
+ ulx = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ uly = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2, &obj2);
+ lrx = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(3, &obj2);
+ lry = obj2.getNum();
+ obj2.free();
+ writePSFmt("%ALDImageCropFixed: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n",
+ ulx, uly, lrx, lry);
+ }
+ obj1.free();
+
+ dict->lookup("GrayMap", &obj1);
+ if (obj1.isArray()) {
+ writePS("%ALDImageGrayMap:");
+ for (i = 0; i < obj1.arrayGetLength(); i += 16) {
+ if (i > 0) {
+ writePS("\n%%+");
+ }
+ for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) {
+ obj1.arrayGet(i+j, &obj2);
+ writePSFmt(" {0:d}", obj2.getInt());
+ obj2.free();
+ }
+ }
+ writePS("\n");
+ }
+ obj1.free();
+
+ dict->lookup("ID", &obj1);
+ if (obj1.isString()) {
+ writePSFmt("%ALDImageID: {0:t}\n", obj1.getString());
+ }
+ obj1.free();
+
+ dict->lookup("ImageType", &obj1);
+ if (obj1.isArray() && obj1.arrayGetLength() == 2) {
+ obj1.arrayGet(0, &obj2);
+ samples = obj2.getInt();
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ bits = obj2.getInt();
+ obj2.free();
+ writePSFmt("%ALDImageType: {0:d} {1:d}\n", samples, bits);
+ }
+ obj1.free();
+
+ dict->lookup("Overprint", &obj1);
+ if (obj1.isBool()) {
+ writePSFmt("%ALDImageOverprint: {0:s}\n",
+ obj1.getBool() ? "true" : "false");
+ }
+ obj1.free();
+
+ dict->lookup("Position", &obj1);
+ if (obj1.isArray() && obj1.arrayGetLength() == 8) {
+ obj1.arrayGet(0, &obj2);
+ llx = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ lly = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2, &obj2);
+ ulx = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(3, &obj2);
+ uly = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(4, &obj2);
+ urx = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(5, &obj2);
+ ury = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(6, &obj2);
+ lrx = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(7, &obj2);
+ lry = obj2.getNum();
+ obj2.free();
+ opiTransform(state, llx, lly, &tllx, &tlly);
+ opiTransform(state, ulx, uly, &tulx, &tuly);
+ opiTransform(state, urx, ury, &turx, &tury);
+ opiTransform(state, lrx, lry, &tlrx, &tlry);
+ writePSFmt("%ALDImagePosition: {0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g}\n",
+ tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry);
+ obj2.free();
+ }
+ obj1.free();
+
+ dict->lookup("Resolution", &obj1);
+ if (obj1.isArray() && obj1.arrayGetLength() == 2) {
+ obj1.arrayGet(0, &obj2);
+ horiz = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ vert = obj2.getNum();
+ obj2.free();
+ writePSFmt("%ALDImageResoution: {0:.4g} {1:.4g}\n", horiz, vert);
+ obj2.free();
+ }
+ obj1.free();
+
+ dict->lookup("Size", &obj1);
+ if (obj1.isArray() && obj1.arrayGetLength() == 2) {
+ obj1.arrayGet(0, &obj2);
+ width = obj2.getInt();
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ height = obj2.getInt();
+ obj2.free();
+ writePSFmt("%ALDImageDimensions: {0:d} {1:d}\n", width, height);
+ }
+ obj1.free();
+
+ //~ ignoring 'Tags' entry
+ //~ need to use writePSString() and deal with >255-char lines
+
+ dict->lookup("Tint", &obj1);
+ if (obj1.isNum()) {
+ writePSFmt("%ALDImageTint: {0:.4g}\n", obj1.getNum());
+ }
+ obj1.free();
+
+ dict->lookup("Transparency", &obj1);
+ if (obj1.isBool()) {
+ writePSFmt("%ALDImageTransparency: {0:s}\n",
+ obj1.getBool() ? "true" : "false");
+ }
+ obj1.free();
+
+ writePS("%%BeginObject: image\n");
+ writePS("opiMatrix2 setmatrix\n");
+ ++opi13Nest;
+}
+
+// Convert PDF user space coordinates to PostScript default user space
+// coordinates. This has to account for both the PDF CTM and the
+// PSOutputDev page-fitting transform.
+void PSOutputDev::opiTransform(GfxState *state, double x0, double y0,
+ double *x1, double *y1) {
+ double t;
+
+ state->transform(x0, y0, x1, y1);
+ *x1 += tx;
+ *y1 += ty;
+ if (rotate == 90) {
+ t = *x1;
+ *x1 = -*y1;
+ *y1 = t;
+ } else if (rotate == 180) {
+ *x1 = -*x1;
+ *y1 = -*y1;
+ } else if (rotate == 270) {
+ t = *x1;
+ *x1 = *y1;
+ *y1 = -t;
+ }
+ *x1 *= xScale;
+ *y1 *= yScale;
+}
+
+void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) {
+ Object dict;
+
+ if (globalParams->getPSOPI()) {
+ opiDict->lookup("2.0", &dict);
+ if (dict.isDict()) {
+ writePS("%%EndIncludedImage\n");
+ writePS("%%EndOPI\n");
+ writePS("grestore\n");
+ --opi20Nest;
+ dict.free();
+ } else {
+ dict.free();
+ opiDict->lookup("1.3", &dict);
+ if (dict.isDict()) {
+ writePS("%%EndObject\n");
+ writePS("restore\n");
+ --opi13Nest;
+ }
+ dict.free();
+ }
+ }
+}
+
+GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) {
+ if (fileSpec->isString()) {
+ fileSpec->copy(fileName);
+ return gTrue;
+ }
+ if (fileSpec->isDict()) {
+ fileSpec->dictLookup("DOS", fileName);
+ if (fileName->isString()) {
+ return gTrue;
+ }
+ fileName->free();
+ fileSpec->dictLookup("Mac", fileName);
+ if (fileName->isString()) {
+ return gTrue;
+ }
+ fileName->free();
+ fileSpec->dictLookup("Unix", fileName);
+ if (fileName->isString()) {
+ return gTrue;
+ }
+ fileName->free();
+ fileSpec->dictLookup("F", fileName);
+ if (fileName->isString()) {
+ return gTrue;
+ }
+ fileName->free();
+ }
+ return gFalse;
+}
+#endif // OPI_SUPPORT
+
+void PSOutputDev::type3D0(GfxState *state, double wx, double wy) {
+ writePSFmt("{0:.6g} {1:.6g} setcharwidth\n", wx, wy);
+ writePS("q\n");
+ t3NeedsRestore = gTrue;
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::type3D1(GfxState *state, double wx, double wy,
+ double llx, double lly, double urx, double ury) {
+ if (t3String) {
+ error(errSyntaxError, -1, "Multiple 'd1' operators in Type 3 CharProc");
+ return;
+ }
+ t3WX = wx;
+ t3WY = wy;
+ t3LLX = llx;
+ t3LLY = lly;
+ t3URX = urx;
+ t3URY = ury;
+ t3String = new GString();
+ writePS("q\n");
+ t3FillColorOnly = gTrue;
+ t3Cacheable = gTrue;
+ t3NeedsRestore = gTrue;
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::drawForm(Ref id) {
+ writePSFmt("f_{0:d}_{1:d}\n", id.num, id.gen);
+ noStateChanges = gFalse;
+}
+
+void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {
+ Stream *str;
+ char buf[4096];
+ int n;
+
+ if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {
+ str = level1Stream;
+ } else {
+ str = psStream;
+ }
+ str->reset();
+ while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
+ writePSBlock(buf, n);
+ }
+ str->close();
+ noStateChanges = gFalse;
+}
+
+//~ can nextFunc be reset to 0 -- maybe at the start of each page?
+//~ or maybe at the start of each color space / pattern?
+void PSOutputDev::cvtFunction(Function *func) {
+ SampledFunction *func0;
+ ExponentialFunction *func2;
+ StitchingFunction *func3;
+ PostScriptFunction *func4;
+ int thisFunc, m, n, nSamples, i, j, k;
+
+ switch (func->getType()) {
+
+ case -1: // identity
+ writePS("{}\n");
+ break;
+
+ case 0: // sampled
+ func0 = (SampledFunction *)func;
+ thisFunc = nextFunc++;
+ m = func0->getInputSize();
+ n = func0->getOutputSize();
+ nSamples = n;
+ for (i = 0; i < m; ++i) {
+ nSamples *= func0->getSampleSize(i);
+ }
+ writePSFmt("/xpdfSamples{0:d} [\n", thisFunc);
+ for (i = 0; i < nSamples; ++i) {
+ writePSFmt("{0:.6g}\n", func0->getSamples()[i]);
+ }
+ writePS("] def\n");
+ writePSFmt("{{ {0:d} array {1:d} array {2:d} 2 roll\n", 2*m, m, m+2);
+ // [e01] [efrac] x0 x1 ... xm-1
+ for (i = m-1; i >= 0; --i) {
+ // [e01] [efrac] x0 x1 ... xi
+ writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add\n",
+ func0->getDomainMin(i),
+ (func0->getEncodeMax(i) - func0->getEncodeMin(i)) /
+ (func0->getDomainMax(i) - func0->getDomainMin(i)),
+ func0->getEncodeMin(i));
+ // [e01] [efrac] x0 x1 ... xi-1 xi'
+ writePSFmt("dup 0 lt {{ pop 0 }} {{ dup {0:d} gt {{ pop {1:d} }} if }} ifelse\n",
+ func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1);
+ // [e01] [efrac] x0 x1 ... xi-1 xi'
+ writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n");
+ // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi')
+ writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, i);
+ // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi')
+ writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, 2*i+1);
+ // [e01] [efrac] x0 x1 ... xi-1 floor(xi')
+ writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+2, 2*i);
+ // [e01] [efrac] x0 x1 ... xi-1
+ }
+ // [e01] [efrac]
+ for (i = 0; i < n; ++i) {
+ // [e01] [efrac] y(0) ... y(i-1)
+ for (j = 0; j < (1<<m); ++j) {
+ // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(j-1)
+ writePSFmt("xpdfSamples{0:d}\n", thisFunc);
+ k = m - 1;
+ writePSFmt("{0:d} index {1:d} get\n", i+j+2, 2 * k + ((j >> k) & 1));
+ for (k = m - 2; k >= 0; --k) {
+ writePSFmt("{0:d} mul {1:d} index {2:d} get add\n",
+ func0->getSampleSize(k),
+ i + j + 3,
+ 2 * k + ((j >> k) & 1));
+ }
+ if (n > 1) {
+ writePSFmt("{0:d} mul {1:d} add ", n, i);
+ }
+ writePS("get\n");
+ }
+ // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1)
+ for (j = 0; j < m; ++j) {
+ // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1)
+ for (k = 0; k < (1 << (m - j)); k += 2) {
+ // [e01] [efrac] y(0) ... y(i-1) <k/2 s' values> <2^(m-j)-k s values>
+ writePSFmt("{0:d} index {1:d} get dup\n",
+ i + k/2 + (1 << (m-j)) - k, j);
+ writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n");
+ writePSFmt("{0:d} 1 roll\n", k/2 + (1 << (m-j)) - k - 1);
+ }
+ // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1)
+ }
+ // [e01] [efrac] y(0) ... y(i-1) s
+ writePSFmt("{0:.6g} mul {1:.6g} add\n",
+ func0->getDecodeMax(i) - func0->getDecodeMin(i),
+ func0->getDecodeMin(i));
+ writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
+ func0->getRangeMin(i), func0->getRangeMin(i),
+ func0->getRangeMax(i), func0->getRangeMax(i));
+ // [e01] [efrac] y(0) ... y(i-1) y(i)
+ }
+ // [e01] [efrac] y(0) ... y(n-1)
+ writePSFmt("{0:d} {1:d} roll pop pop }}\n", n+2, n);
+ break;
+
+ case 2: // exponential
+ func2 = (ExponentialFunction *)func;
+ n = func2->getOutputSize();
+ writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
+ func2->getDomainMin(0), func2->getDomainMin(0),
+ func2->getDomainMax(0), func2->getDomainMax(0));
+ // x
+ for (i = 0; i < n; ++i) {
+ // x y(0) .. y(i-1)
+ writePSFmt("{0:d} index {1:.6g} exp {2:.6g} mul {3:.6g} add\n",
+ i, func2->getE(), func2->getC1()[i] - func2->getC0()[i],
+ func2->getC0()[i]);
+ if (func2->getHasRange()) {
+ writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
+ func2->getRangeMin(i), func2->getRangeMin(i),
+ func2->getRangeMax(i), func2->getRangeMax(i));
+ }
+ }
+ // x y(0) .. y(n-1)
+ writePSFmt("{0:d} {1:d} roll pop }}\n", n+1, n);
+ break;
+
+ case 3: // stitching
+ func3 = (StitchingFunction *)func;
+ thisFunc = nextFunc++;
+ for (i = 0; i < func3->getNumFuncs(); ++i) {
+ cvtFunction(func3->getFunc(i));
+ writePSFmt("/xpdfFunc{0:d}_{1:d} exch def\n", thisFunc, i);
+ }
+ writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n",
+ func3->getDomainMin(0), func3->getDomainMin(0),
+ func3->getDomainMax(0), func3->getDomainMax(0));
+ for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
+ writePSFmt("dup {0:.6g} lt {{ {1:.6g} sub {2:.6g} mul {3:.6g} add xpdfFunc{4:d}_{5:d} }} {{\n",
+ func3->getBounds()[i+1],
+ func3->getBounds()[i],
+ func3->getScale()[i],
+ func3->getEncode()[2*i],
+ thisFunc, i);
+ }
+ writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add xpdfFunc{3:d}_{4:d}\n",
+ func3->getBounds()[i],
+ func3->getScale()[i],
+ func3->getEncode()[2*i],
+ thisFunc, i);
+ for (i = 0; i < func3->getNumFuncs() - 1; ++i) {
+ writePS("} ifelse\n");
+ }
+ writePS("}\n");
+ break;
+
+ case 4: // PostScript
+ func4 = (PostScriptFunction *)func;
+ writePS(func4->getCodeString()->getCString());
+ writePS("\n");
+ break;
+ }
+}
+
+void PSOutputDev::writePSChar(char c) {
+ if (t3String) {
+ t3String->append(c);
+ } else {
+ (*outputFunc)(outputStream, &c, 1);
+ }
+}
+
+void PSOutputDev::writePSBlock(char *s, int len) {
+ if (t3String) {
+ t3String->append(s, len);
+ } else {
+ (*outputFunc)(outputStream, s, len);
+ }
+}
+
+void PSOutputDev::writePS(const char *s) {
+ if (t3String) {
+ t3String->append(s);
+ } else {
+ (*outputFunc)(outputStream, s, (int)strlen(s));
+ }
+}
+
+void PSOutputDev::writePSFmt(const char *fmt, ...) {
+ va_list args;
+ GString *buf;
+
+ va_start(args, fmt);
+ if (t3String) {
+ t3String->appendfv((char *)fmt, args);
+ } else {
+ buf = GString::formatv((char *)fmt, args);
+ (*outputFunc)(outputStream, buf->getCString(), buf->getLength());
+ delete buf;
+ }
+ va_end(args);
+}
+
+void PSOutputDev::writePSString(GString *s) {
+ Guchar *p;
+ int n, line;
+ char buf[8];
+
+ writePSChar('(');
+ line = 1;
+ for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) {
+ if (line >= 64) {
+ writePSChar('\\');
+ writePSChar('\n');
+ line = 0;
+ }
+ if (*p == '(' || *p == ')' || *p == '\\') {
+ writePSChar('\\');
+ writePSChar((char)*p);
+ line += 2;
+ } else if (*p < 0x20 || *p >= 0x80) {
+ snprintf(buf, sizeof(buf), "\\%03o", *p);
+ writePS(buf);
+ line += 4;
+ } else {
+ writePSChar((char)*p);
+ ++line;
+ }
+ }
+ writePSChar(')');
+}
+
+void PSOutputDev::writePSName(const char *s) {
+ const char *p;
+ char c;
+
+ p = s;
+ while ((c = *p++)) {
+ if (c <= (char)0x20 || c >= (char)0x7f ||
+ c == '(' || c == ')' || c == '<' || c == '>' ||
+ c == '[' || c == ']' || c == '{' || c == '}' ||
+ c == '/' || c == '%') {
+ writePSFmt("#{0:02x}", c & 0xff);
+ } else {
+ writePSChar(c);
+ }
+ }
+}
+
+GString *PSOutputDev::filterPSName(GString *name) {
+ GString *name2;
+ char buf[8];
+ int i;
+ char c;
+
+ name2 = new GString();
+
+ // ghostscript chokes on names that begin with out-of-limits
+ // numbers, e.g., 1e4foo is handled correctly (as a name), but
+ // 1e999foo generates a limitcheck error
+ c = name->getChar(0);
+ if (c >= '0' && c <= '9') {
+ name2->append('f');
+ }
+
+ for (i = 0; i < name->getLength(); ++i) {
+ c = name->getChar(i);
+ if (c <= (char)0x20 || c >= (char)0x7f ||
+ c == '(' || c == ')' || c == '<' || c == '>' ||
+ c == '[' || c == ']' || c == '{' || c == '}' ||
+ c == '/' || c == '%') {
+ snprintf(buf, sizeof(buf), "#%02x", c & 0xff);
+ name2->append(buf);
+ } else {
+ name2->append(c);
+ }
+ }
+ return name2;
+}
+
+// Write a DSC-compliant <textline>.
+void PSOutputDev::writePSTextLine(GString *s) {
+ TextString *ts;
+ Unicode *u;
+ int i, j;
+ int c;
+
+ // - DSC comments must be printable ASCII; control chars and
+ // backslashes have to be escaped (we do cheap Unicode-to-ASCII
+ // conversion by simply ignoring the high byte)
+ // - lines are limited to 255 chars (we limit to 200 here to allow
+ // for the keyword, which was emitted by the caller)
+ // - lines that start with a left paren are treated as <text>
+ // instead of <textline>, so we escape a leading paren
+ ts = new TextString(s);
+ u = ts->getUnicode();
+ for (i = 0, j = 0; i < ts->getLength() && j < 200; ++i) {
+ c = u[i] & 0xff;
+ if (c == '\\') {
+ writePS("\\\\");
+ j += 2;
+ } else if (c < 0x20 || c > 0x7e || (j == 0 && c == '(')) {
+ writePSFmt("\\{0:03o}", c);
+ j += 4;
+ } else {
+ writePSChar((char)c);
+ ++j;
+ }
+ }
+ writePS("\n");
+ delete ts;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h
new file mode 100644
index 00000000000..466f81f1992
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PSOutputDev.h
@@ -0,0 +1,510 @@
+//========================================================================
+//
+// PSOutputDev.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PSOUTPUTDEV_H
+#define PSOUTPUTDEV_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stddef.h>
+#include "config.h"
+#include "Object.h"
+#include "GlobalParams.h"
+#include "OutputDev.h"
+
+class GHash;
+class PDFDoc;
+class XRef;
+class Function;
+class GfxPath;
+class GfxFont;
+class GfxColorSpace;
+class GfxDeviceGrayColorSpace;
+class GfxCalGrayColorSpace;
+class GfxDeviceRGBColorSpace;
+class GfxCalRGBColorSpace;
+class GfxDeviceCMYKColorSpace;
+class GfxLabColorSpace;
+class GfxICCBasedColorSpace;
+class GfxIndexedColorSpace;
+class GfxSeparationColorSpace;
+class GfxDeviceNColorSpace;
+class GfxFunctionShading;
+class GfxAxialShading;
+class GfxRadialShading;
+class PDFRectangle;
+class PSOutCustomColor;
+class PSOutputDev;
+class PSFontFileInfo;
+
+//------------------------------------------------------------------------
+// PSOutputDev
+//------------------------------------------------------------------------
+
+enum PSOutMode {
+ psModePS,
+ psModeEPS,
+ psModeForm
+};
+
+enum PSFileType {
+ psFile, // write to file
+ psPipe, // write to pipe
+ psStdout, // write to stdout
+ psGeneric // write to a generic stream
+};
+
+enum PSOutCustomCodeLocation {
+ psOutCustomDocSetup,
+ psOutCustomPageSetup
+};
+
+typedef void (*PSOutputFunc)(void *stream, const char *data, int len);
+
+typedef GString *(*PSOutCustomCodeCbk)(PSOutputDev *psOut,
+ PSOutCustomCodeLocation loc, int n,
+ void *data);
+
+class PSOutputDev: public OutputDev {
+public:
+
+ // Open a PostScript output file, and write the prolog.
+ PSOutputDev(char *fileName, PDFDoc *docA,
+ int firstPageA, int lastPageA, PSOutMode modeA,
+ int imgLLXA = 0, int imgLLYA = 0,
+ int imgURXA = 0, int imgURYA = 0,
+ GBool manualCtrlA = gFalse,
+ PSOutCustomCodeCbk customCodeCbkA = NULL,
+ void *customCodeCbkDataA = NULL,
+ GBool honorUserUnitA = gFalse,
+ GBool fileNameIsUTF8 = gFalse);
+
+ // Open a PSOutputDev that will write to a generic stream.
+ PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
+ PDFDoc *docA,
+ int firstPageA, int lastPageA, PSOutMode modeA,
+ int imgLLXA = 0, int imgLLYA = 0,
+ int imgURXA = 0, int imgURYA = 0,
+ GBool manualCtrlA = gFalse,
+ PSOutCustomCodeCbk customCodeCbkA = NULL,
+ void *customCodeCbkDataA = NULL,
+ GBool honorUserUnitA = gFalse);
+
+ // Destructor -- writes the trailer and closes the file.
+ virtual ~PSOutputDev();
+
+ // Check if file was successfully created.
+ virtual GBool isOk() { return ok; }
+
+ // Returns false if there have been any errors on the output stream.
+ GBool checkIO();
+
+ //---- get info about output device
+
+ // Does this device use upside-down coordinates?
+ // (Upside-down means (0,0) is the top left corner of the page.)
+ virtual GBool upsideDown() { return gFalse; }
+
+ // Does this device use drawChar() or drawString()?
+ virtual GBool useDrawChar() { return gFalse; }
+
+ // Does this device use tilingPatternFill()? If this returns false,
+ // tiling pattern fills will be reduced to a series of other drawing
+ // operations.
+ virtual GBool useTilingPatternFill() { return gTrue; }
+
+ // Does this device use drawForm()? If this returns false,
+ // form-type XObjects will be interpreted (i.e., unrolled).
+ virtual GBool useDrawForm() { return preload; }
+
+ // Does this device use beginType3Char/endType3Char? Otherwise,
+ // text in Type 3 fonts will be drawn with drawChar/drawString.
+ virtual GBool interpretType3Chars() { return gFalse; }
+
+ //----- header/trailer (used only if manualCtrl is true)
+
+ // Write the document-level header.
+ void writeHeader(PDFRectangle *mediaBox, PDFRectangle *cropBox,
+ int pageRotate);
+
+ // Write the Xpdf procset.
+ void writeXpdfProcset();
+
+ // Write the document-level setup.
+ void writeDocSetup(Catalog *catalog);
+
+ // Write the trailer for the current page.
+ void writePageTrailer();
+
+ // Write the document trailer.
+ void writeTrailer();
+
+ //----- initialization and control
+
+ // Check to see if a page slice should be displayed. If this
+ // returns false, the page display is aborted. Typically, an
+ // OutputDev will use some alternate means to display the page
+ // before returning false.
+ virtual GBool checkPageSlice(Page *page, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool crop,
+ int sliceX, int sliceY, int sliceW, int sliceH,
+ GBool printing,
+ GBool (*abortCheckCbk)(void *data) = NULL,
+ void *abortCheckCbkData = NULL);
+
+ // Start a page.
+ virtual void startPage(int pageNum, GfxState *state);
+
+ // End a page.
+ virtual void endPage();
+
+ //----- save/restore graphics state
+ virtual void saveState(GfxState *state);
+ virtual void restoreState(GfxState *state);
+
+ //----- update graphics state
+ virtual void updateCTM(GfxState *state, double m11, double m12,
+ double m21, double m22, double m31, double m32);
+ virtual void updateLineDash(GfxState *state);
+ virtual void updateFlatness(GfxState *state);
+ virtual void updateLineJoin(GfxState *state);
+ virtual void updateLineCap(GfxState *state);
+ virtual void updateMiterLimit(GfxState *state);
+ virtual void updateLineWidth(GfxState *state);
+ virtual void updateFillColorSpace(GfxState *state);
+ virtual void updateStrokeColorSpace(GfxState *state);
+ virtual void updateFillColor(GfxState *state);
+ virtual void updateStrokeColor(GfxState *state);
+ virtual void updateFillOverprint(GfxState *state);
+ virtual void updateStrokeOverprint(GfxState *state);
+ virtual void updateOverprintMode(GfxState *state);
+ virtual void updateTransfer(GfxState *state);
+
+ //----- update text state
+ virtual void updateFont(GfxState *state);
+ virtual void updateTextMat(GfxState *state);
+ virtual void updateCharSpace(GfxState *state);
+ virtual void updateRender(GfxState *state);
+ virtual void updateRise(GfxState *state);
+ virtual void updateWordSpace(GfxState *state);
+ virtual void updateHorizScaling(GfxState *state);
+ virtual void updateTextPos(GfxState *state);
+ virtual void updateTextShift(GfxState *state, double shift);
+ virtual void saveTextPos(GfxState *state);
+ virtual void restoreTextPos(GfxState *state);
+
+ //----- path painting
+ virtual void stroke(GfxState *state);
+ virtual void fill(GfxState *state);
+ virtual void eoFill(GfxState *state);
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
+ int paintType, int tilingType, Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep);
+ virtual GBool shadedFill(GfxState *state, GfxShading *shading);
+
+ //----- path clipping
+ virtual void clip(GfxState *state);
+ virtual void eoClip(GfxState *state);
+ virtual void clipToStrokePath(GfxState *state);
+
+ //----- text drawing
+ virtual void drawString(GfxState *state, GString *s);
+ virtual void endTextObject(GfxState *state);
+
+ //----- image drawing
+ virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate);
+ virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg, GBool interpolate);
+ virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GBool maskInvert, GBool interpolate);
+
+#if OPI_SUPPORT
+ //----- OPI functions
+ virtual void opiBegin(GfxState *state, Dict *opiDict);
+ virtual void opiEnd(GfxState *state, Dict *opiDict);
+#endif
+
+ //----- Type 3 font operators
+ virtual void type3D0(GfxState *state, double wx, double wy);
+ virtual void type3D1(GfxState *state, double wx, double wy,
+ double llx, double lly, double urx, double ury);
+
+ //----- form XObjects
+ virtual void drawForm(Ref ref);
+
+ //----- PostScript XObjects
+ virtual void psXObject(Stream *psStream, Stream *level1Stream);
+
+ //----- miscellaneous
+ void setImageableArea(int imgLLXA, int imgLLYA, int imgURXA, int imgURYA)
+ { imgLLX = imgLLXA; imgLLY = imgLLYA; imgURX = imgURXA; imgURY = imgURYA; }
+ void setOffset(double x, double y)
+ { tx0 = x; ty0 = y; }
+ void setScale(double x, double y)
+ { xScale0 = x; yScale0 = y; }
+ void setRotate(int rotateA)
+ { rotate0 = rotateA; }
+ void setClip(double llx, double lly, double urx, double ury)
+ { clipLLX0 = llx; clipLLY0 = lly; clipURX0 = urx; clipURY0 = ury; }
+ void setUnderlayCbk(void (*cbk)(PSOutputDev *psOut, void *data),
+ void *data)
+ { underlayCbk = cbk; underlayCbkData = data; }
+ void setOverlayCbk(void (*cbk)(PSOutputDev *psOut, void *data),
+ void *data)
+ { overlayCbk = cbk; overlayCbkData = data; }
+
+ void writePSChar(char c);
+ void writePSBlock(char *s, int len);
+ void writePS(const char *s);
+ void writePSFmt(const char *fmt, ...);
+ void writePSString(GString *s);
+ void writePSName(const char *s);
+
+private:
+
+ void init(PSOutputFunc outputFuncA, void *outputStreamA,
+ PSFileType fileTypeA, PDFDoc *docA,
+ int firstPageA, int lastPageA, PSOutMode modeA,
+ int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
+ GBool manualCtrlA, GBool honorUserUnitA);
+ GBool checkIfPageNeedsToBeRasterized(int pg);
+ void setupResources(Dict *resDict);
+ void setupFonts(Dict *resDict);
+ void setupFont(GfxFont *font, Dict *parentResDict);
+ PSFontFileInfo *setupEmbeddedType1Font(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupExternalType1Font(GfxFont *font, GString *fileName);
+ PSFontFileInfo *setupEmbeddedType1CFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupExternalTrueTypeFont(GfxFont *font, GString *fileName,
+ int fontNum);
+ PSFontFileInfo *setupEmbeddedCIDType0Font(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
+ GBool needVerticalMetrics);
+ PSFontFileInfo *setupExternalCIDTrueTypeFont(GfxFont *font,
+ GString *fileName,
+ int fontNum,
+ GBool needVerticalMetrics);
+ PSFontFileInfo *setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id);
+ PSFontFileInfo *setupExternalOpenTypeCFFFont(GfxFont *font,
+ GString *fileName);
+ PSFontFileInfo *setupType3Font(GfxFont *font, Dict *parentResDict);
+ GString *makePSFontName(GfxFont *font, Ref *id);
+ GString *fixType1Font(GString *font, int length1, int length2);
+ GBool splitType1PFA(Guchar *font, int fontSize,
+ int length1, int length2,
+ GString *textSection, GString *binSection);
+ GBool splitType1PFB(Guchar *font, int fontSize,
+ GString *textSection, GString *binSection);
+ GString *asciiHexDecodeType1EexecSection(GString *in);
+ GBool fixType1EexecSection(GString *binSection, GString *out);
+ GString *copyType1PFA(Guchar *font, int fontSize);
+ GString *copyType1PFB(Guchar *font, int fontSize);
+ void renameType1Font(GString *font, GString *name);
+ void setupDefaultFont();
+ void setupImages(Dict *resDict);
+ void setupImage(Ref id, Stream *str, GBool mask, Array *colorKeyMask);
+ void setupForms(Dict *resDict);
+ void setupForm(Object *strRef, Object *strObj);
+ void addProcessColor(double c, double m, double y, double k);
+ void addCustomColor(GfxState *state, GfxSeparationColorSpace *sepCS);
+ void addCustomColors(GfxState *state, GfxDeviceNColorSpace *devnCS);
+ void tilingPatternFillL1(GfxState *state, Gfx *gfx, Object *strRef,
+ int paintType, int tilingType, Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep);
+ void tilingPatternFillL2(GfxState *state, Gfx *gfx, Object *strRef,
+ int paintType, int tilingType, Dict *resDict,
+ 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,
+ GBool invert, GBool inlineImg,
+ Stream *str, int width, int height, int len);
+ void doImageL1Sep(GfxState *state, GfxImageColorMap *colorMap,
+ GBool invert, GBool inlineImg,
+ Stream *str, int width, int height, int len);
+ void doImageL2(Object *ref, GfxState *state,
+ GfxImageColorMap *colorMap,
+ GBool invert, GBool inlineImg,
+ Stream *str, int width, int height, int len,
+ int *maskColors, Stream *maskStr,
+ int maskWidth, int maskHeight, GBool maskInvert);
+ void convertColorKeyMaskToClipRects(GfxImageColorMap *colorMap,
+ Stream *str,
+ int width, int height,
+ int *maskColors);
+ void convertExplicitMaskToClipRects(Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GBool maskInvert);
+ void doImageL3(Object *ref, GfxState *state,
+ GfxImageColorMap *colorMap,
+ GBool invert, GBool inlineImg,
+ Stream *str, int width, int height, int len,
+ int *maskColors, Stream *maskStr,
+ int maskWidth, int maskHeight, GBool maskInvert);
+ void dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace,
+ GBool genXform, GBool updateColors,
+ GBool map01);
+ void dumpDeviceGrayColorSpace(GfxDeviceGrayColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01);
+ void dumpCalGrayColorSpace(GfxCalGrayColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01);
+ void dumpDeviceRGBColorSpace(GfxDeviceRGBColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01);
+ void dumpCalRGBColorSpace(GfxCalRGBColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01);
+ void dumpDeviceCMYKColorSpace(GfxDeviceCMYKColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01);
+ void dumpLabColorSpace(GfxLabColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01);
+ void dumpICCBasedColorSpace(GfxState *state, GfxICCBasedColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01);
+ void dumpIndexedColorSpace(GfxState *state,
+ GfxIndexedColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01);
+ void dumpSeparationColorSpace(GfxState *state,
+ GfxSeparationColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01);
+ void dumpDeviceNColorSpaceL2(GfxState *state, GfxDeviceNColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01);
+ void dumpDeviceNColorSpaceL3(GfxState *state, GfxDeviceNColorSpace *cs,
+ GBool genXform, GBool updateColors,
+ GBool map01);
+ GString *createDeviceNTintFunc(GfxDeviceNColorSpace *cs);
+#if OPI_SUPPORT
+ void opiBegin20(GfxState *state, Dict *dict);
+ void opiBegin13(GfxState *state, Dict *dict);
+ void opiTransform(GfxState *state, double x0, double y0,
+ double *x1, double *y1);
+ GBool getFileSpec(Object *fileSpec, Object *fileName);
+#endif
+ void cvtFunction(Function *func);
+ GString *filterPSName(GString *name);
+ void writePSTextLine(GString *s);
+
+ PSLevel level; // PostScript level
+ PSOutMode mode; // PostScript mode (PS, EPS, form)
+ int paperWidth; // width of paper, in pts
+ int paperHeight; // height of paper, in pts
+ GBool paperMatch; // true if paper size is set to match each page
+ int imgLLX, imgLLY, // imageable area, in pts
+ imgURX, imgURY;
+ GBool preload; // load all images into memory, and
+ // predefine forms
+
+ PSOutputFunc outputFunc;
+ void *outputStream;
+ PSFileType fileType; // file / pipe / stdout
+ GBool manualCtrl;
+ int seqPage; // current sequential page number
+ void (*underlayCbk)(PSOutputDev *psOut, void *data);
+ void *underlayCbkData;
+ void (*overlayCbk)(PSOutputDev *psOut, void *data);
+ void *overlayCbkData;
+ GString *(*customCodeCbk)(PSOutputDev *psOut,
+ PSOutCustomCodeLocation loc, int n,
+ void *data);
+ void *customCodeCbkData;
+ GBool honorUserUnit;
+
+ PDFDoc *doc;
+ XRef *xref; // the xref table for this PDF file
+
+ int firstPage; // first output page
+ int lastPage; // last output page
+ char *rasterizePage; // boolean for each page - true if page
+ // needs to be rasterized
+
+ GList *fontInfo; // info for each font [PSFontInfo]
+ GHash *fontFileInfo; // info for each font file [PSFontFileInfo]
+ Ref *imgIDs; // list of image IDs for in-memory images
+ int imgIDLen; // number of entries in imgIDs array
+ int imgIDSize; // size of imgIDs array
+ Ref *formIDs; // list of IDs for predefined forms
+ int formIDLen; // number of entries in formIDs array
+ int formIDSize; // size of formIDs array
+ char *visitedResources; // vector of resource objects already visited
+ GBool noStateChanges; // true if there have been no state changes
+ // since the last save
+ GList *saveStack; // "no state changes" flag for each
+ // pending save
+ int numTilingPatterns; // current number of nested tiling patterns
+ int nextFunc; // next unique number to use for a function
+
+ GList *paperSizes; // list of used paper sizes, if paperMatch
+ // is true [PSOutPaperSize]
+ double tx0, ty0; // global translation
+ double xScale0, yScale0; // global scaling
+ int rotate0; // rotation angle (0, 90, 180, 270)
+ double clipLLX0, clipLLY0,
+ clipURX0, clipURY0;
+ double tx, ty; // global translation for current page
+ double xScale, yScale; // global scaling for current page
+ int rotate; // rotation angle for current page
+ double epsX1, epsY1, // EPS bounding box (unrotated)
+ epsX2, epsY2;
+
+ GString *embFontList; // resource comments for embedded fonts
+
+ int processColors; // used process colors
+ PSOutCustomColor // used custom colors
+ *customColors;
+
+ GBool haveTextClip; // set if text has been drawn with a
+ // clipping render mode
+
+ GBool inType3Char; // inside a Type 3 CharProc
+ GString *t3String; // Type 3 content string
+ double t3WX, t3WY, // Type 3 character parameters
+ t3LLX, t3LLY, t3URX, t3URY;
+ GBool t3FillColorOnly; // operators should only use the fill color
+ GBool t3Cacheable; // cleared if char is not cacheable
+ GBool t3NeedsRestore; // set if a 'q' operator was issued
+
+#if OPI_SUPPORT
+ int opi13Nest; // nesting level of OPI 1.3 objects
+ int opi20Nest; // nesting level of OPI 2.0 objects
+#endif
+
+ GBool ok; // set up ok?
+
+ friend class WinPDFPrinter;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PSTokenizer.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PSTokenizer.cc
new file mode 100644
index 00000000000..bbdb0ec44d5
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PSTokenizer.cc
@@ -0,0 +1,136 @@
+//========================================================================
+//
+// PSTokenizer.cc
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "gmempp.h"
+#include "PSTokenizer.h"
+
+//------------------------------------------------------------------------
+
+// A '1' in this array means the character is white space. A '1' or
+// '2' means the character ends a name or command.
+static char specialChars[256] = {
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, // 2x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 3x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 5x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx
+};
+
+//------------------------------------------------------------------------
+
+PSTokenizer::PSTokenizer(int (*getCharFuncA)(void *), void *dataA) {
+ getCharFunc = getCharFuncA;
+ data = dataA;
+ charBuf = -1;
+}
+
+PSTokenizer::~PSTokenizer() {
+}
+
+GBool PSTokenizer::getToken(char *buf, int size, int *length) {
+ GBool comment, backslash;
+ int c;
+ int i;
+
+ // skip whitespace and comments
+ comment = gFalse;
+ while (1) {
+ if ((c = getChar()) == EOF) {
+ buf[0] = '\0';
+ *length = 0;
+ return gFalse;
+ }
+ if (comment) {
+ if (c == '\x0a' || c == '\x0d') {
+ comment = gFalse;
+ }
+ } else if (c == '%') {
+ comment = gTrue;
+ } else if (specialChars[c] != 1) {
+ break;
+ }
+ }
+
+ // read a token
+ i = 0;
+ buf[i++] = (char)c;
+ if (c == '(') {
+ backslash = gFalse;
+ while ((c = lookChar()) != EOF) {
+ if (i < size - 1) {
+ buf[i++] = (char)c;
+ }
+ getChar();
+ if (c == '\\') {
+ backslash = gTrue;
+ } else if (!backslash && c == ')') {
+ break;
+ } else {
+ backslash = gFalse;
+ }
+ }
+ } else if (c == '<') {
+ while ((c = lookChar()) != EOF) {
+ getChar();
+ if (i < size - 1 && specialChars[c] != 1) {
+ buf[i++] = (char)c;
+ }
+ if (c == '>') {
+ break;
+ }
+ }
+ } else if (c != '[' && c != ']') {
+ while ((c = lookChar()) != EOF && !specialChars[c]) {
+ getChar();
+ if (i < size - 1) {
+ buf[i++] = (char)c;
+ }
+ }
+ }
+ buf[i] = '\0';
+ *length = i;
+
+ return gTrue;
+}
+
+int PSTokenizer::lookChar() {
+ if (charBuf < 0) {
+ charBuf = (*getCharFunc)(data);
+ }
+ return charBuf;
+}
+
+int PSTokenizer::getChar() {
+ int c;
+
+ if (charBuf < 0) {
+ charBuf = (*getCharFunc)(data);
+ }
+ c = charBuf;
+ charBuf = -1;
+ return c;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PSTokenizer.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PSTokenizer.h
new file mode 100644
index 00000000000..4d5ee97f4dd
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PSTokenizer.h
@@ -0,0 +1,41 @@
+//========================================================================
+//
+// PSTokenizer.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PSTOKENIZER_H
+#define PSTOKENIZER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+
+class PSTokenizer {
+public:
+
+ PSTokenizer(int (*getCharFuncA)(void *), void *dataA);
+ ~PSTokenizer();
+
+ // Get the next PostScript token. Returns false at end-of-stream.
+ GBool getToken(char *buf, int size, int *length);
+
+private:
+
+ int lookChar();
+ int getChar();
+
+ int (*getCharFunc)(void *);
+ void *data;
+ int charBuf;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc
new file mode 100644
index 00000000000..ace0b33d85c
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Page.cc
@@ -0,0 +1,508 @@
+//========================================================================
+//
+// Page.cc
+//
+// Copyright 1996-2007 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "gmempp.h"
+#include "Trace.h"
+#include "GlobalParams.h"
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "PDFDoc.h"
+#include "XRef.h"
+#include "Link.h"
+#include "OutputDev.h"
+#ifndef PDF_PARSER_ONLY
+#include "Gfx.h"
+#include "GfxState.h"
+#include "Annot.h"
+#include "AcroForm.h"
+#endif
+#include "Error.h"
+#include "Catalog.h"
+#include "Page.h"
+
+//------------------------------------------------------------------------
+// PDFRectangle
+//------------------------------------------------------------------------
+
+void PDFRectangle::clipTo(PDFRectangle *rect) {
+ if (x1 < rect->x1) {
+ x1 = rect->x1;
+ } else if (x1 > rect->x2) {
+ x1 = rect->x2;
+ }
+ if (x2 < rect->x1) {
+ x2 = rect->x1;
+ } else if (x2 > rect->x2) {
+ x2 = rect->x2;
+ }
+ if (y1 < rect->y1) {
+ y1 = rect->y1;
+ } else if (y1 > rect->y2) {
+ y1 = rect->y2;
+ }
+ if (y2 < rect->y1) {
+ y2 = rect->y1;
+ } else if (y2 > rect->y2) {
+ y2 = rect->y2;
+ }
+}
+
+//------------------------------------------------------------------------
+// PageAttrs
+//------------------------------------------------------------------------
+
+PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
+ Object obj1;
+
+ // get old/default values
+ if (attrs) {
+ mediaBox = attrs->mediaBox;
+ cropBox = attrs->cropBox;
+ haveCropBox = attrs->haveCropBox;
+ rotate = attrs->rotate;
+ attrs->resources.copy(&resources);
+ } else {
+ // set default MediaBox to 8.5" x 11" -- this shouldn't be necessary
+ // but some (non-compliant) PDF files don't specify a MediaBox
+ mediaBox.x1 = 0;
+ mediaBox.y1 = 0;
+ mediaBox.x2 = 612;
+ mediaBox.y2 = 792;
+ cropBox.x1 = cropBox.y1 = cropBox.x2 = cropBox.y2 = 0;
+ haveCropBox = gFalse;
+ rotate = 0;
+ resources.initNull();
+ }
+
+ // media box
+ readBox(dict, "MediaBox", &mediaBox);
+
+ // crop box
+ if (readBox(dict, "CropBox", &cropBox)) {
+ haveCropBox = gTrue;
+ }
+ if (!haveCropBox) {
+ cropBox = mediaBox;
+ }
+
+ // other boxes
+ bleedBox = cropBox;
+ readBox(dict, "BleedBox", &bleedBox);
+ trimBox = cropBox;
+ readBox(dict, "TrimBox", &trimBox);
+ artBox = cropBox;
+ readBox(dict, "ArtBox", &artBox);
+
+ // rotate
+ dict->lookup("Rotate", &obj1);
+ if (obj1.isInt()) {
+ rotate = obj1.getInt();
+ }
+ obj1.free();
+ while (rotate < 0) {
+ rotate += 360;
+ }
+ while (rotate >= 360) {
+ rotate -= 360;
+ }
+
+ // misc attributes
+ dict->lookup("LastModified", &lastModified);
+ dict->lookup("BoxColorInfo", &boxColorInfo);
+ dict->lookup("Group", &group);
+ dict->lookup("Metadata", &metadata);
+ dict->lookup("PieceInfo", &pieceInfo);
+ dict->lookup("SeparationInfo", &separationInfo);
+ if (dict->lookup("UserUnit", &obj1)->isNum()) {
+ userUnit = obj1.getNum();
+ if (userUnit < 1) {
+ userUnit = 1;
+ }
+ } else {
+ userUnit = 1;
+ }
+ obj1.free();
+
+ // resource dictionary
+ dict->lookup("Resources", &obj1);
+ if (obj1.isDict()) {
+ resources.free();
+ obj1.copy(&resources);
+ }
+ obj1.free();
+}
+
+PageAttrs::PageAttrs() {
+ mediaBox.x1 = mediaBox.y1 = 0;
+ mediaBox.x2 = mediaBox.y2 = 50;
+ cropBox = mediaBox;
+ haveCropBox = gFalse;
+ bleedBox = cropBox;
+ trimBox = cropBox;
+ artBox = cropBox;
+ rotate = 0;
+ lastModified.initNull();
+ boxColorInfo.initNull();
+ group.initNull();
+ metadata.initNull();
+ pieceInfo.initNull();
+ separationInfo.initNull();
+ resources.initNull();
+}
+
+PageAttrs::~PageAttrs() {
+ lastModified.free();
+ boxColorInfo.free();
+ group.free();
+ metadata.free();
+ pieceInfo.free();
+ separationInfo.free();
+ resources.free();
+}
+
+void PageAttrs::clipBoxes() {
+ cropBox.clipTo(&mediaBox);
+ bleedBox.clipTo(&mediaBox);
+ trimBox.clipTo(&mediaBox);
+ artBox.clipTo(&mediaBox);
+}
+
+GBool PageAttrs::readBox(Dict *dict, const char *key, PDFRectangle *box) {
+ PDFRectangle tmp;
+ double t;
+ Object obj1, obj2;
+ GBool ok;
+
+ dict->lookup(key, &obj1);
+ if (obj1.isArray() && obj1.arrayGetLength() == 4) {
+ ok = gTrue;
+ obj1.arrayGet(0, &obj2);
+ if (obj2.isNum()) {
+ tmp.x1 = obj2.getNum();
+ } else {
+ ok = gFalse;
+ }
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ if (obj2.isNum()) {
+ tmp.y1 = obj2.getNum();
+ } else {
+ ok = gFalse;
+ }
+ obj2.free();
+ obj1.arrayGet(2, &obj2);
+ if (obj2.isNum()) {
+ tmp.x2 = obj2.getNum();
+ } else {
+ ok = gFalse;
+ }
+ obj2.free();
+ obj1.arrayGet(3, &obj2);
+ if (obj2.isNum()) {
+ tmp.y2 = obj2.getNum();
+ } else {
+ ok = gFalse;
+ }
+ obj2.free();
+ if (ok) {
+ if (tmp.x1 > tmp.x2) {
+ t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t;
+ }
+ if (tmp.y1 > tmp.y2) {
+ t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t;
+ }
+ *box = tmp;
+ }
+ } else {
+ ok = gFalse;
+ }
+ obj1.free();
+ return ok;
+}
+
+//------------------------------------------------------------------------
+// Page
+//------------------------------------------------------------------------
+
+Page::Page(PDFDoc *docA, int numA, Dict *pageDict, PageAttrs *attrsA) {
+ ok = gTrue;
+ doc = docA;
+ xref = doc->getXRef();
+ num = numA;
+
+ // get attributes
+ attrs = attrsA;
+ attrs->clipBoxes();
+
+ // annotations
+ pageDict->lookupNF("Annots", &annots);
+ if (!(annots.isRef() || annots.isArray() || annots.isNull())) {
+ error(errSyntaxError, -1,
+ "Page annotations object (page {0:d}) is wrong type ({1:s})",
+ num, annots.getTypeName());
+ annots.free();
+ goto err2;
+ }
+
+ // contents
+ pageDict->lookupNF("Contents", &contents);
+ if (!(contents.isRef() || contents.isArray() ||
+ contents.isNull())) {
+ error(errSyntaxError, -1,
+ "Page contents object (page {0:d}) is wrong type ({1:s})",
+ num, contents.getTypeName());
+ contents.free();
+ goto err1;
+ }
+
+ // thumbnail
+ pageDict->lookupNF("Thumb", &thumbnail);
+ if (!thumbnail.isRef()) {
+ if (!thumbnail.isNull()) {
+ thumbnail.free();
+ thumbnail.initNull();
+ }
+ }
+
+ return;
+
+ err2:
+ annots.initNull();
+ err1:
+ contents.initNull();
+ thumbnail.initNull();
+ ok = gFalse;
+}
+
+Page::Page(PDFDoc *docA, int numA) {
+ doc = docA;
+ xref = doc->getXRef();
+ num = numA;
+ attrs = new PageAttrs();
+ annots.initNull();
+ contents.initNull();
+ thumbnail.initNull();
+ ok = gTrue;
+}
+
+Page::~Page() {
+ delete attrs;
+ annots.free();
+ contents.free();
+ thumbnail.free();
+}
+
+Links *Page::getLinks() {
+ Links *links;
+ Object obj;
+
+ links = new Links(getAnnots(&obj), doc->getCatalog()->getBaseURI());
+ obj.free();
+ return links;
+}
+
+void Page::display(OutputDev *out, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool crop,
+ GBool printing,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+ displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
+ -1, -1, -1, -1, printing,
+ abortCheckCbk, abortCheckCbkData);
+}
+
+void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool crop,
+ int sliceX, int sliceY, int sliceW, int sliceH,
+ GBool printing,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+#ifndef PDF_PARSER_ONLY
+ PDFRectangle *mediaBox, *cropBox;
+ PDFRectangle box;
+ Gfx *gfx;
+ Object obj;
+ Annots *annotList;
+ AcroForm *form;
+ int i;
+
+ if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop,
+ sliceX, sliceY, sliceW, sliceH,
+ printing, abortCheckCbk, abortCheckCbkData)) {
+ return;
+ }
+
+ traceBegin(this, "begin page");
+
+ rotate += getRotate();
+ if (rotate >= 360) {
+ rotate -= 360;
+ } else if (rotate < 0) {
+ rotate += 360;
+ }
+
+ makeBox(hDPI, vDPI, rotate, useMediaBox, out->upsideDown(),
+ sliceX, sliceY, sliceW, sliceH, &box, &crop);
+ cropBox = getCropBox();
+
+ if (globalParams->getPrintCommands()) {
+ mediaBox = getMediaBox();
+ printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
+ mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2);
+ printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
+ cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
+ printf("***** Rotate = %d\n", attrs->getRotate());
+ }
+
+ gfx = new Gfx(doc, out, num, attrs->getResourceDict(),
+ hDPI, vDPI, &box, crop ? cropBox : (PDFRectangle *)NULL,
+ rotate, abortCheckCbk, abortCheckCbkData);
+ contents.fetch(xref, &obj);
+ if (!obj.isNull()) {
+ gfx->saveState();
+ gfx->display(&contents);
+ gfx->endOfPage();
+ }
+ obj.free();
+
+ // draw (non-form) annotations
+ if (globalParams->getDrawAnnotations()) {
+ annotList = new Annots(doc, getAnnots(&obj));
+ obj.free();
+ annotList->generateAnnotAppearances();
+ if (annotList->getNumAnnots() > 0) {
+ if (globalParams->getPrintCommands()) {
+ printf("***** Annotations\n");
+ }
+ for (i = 0; i < annotList->getNumAnnots(); ++i) {
+ if (abortCheckCbk && (*abortCheckCbk)(abortCheckCbkData)) {
+ break;
+ }
+ annotList->getAnnot(i)->draw(gfx, printing);
+ }
+ }
+ delete annotList;
+ }
+
+ // draw form fields
+ if (globalParams->getDrawFormFields()) {
+ if ((form = doc->getCatalog()->getForm())) {
+ if (!(abortCheckCbk && (*abortCheckCbk)(abortCheckCbkData))) {
+ form->draw(num, gfx, printing);
+ }
+ }
+ }
+
+ delete gfx;
+#endif // PDF_PARSER_ONLY
+
+ traceEnd(this, "end page");
+}
+
+void Page::makeBox(double hDPI, double vDPI, int rotate,
+ GBool useMediaBox, GBool upsideDown,
+ double sliceX, double sliceY, double sliceW, double sliceH,
+ PDFRectangle *box, GBool *crop) {
+ PDFRectangle *mediaBox, *cropBox, *baseBox;
+ double kx, ky;
+
+ mediaBox = getMediaBox();
+ cropBox = getCropBox();
+ if (sliceW >= 0 && sliceH >= 0) {
+ baseBox = useMediaBox ? mediaBox : cropBox;
+ kx = 72.0 / hDPI;
+ ky = 72.0 / vDPI;
+ if (rotate == 90) {
+ if (upsideDown) {
+ box->x1 = baseBox->x1 + ky * sliceY;
+ box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
+ } else {
+ box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
+ box->x2 = baseBox->x2 - ky * sliceY;
+ }
+ box->y1 = baseBox->y1 + kx * sliceX;
+ box->y2 = baseBox->y1 + kx * (sliceX + sliceW);
+ } else if (rotate == 180) {
+ box->x1 = baseBox->x2 - kx * (sliceX + sliceW);
+ box->x2 = baseBox->x2 - kx * sliceX;
+ if (upsideDown) {
+ box->y1 = baseBox->y1 + ky * sliceY;
+ box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
+ } else {
+ box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
+ box->y2 = baseBox->y2 - ky * sliceY;
+ }
+ } else if (rotate == 270) {
+ if (upsideDown) {
+ box->x1 = baseBox->x2 - ky * (sliceY + sliceH);
+ box->x2 = baseBox->x2 - ky * sliceY;
+ } else {
+ box->x1 = baseBox->x1 + ky * sliceY;
+ box->x2 = baseBox->x1 + ky * (sliceY + sliceH);
+ }
+ box->y1 = baseBox->y2 - kx * (sliceX + sliceW);
+ box->y2 = baseBox->y2 - kx * sliceX;
+ } else {
+ box->x1 = baseBox->x1 + kx * sliceX;
+ box->x2 = baseBox->x1 + kx * (sliceX + sliceW);
+ if (upsideDown) {
+ box->y1 = baseBox->y2 - ky * (sliceY + sliceH);
+ box->y2 = baseBox->y2 - ky * sliceY;
+ } else {
+ box->y1 = baseBox->y1 + ky * sliceY;
+ box->y2 = baseBox->y1 + ky * (sliceY + sliceH);
+ }
+ }
+ } else if (useMediaBox) {
+ *box = *mediaBox;
+ } else {
+ *box = *cropBox;
+ *crop = gFalse;
+ }
+}
+
+void Page::processLinks(OutputDev *out) {
+ Links *links;
+ int i;
+
+ links = getLinks();
+ for (i = 0; i < links->getNumLinks(); ++i) {
+ out->processLink(links->getLink(i));
+ }
+ delete links;
+}
+
+void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool upsideDown) {
+#ifndef PDF_PARSER_ONLY
+ GfxState *state;
+ int i;
+
+ rotate += getRotate();
+ if (rotate >= 360) {
+ rotate -= 360;
+ } else if (rotate < 0) {
+ rotate += 360;
+ }
+ state = new GfxState(hDPI, vDPI,
+ useMediaBox ? getMediaBox() : getCropBox(),
+ rotate, upsideDown);
+ for (i = 0; i < 6; ++i) {
+ ctm[i] = state->getCTM()[i];
+ }
+ delete state;
+#endif
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h
new file mode 100644
index 00000000000..8ec5a10995a
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Page.h
@@ -0,0 +1,207 @@
+//========================================================================
+//
+// Page.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PAGE_H
+#define PAGE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+
+class Dict;
+class PDFDoc;
+class XRef;
+class OutputDev;
+class Links;
+
+//------------------------------------------------------------------------
+
+class PDFRectangle {
+public:
+ double x1, y1, x2, y2;
+
+ PDFRectangle() { x1 = y1 = x2 = y2 = 0; }
+ PDFRectangle(double x1A, double y1A, double x2A, double y2A)
+ { x1 = x1A; y1 = y1A; x2 = x2A; y2 = y2A; }
+ GBool isValid() { return x1 != 0 || y1 != 0 || x2 != 0 || y2 != 0; }
+ void clipTo(PDFRectangle *rect);
+};
+
+//------------------------------------------------------------------------
+// PageAttrs
+//------------------------------------------------------------------------
+
+class PageAttrs {
+public:
+
+ // Construct a new PageAttrs object by merging a dictionary
+ // (of type Pages or Page) into another PageAttrs object. If
+ // <attrs> is NULL, uses defaults.
+ PageAttrs(PageAttrs *attrs, Dict *dict);
+
+ // Construct a new PageAttrs object for an empty page (only used
+ // when there is an error in the page tree).
+ PageAttrs();
+
+ // Destructor.
+ ~PageAttrs();
+
+ // Accessors.
+ PDFRectangle *getMediaBox() { return &mediaBox; }
+ PDFRectangle *getCropBox() { return &cropBox; }
+ GBool isCropped() { return haveCropBox; }
+ PDFRectangle *getBleedBox() { return &bleedBox; }
+ PDFRectangle *getTrimBox() { return &trimBox; }
+ PDFRectangle *getArtBox() { return &artBox; }
+ int getRotate() { return rotate; }
+ GString *getLastModified()
+ { return lastModified.isString()
+ ? lastModified.getString() : (GString *)NULL; }
+ Dict *getBoxColorInfo()
+ { return boxColorInfo.isDict() ? boxColorInfo.getDict() : (Dict *)NULL; }
+ Dict *getGroup()
+ { return group.isDict() ? group.getDict() : (Dict *)NULL; }
+ Stream *getMetadata()
+ { return metadata.isStream() ? metadata.getStream() : (Stream *)NULL; }
+ Dict *getPieceInfo()
+ { return pieceInfo.isDict() ? pieceInfo.getDict() : (Dict *)NULL; }
+ Dict *getSeparationInfo()
+ { return separationInfo.isDict()
+ ? separationInfo.getDict() : (Dict *)NULL; }
+ double getUserUnit() { return userUnit; }
+ Dict *getResourceDict()
+ { return resources.isDict() ? resources.getDict() : (Dict *)NULL; }
+
+ // Clip all other boxes to the MediaBox.
+ void clipBoxes();
+
+private:
+
+ GBool readBox(Dict *dict, const char *key, PDFRectangle *box);
+
+ PDFRectangle mediaBox;
+ PDFRectangle cropBox;
+ GBool haveCropBox;
+ PDFRectangle bleedBox;
+ PDFRectangle trimBox;
+ PDFRectangle artBox;
+ int rotate;
+ Object lastModified;
+ Object boxColorInfo;
+ Object group;
+ Object metadata;
+ Object pieceInfo;
+ Object separationInfo;
+ double userUnit;
+ Object resources;
+};
+
+//------------------------------------------------------------------------
+// Page
+//------------------------------------------------------------------------
+
+class Page {
+public:
+
+ // Constructor.
+ Page(PDFDoc *docA, int numA, Dict *pageDict, PageAttrs *attrsA);
+
+ // Create an empty page (only used when there is an error in the
+ // page tree).
+ Page(PDFDoc *docA, int numA);
+
+ // Destructor.
+ ~Page();
+
+ // Is page valid?
+ GBool isOk() { return ok; }
+
+ // 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(); }
+ double getMediaWidth()
+ { return attrs->getMediaBox()->x2 - attrs->getMediaBox()->x1; }
+ double getMediaHeight()
+ { return attrs->getMediaBox()->y2 - attrs->getMediaBox()->y1; }
+ double getCropWidth()
+ { return attrs->getCropBox()->x2 - attrs->getCropBox()->x1; }
+ double getCropHeight()
+ { return attrs->getCropBox()->y2 - attrs->getCropBox()->y1; }
+ PDFRectangle *getBleedBox() { return attrs->getBleedBox(); }
+ PDFRectangle *getTrimBox() { return attrs->getTrimBox(); }
+ PDFRectangle *getArtBox() { return attrs->getArtBox(); }
+ int getRotate() { return attrs->getRotate(); }
+ GString *getLastModified() { return attrs->getLastModified(); }
+ Dict *getBoxColorInfo() { return attrs->getBoxColorInfo(); }
+ Dict *getGroup() { return attrs->getGroup(); }
+ Stream *getMetadata() { return attrs->getMetadata(); }
+ Dict *getPieceInfo() { return attrs->getPieceInfo(); }
+ Dict *getSeparationInfo() { return attrs->getSeparationInfo(); }
+ double getUserUnit() { return attrs->getUserUnit(); }
+
+ // Get resource dictionary.
+ Dict *getResourceDict() { return attrs->getResourceDict(); }
+
+ // Get annotations array.
+ Object *getAnnots(Object *obj) { return annots.fetch(xref, obj); }
+
+ // Return a list of links.
+ Links *getLinks();
+
+ // Get contents.
+ Object *getContents(Object *obj) { return contents.fetch(xref, obj); }
+
+ // Get the page's thumbnail image.
+ Object *getThumbnail(Object *obj) { return thumbnail.fetch(xref, obj); }
+
+ // Display a page.
+ void display(OutputDev *out, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool crop,
+ GBool printing,
+ GBool (*abortCheckCbk)(void *data) = NULL,
+ void *abortCheckCbkData = NULL);
+
+ // Display part of a page.
+ void displaySlice(OutputDev *out, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool crop,
+ int sliceX, int sliceY, int sliceW, int sliceH,
+ GBool printing,
+ GBool (*abortCheckCbk)(void *data) = NULL,
+ void *abortCheckCbkData = NULL);
+
+ void makeBox(double hDPI, double vDPI, int rotate,
+ GBool useMediaBox, GBool upsideDown,
+ double sliceX, double sliceY, double sliceW, double sliceH,
+ PDFRectangle *box, GBool *crop);
+
+ void processLinks(OutputDev *out);
+
+ // Get the page's default CTM.
+ void getDefaultCTM(double *ctm, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool upsideDown);
+
+private:
+
+ PDFDoc *doc;
+ XRef *xref; // the xref table for this PDF file
+ int num; // page number
+ PageAttrs *attrs; // page attributes
+ Object annots; // annotations array
+ Object contents; // page contents
+ Object thumbnail; // reference to thumbnail image
+ GBool ok; // true if page is valid
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Parser.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Parser.cc
new file mode 100644
index 00000000000..87e3fc5e24b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Parser.cc
@@ -0,0 +1,269 @@
+//========================================================================
+//
+// Parser.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include <string.h>
+#include "gmempp.h"
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Decrypt.h"
+#include "Parser.h"
+#include "XRef.h"
+#include "Error.h"
+
+// Max number of nested objects. This is used to catch infinite loops
+// in the object structure.
+#define recursionLimit 500
+
+Parser::Parser(XRef *xrefA, Lexer *lexerA, GBool allowStreamsA) {
+ xref = xrefA;
+ lexer = lexerA;
+ inlineImg = 0;
+ allowStreams = allowStreamsA;
+ lexer->getObj(&buf1);
+ lexer->getObj(&buf2);
+}
+
+Parser::~Parser() {
+ buf1.free();
+ buf2.free();
+ delete lexer;
+}
+
+Object *Parser::getObj(Object *obj, GBool simpleOnly,
+ Guchar *fileKey,
+ CryptAlgorithm encAlgorithm, int keyLength,
+ int objNum, int objGen, int recursion) {
+ char *key;
+ Stream *str;
+ Object obj2;
+ int num;
+ DecryptStream *decrypt;
+ GString *s, *s2;
+ int c;
+
+ // refill buffer after inline image data
+ if (inlineImg == 2) {
+ buf1.free();
+ buf2.free();
+ lexer->getObj(&buf1);
+ lexer->getObj(&buf2);
+ inlineImg = 0;
+ }
+
+ // array
+ if (!simpleOnly && recursion < recursionLimit && buf1.isCmd("[")) {
+ shift();
+ obj->initArray(xref);
+ while (!buf1.isCmd("]") && !buf1.isEOF())
+ obj->arrayAdd(getObj(&obj2, gFalse, fileKey, encAlgorithm, keyLength,
+ objNum, objGen, recursion + 1));
+ if (buf1.isEOF())
+ error(errSyntaxError, getPos(), "End of file inside array");
+ shift();
+
+ // dictionary or stream
+ } else if (!simpleOnly && recursion < recursionLimit && buf1.isCmd("<<")) {
+ shift();
+ obj->initDict(xref);
+ while (!buf1.isCmd(">>") && !buf1.isEOF()) {
+ if (!buf1.isName()) {
+ error(errSyntaxError, getPos(),
+ "Dictionary key must be a name object");
+ shift();
+ } else {
+ key = copyString(buf1.getName());
+ shift();
+ if (buf1.isEOF() || buf1.isError()) {
+ gfree(key);
+ break;
+ }
+ obj->dictAdd(key, getObj(&obj2, gFalse,
+ fileKey, encAlgorithm, keyLength,
+ objNum, objGen, recursion + 1));
+ }
+ }
+ if (buf1.isEOF())
+ error(errSyntaxError, getPos(), "End of file inside dictionary");
+ // stream objects are not allowed inside content streams or
+ // object streams
+ if (allowStreams && buf2.isCmd("stream")) {
+ if ((str = makeStream(obj, fileKey, encAlgorithm, keyLength,
+ objNum, objGen, recursion + 1))) {
+ obj->initStream(str);
+ } else {
+ obj->free();
+ obj->initError();
+ }
+ } else {
+ shift();
+ }
+
+ // indirect reference or integer
+ } else if (buf1.isInt()) {
+ num = buf1.getInt();
+ shift();
+ if (buf1.isInt() && buf2.isCmd("R")) {
+ obj->initRef(num, buf1.getInt());
+ shift();
+ shift();
+ } else {
+ obj->initInt(num);
+ }
+
+ // string
+ } else if (buf1.isString() && fileKey) {
+ s = buf1.getString();
+ s2 = new GString();
+ obj2.initNull();
+ decrypt = new DecryptStream(new MemStream(s->getCString(), 0,
+ s->getLength(), &obj2),
+ fileKey, encAlgorithm, keyLength,
+ objNum, objGen);
+ decrypt->reset();
+ while ((c = decrypt->getChar()) != EOF) {
+ s2->append((char)c);
+ }
+ delete decrypt;
+ obj->initString(s2);
+ shift();
+
+ // simple object
+ } else {
+ buf1.copy(obj);
+ shift();
+ }
+
+ return obj;
+}
+
+Stream *Parser::makeStream(Object *dict, Guchar *fileKey,
+ CryptAlgorithm encAlgorithm, int keyLength,
+ int objNum, int objGen, int recursion) {
+ Object obj;
+ BaseStream *baseStr;
+ Stream *str, *str2;
+ GFileOffset pos, endPos, length;
+ char endstreamBuf[8];
+ GBool foundEndstream;
+ int c, i;
+
+ // get stream start position
+ lexer->skipToNextLine();
+ if (!(str = lexer->getStream())) {
+ return NULL;
+ }
+ pos = str->getPos();
+
+ // check for length in damaged file
+ if (xref && xref->getStreamEnd(pos, &endPos)) {
+ length = endPos - pos;
+
+ // get length from the stream object
+ } else {
+ dict->dictLookup("Length", &obj, recursion);
+ if (obj.isInt()) {
+ length = (GFileOffset)(Guint)obj.getInt();
+ obj.free();
+ } else {
+ error(errSyntaxError, getPos(), "Bad 'Length' attribute in stream");
+ obj.free();
+ return NULL;
+ }
+ }
+
+ // in badly damaged PDF files, we can run off the end of the input
+ // stream immediately after the "stream" token
+ if (!lexer->getStream()) {
+ return NULL;
+ }
+ // copy the base stream (Lexer will free stream objects when it gets
+ // to end of stream -- which can happen in the shift() calls below)
+ baseStr = (BaseStream *)lexer->getStream()->getBaseStream()->copy();
+
+ // make new base stream
+ str = baseStr->makeSubStream(pos, gTrue, length, dict);
+
+ // skip over stream data
+ lexer->setPos(pos + length);
+
+ // check for 'endstream'
+ // NB: we never reuse the Parser object to parse objects after a
+ // stream, and we could (if the PDF file is damaged) be in the
+ // middle of binary data at this point, so we check the stream data
+ // directly for 'endstream', rather than calling shift() to parse
+ // objects
+ foundEndstream = gFalse;
+ if ((str2 = lexer->getStream())) {
+ // skip up to 100 whitespace chars
+ for (i = 0; i < 100; ++i) {
+ c = str2->getChar();
+ if (!Lexer::isSpace(c)) {
+ break;
+ }
+ }
+ if (c == 'e') {
+ if (str2->getBlock(endstreamBuf, 8) == 8 ||
+ !memcmp(endstreamBuf, "ndstream", 8)) {
+ foundEndstream = gTrue;
+ }
+ }
+ }
+ if (!foundEndstream) {
+ error(errSyntaxError, getPos(), "Missing 'endstream'");
+ // kludge for broken PDF files: just add 5k to the length, and
+ // hope it's enough
+ // (dict is now owned by str, so we need to copy it before deleting str)
+ dict->copy(&obj);
+ delete str;
+ length += 5000;
+ str = baseStr->makeSubStream(pos, gTrue, length, &obj);
+ }
+
+ // free the copied base stream
+ delete baseStr;
+
+ // handle decryption
+ if (fileKey) {
+ str = new DecryptStream(str, fileKey, encAlgorithm, keyLength,
+ objNum, objGen);
+ }
+
+ // get filters
+ str = str->addFilters(dict, recursion);
+
+ return str;
+}
+
+void Parser::shift() {
+ if (inlineImg > 0) {
+ if (inlineImg < 2) {
+ ++inlineImg;
+ } else {
+ // in a damaged content stream, if 'ID' shows up in the middle
+ // of a dictionary, we need to reset
+ inlineImg = 0;
+ }
+ } else if (buf2.isCmd("ID")) {
+ lexer->skipChar(); // skip char after 'ID' command
+ inlineImg = 1;
+ }
+ buf1.free();
+ buf1 = buf2;
+ if (inlineImg > 0) // don't buffer inline image data
+ buf2.initNull();
+ else
+ lexer->getObj(&buf2);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Parser.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Parser.h
new file mode 100644
index 00000000000..bfe5a52bdfc
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Parser.h
@@ -0,0 +1,65 @@
+//========================================================================
+//
+// Parser.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PARSER_H
+#define PARSER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Lexer.h"
+
+//------------------------------------------------------------------------
+// Parser
+//------------------------------------------------------------------------
+
+class Parser {
+public:
+
+ // Constructor.
+ Parser(XRef *xrefA, Lexer *lexerA, GBool allowStreamsA);
+
+ // Destructor.
+ ~Parser();
+
+ // Get the next object from the input stream. If <simpleOnly> is
+ // true, do not parse compound objects (arrays, dictionaries, or
+ // streams).
+ Object *getObj(Object *obj, GBool simpleOnly = gFalse,
+ Guchar *fileKey = NULL,
+ CryptAlgorithm encAlgorithm = cryptRC4, int keyLength = 0,
+ int objNum = 0, int objGen = 0, int recursion = 0);
+
+ // Get stream index (for arrays of streams).
+ int getStreamIndex() { return lexer->getStreamIndex(); }
+
+ // Get stream.
+ Stream *getStream() { return lexer->getStream(); }
+
+ // Get current position in file.
+ GFileOffset getPos() { return lexer->getPos(); }
+
+private:
+
+ XRef *xref; // the xref table for this PDF file
+ Lexer *lexer; // input stream
+ GBool allowStreams; // parse stream objects?
+ Object buf1, buf2; // next two tokens
+ int inlineImg; // set when inline image data is encountered
+
+ Stream *makeStream(Object *dict, Guchar *fileKey,
+ CryptAlgorithm encAlgorithm, int keyLength,
+ int objNum, int objGen, int recursion);
+ void shift();
+};
+
+#endif
+
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc
new file mode 100644
index 00000000000..e5e00b85f51
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.cc
@@ -0,0 +1,308 @@
+//========================================================================
+//
+// PreScanOutputDev.cc
+//
+// Copyright 2005 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <math.h>
+#include "gmempp.h"
+#include "GlobalParams.h"
+#include "Page.h"
+#include "Gfx.h"
+#include "GfxFont.h"
+#include "Link.h"
+#include "PreScanOutputDev.h"
+
+//------------------------------------------------------------------------
+// PreScanOutputDev
+//------------------------------------------------------------------------
+
+PreScanOutputDev::PreScanOutputDev() {
+ clearStats();
+}
+
+PreScanOutputDev::~PreScanOutputDev() {
+}
+
+void PreScanOutputDev::startPage(int pageNum, GfxState *state) {
+}
+
+void PreScanOutputDev::endPage() {
+}
+
+void PreScanOutputDev::stroke(GfxState *state) {
+ double *dash;
+ int dashLen;
+ double dashStart;
+
+ check(state, state->getStrokeColorSpace(), state->getStrokeColor(),
+ state->getStrokeOpacity(), state->getBlendMode());
+ state->getLineDash(&dash, &dashLen, &dashStart);
+ if (dashLen != 0) {
+ gdi = gFalse;
+ }
+}
+
+void PreScanOutputDev::fill(GfxState *state) {
+ check(state, state->getFillColorSpace(), state->getFillColor(),
+ state->getFillOpacity(), state->getBlendMode());
+}
+
+void PreScanOutputDev::eoFill(GfxState *state) {
+ check(state, state->getFillColorSpace(), state->getFillColor(),
+ state->getFillOpacity(), state->getBlendMode());
+}
+
+void PreScanOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
+ Object *strRef,
+ int paintType, int tilingType,
+ Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep) {
+ if (paintType == 1) {
+ gfx->drawForm(strRef, resDict, mat, bbox);
+ } else {
+ check(state, state->getFillColorSpace(), state->getFillColor(),
+ state->getFillOpacity(), state->getBlendMode());
+ }
+}
+
+GBool PreScanOutputDev::shadedFill(GfxState *state, GfxShading *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
+}
+
+void PreScanOutputDev::eoClip(GfxState *state) {
+ //~ see clip()
+}
+
+void PreScanOutputDev::beginStringOp(GfxState *state) {
+ int render;
+ GfxFont *font;
+ double m11, m12, m21, m22;
+ GBool simpleTTF;
+
+ render = state->getRender();
+ if (!(render & 1)) {
+ check(state, state->getFillColorSpace(), state->getFillColor(),
+ state->getFillOpacity(), state->getBlendMode());
+ }
+ if ((render & 3) == 1 || (render & 3) == 2) {
+ check(state, state->getStrokeColorSpace(), state->getStrokeColor(),
+ state->getStrokeOpacity(), state->getBlendMode());
+ }
+
+ font = state->getFont();
+ state->getFontTransMat(&m11, &m12, &m21, &m22);
+ //~ this should check for external fonts that are non-TrueType
+ simpleTTF = fabs(m11 + m22) < 0.01 &&
+ m11 > 0 &&
+ fabs(m12) < 0.01 &&
+ fabs(m21) < 0.01 &&
+ fabs(state->getHorizScaling() - 1) < 0.001 &&
+ (font->getType() == fontTrueType ||
+ font->getType() == fontTrueTypeOT);
+ if (simpleTTF) {
+ //~ need to create a FoFiTrueType object, and check for a Unicode cmap
+ }
+ if (state->getRender() != 0 || !simpleTTF) {
+ gdi = gFalse;
+ }
+}
+
+void PreScanOutputDev::endStringOp(GfxState *state) {
+}
+
+GBool PreScanOutputDev::beginType3Char(GfxState *state, double x, double y,
+ double dx, double dy,
+ CharCode code, Unicode *u, int uLen) {
+ // return false so all Type 3 chars get rendered (no caching)
+ return gFalse;
+}
+
+void PreScanOutputDev::endType3Char(GfxState *state) {
+}
+
+void PreScanOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate) {
+ check(state, state->getFillColorSpace(), state->getFillColor(),
+ state->getFillOpacity(), state->getBlendMode());
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ patternImgMask = gTrue;
+ }
+ gdi = gFalse;
+
+ if (inlineImg) {
+ str->reset();
+ str->discardChars(height * ((width + 7) / 8));
+ str->close();
+ }
+}
+
+void PreScanOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
+ GfxColorSpace *colorSpace;
+
+ colorSpace = colorMap->getColorSpace();
+ if (colorSpace->getMode() == csIndexed) {
+ colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase();
+ }
+ if (colorSpace->getMode() == csDeviceGray ||
+ colorSpace->getMode() == csCalGray) {
+ if (colorMap->getBits() > 1) {
+ mono = gFalse;
+ }
+ } else {
+ gray = gFalse;
+ mono = gFalse;
+ }
+ if (state->getFillOpacity() != 1 ||
+ state->getBlendMode() != gfxBlendNormal) {
+ transparency = gTrue;
+ }
+ gdi = gFalse;
+
+ if (inlineImg) {
+ str->reset();
+ str->discardChars(height * ((width * colorMap->getNumPixelComps() *
+ colorMap->getBits() + 7) / 8));
+ str->close();
+ }
+}
+
+void PreScanOutputDev::drawMaskedImage(GfxState *state, Object *ref,
+ Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GBool maskInvert, GBool interpolate) {
+ GfxColorSpace *colorSpace;
+
+ colorSpace = colorMap->getColorSpace();
+ if (colorSpace->getMode() == csIndexed) {
+ colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase();
+ }
+ if (colorSpace->getMode() == csDeviceGray ||
+ colorSpace->getMode() == csCalGray) {
+ if (colorMap->getBits() > 1) {
+ mono = gFalse;
+ }
+ } else {
+ gray = gFalse;
+ mono = gFalse;
+ }
+ if (state->getFillOpacity() != 1 ||
+ state->getBlendMode() != gfxBlendNormal) {
+ transparency = gTrue;
+ }
+ gdi = gFalse;
+}
+
+void PreScanOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
+ Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ double *matte, GBool interpolate) {
+ GfxColorSpace *colorSpace;
+
+ colorSpace = colorMap->getColorSpace();
+ if (colorSpace->getMode() == csIndexed) {
+ colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase();
+ }
+ if (colorSpace->getMode() != csDeviceGray &&
+ colorSpace->getMode() != csCalGray) {
+ gray = gFalse;
+ }
+ mono = gFalse;
+ transparency = gTrue;
+ gdi = gFalse;
+}
+
+void PreScanOutputDev::beginTransparencyGroup(
+ GfxState *state, double *bbox,
+ GfxColorSpace *blendingColorSpace,
+ GBool isolated, GBool knockout,
+ GBool forSoftMask) {
+ transparency = gTrue;
+ gdi = gFalse;
+}
+
+void PreScanOutputDev::check(GfxState *state,
+ GfxColorSpace *colorSpace, GfxColor *color,
+ double opacity, GfxBlendMode blendMode) {
+ GfxGray gr;
+ GfxCMYK cmyk;
+ GfxRGB rgb;
+
+ if (colorSpace->getMode() == csPattern) {
+ mono = gFalse;
+ gray = gFalse;
+ gdi = gFalse;
+ } else if (colorSpace->getMode() == csDeviceGray ||
+ colorSpace->getMode() == csCalGray) {
+ colorSpace->getGray(color, &gr, state->getRenderingIntent());
+ if (!(gr == 0 || gr == gfxColorComp1)) {
+ mono = gFalse;
+ }
+ } else if (colorSpace->getMode() == csDeviceCMYK) {
+ colorSpace->getCMYK(color, &cmyk, state->getRenderingIntent());
+ if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) {
+ mono = gFalse;
+ gray = gFalse;
+ } else if (!(cmyk.k == 0 || cmyk.k == gfxColorComp1)) {
+ mono = gFalse;
+ }
+ } else {
+ colorSpace->getRGB(color, &rgb, state->getRenderingIntent());
+ if (rgb.r != rgb.g || rgb.g != rgb.b || rgb.b != rgb.r) {
+ mono = gFalse;
+ gray = gFalse;
+ } else if (!((rgb.r == 0 && rgb.g == 0 && rgb.b == 0) ||
+ (rgb.r == gfxColorComp1 &&
+ rgb.g == gfxColorComp1 &&
+ rgb.b == gfxColorComp1))) {
+ mono = gFalse;
+ }
+ }
+ if (opacity != 1 || blendMode != gfxBlendNormal) {
+ transparency = gTrue;
+ }
+}
+
+void PreScanOutputDev::clearStats() {
+ mono = gTrue;
+ gray = gTrue;
+ transparency = gFalse;
+ patternImgMask = gFalse;
+ gdi = gTrue;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h
new file mode 100644
index 00000000000..253733772bf
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/PreScanOutputDev.h
@@ -0,0 +1,149 @@
+//========================================================================
+//
+// PreScanOutputDev.h
+//
+// Copyright 2005 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PRESCANOUTPUTDEV_H
+#define PRESCANOUTPUTDEV_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "GfxState.h"
+#include "OutputDev.h"
+
+//------------------------------------------------------------------------
+// PreScanOutputDev
+//------------------------------------------------------------------------
+
+class PreScanOutputDev: public OutputDev {
+public:
+
+ // Constructor.
+ PreScanOutputDev();
+
+ // Destructor.
+ virtual ~PreScanOutputDev();
+
+ //----- get info about output device
+
+ // Does this device use upside-down coordinates?
+ // (Upside-down means (0,0) is the top left corner of the page.)
+ virtual GBool upsideDown() { return gTrue; }
+
+ // Does this device use drawChar() or drawString()?
+ virtual GBool useDrawChar() { return gTrue; }
+
+ // Does this device use tilingPatternFill()? If this returns false,
+ // tiling pattern fills will be reduced to a series of other drawing
+ // operations.
+ virtual GBool useTilingPatternFill() { 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; }
+
+ //----- initialization and control
+
+ // Start a page.
+ virtual void startPage(int pageNum, GfxState *state);
+
+ // End a page.
+ virtual void endPage();
+
+ //----- path painting
+ virtual void stroke(GfxState *state);
+ virtual void fill(GfxState *state);
+ virtual void eoFill(GfxState *state);
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
+ int paintType, int tilingType, Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep);
+ virtual GBool shadedFill(GfxState *state, GfxShading *shading);
+
+ //----- path clipping
+ virtual void clip(GfxState *state);
+ virtual void eoClip(GfxState *state);
+
+ //----- text drawing
+ virtual void beginStringOp(GfxState *state);
+ virtual void endStringOp(GfxState *state);
+ virtual GBool beginType3Char(GfxState *state, double x, double y,
+ double dx, double dy,
+ CharCode code, Unicode *u, int uLen);
+ virtual void endType3Char(GfxState *state);
+
+ //----- image drawing
+ virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate);
+ virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg, GBool interpolate);
+ virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ 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,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ double *matte, GBool interpolate);
+
+ //----- transparency groups and soft masks
+ virtual void beginTransparencyGroup(GfxState *state, double *bbox,
+ GfxColorSpace *blendingColorSpace,
+ GBool isolated, GBool knockout,
+ GBool forSoftMask);
+
+ //----- special access
+
+ // Returns true if the operations performed since the last call to
+ // clearStats() are all monochrome (black or white).
+ GBool isMonochrome() { return mono; }
+
+ // Returns true if the operations performed since the last call to
+ // clearStats() are all gray.
+ GBool isGray() { return gray; }
+
+ // Returns true if the operations performed since the last call to
+ // clearStats() included any transparency.
+ GBool usesTransparency() { return transparency; }
+
+ // Returns true if the operations performed since the last call to
+ // clearStats() included any image mask fills with a pattern color
+ // space.
+ GBool usesPatternImageMask() { return patternImgMask; }
+
+ // Returns true if the operations performed since the last call to
+ // clearStats() are all rasterizable by GDI calls in GDIOutputDev.
+ GBool isAllGDI() { return gdi; }
+
+ // Clear the stats used by the above functions.
+ void clearStats();
+
+private:
+
+ void check(GfxState *state, GfxColorSpace *colorSpace, GfxColor *color,
+ double opacity, GfxBlendMode blendMode);
+
+ GBool mono;
+ GBool gray;
+ GBool transparency;
+ GBool patternImgMask;
+ GBool gdi;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/SecurityHandler.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/SecurityHandler.cc
new file mode 100644
index 00000000000..4a7d0954c40
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/SecurityHandler.cc
@@ -0,0 +1,373 @@
+//========================================================================
+//
+// SecurityHandler.cc
+//
+// Copyright 2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmempp.h"
+#include "GString.h"
+#include "PDFDoc.h"
+#include "Decrypt.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "PDFCore.h"
+#include "SecurityHandler.h"
+
+//------------------------------------------------------------------------
+// SecurityHandler
+//------------------------------------------------------------------------
+
+SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) {
+ Object filterObj;
+ SecurityHandler *secHdlr;
+
+ encryptDictA->dictLookup("Filter", &filterObj);
+ if (filterObj.isName("Standard")) {
+ secHdlr = new StandardSecurityHandler(docA, encryptDictA);
+ } else if (filterObj.isName()) {
+ error(errSyntaxError, -1, "Couldn't find the '{0:s}' security handler",
+ filterObj.getName());
+ secHdlr = NULL;
+ } else {
+ error(errSyntaxError, -1,
+ "Missing or invalid 'Filter' entry in encryption dictionary");
+ secHdlr = NULL;
+ }
+ filterObj.free();
+ return secHdlr;
+}
+
+SecurityHandler::SecurityHandler(PDFDoc *docA) {
+ doc = docA;
+}
+
+SecurityHandler::~SecurityHandler() {
+}
+
+GBool SecurityHandler::checkEncryption(GString *ownerPassword,
+ GString *userPassword) {
+ void *authData;
+ GBool ok;
+ int i;
+
+ if (ownerPassword || userPassword) {
+ authData = makeAuthData(ownerPassword, userPassword);
+ } else {
+ authData = NULL;
+ }
+ ok = authorize(authData);
+ if (authData) {
+ freeAuthData(authData);
+ }
+ for (i = 0; !ok && i < 3; ++i) {
+ if (!(authData = getAuthData())) {
+ break;
+ }
+ ok = authorize(authData);
+ if (authData) {
+ freeAuthData(authData);
+ }
+ }
+ if (!ok) {
+ error(errCommandLine, -1, "Incorrect password");
+ }
+ return ok;
+}
+
+//------------------------------------------------------------------------
+// StandardSecurityHandler
+//------------------------------------------------------------------------
+
+class StandardAuthData {
+public:
+
+ StandardAuthData(GString *ownerPasswordA, GString *userPasswordA) {
+ ownerPassword = ownerPasswordA;
+ userPassword = userPasswordA;
+ }
+
+ ~StandardAuthData() {
+ if (ownerPassword) {
+ delete ownerPassword;
+ }
+ if (userPassword) {
+ delete userPassword;
+ }
+ }
+
+ GString *ownerPassword;
+ GString *userPassword;
+};
+
+StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
+ Object *encryptDictA):
+ SecurityHandler(docA)
+{
+ Object versionObj, revisionObj, lengthObj;
+ Object ownerKeyObj, userKeyObj, ownerEncObj, userEncObj;
+ Object permObj, fileIDObj, fileIDObj1;
+ Object cryptFiltersObj, streamFilterObj, stringFilterObj;
+ Object cryptFilterObj, cfmObj, cfLengthObj;
+ Object encryptMetadataObj;
+
+ ok = gFalse;
+ fileID = NULL;
+ ownerKey = NULL;
+ userKey = NULL;
+ ownerEnc = NULL;
+ userEnc = NULL;
+ fileKeyLength = 0;
+
+ //--- get the main parameters
+ encryptDictA->dictLookup("V", &versionObj);
+ encryptDictA->dictLookup("R", &revisionObj);
+ encryptDictA->dictLookup("Length", &lengthObj);
+ encryptDictA->dictLookup("O", &ownerKeyObj);
+ encryptDictA->dictLookup("U", &userKeyObj);
+ encryptDictA->dictLookup("OE", &ownerEncObj);
+ encryptDictA->dictLookup("UE", &userEncObj);
+ encryptDictA->dictLookup("P", &permObj);
+ doc->getXRef()->getTrailerDict()->dictLookup("ID", &fileIDObj);
+ if (!versionObj.isInt() ||
+ !revisionObj.isInt() ||
+ !permObj.isInt() ||
+ !ownerKeyObj.isString() ||
+ !userKeyObj.isString()) {
+ error(errSyntaxError, -1, "Invalid encryption parameters");
+ goto done;
+ }
+ encVersion = versionObj.getInt();
+ encRevision = revisionObj.getInt();
+ encAlgorithm = cryptRC4;
+ // revision 2 forces a 40-bit key - some buggy PDF generators
+ // set the Length value incorrectly
+ if (encRevision == 2 || !lengthObj.isInt()) {
+ fileKeyLength = 5;
+ } else {
+ fileKeyLength = lengthObj.getInt() / 8;
+ }
+ encryptMetadata = gTrue;
+
+ //--- check for a crypt filter (which can modify the parameters)
+ //~ this currently only handles a subset of crypt filter functionality
+ //~ (in particular, it ignores the EFF entry in encryptDictA, and
+ //~ doesn't handle the case where StmF, StrF, and EFF are not all the
+ //~ same)
+ if ((encVersion == 4 || encVersion == 5) &&
+ (encRevision == 4 || encRevision == 5 || encRevision == 6)) {
+ encryptDictA->dictLookup("CF", &cryptFiltersObj);
+ encryptDictA->dictLookup("StmF", &streamFilterObj);
+ encryptDictA->dictLookup("StrF", &stringFilterObj);
+ if (cryptFiltersObj.isDict() &&
+ streamFilterObj.isName() &&
+ stringFilterObj.isName() &&
+ !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) {
+ if (!strcmp(streamFilterObj.getName(), "Identity")) {
+ // no encryption on streams or strings
+ stringFilterObj.free();
+ streamFilterObj.free();
+ cryptFiltersObj.free();
+ goto done;
+ }
+ if (cryptFiltersObj.dictLookup(streamFilterObj.getName(),
+ &cryptFilterObj)->isDict()) {
+ cryptFilterObj.dictLookup("CFM", &cfmObj);
+ if (cfmObj.isName("V2")) {
+ if (cryptFilterObj.dictLookup("Length",
+ &cfLengthObj)->isInt()) {
+ fileKeyLength = cfLengthObj.getInt();
+ }
+ cfLengthObj.free();
+ encVersion = 2;
+ encRevision = 3;
+ } else if (cfmObj.isName("AESV2")) {
+ if (cryptFilterObj.dictLookup("Length",
+ &cfLengthObj)->isInt()) {
+ fileKeyLength = cfLengthObj.getInt();
+ }
+ cfLengthObj.free();
+ encVersion = 2;
+ encRevision = 3;
+ encAlgorithm = cryptAES;
+ } else if (cfmObj.isName("AESV3")) {
+ if (cryptFilterObj.dictLookup("Length",
+ &cfLengthObj)->isInt()) {
+ fileKeyLength = cfLengthObj.getInt();
+ }
+ cfLengthObj.free();
+ if (fileKeyLength == 16) {
+ // this isn't allowed by the spec, but Adobe supports it
+ encVersion = 2;
+ encRevision = 3;
+ encAlgorithm = cryptAES;
+ } else {
+ encVersion = 5;
+ if (encRevision != 5 && encRevision != 6) {
+ encRevision = 6;
+ }
+ encAlgorithm = cryptAES256;
+ }
+ }
+ cfmObj.free();
+ }
+ cryptFilterObj.free();
+ }
+ stringFilterObj.free();
+ streamFilterObj.free();
+ cryptFiltersObj.free();
+ if (encryptDictA->dictLookup("EncryptMetadata",
+ &encryptMetadataObj)->isBool()) {
+ encryptMetadata = encryptMetadataObj.getBool();
+ }
+ encryptMetadataObj.free();
+ }
+
+ //--- version-specific parameters
+ if (encRevision <= 4) {
+ if (ownerKeyObj.getString()->getLength() != 32 ||
+ userKeyObj.getString()->getLength() != 32) {
+ error(errSyntaxError, -1, "Invalid encryption key length");
+ // this is non-fatal -- see below
+ }
+ } else if (encRevision <= 6) {
+ // the spec says 48 bytes, but Acrobat pads them out longer
+ if (ownerKeyObj.getString()->getLength() < 48 ||
+ userKeyObj.getString()->getLength() < 48 ||
+ !ownerEncObj.isString() ||
+ ownerEncObj.getString()->getLength() != 32 ||
+ !userEncObj.isString() ||
+ userEncObj.getString()->getLength() != 32) {
+ error(errSyntaxError, -1, "Invalid encryption key length");
+ goto done;
+ }
+ }
+ permFlags = permObj.getInt();
+ ownerKey = ownerKeyObj.getString()->copy();
+ userKey = userKeyObj.getString()->copy();
+ if (encRevision <= 4) {
+ // Adobe apparently zero-pads the U value (and maybe the O value?)
+ // if it's short
+ while (ownerKey->getLength() < 32) {
+ ownerKey->append((char)0x00);
+ }
+ while (userKey->getLength() < 32) {
+ userKey->append((char)0x00);
+ }
+ }
+ if (encVersion >= 1 && encVersion <= 2 &&
+ encRevision >= 2 && encRevision <= 3) {
+ if (fileIDObj.isArray()) {
+ if (fileIDObj.arrayGet(0, &fileIDObj1)->isString()) {
+ fileID = fileIDObj1.getString()->copy();
+ } else {
+ fileID = new GString();
+ }
+ fileIDObj1.free();
+ } else {
+ fileID = new GString();
+ }
+ if (fileKeyLength > 16 || fileKeyLength <= 0) {
+ fileKeyLength = 16;
+ }
+ ok = gTrue;
+ } else if (encVersion == 5 && (encRevision == 5 || encRevision == 6)) {
+ fileID = new GString(); // unused for V=R=5
+ ownerEnc = ownerEncObj.getString()->copy();
+ userEnc = userEncObj.getString()->copy();
+ if (fileKeyLength > 32 || fileKeyLength <= 0) {
+ fileKeyLength = 32;
+ }
+ ok = gTrue;
+ } else {
+ error(errUnimplemented, -1,
+ "Unsupported version/revision ({0:d}/{1:d}) of Standard security handler",
+ encVersion, encRevision);
+ }
+
+ done:
+ fileIDObj.free();
+ permObj.free();
+ userEncObj.free();
+ ownerEncObj.free();
+ userKeyObj.free();
+ ownerKeyObj.free();
+ lengthObj.free();
+ revisionObj.free();
+ versionObj.free();
+}
+
+StandardSecurityHandler::~StandardSecurityHandler() {
+ if (fileID) {
+ delete fileID;
+ }
+ if (ownerKey) {
+ delete ownerKey;
+ }
+ if (userKey) {
+ delete userKey;
+ }
+ if (ownerEnc) {
+ delete ownerEnc;
+ }
+ if (userEnc) {
+ delete userEnc;
+ }
+}
+
+GBool StandardSecurityHandler::isUnencrypted() {
+ return encVersion == -1 && encRevision == -1;
+}
+
+void *StandardSecurityHandler::makeAuthData(GString *ownerPassword,
+ GString *userPassword) {
+ return new StandardAuthData(ownerPassword ? ownerPassword->copy()
+ : (GString *)NULL,
+ userPassword ? userPassword->copy()
+ : (GString *)NULL);
+}
+
+void *StandardSecurityHandler::getAuthData() {
+ PDFCore *core;
+ GString *password;
+
+ if (!(core = doc->getCore()) ||
+ !(password = core->getPassword())) {
+ return NULL;
+ }
+ return new StandardAuthData(password, password->copy());
+}
+
+void StandardSecurityHandler::freeAuthData(void *authData) {
+ delete (StandardAuthData *)authData;
+}
+
+GBool StandardSecurityHandler::authorize(void *authData) {
+ GString *ownerPassword, *userPassword;
+
+ if (!ok) {
+ return gFalse;
+ }
+ if (authData) {
+ ownerPassword = ((StandardAuthData *)authData)->ownerPassword;
+ userPassword = ((StandardAuthData *)authData)->userPassword;
+ } else {
+ ownerPassword = NULL;
+ userPassword = NULL;
+ }
+ if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength,
+ ownerKey, userKey, ownerEnc, userEnc,
+ permFlags, fileID,
+ ownerPassword, userPassword, fileKey,
+ encryptMetadata, &ownerPasswordOk)) {
+ return gFalse;
+ }
+ return gTrue;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/SecurityHandler.h b/Build/source/libs/xpdf/xpdf-src/xpdf/SecurityHandler.h
new file mode 100644
index 00000000000..63462339bff
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/SecurityHandler.h
@@ -0,0 +1,127 @@
+//========================================================================
+//
+// SecurityHandler.h
+//
+// Copyright 2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SECURITYHANDLER_H
+#define SECURITYHANDLER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+
+class GString;
+class PDFDoc;
+struct XpdfSecurityHandler;
+
+//------------------------------------------------------------------------
+// SecurityHandler
+//------------------------------------------------------------------------
+
+class SecurityHandler {
+public:
+
+ static SecurityHandler *make(PDFDoc *docA, Object *encryptDictA);
+
+ SecurityHandler(PDFDoc *docA);
+ virtual ~SecurityHandler();
+
+ // Returns true if the file is actually unencrypted.
+ virtual GBool isUnencrypted() { return gFalse; }
+
+ // Check the document's encryption. If the document is encrypted,
+ // this will first try <ownerPassword> and <userPassword> (in
+ // "batch" mode), and if those fail, it will attempt to request a
+ // password from the user. This is the high-level function that
+ // calls the lower level functions for the specific security handler
+ // (requesting a password three times, etc.). Returns true if the
+ // document can be opened (if it's unencrypted, or if a correct
+ // password is obtained); false otherwise (encrypted and no correct
+ // password).
+ GBool checkEncryption(GString *ownerPassword,
+ GString *userPassword);
+
+ // Create authorization data for the specified owner and user
+ // passwords. If the security handler doesn't support "batch" mode,
+ // this function should return NULL.
+ virtual void *makeAuthData(GString *ownerPassword,
+ GString *userPassword) = 0;
+
+ // Construct authorization data, typically by prompting the user for
+ // a password. Returns an authorization data object, or NULL to
+ // cancel.
+ virtual void *getAuthData() = 0;
+
+ // Free the authorization data returned by makeAuthData or
+ // getAuthData.
+ virtual void freeAuthData(void *authData) = 0;
+
+ // Attempt to authorize the document, using the supplied
+ // authorization data (which may be NULL). Returns true if
+ // successful (i.e., if at least the right to open the document was
+ // granted).
+ virtual GBool authorize(void *authData) = 0;
+
+ // Return the various authorization parameters. These are only
+ // valid after authorize has returned true.
+ virtual int getPermissionFlags() = 0;
+ virtual GBool getOwnerPasswordOk() = 0;
+ virtual Guchar *getFileKey() = 0;
+ virtual int getFileKeyLength() = 0;
+ virtual int getEncVersion() = 0;
+ virtual CryptAlgorithm getEncAlgorithm() = 0;
+
+protected:
+
+ PDFDoc *doc;
+};
+
+//------------------------------------------------------------------------
+// StandardSecurityHandler
+//------------------------------------------------------------------------
+
+class StandardSecurityHandler: public SecurityHandler {
+public:
+
+ StandardSecurityHandler(PDFDoc *docA, Object *encryptDictA);
+ virtual ~StandardSecurityHandler();
+
+ virtual GBool isUnencrypted();
+ virtual void *makeAuthData(GString *ownerPassword,
+ GString *userPassword);
+ virtual void *getAuthData();
+ virtual void freeAuthData(void *authData);
+ virtual GBool authorize(void *authData);
+ virtual int getPermissionFlags() { return permFlags; }
+ virtual GBool getOwnerPasswordOk() { return ownerPasswordOk; }
+ virtual Guchar *getFileKey() { return fileKey; }
+ virtual int getFileKeyLength() { return fileKeyLength; }
+ virtual int getEncVersion() { return encVersion; }
+ virtual CryptAlgorithm getEncAlgorithm() { return encAlgorithm; }
+
+private:
+
+ int permFlags;
+ GBool ownerPasswordOk;
+ Guchar fileKey[32];
+ int fileKeyLength;
+ int encVersion;
+ int encRevision;
+ CryptAlgorithm encAlgorithm;
+ GBool encryptMetadata;
+
+ GString *ownerKey, *userKey;
+ GString *ownerEnc, *userEnc;
+ GString *fileID;
+ GBool ok;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc
new file mode 100644
index 00000000000..d8dc24e0fc6
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.cc
@@ -0,0 +1,1327 @@
+//========================================================================
+//
+// ShadingImage.cc
+//
+// Copyright 2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <math.h>
+#include "Trace.h"
+#include "GfxState.h"
+#include "SplashBitmap.h"
+#include "SplashPattern.h"
+#include "SplashPath.h"
+#include "Splash.h"
+#include "ShadingImage.h"
+
+// Max recursive depth for a patch mesh shading fill.
+#define patchMaxDepth 10
+
+// Max delta allowed in any color component for a patch mesh shading
+// fill.
+#define patchColorDelta (dblToCol(1 / 256.0))
+
+SplashBitmap *ShadingImage::generateBitmap(GfxState *state,
+ GfxShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut) {
+ switch (shading->getType()) {
+ case 1:
+ return generateFunctionBitmap(state, (GfxFunctionShading *)shading,
+ mode, reverseVideo,
+ parentSplash, parentBitmap, xOut, yOut);
+ break;
+ case 2:
+ return generateAxialBitmap(state, (GfxAxialShading *)shading,
+ mode, reverseVideo,
+ parentSplash, parentBitmap, xOut, yOut);
+ break;
+ case 3:
+ return generateRadialBitmap(state, (GfxRadialShading *)shading,
+ mode, reverseVideo,
+ parentSplash, parentBitmap, xOut, yOut);
+ break;
+ case 4:
+ case 5:
+ return generateGouraudTriangleBitmap(state,
+ (GfxGouraudTriangleShading *)shading,
+ mode, reverseVideo,
+ parentSplash, parentBitmap,
+ xOut, yOut);
+ break;
+ case 6:
+ case 7:
+ return generatePatchMeshBitmap(state, (GfxPatchMeshShading *)shading,
+ mode, reverseVideo,
+ parentSplash, parentBitmap, xOut, yOut);
+ break;
+ default:
+ return NULL;
+ }
+}
+
+SplashBitmap *ShadingImage::generateFunctionBitmap(GfxState *state,
+ GfxFunctionShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut) {
+ // get the shading parameters
+ double x0, y0, x1, y1;
+ shading->getDomain(&x0, &y0, &x1, &y1);
+ double *patternMat = shading->getMatrix();
+
+ // get the clip bbox
+ double fxMin, fyMin, fxMax, fyMax;
+ state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax);
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // convert to integer coords
+ int xMin = (int)floor(fxMin);
+ int yMin = (int)floor(fyMin);
+ int xMax = (int)floor(fxMax) + 1;
+ int yMax = (int)floor(fyMax) + 1;
+ int bitmapWidth = xMax - xMin;
+ int bitmapHeight = yMax - yMin;
+
+ // allocate the bitmap
+ traceMessage("function shading fill bitmap");
+ SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode,
+ gTrue, gTrue, parentBitmap);
+ int nComps = splashColorModeNComps[mode];
+
+ // compute the domain -> device space transform = mat * CTM
+ double *ctm = state->getCTM();
+ double mat[6];
+ mat[0] = patternMat[0] * ctm[0] + patternMat[1] * ctm[2];
+ mat[1] = patternMat[0] * ctm[1] + patternMat[1] * ctm[3];
+ mat[2] = patternMat[2] * ctm[0] + patternMat[3] * ctm[2];
+ mat[3] = patternMat[2] * ctm[1] + patternMat[3] * ctm[3];
+ mat[4] = patternMat[4] * ctm[0] + patternMat[5] * ctm[2] + ctm[4];
+ mat[5] = patternMat[4] * ctm[1] + patternMat[5] * ctm[3] + ctm[5];
+
+ // compute the device space -> domain transform
+ double det = mat[0] * mat[3] - mat[1] * mat[2];
+ if (fabs(det) < 0.000001) {
+ return NULL;
+ }
+ det = 1 / det;
+ double iMat[6];
+ iMat[0] = mat[3] * det;
+ iMat[1] = -mat[1] * det;
+ iMat[2] = -mat[2] * det;
+ iMat[3] = mat[0] * det;
+ iMat[4] = (mat[2] * mat[5] - mat[3] * mat[4]) * det;
+ iMat[5] = (mat[1] * mat[4] - mat[0] * mat[5]) * det;
+
+ // fill the bitmap
+ SplashColorPtr dataPtr = bitmap->getDataPtr();
+ Guchar *alphaPtr = bitmap->getAlphaPtr();
+ for (int y = 0; y < bitmapHeight; ++y) {
+ for (int x = 0; x < bitmapWidth; ++x) {
+
+ // convert coords to the pattern domain
+ double tx = xMin + x + 0.5;
+ double ty = yMin + y + 0.5;
+ double xx = tx * iMat[0] + ty * iMat[2] + iMat[4];
+ double yy = tx * iMat[1] + ty * iMat[3] + iMat[5];
+
+ // get the color
+ if (xx >= x0 && xx <= x1 && yy >= y0 && yy <= y1) {
+ GfxColor color;
+ shading->getColor(xx, yy, &color);
+ SplashColor sColor;
+ computeShadingColor(state, mode, reverseVideo, &color, sColor);
+ for (int i = 0; i < nComps; ++i) {
+ *dataPtr++ = sColor[i];
+ }
+ *alphaPtr++ = 0xff;
+ } else {
+ dataPtr += nComps;
+ *alphaPtr++ = 0;
+ }
+ }
+ }
+
+ *xOut = xMin;
+ *yOut = yMin;
+ return bitmap;
+}
+
+SplashBitmap *ShadingImage::generateAxialBitmap(GfxState *state,
+ GfxAxialShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut) {
+ // get the shading parameters
+ double x0, y0, x1, y1;
+ shading->getCoords(&x0, &y0, &x1, &y1);
+ double t0 = shading->getDomain0();
+ double t1 = shading->getDomain1();
+ GBool ext0 = shading->getExtend0();
+ GBool ext1 = shading->getExtend1();
+ double dx = x1 - x0;
+ double dy = y1 - y0;
+ double d = dx * dx + dy * dy;
+ GBool dZero = fabs(d) < 0.0001;
+ if (!dZero) {
+ d = 1 / d;
+ }
+ if (dZero && !ext0 && !ext1) {
+ return NULL;
+ }
+
+ // get the clip bbox
+ double fxMin, fyMin, fxMax, fyMax;
+ state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax);
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // convert to integer coords
+ int xMin = (int)floor(fxMin);
+ int yMin = (int)floor(fyMin);
+ int xMax = (int)floor(fxMax) + 1;
+ int yMax = (int)floor(fyMax) + 1;
+ int bitmapWidth = xMax - xMin;
+ int bitmapHeight = yMax - yMin;
+
+ // compute the inverse CTM
+ double *ctm = state->getCTM();
+ double det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
+ if (fabs(det) < 0.000001) {
+ return NULL;
+ }
+ det = 1 / det;
+ double ictm[6];
+ ictm[0] = ctm[3] * det;
+ ictm[1] = -ctm[1] * det;
+ ictm[2] = -ctm[2] * det;
+ ictm[3] = ctm[0] * det;
+ ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+ ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+
+ // convert axis endpoints to device space
+ double xx0, yy0, xx1, yy1;
+ state->transform(x0, y0, &xx0, &yy0);
+ state->transform(x1, y1, &xx1, &yy1);
+
+ // allocate the bitmap
+ traceMessage("axial shading fill bitmap");
+ SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode,
+ gTrue, gTrue, parentBitmap);
+ int nComps = splashColorModeNComps[mode];
+
+ // special case: zero-length axis
+ if (dZero) {
+ GfxColor color;
+ if (ext0) {
+ shading->getColor(t0, &color);
+ } else {
+ shading->getColor(t1, &color);
+ }
+ SplashColor sColor;
+ computeShadingColor(state, mode, reverseVideo, &color, sColor);
+ SplashColorPtr dataPtr = bitmap->getDataPtr();
+ for (int y = 0; y < bitmapHeight; ++y) {
+ for (int x = 0; x < bitmapWidth; ++x) {
+ for (int i = 0; i < nComps; ++i) {
+ *dataPtr++ = sColor[i];
+ }
+ }
+ }
+ memset(bitmap->getAlphaPtr(), 0xff, (size_t)bitmapWidth * bitmapHeight);
+
+ // special case: horizontal axis (in device space)
+ } else if (fabs(yy0 - yy1) < 0.01) {
+ for (int x = 0; x < bitmapWidth; ++x) {
+ SplashColorPtr dataPtr = bitmap->getDataPtr() + x * nComps;
+ Guchar *alphaPtr = bitmap->getAlphaPtr() + x;
+ double tx = xMin + x + 0.5;
+ double ty = yMin + 0.5;
+ double xx = tx * ictm[0] + ty * ictm[2] + ictm[4];
+ double yy = tx * ictm[1] + ty * ictm[3] + ictm[5];
+ double s = ((xx - x0) * dx + (yy - y0) * dy) * d;
+ GBool go = gFalse;
+ if (s < 0) {
+ go = ext0;
+ } else if (s > 1) {
+ go = ext1;
+ } else {
+ go = gTrue;
+ }
+ if (go) {
+ GfxColor color;
+ if (s <= 0) {
+ shading->getColor(t0, &color);
+ } else if (s >= 1) {
+ shading->getColor(t1, &color);
+ } else {
+ double t = t0 + s * (t1 - t0);
+ shading->getColor(t, &color);
+ }
+ SplashColor sColor;
+ computeShadingColor(state, mode, reverseVideo, &color, sColor);
+ for (int y = 0; y < bitmapHeight; ++y) {
+ for (int i = 0; i < nComps; ++i) {
+ dataPtr[i] = sColor[i];
+ }
+ dataPtr += bitmap->getRowSize();
+ *alphaPtr = 0xff;
+ alphaPtr += bitmapWidth;
+ }
+ } else {
+ for (int y = 0; y < bitmapHeight; ++y) {
+ *alphaPtr = 0;
+ alphaPtr += bitmapWidth;
+ }
+ }
+ }
+
+ // special case: vertical axis (in device space)
+ } else if (fabs(xx0 - xx1) < 0.01) {
+ for (int y = 0; y < bitmapHeight; ++y) {
+ SplashColorPtr dataPtr = bitmap->getDataPtr() + y * bitmap->getRowSize();
+ Guchar *alphaPtr = bitmap->getAlphaPtr() + y * bitmapWidth;
+ double tx = xMin + 0.5;
+ double ty = yMin + y + 0.5;
+ double xx = tx * ictm[0] + ty * ictm[2] + ictm[4];
+ double yy = tx * ictm[1] + ty * ictm[3] + ictm[5];
+ double s = ((xx - x0) * dx + (yy - y0) * dy) * d;
+ GBool go = gFalse;
+ if (s < 0) {
+ go = ext0;
+ } else if (s > 1) {
+ go = ext1;
+ } else {
+ go = gTrue;
+ }
+ if (go) {
+ GfxColor color;
+ if (s <= 0) {
+ shading->getColor(t0, &color);
+ } else if (s >= 1) {
+ shading->getColor(t1, &color);
+ } else {
+ double t = t0 + s * (t1 - t0);
+ shading->getColor(t, &color);
+ }
+ SplashColor sColor;
+ computeShadingColor(state, mode, reverseVideo, &color, sColor);
+ for (int x = 0; x < bitmapWidth; ++x) {
+ for (int i = 0; i < nComps; ++i) {
+ dataPtr[i] = sColor[i];
+ }
+ dataPtr += nComps;
+ }
+ memset(alphaPtr, 0xff, bitmapWidth);
+ } else {
+ memset(alphaPtr, 0, bitmapWidth);
+ }
+ }
+
+ // general case
+ } else {
+ // pre-compute colors along the axis
+ int nColors = (int)(1.5 * sqrt((xx1 - xx0) * (xx1 - xx0)
+ + (yy1 - yy0) * (yy1 - yy0)));
+ if (nColors < 16) {
+ nColors = 16;
+ } else if (nColors > 1024) {
+ nColors = 1024;
+ }
+ SplashColorPtr sColors = (SplashColorPtr)gmallocn(nColors, nComps);
+ SplashColorPtr sColor = sColors;
+ for (int i = 0; i < nColors; ++i) {
+ double s = (double)i / (double)(nColors - 1);
+ double t = t0 + s * (t1 - t0);
+ GfxColor color;
+ shading->getColor(t, &color);
+ computeShadingColor(state, mode, reverseVideo, &color, sColor);
+ sColor += nComps;
+ }
+
+ SplashColorPtr dataPtr = bitmap->getDataPtr();
+ Guchar *alphaPtr = bitmap->getAlphaPtr();
+ for (int y = 0; y < bitmapHeight; ++y) {
+ for (int x = 0; x < bitmapWidth; ++x) {
+
+ // convert coords to user space
+ double tx = xMin + x + 0.5;
+ double ty = yMin + y + 0.5;
+ double xx = tx * ictm[0] + ty * ictm[2] + ictm[4];
+ double yy = tx * ictm[1] + ty * ictm[3] + ictm[5];
+
+ // compute the position along the axis
+ double s = ((xx - x0) * dx + (yy - y0) * dy) * d;
+ GBool go = gFalse;
+ if (s < 0) {
+ go = ext0;
+ } else if (s > 1) {
+ go = ext1;
+ } else {
+ go = gTrue;
+ }
+ if (go) {
+ if (s <= 0) {
+ sColor = sColors;
+ } else if (s >= 1) {
+ sColor = sColors + (nColors - 1) * nComps;
+ } else {
+ int i = (int)((nColors - 1) * s + 0.5);
+ sColor = sColors + i * nComps;
+ }
+ for (int i = 0; i < nComps; ++i) {
+ *dataPtr++ = sColor[i];
+ }
+ *alphaPtr++ = 0xff;
+ } else {
+ dataPtr += nComps;
+ *alphaPtr++ = 0;
+ }
+ }
+ }
+ gfree(sColors);
+ }
+
+ *xOut = xMin;
+ *yOut = yMin;
+ return bitmap;
+}
+
+SplashBitmap *ShadingImage::generateRadialBitmap(GfxState *state,
+ GfxRadialShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut) {
+ // get the shading parameters
+ double x0, y0, r0, x1, y1, r1;
+ shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
+ double t0 = shading->getDomain0();
+ double t1 = shading->getDomain1();
+ GBool ext0 = shading->getExtend0();
+ GBool ext1 = shading->getExtend1();
+ double h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
+ GBool enclosed = fabs(r1 - r0) >= h;
+
+ // get the clip bbox
+ double fxMin, fyMin, fxMax, fyMax;
+ state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax);
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // intersect with shading region (in user space): if the extend
+ // flags are false (or just the larger extend flag is false, in the
+ // "enclosed" case), we can use the bbox for the two circles
+ if ((!ext0 && !ext1) ||
+ (enclosed && !(r0 > r1 ? ext0 : ext1))) {
+ double uxMin = (x0 - r0) < (x1 - r1) ? (x0 - r0) : (x1 - r1);
+ double uxMax = (x0 + r0) > (x1 + r1) ? (x0 + r0) : (x1 + r1);
+ double uyMin = (y0 - r0) < (y1 - r1) ? (y0 - r0) : (y1 - r1);
+ double uyMax = (y0 + r0) > (y1 + r1) ? (y0 + r0) : (y1 + r1);
+ double dxMin, dyMin, dxMax, dyMax;
+ transformBBox(state, uxMin, uyMin, uxMax, uyMax,
+ &dxMin, &dyMin, &dxMax, &dyMax);
+ if (dxMin > fxMin) {
+ fxMin = dxMin;
+ }
+ if (dxMax < dxMax) {
+ fxMax = dxMax;
+ }
+ if (dyMin > fyMin) {
+ fyMin = dyMin;
+ }
+ if (dyMax < fyMax) {
+ fyMax = dyMax;
+ }
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+ }
+
+ // convert to integer coords
+ int xMin = (int)floor(fxMin);
+ int yMin = (int)floor(fyMin);
+ int xMax = (int)floor(fxMax) + 1;
+ int yMax = (int)floor(fyMax) + 1;
+ int bitmapWidth = xMax - xMin;
+ int bitmapHeight = yMax - yMin;
+
+ // compute the inverse CTM
+ double *ctm = state->getCTM();
+ double det = ctm[0] * ctm[3] - ctm[1] * ctm[2];
+ if (fabs(det) < 0.000001) {
+ return NULL;
+ }
+ det = 1 / det;
+ double ictm[6];
+ ictm[0] = ctm[3] * det;
+ ictm[1] = -ctm[1] * det;
+ ictm[2] = -ctm[2] * det;
+ ictm[3] = ctm[0] * det;
+ ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+ ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+
+ // allocate the bitmap
+ traceMessage("radial shading fill bitmap");
+ SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode,
+ gTrue, gTrue, parentBitmap);
+ int nComps = splashColorModeNComps[mode];
+
+ // pre-compute colors along the axis
+ int nColors = (int)sqrt((double)(bitmapWidth * bitmapWidth
+ + bitmapHeight * bitmapHeight));
+ if (nColors < 16) {
+ nColors = 16;
+ } else if (nColors > 1024) {
+ nColors = 1024;
+ }
+ SplashColorPtr sColors = (SplashColorPtr)gmallocn(nColors, nComps);
+ SplashColorPtr sColor = sColors;
+ for (int i = 0; i < nColors; ++i) {
+ double s = (double)i / (double)(nColors - 1);
+ double t = t0 + s * (t1 - t0);
+ GfxColor color;
+ shading->getColor(t, &color);
+ computeShadingColor(state, mode, reverseVideo, &color, sColor);
+ sColor += nComps;
+ }
+
+ // special case: in the "enclosed" + extended case, we can fill the
+ // bitmap with the outer color and just render inside the larger
+ // circle
+ int bxMin, byMin, bxMax, byMax;
+ if (enclosed &&
+ ((r0 > r1 && ext0) || (r1 > r0 && ext1))) {
+ double uxMin, uyMin, uxMax, uyMax;
+ if (r0 > r1) {
+ sColor = sColors;
+ uxMin = x0 - r0;
+ uxMax = x0 + r0;
+ uyMin = y0 - r0;
+ uyMax = y0 + r0;
+ } else {
+ sColor = sColors + (nColors - 1) * nComps;
+ uxMin = x1 - r1;
+ uxMax = x1 + r1;
+ uyMin = y1 - r1;
+ uyMax = y1 + r1;
+ }
+
+ // convert bbox of larger circle to device space
+ double dxMin, dyMin, dxMax, dyMax;
+ transformBBox(state, uxMin, uyMin, uxMax, uyMax,
+ &dxMin, &dyMin, &dxMax, &dyMax);
+ bxMin = (int)floor(dxMin - xMin);
+ if (bxMin < 0) {
+ bxMin = 0;
+ }
+ byMin = (int)floor(dyMin - yMin);
+ if (byMin < 0) {
+ byMin = 0;
+ }
+ bxMax = (int)floor(dxMax - xMin) + 1;
+ if (bxMax > bitmapWidth) {
+ bxMax = bitmapWidth;
+ }
+ byMax = (int)floor(dyMax - yMin) + 1;
+ if (byMax > bitmapHeight) {
+ byMax = bitmapHeight;
+ }
+
+ // fill bitmap (except for the rectangle containing the larger circle)
+ SplashColorPtr dataPtr = bitmap->getDataPtr();
+ Guchar *alphaPtr = bitmap->getAlphaPtr();
+ for (int y = 0; y < bitmapHeight; ++y) {
+ for (int x = 0; x < bitmapWidth; ++x) {
+ if (y >= byMin && y < byMax && x >= bxMin && x < bxMax) {
+ dataPtr += nComps;
+ ++alphaPtr;
+ } else {
+ for (int i = 0; i < nComps; ++i) {
+ *dataPtr++ = sColor[i];
+ }
+ *alphaPtr++ = 0xff;
+ }
+ }
+ }
+
+ } else {
+ bxMin = 0;
+ byMin = 0;
+ bxMax = bitmapWidth;
+ byMax = bitmapHeight;
+ }
+
+ // render the shading into the bitmap
+ double dx = x1 - x0;
+ double dy = y1 - y0;
+ double dr = r1 - r0;
+ double r0dr = r0 * dr;
+ double r02 = r0 * r0;
+ double a = dx * dx + dy * dy - dr * dr;
+ GBool aIsZero;
+ double a2;
+ if (fabs(a) < 0.00001) {
+ aIsZero = gTrue;
+ a2 = 0;
+ } else {
+ aIsZero = gFalse;
+ a2 = 1 / (2 * a);
+ }
+ for (int y = byMin; y < byMax; ++y) {
+ SplashColorPtr dataPtr = bitmap->getDataPtr()
+ + y * bitmap->getRowSize() + bxMin * nComps;
+ Guchar *alphaPtr = bitmap->getAlphaPtr()
+ + y * bitmap->getAlphaRowSize() + bxMin;
+ for (int x = bxMin; x < bxMax; ++x) {
+
+ // convert coords to user space
+ double tx = xMin + x + 0.5;
+ double ty = yMin + y + 0.5;
+ double xx = tx * ictm[0] + ty * ictm[2] + ictm[4];
+ double yy = tx * ictm[1] + ty * ictm[3] + ictm[5];
+
+ // compute the radius of the circle at x,y
+ double b = 2 * ((xx - x0) * dx + (yy - y0) * dy + r0dr);
+ double c = (xx - x0) * (xx - x0) + (yy - y0) * (yy - y0) - r02;
+ double s = 0;
+ GBool go = gFalse;
+ if (aIsZero) {
+ if (fabs(b) < 0.000001) {
+ if (c <= 0) {
+ if (ext0) {
+ s = 0;
+ go = gTrue;
+ }
+ } else {
+ if (ext1) {
+ s = 1;
+ go = gTrue;
+ }
+ }
+ } else {
+ double s0 = c / b;
+ double rs0 = r0 + s0 * (r1 - r0);
+ if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) {
+ s = s0;
+ go = gTrue;
+ }
+ }
+ } else {
+ double e = b*b - 4*a*c;
+ if (e >= 0) {
+ double es = sqrt(e);
+ double s0 = (b + es) * a2;
+ double s1 = (b - es) * a2;
+ double rs0 = r0 + s0 * (r1 - r0);
+ double rs1 = r0 + s1 * (r1 - r0);
+ if (s0 > s1) {
+ if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) {
+ s = s0;
+ go = gTrue;
+ } else if ((s1 >= 0 || ext0) && (s1 <= 1 || ext1) && rs1 >= 0) {
+ s = s1;
+ go = gTrue;
+ }
+ } else {
+ if ((s1 >= 0 || ext0) && (s1 <= 1 || ext1) && rs1 >= 0) {
+ s = s1;
+ go = gTrue;
+ } else if ((s0 >= 0 || ext0) && (s0 <= 1 || ext1) && rs0 >= 0) {
+ s = s0;
+ go = gTrue;
+ }
+ }
+ }
+ }
+ if (!go) {
+ dataPtr += nComps;
+ *alphaPtr++ = 0x00;
+ continue;
+ }
+ if (s <= 0) {
+ sColor = sColors;
+ } else if (s >= 1) {
+ sColor = sColors + (nColors - 1) * nComps;
+ } else {
+ int i = (int)((nColors - 1) * s + 0.5);
+ sColor = sColors + i * nComps;
+ }
+ for (int i = 0; i < nComps; ++i) {
+ *dataPtr++ = sColor[i];
+ }
+ *alphaPtr++ = 0xff;
+ }
+ }
+
+ gfree(sColors);
+
+ *xOut = xMin;
+ *yOut = yMin;
+ return bitmap;
+}
+
+SplashBitmap *ShadingImage::generateGouraudTriangleBitmap(
+ GfxState *state,
+ GfxGouraudTriangleShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut) {
+ // get the clip bbox
+ double fxMin, fyMin, fxMax, fyMax;
+ state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax);
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // get the shading bbox
+ double tx0, ty0, tx1, ty1, dx, dy, txMin, tyMin, txMax, tyMax;
+ shading->getBBox(&tx0, &ty0, &tx1, &ty1);
+ state->transform(tx0, ty0, &dx, &dy);
+ txMin = txMax = dx;
+ tyMin = tyMax = dy;
+ state->transform(tx0, ty1, &dx, &dy);
+ if (dx < txMin) {
+ txMin = dx;
+ } else if (dx > txMax) {
+ txMax = dx;
+ }
+ if (dy < tyMin) {
+ tyMin = dy;
+ } else if (dy > tyMax) {
+ tyMax = dy;
+ }
+ state->transform(tx1, ty0, &dx, &dy);
+ if (dx < txMin) {
+ txMin = dx;
+ } else if (dx > txMax) {
+ txMax = dx;
+ }
+ if (dy < tyMin) {
+ tyMin = dy;
+ } else if (dy > tyMax) {
+ tyMax = dy;
+ }
+ state->transform(tx1, ty1, &dx, &dy);
+ if (dx < txMin) {
+ txMin = dx;
+ } else if (dx > txMax) {
+ txMax = dx;
+ }
+ if (dy < tyMin) {
+ tyMin = dy;
+ } else if (dy > tyMax) {
+ tyMax = dy;
+ }
+ if (txMin > fxMin) {
+ fxMin = txMin;
+ }
+ if (txMax < fxMax) {
+ fxMax = txMax;
+ }
+ if (tyMin > fyMin) {
+ fyMin = tyMin;
+ }
+ if (tyMax < fyMax) {
+ fyMax = tyMax;
+ }
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // convert to integer coords
+ int xMin = (int)floor(fxMin);
+ int yMin = (int)floor(fyMin);
+ int xMax = (int)floor(fxMax) + 1;
+ int yMax = (int)floor(fyMax) + 1;
+ int bitmapWidth = xMax - xMin;
+ int bitmapHeight = yMax - yMin;
+
+ // allocate the bitmap
+ traceMessage("Gouraud triangle shading fill bitmap");
+ SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode,
+ gTrue, gTrue, parentBitmap);
+
+ // clear the bitmap
+ memset(bitmap->getDataPtr(), 0, bitmap->getHeight() * bitmap->getRowSize());
+ memset(bitmap->getAlphaPtr(), 0, bitmap->getHeight() * bitmap->getWidth());
+
+ // draw the triangles
+ for (int i = 0; i < shading->getNTriangles(); ++i) {
+ double x0, y0, x1, y1, x2, y2;
+ double color0[gfxColorMaxComps];
+ double color1[gfxColorMaxComps];
+ double color2[gfxColorMaxComps];
+ shading->getTriangle(i, &x0, &y0, color0,
+ &x1, &y1, color1,
+ &x2, &y2, color2);
+ gouraudFillTriangle(state, bitmap, mode, reverseVideo,
+ xMin, yMin, xMax, yMax,
+ x0, y0, color0, x1, y1, color1, x2, y2, color2,
+ shading);
+ }
+
+ *xOut = xMin;
+ *yOut = yMin;
+ return bitmap;
+}
+
+void ShadingImage::gouraudFillTriangle(GfxState *state, SplashBitmap *bitmap,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ int xMin, int yMin, int xMax, int yMax,
+ double x0, double y0, double *color0,
+ double x1, double y1, double *color1,
+ double x2, double y2, double *color2,
+ GfxGouraudTriangleShading *shading) {
+ int nShadingComps = shading->getNComps();
+ int nBitmapComps = splashColorModeNComps[mode];
+
+ //--- transform the vertices to device space, sort by y
+ double dx0, dy0, dx1, dy1, dx2, dy2;
+ state->transform(x0, y0, &dx0, &dy0);
+ state->transform(x1, y1, &dx1, &dy1);
+ state->transform(x2, y2, &dx2, &dy2);
+ if (dy0 > dy1) {
+ double t = dx0; dx0 = dx1; dx1 = t;
+ t = dy0; dy0 = dy1; dy1 = t;
+ double *tc = color0; color0 = color1; color1 = tc;
+ }
+ if (dy1 > dy2) {
+ double t = dx1; dx1 = dx2; dx2 = t;
+ t = dy1; dy1 = dy2; dy2 = t;
+ double *tc = color1; color1 = color2; color2 = tc;
+ }
+ if (dy0 > dy1) {
+ double t = dx0; dx0 = dx1; dx1 = t;
+ t = dy0; dy0 = dy1; dy1 = t;
+ double *tc = color0; color0 = color1; color1 = tc;
+ }
+
+ //--- y loop
+ int syMin = (int)floor(dy0);
+ if (syMin < yMin) {
+ syMin = yMin;
+ }
+ int syMax = (int)floor(dy2) + 1;
+ if (syMax > yMax) {
+ syMax = yMax;
+ }
+ for (int sy = syMin; sy < syMax; ++sy) {
+
+ //--- vertical interpolation
+ double xx0, xx1;
+ double cc0[gfxColorMaxComps], cc1[gfxColorMaxComps];
+ if (sy <= dy0) {
+ xx0 = xx1 = dx0;
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc0[i] = cc1[i] = color0[i];
+ }
+ } else if (sy >= dy2) {
+ xx0 = xx1 = dx2;
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc0[i] = cc1[i] = color2[i];
+ }
+ } else {
+ if (sy <= dy1) {
+ double interp = (sy - dy0) / (dy1 - dy0);
+ xx0 = dx0 + interp * (dx1 - dx0);
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc0[i] = color0[i] + interp * (color1[i] - color0[i]);
+ }
+ } else {
+ double interp = (sy - dy1) / (dy2 - dy1);
+ xx0 = dx1 + interp * (dx2 - dx1);
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc0[i] = color1[i] + interp * (color2[i] - color1[i]);
+ }
+ }
+ double interp = (sy - dy0) / (dy2 - dy0);
+ xx1 = dx0 + interp * (dx2 - dx0);
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc1[i] = color0[i] + interp * (color2[i] - color0[i]);
+ }
+ }
+
+ //--- x loop
+ if (xx0 > xx1) {
+ double t = xx0; xx0 = xx1; xx1 = t;
+ for (int i = 0; i < nShadingComps; ++i) {
+ t = cc0[i]; cc0[i] = cc1[i]; cc1[i] = t;
+ }
+ }
+ int sxMin = (int)floor(xx0);
+ if (sxMin < xMin) {
+ sxMin = xMin;
+ }
+ int sxMax = (int)floor(xx1) + 1;
+ if (sxMax > xMax) {
+ sxMax = xMax;
+ }
+ SplashColorPtr dataPtr = bitmap->getDataPtr()
+ + (sy - yMin) * bitmap->getRowSize()
+ + (sxMin - xMin) * nBitmapComps;
+ if (sxMin < sxMax) {
+ Guchar *alphaPtr = bitmap->getAlphaPtr()
+ + (sy - yMin) * bitmap->getWidth()
+ + (sxMin - xMin);
+ memset(alphaPtr, 0xff, sxMax - sxMin);
+ }
+ for (int sx = sxMin; sx < sxMax; ++sx) {
+
+ //--- horizontal interpolation
+ double cc[gfxColorMaxComps];
+ if (sx <= xx0) {
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc[i] = cc0[i];
+ }
+ } else if (sx >= xx1) {
+ for (int i = 0; i < nShadingComps; ++i) {
+ cc[i] = cc1[i];
+ }
+ } else {
+ for (int i = 0; i < nShadingComps; ++i) {
+ double interp = (sx - xx0) / (xx1 - xx0);
+ cc[i] = cc0[i] + interp * (cc1[i] - cc0[i]);
+ }
+ }
+
+ //--- compute color and set pixel
+ GfxColor gColor;
+ shading->getColor(cc, &gColor);
+ SplashColor sColor;
+ computeShadingColor(state, mode, reverseVideo, &gColor, sColor);
+ for (int i = 0; i < nBitmapComps; ++i) {
+ dataPtr[i] = sColor[i];
+ }
+ dataPtr += nBitmapComps;
+ }
+ }
+}
+
+SplashBitmap *ShadingImage::generatePatchMeshBitmap(
+ GfxState *state,
+ GfxPatchMeshShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut) {
+ // get the clip bbox
+ double fxMin, fyMin, fxMax, fyMax;
+ state->getClipBBox(&fxMin, &fyMin, &fxMax, &fyMax);
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // get the shading bbox
+ double tx0, ty0, tx1, ty1, dx, dy, txMin, tyMin, txMax, tyMax;
+ shading->getBBox(&tx0, &ty0, &tx1, &ty1);
+ state->transform(tx0, ty0, &dx, &dy);
+ txMin = txMax = dx;
+ tyMin = tyMax = dy;
+ state->transform(tx0, ty1, &dx, &dy);
+ if (dx < txMin) {
+ txMin = dx;
+ } else if (dx > txMax) {
+ txMax = dx;
+ }
+ if (dy < tyMin) {
+ tyMin = dy;
+ } else if (dy > tyMax) {
+ tyMax = dy;
+ }
+ state->transform(tx1, ty0, &dx, &dy);
+ if (dx < txMin) {
+ txMin = dx;
+ } else if (dx > txMax) {
+ txMax = dx;
+ }
+ if (dy < tyMin) {
+ tyMin = dy;
+ } else if (dy > tyMax) {
+ tyMax = dy;
+ }
+ state->transform(tx1, ty1, &dx, &dy);
+ if (dx < txMin) {
+ txMin = dx;
+ } else if (dx > txMax) {
+ txMax = dx;
+ }
+ if (dy < tyMin) {
+ tyMin = dy;
+ } else if (dy > tyMax) {
+ tyMax = dy;
+ }
+ if (txMin > fxMin) {
+ fxMin = txMin;
+ }
+ if (txMax < fxMax) {
+ fxMax = txMax;
+ }
+ if (tyMin > fyMin) {
+ fyMin = tyMin;
+ }
+ if (tyMax < fyMax) {
+ fyMax = tyMax;
+ }
+ if (fxMin > fxMax || fyMin > fyMax) {
+ return NULL;
+ }
+
+ // convert to integer coords
+ int xMin = (int)floor(fxMin);
+ int yMin = (int)floor(fyMin);
+ int xMax = (int)floor(fxMax) + 1;
+ int yMax = (int)floor(fyMax) + 1;
+ int bitmapWidth = xMax - xMin;
+ int bitmapHeight = yMax - yMin;
+
+ // allocate the bitmap
+ traceMessage("Gouraud triangle shading fill bitmap");
+ SplashBitmap *bitmap = new SplashBitmap(bitmapWidth, bitmapHeight, 1, mode,
+ gTrue, gTrue, parentBitmap);
+
+ // allocate a Splash object
+ // vector antialiasing is disabled to avoid artifacts along triangle edges
+ Splash *splash = new Splash(bitmap, gFalse,
+ parentSplash->getImageCache(),
+ parentSplash->getScreen());
+ SplashColor zero;
+ for (int i = 0; i < splashColorModeNComps[mode]; ++i) {
+ zero[i] = 0;
+ }
+ splash->clear(zero, 0x00);
+
+ // draw the patches
+ int start;
+ if (shading->getNPatches() > 128) {
+ start = 3;
+ } else if (shading->getNPatches() > 64) {
+ start = 2;
+ } else if (shading->getNPatches() > 16) {
+ start = 1;
+ } else {
+ start = 0;
+ }
+ for (int i = 0; i < shading->getNPatches(); ++i) {
+ fillPatch(state, splash, mode, reverseVideo,
+ xMin, yMin, shading->getPatch(i), shading, start);
+ }
+
+ delete splash;
+
+ *xOut = xMin;
+ *yOut = yMin;
+ return bitmap;
+}
+
+void ShadingImage::fillPatch(GfxState *state, Splash *splash,
+ SplashColorMode mode, GBool reverseVideo,
+ int xMin, int yMin,
+ GfxPatch *patch,
+ GfxPatchMeshShading *shading,
+ int depth) {
+ GfxColor c00;
+ shading->getColor(patch->color[0][0], &c00);
+ GBool stop = gFalse;
+
+ // stop subdivision at max depth
+ if (depth == patchMaxDepth) {
+ stop = gTrue;
+ }
+
+ // stop subdivision if colors are close enough
+ if (!stop) {
+ int nComps = shading->getColorSpace()->getNComps();
+ GfxColor c01, c10, c11;
+ shading->getColor(patch->color[0][1], &c01);
+ shading->getColor(patch->color[1][0], &c10);
+ shading->getColor(patch->color[1][1], &c11);
+ int i;
+ for (i = 0; i < nComps; ++i) {
+ if (abs(c00.c[i] - c01.c[i]) > patchColorDelta ||
+ abs(c01.c[i] - c11.c[i]) > patchColorDelta ||
+ abs(c11.c[i] - c10.c[i]) > patchColorDelta ||
+ abs(c10.c[i] - c00.c[i]) > patchColorDelta) {
+ break;
+ }
+ }
+ if (i == nComps) {
+ stop = gTrue;
+ }
+ }
+
+ // stop subdivision if patch is small enough
+ if (!stop) {
+ double xxMin = 0;
+ double yyMin = 0;
+ double xxMax = 0;
+ double yyMax = 0;
+ for (int j = 0; j < 4; ++j) {
+ for (int i = 0; i < 4; ++i) {
+ double xx, yy;
+ state->transformDelta(patch->x[i][j], patch->y[i][j], &xx, &yy);
+ if (i == 0 && j == 0) {
+ xxMin = xxMax = xx;
+ yyMin = yyMax = yy;
+ } else {
+ if (xx < xxMin) {
+ xxMin = xx;
+ } else if (xx > xxMax) {
+ xxMax = xx;
+ }
+ if (yy < yyMin) {
+ yyMin = yy;
+ } else if (yy > yyMax) {
+ yyMax = yy;
+ }
+ }
+ }
+ }
+ if (xxMax - xxMin < 1 && yyMax - yyMin < 1) {
+ stop = gTrue;
+ }
+ }
+
+ // draw the patch
+ if (stop) {
+ SplashColor sColor;
+ computeShadingColor(state, mode, reverseVideo, &c00, sColor);
+ splash->setFillPattern(new SplashSolidColor(sColor));
+ SplashPath *path = new SplashPath();
+ double xx0, yy0, xx1, yy1, xx2, yy2, xx3, yy3;
+ state->transform(patch->x[0][0], patch->y[0][0], &xx0, &yy0);
+ path->moveTo(xx0 - xMin, yy0 - yMin);
+ state->transform(patch->x[0][1], patch->y[0][1], &xx1, &yy1);
+ state->transform(patch->x[0][2], patch->y[0][2], &xx2, &yy2);
+ state->transform(patch->x[0][3], patch->y[0][3], &xx3, &yy3);
+ path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin,
+ xx3 - xMin, yy3 - yMin);
+ state->transform(patch->x[1][3], patch->y[1][3], &xx1, &yy1);
+ state->transform(patch->x[2][3], patch->y[2][3], &xx2, &yy2);
+ state->transform(patch->x[3][3], patch->y[3][3], &xx3, &yy3);
+ path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin,
+ xx3 - xMin, yy3 - yMin);
+ state->transform(patch->x[3][2], patch->y[3][2], &xx1, &yy1);
+ state->transform(patch->x[3][1], patch->y[3][1], &xx2, &yy2);
+ state->transform(patch->x[3][0], patch->y[3][0], &xx3, &yy3);
+ path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin,
+ xx3 - xMin, yy3 - yMin);
+ state->transform(patch->x[2][0], patch->y[2][0], &xx1, &yy1);
+ state->transform(patch->x[1][0], patch->y[1][0], &xx2, &yy2);
+ path->curveTo(xx1 - xMin, yy1 - yMin, xx2 - xMin, yy2 - yMin,
+ xx0 - xMin, yy0 - yMin);
+ path->close();
+ splash->fill(path, gFalse);
+ delete path;
+
+ // subdivide the patch
+ } else {
+ double xx[4][8], yy[4][8];
+ for (int i = 0; i < 4; ++i) {
+ xx[i][0] = patch->x[i][0];
+ yy[i][0] = patch->y[i][0];
+ xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
+ yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
+ double xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
+ double yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
+ xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
+ yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
+ xx[i][2] = 0.5 * (xx[i][1] + xxm);
+ yy[i][2] = 0.5 * (yy[i][1] + yym);
+ xx[i][5] = 0.5 * (xxm + xx[i][6]);
+ yy[i][5] = 0.5 * (yym + yy[i][6]);
+ xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
+ yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
+ xx[i][7] = patch->x[i][3];
+ yy[i][7] = patch->y[i][3];
+ }
+ GfxPatch patch00, patch01, patch10, patch11;
+ for (int i = 0; i < 4; ++i) {
+ patch00.x[0][i] = xx[0][i];
+ patch00.y[0][i] = yy[0][i];
+ patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
+ patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
+ double xxm = 0.5 * (xx[1][i] + xx[2][i]);
+ double yym = 0.5 * (yy[1][i] + yy[2][i]);
+ patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
+ patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
+ patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
+ patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
+ patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
+ patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
+ patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
+ patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
+ patch10.x[0][i] = patch00.x[3][i];
+ patch10.y[0][i] = patch00.y[3][i];
+ patch10.x[3][i] = xx[3][i];
+ patch10.y[3][i] = yy[3][i];
+ }
+ for (int i = 4; i < 8; ++i) {
+ patch01.x[0][i-4] = xx[0][i];
+ patch01.y[0][i-4] = yy[0][i];
+ patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
+ patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
+ double xxm = 0.5 * (xx[1][i] + xx[2][i]);
+ double yym = 0.5 * (yy[1][i] + yy[2][i]);
+ patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
+ patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
+ patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
+ patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
+ patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
+ patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
+ patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
+ patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
+ patch11.x[0][i-4] = patch01.x[3][i-4];
+ patch11.y[0][i-4] = patch01.y[3][i-4];
+ patch11.x[3][i-4] = xx[3][i];
+ patch11.y[3][i-4] = yy[3][i];
+ }
+ for (int i = 0; i < shading->getNComps(); ++i) {
+ patch00.color[0][0][i] = patch->color[0][0][i];
+ patch00.color[0][1][i] = 0.5 * (patch->color[0][0][i] +
+ patch->color[0][1][i]);
+ patch01.color[0][0][i] = patch00.color[0][1][i];
+ patch01.color[0][1][i] = patch->color[0][1][i];
+ patch01.color[1][1][i] = 0.5 * (patch->color[0][1][i] +
+ patch->color[1][1][i]);
+ patch11.color[0][1][i] = patch01.color[1][1][i];
+ patch11.color[1][1][i] = patch->color[1][1][i];
+ patch11.color[1][0][i] = 0.5 * (patch->color[1][1][i] +
+ patch->color[1][0][i]);
+ patch10.color[1][1][i] = patch11.color[1][0][i];
+ patch10.color[1][0][i] = patch->color[1][0][i];
+ patch10.color[0][0][i] = 0.5 * (patch->color[1][0][i] +
+ patch->color[0][0][i]);
+ patch00.color[1][0][i] = patch10.color[0][0][i];
+ patch00.color[1][1][i] = 0.5 * (patch00.color[1][0][i] +
+ patch01.color[1][1][i]);
+ patch01.color[1][0][i] = patch00.color[1][1][i];
+ patch11.color[0][0][i] = patch00.color[1][1][i];
+ patch10.color[0][1][i] = patch00.color[1][1][i];
+ }
+ fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch00,
+ shading, depth + 1);
+ fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch10,
+ shading, depth + 1);
+ fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch01,
+ shading, depth + 1);
+ fillPatch(state, splash, mode, reverseVideo, xMin, yMin, &patch11,
+ shading, depth + 1);
+ }
+}
+
+void ShadingImage::computeShadingColor(GfxState *state,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ GfxColor *color,
+ SplashColorPtr sColor) {
+ GfxGray gray;
+ GfxRGB rgb;
+#if SPLASH_CMYK
+ GfxCMYK cmyk;
+#endif
+
+ state->setFillColor(color);
+ switch (mode) {
+ case splashModeMono8:
+ state->getFillGray(&gray);
+ if (reverseVideo) {
+ gray = gfxColorComp1 - gray;
+ }
+ sColor[0] = colToByte(gray);
+ break;
+ case splashModeRGB8:
+ state->getFillRGB(&rgb);
+ if (reverseVideo) {
+ rgb.r = gfxColorComp1 - rgb.r;
+ rgb.g = gfxColorComp1 - rgb.g;
+ rgb.b = gfxColorComp1 - rgb.b;
+ }
+ sColor[0] = colToByte(rgb.r);
+ sColor[1] = colToByte(rgb.g);
+ sColor[2] = colToByte(rgb.b);
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ state->getFillCMYK(&cmyk);
+ sColor[0] = colToByte(cmyk.c);
+ sColor[1] = colToByte(cmyk.m);
+ sColor[2] = colToByte(cmyk.y);
+ sColor[3] = colToByte(cmyk.k);
+ break;
+#endif
+ case splashModeMono1:
+ case splashModeBGR8:
+ // mode cannot be Mono1 or BGR8
+ break;
+ }
+}
+
+// Transform a user space bbox to a device space bbox.
+void ShadingImage::transformBBox(GfxState *state,
+ double uxMin, double uyMin,
+ double uxMax, double uyMax,
+ double *dxMin, double *dyMin,
+ double *dxMax, double *dyMax) {
+ double tx, ty;
+ state->transform(uxMin, uyMin, &tx, &ty);
+ *dxMin = *dxMax = tx;
+ *dyMin = *dyMax = ty;
+ state->transform(uxMin, uyMax, &tx, &ty);
+ if (tx < *dxMin) {
+ *dxMin = tx;
+ } else if (tx > *dxMax) {
+ *dxMax = tx;
+ }
+ if (ty < *dyMin) {
+ *dyMin = ty;
+ } else if (ty > *dyMax) {
+ *dyMax = ty;
+ }
+ state->transform(uxMax, uyMin, &tx, &ty);
+ if (tx < *dxMin) {
+ *dxMin = tx;
+ } else if (tx > *dxMax) {
+ *dxMax = tx;
+ }
+ if (ty < *dyMin) {
+ *dyMin = ty;
+ } else if (ty > *dyMax) {
+ *dyMax = ty;
+ }
+ state->transform(uxMax, uyMax, &tx, &ty);
+ if (tx < *dxMin) {
+ *dxMin = tx;
+ } else if (tx > *dxMax) {
+ *dxMax = tx;
+ }
+ if (ty < *dyMin) {
+ *dyMin = ty;
+ } else if (ty > *dyMax) {
+ *dyMax = ty;
+ }
+}
+
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h b/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h
new file mode 100644
index 00000000000..3bbff6922a4
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/ShadingImage.h
@@ -0,0 +1,106 @@
+//========================================================================
+//
+// ShadingImage.h
+//
+// Convert shading patterns to bitmaps.
+//
+// Copyright 2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SHADINGIMAGE_H
+#define SHADINGIMAGE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+class GfxState;
+class GfxShading;
+class SplashBitmap;
+class Splash;
+
+class ShadingImage {
+public:
+
+ // Generate a [mode] bitmap for [shading], using the clipping and
+ // CTM info from [state]. Returns the bitmap and sets [xOut],[yOut]
+ // to the upper-left corner of the region in device space. Returns
+ // NULL if the clip region is empty.
+ static SplashBitmap *generateBitmap(GfxState *state, GfxShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut);
+
+
+private:
+
+ static SplashBitmap *generateFunctionBitmap(GfxState *state,
+ GfxFunctionShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut);
+ static SplashBitmap *generateAxialBitmap(GfxState *state,
+ GfxAxialShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut);
+ static SplashBitmap *generateRadialBitmap(GfxState *state,
+ GfxRadialShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut);
+ static SplashBitmap *generateGouraudTriangleBitmap(
+ GfxState *state,
+ GfxGouraudTriangleShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut);
+ static void gouraudFillTriangle(GfxState *state, SplashBitmap *bitmap,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ int xMin, int yMin, int xMax, int yMax,
+ double x0, double y0, double *color0,
+ double x1, double y1, double *color1,
+ double x2, double y2, double *color2,
+ GfxGouraudTriangleShading *shading);
+ static SplashBitmap *generatePatchMeshBitmap(GfxState *state,
+ GfxPatchMeshShading *shading,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ Splash *parentSplash,
+ SplashBitmap *parentBitmap,
+ int *xOut, int *yOut);
+ static void fillPatch(GfxState *state, Splash *splash,
+ SplashColorMode mode, GBool reverseVideo,
+ int xMin, int yMin,
+ GfxPatch *patch,
+ GfxPatchMeshShading *shading,
+ int depth);
+ static void computeShadingColor(GfxState *state,
+ SplashColorMode mode,
+ GBool reverseVideo,
+ GfxColor *color,
+ SplashColorPtr sColor);
+ static void transformBBox(GfxState *state,
+ double uxMin, double uyMin,
+ double uxMax, double uyMax,
+ double *dxMin, double *dyMin,
+ double *dxMax, double *dyMax);
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc
new file mode 100644
index 00000000000..89c7c02a7b6
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.cc
@@ -0,0 +1,4497 @@
+//========================================================================
+//
+// SplashOutputDev.cc
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include <math.h>
+#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"
+#include "BuiltinFont.h"
+#include "BuiltinFontTables.h"
+#include "FoFiTrueType.h"
+#include "FoFiType1C.h"
+#include "JPXStream.h"
+#include "SplashBitmap.h"
+#include "SplashGlyphBitmap.h"
+#include "SplashPattern.h"
+#include "SplashScreen.h"
+#include "SplashPath.h"
+#include "SplashState.h"
+#include "SplashErrorCodes.h"
+#include "SplashFontEngine.h"
+#include "SplashFont.h"
+#include "SplashFontFile.h"
+#include "SplashFontFileID.h"
+#include "Splash.h"
+#include "SplashOutputDev.h"
+
+#ifdef VMS
+#if (__VMS_VER < 70000000)
+extern "C" int unlink(char *filename);
+#endif
+#endif
+
+//------------------------------------------------------------------------
+
+// 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
+#define type3FontCacheSize (128*1024)
+
+// Map StrokeAdjustMode (from GlobalParams) to SplashStrokeAdjustMode
+// (for Splash).
+static SplashStrokeAdjustMode mapStrokeAdjustMode[3] = {
+ splashStrokeAdjustOff,
+ splashStrokeAdjustNormal,
+ splashStrokeAdjustCAD
+};
+
+//------------------------------------------------------------------------
+
+// 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;
+}
+
+
+//------------------------------------------------------------------------
+// Blend functions
+//------------------------------------------------------------------------
+
+static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend, SplashColorMode cm) {
+ int i;
+
+ for (i = 0; i < splashColorModeNComps[cm]; ++i) {
+ blend[i] = (Guchar)((dest[i] * src[i]) / 255);
+ }
+}
+
+static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend, SplashColorMode cm) {
+ int i;
+
+ for (i = 0; i < splashColorModeNComps[cm]; ++i) {
+ blend[i] = (Guchar)(dest[i] + src[i] - (dest[i] * src[i]) / 255);
+ }
+}
+
+// note: this is the same as HardLight, with src/dest reversed
+static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend, SplashColorMode cm) {
+ int i;
+
+ for (i = 0; i < splashColorModeNComps[cm]; ++i) {
+ // the spec says "if Cs <= 0.5" -- note that 0x80 is 128/255=0.5020
+ blend[i] = dest[i] < 0x80
+ ? (Guchar)((src[i] * 2 * dest[i]) / 255)
+ : (Guchar)(255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255);
+ }
+}
+
+static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend, SplashColorMode cm) {
+ int i;
+
+ for (i = 0; i < splashColorModeNComps[cm]; ++i) {
+ blend[i] = dest[i] < src[i] ? dest[i] : src[i];
+ }
+}
+
+static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend, SplashColorMode cm) {
+ int i;
+
+ for (i = 0; i < splashColorModeNComps[cm]; ++i) {
+ blend[i] = dest[i] > src[i] ? dest[i] : src[i];
+ }
+}
+
+static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend,
+ SplashColorMode cm) {
+ int i;
+
+ for (i = 0; i < splashColorModeNComps[cm]; ++i) {
+ if (dest[i] == 0) {
+ blend[i] = 0;
+ } else if (dest[i] >= 255 - src[i]) {
+ blend[i] = 255;
+ } else {
+ blend[i] = (Guchar)((dest[i] * 255) / (255 - src[i]));
+ }
+ }
+}
+
+static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend, SplashColorMode cm) {
+ int i;
+
+ for (i = 0; i < splashColorModeNComps[cm]; ++i) {
+ if (dest[i] == 255) {
+ blend[i] = 255;
+ } else if (255 - dest[i] >= src[i]) {
+ blend[i] = 0;
+ } else {
+ blend[i] = (Guchar)(255 - (((255 - dest[i]) * 255) / src[i]));
+ }
+ }
+}
+
+static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend, SplashColorMode cm) {
+ int i;
+
+ for (i = 0; i < splashColorModeNComps[cm]; ++i) {
+ // the spec says "if Cs <= 0.5" -- note that 0x80 is 128/255=0.5020
+ blend[i] = src[i] < 0x80
+ ? (Guchar)((dest[i] * 2 * src[i]) / 255)
+ : (Guchar)(255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255);
+ }
+}
+
+static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend, SplashColorMode cm) {
+ int i, x;
+
+ for (i = 0; i < splashColorModeNComps[cm]; ++i) {
+ // the spec says "if Cs <= 0.5" -- note that 0x80 is 128/255=0.5020
+ if (src[i] < 0x80) {
+ blend[i] = (Guchar)(dest[i] -
+ (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) /
+ (255 * 255));
+ } else {
+ // the spec says "if Cb <= 0.25" -- note that 0x40 is 64/255=0.2510
+ if (dest[i] < 0x40) {
+ x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255)
+ + 4 * 255) * dest[i]) / 255;
+ } else {
+ x = (int)sqrt(255.0 * dest[i]);
+ }
+ blend[i] = (Guchar)(dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255);
+ }
+ }
+}
+
+static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend,
+ SplashColorMode cm) {
+ int i;
+
+ for (i = 0; i < splashColorModeNComps[cm]; ++i) {
+ blend[i] = dest[i] < src[i] ? (Guchar)(src[i] - dest[i])
+ : (Guchar)(dest[i] - src[i]);
+ }
+}
+
+static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend, SplashColorMode cm) {
+ int i;
+
+ for (i = 0; i < splashColorModeNComps[cm]; ++i) {
+ blend[i] = (Guchar)(dest[i] + src[i] - (2 * dest[i] * src[i]) / 255);
+ }
+}
+
+static int getLum(int r, int g, int b) {
+ return (int)(0.3 * r + 0.59 * g + 0.11 * b);
+}
+
+static int getSat(int r, int g, int b) {
+ int rgbMin, rgbMax;
+
+ rgbMin = rgbMax = r;
+ if (g < rgbMin) {
+ rgbMin = g;
+ } else if (g > rgbMax) {
+ rgbMax = g;
+ }
+ if (b < rgbMin) {
+ rgbMin = b;
+ } else if (b > rgbMax) {
+ rgbMax = b;
+ }
+ return rgbMax - rgbMin;
+}
+
+static void clipColor(int rIn, int gIn, int bIn,
+ Guchar *rOut, Guchar *gOut, Guchar *bOut) {
+ int lum, rgbMin, rgbMax, r, g, b;
+
+ lum = getLum(rIn, gIn, bIn);
+ rgbMin = rgbMax = rIn;
+ if (gIn < rgbMin) {
+ rgbMin = gIn;
+ } else if (gIn > rgbMax) {
+ rgbMax = gIn;
+ }
+ if (bIn < rgbMin) {
+ rgbMin = bIn;
+ } else if (bIn > rgbMax) {
+ rgbMax = bIn;
+ }
+ r = rIn;
+ g = gIn;
+ b = bIn;
+ if (rgbMin < 0) {
+ r = lum + ((r - lum) * lum) / (lum - rgbMin);
+ g = lum + ((g - lum) * lum) / (lum - rgbMin);
+ b = lum + ((b - lum) * lum) / (lum - rgbMin);
+ }
+ if (rgbMax > 255) {
+ r = lum + ((r - lum) * (255 - lum)) / (rgbMax - lum);
+ g = lum + ((g - lum) * (255 - lum)) / (rgbMax - lum);
+ b = lum + ((b - lum) * (255 - lum)) / (rgbMax - lum);
+ }
+ *rOut = (Guchar)r;
+ *gOut = (Guchar)g;
+ *bOut = (Guchar)b;
+}
+
+static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum,
+ Guchar *rOut, Guchar *gOut, Guchar *bOut) {
+ int d;
+
+ d = lum - getLum(rIn, gIn, bIn);
+ clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut);
+}
+
+static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat,
+ Guchar *rOut, Guchar *gOut, Guchar *bOut) {
+ int rgbMin, rgbMid, rgbMax;
+ Guchar *minOut, *midOut, *maxOut;
+
+ if (rIn < gIn) {
+ rgbMin = rIn; minOut = rOut;
+ rgbMid = gIn; midOut = gOut;
+ } else {
+ rgbMin = gIn; minOut = gOut;
+ rgbMid = rIn; midOut = rOut;
+ }
+ if (bIn > rgbMid) {
+ rgbMax = bIn; maxOut = bOut;
+ } else if (bIn > rgbMin) {
+ rgbMax = rgbMid; maxOut = midOut;
+ rgbMid = bIn; midOut = bOut;
+ } else {
+ rgbMax = rgbMid; maxOut = midOut;
+ rgbMid = rgbMin; midOut = minOut;
+ rgbMin = bIn; minOut = bOut;
+ }
+ if (rgbMax > rgbMin) {
+ *midOut = (Guchar)(((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin));
+ *maxOut = (Guchar)sat;
+ } else {
+ *midOut = *maxOut = 0;
+ }
+ *minOut = 0;
+}
+
+static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend, SplashColorMode cm) {
+ Guchar r0, g0, b0;
+
+ switch (cm) {
+ case splashModeMono1:
+ case splashModeMono8:
+ blend[0] = dest[0];
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
+ &r0, &g0, &b0);
+ setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
+ &blend[0], &blend[1], &blend[2]);
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ // NB: inputs have already been converted to additive mode
+ setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
+ &r0, &g0, &b0);
+ setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
+ &blend[0], &blend[1], &blend[2]);
+ blend[3] = dest[3];
+ break;
+#endif
+ }
+}
+
+static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend,
+ SplashColorMode cm) {
+ Guchar r0, g0, b0;
+
+ switch (cm) {
+ case splashModeMono1:
+ case splashModeMono8:
+ blend[0] = dest[0];
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
+ &r0, &g0, &b0);
+ setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
+ &blend[0], &blend[1], &blend[2]);
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ // NB: inputs have already been converted to additive mode
+ setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
+ &r0, &g0, &b0);
+ setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
+ &blend[0], &blend[1], &blend[2]);
+ blend[3] = dest[3];
+ break;
+#endif
+ }
+}
+
+static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend, SplashColorMode cm) {
+
+ switch (cm) {
+ case splashModeMono1:
+ case splashModeMono8:
+ blend[0] = dest[0];
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
+ &blend[0], &blend[1], &blend[2]);
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ // NB: inputs have already been converted to additive mode
+ setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
+ &blend[0], &blend[1], &blend[2]);
+ blend[3] = dest[3];
+ break;
+#endif
+ }
+}
+
+static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
+ SplashColorPtr blend,
+ SplashColorMode cm) {
+
+ switch (cm) {
+ case splashModeMono1:
+ case splashModeMono8:
+ blend[0] = dest[0];
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
+ &blend[0], &blend[1], &blend[2]);
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ // NB: inputs have already been converted to additive mode
+ setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
+ &blend[0], &blend[1], &blend[2]);
+ blend[3] = src[3];
+ break;
+#endif
+ }
+}
+
+// NB: This must match the GfxBlendMode enum defined in GfxState.h.
+SplashBlendFunc splashOutBlendFuncs[] = {
+ NULL,
+ &splashOutBlendMultiply,
+ &splashOutBlendScreen,
+ &splashOutBlendOverlay,
+ &splashOutBlendDarken,
+ &splashOutBlendLighten,
+ &splashOutBlendColorDodge,
+ &splashOutBlendColorBurn,
+ &splashOutBlendHardLight,
+ &splashOutBlendSoftLight,
+ &splashOutBlendDifference,
+ &splashOutBlendExclusion,
+ &splashOutBlendHue,
+ &splashOutBlendSaturation,
+ &splashOutBlendColor,
+ &splashOutBlendLuminosity
+};
+
+
+//------------------------------------------------------------------------
+// SplashOutFontFileID
+//------------------------------------------------------------------------
+
+class SplashOutFontFileID: public SplashFontFileID {
+public:
+
+ SplashOutFontFileID(Ref *rA) {
+ r = *rA;
+ substIdx = -1;
+ oblique = 0;
+ }
+
+ ~SplashOutFontFileID() {}
+
+ GBool matches(SplashFontFileID *id) {
+ return ((SplashOutFontFileID *)id)->r.num == r.num &&
+ ((SplashOutFontFileID *)id)->r.gen == r.gen;
+ }
+
+ void setOblique(double obliqueA) { oblique = obliqueA; }
+ double getOblique() { return oblique; }
+ void setSubstIdx(int substIdxA) { substIdx = substIdxA; }
+ int getSubstIdx() { return substIdx; }
+
+private:
+
+ Ref r;
+ double oblique;
+ int substIdx;
+};
+
+//------------------------------------------------------------------------
+// T3FontCache
+//------------------------------------------------------------------------
+
+struct T3FontCacheTag {
+ Gushort code;
+ Gushort mru; // valid bit (0x8000) and MRU index
+};
+
+class T3FontCache {
+public:
+
+ T3FontCache(Ref *fontID, double m11A, double m12A,
+ double m21A, double m22A,
+ int glyphXA, int glyphYA, int glyphWA, int glyphHA,
+ GBool validBBoxA, GBool aa);
+ ~T3FontCache();
+ GBool matches(Ref *idA, double m11A, double m12A,
+ double m21A, double m22A)
+ { return fontID.num == idA->num && fontID.gen == idA->gen &&
+ m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
+
+ Ref fontID; // PDF font ID
+ double m11, m12, m21, m22; // transform matrix
+ int glyphX, glyphY; // pixel offset of glyph bitmaps
+ int glyphW, glyphH; // size of glyph bitmaps, in pixels
+ GBool validBBox; // false if the bbox was [0 0 0 0]
+ int glyphSize; // size of glyph bitmaps, in bytes
+ int cacheSets; // number of sets in cache
+ int cacheAssoc; // cache associativity (glyphs per set)
+ Guchar *cacheData; // glyph pixmap cache
+ T3FontCacheTag *cacheTags; // cache tags, i.e., char codes
+ int refCount; // active reference count for this T3 font
+};
+
+T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
+ double m21A, double m22A,
+ int glyphXA, int glyphYA, int glyphWA, int glyphHA,
+ GBool validBBoxA, GBool aa) {
+ int i;
+
+ fontID = *fontIDA;
+ m11 = m11A;
+ m12 = m12A;
+ m21 = m21A;
+ m22 = m22A;
+ glyphX = glyphXA;
+ glyphY = glyphYA;
+ glyphW = glyphWA;
+ glyphH = glyphHA;
+ validBBox = validBBoxA;
+ // sanity check for excessively large glyphs (which most likely
+ // indicate an incorrect BBox)
+ i = glyphW * glyphH;
+ if (i > 100000 || glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0) {
+ glyphW = glyphH = 100;
+ validBBox = gFalse;
+ }
+ if (aa) {
+ glyphSize = glyphW * glyphH;
+ } else {
+ glyphSize = ((glyphW + 7) >> 3) * glyphH;
+ }
+ cacheAssoc = type3FontCacheAssoc;
+ for (cacheSets = type3FontCacheMaxSets;
+ cacheSets > 1 &&
+ cacheSets * cacheAssoc * glyphSize > type3FontCacheSize;
+ cacheSets >>= 1) ;
+ cacheData = (Guchar *)gmallocn(cacheSets * cacheAssoc, glyphSize);
+ cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc,
+ sizeof(T3FontCacheTag));
+ for (i = 0; i < cacheSets * cacheAssoc; ++i) {
+ cacheTags[i].mru = (Gushort)(i & (cacheAssoc - 1));
+ }
+ refCount = 0;
+}
+
+T3FontCache::~T3FontCache() {
+ gfree(cacheData);
+ gfree(cacheTags);
+}
+
+struct T3GlyphStack {
+ Gushort code; // character code
+
+ GBool haveDx; // set after seeing a d0/d1 operator
+ GBool doNotCache; // set if we see a gsave/grestore before
+ // the d0/d1
+
+ //----- cache info
+ T3FontCache *cache; // font cache for the current font
+ T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph
+ Guchar *cacheData; // pointer to cache data for the glyph
+
+ //----- saved state
+ SplashBitmap *origBitmap;
+ Splash *origSplash;
+ double origCTM4, origCTM5;
+ SplashStrokeAdjustMode savedStrokeAdjust;
+
+ T3GlyphStack *next; // next object on stack
+};
+
+//------------------------------------------------------------------------
+// SplashTransparencyGroup
+//------------------------------------------------------------------------
+
+struct SplashTransparencyGroup {
+ int tx, ty; // translation coordinates
+ SplashBitmap *tBitmap; // bitmap for transparency group
+ GfxColorSpace *blendingColorSpace;
+ GBool isolated;
+
+ //----- modified region in tBitmap
+ int modXMin, modYMin, modXMax, modYMax;
+
+ //----- saved state
+ SplashBitmap *origBitmap;
+ Splash *origSplash;
+ SplashBitmap *backdropBitmap;
+
+ SplashTransparencyGroup *next;
+};
+
+//------------------------------------------------------------------------
+// SplashOutputDev
+//------------------------------------------------------------------------
+
+SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
+ int bitmapRowPadA,
+ GBool reverseVideoA,
+ SplashColorPtr paperColorA,
+ GBool bitmapTopDownA,
+ GBool allowAntialiasA) {
+ colorMode = colorModeA;
+ bitmapRowPad = bitmapRowPadA;
+ bitmapTopDown = bitmapTopDownA;
+ bitmapUpsideDown = gFalse;
+ noComposite = gFalse;
+ allowAntialias = allowAntialiasA;
+ vectorAntialias = allowAntialias &&
+ globalParams->getVectorAntialias() &&
+ colorMode != splashModeMono1;
+ setupScreenParams(72.0, 72.0);
+ reverseVideo = reverseVideoA;
+ splashColorCopy(paperColor, paperColorA);
+ skipHorizText = gFalse;
+ skipRotatedText = gFalse;
+
+ xref = NULL;
+
+ bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
+ colorMode != splashModeMono1, bitmapTopDown, NULL);
+ splash = new Splash(bitmap, vectorAntialias, NULL, &screenParams);
+ splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(
+ mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
+ splash->setEnablePathSimplification(
+ globalParams->getEnablePathSimplification());
+ splash->clear(paperColor, 0);
+
+ fontEngine = NULL;
+
+ nT3Fonts = 0;
+ t3GlyphStack = NULL;
+
+ font = NULL;
+ needFontUpdate = gFalse;
+ textClipPath = NULL;
+
+ transpGroupStack = NULL;
+
+ nestCount = 0;
+
+ startPageCbk = NULL;
+ startPageCbkData = NULL;
+}
+
+
+void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) {
+ screenParams.size = globalParams->getScreenSize();
+ screenParams.dotRadius = globalParams->getScreenDotRadius();
+ screenParams.gamma = (SplashCoord)globalParams->getScreenGamma();
+ screenParams.blackThreshold =
+ (SplashCoord)globalParams->getScreenBlackThreshold();
+ screenParams.whiteThreshold =
+ (SplashCoord)globalParams->getScreenWhiteThreshold();
+ switch (globalParams->getScreenType()) {
+ case screenDispersed:
+ screenParams.type = splashScreenDispersed;
+ if (screenParams.size < 0) {
+ screenParams.size = 4;
+ }
+ break;
+ case screenClustered:
+ screenParams.type = splashScreenClustered;
+ if (screenParams.size < 0) {
+ screenParams.size = 10;
+ }
+ break;
+ case screenStochasticClustered:
+ screenParams.type = splashScreenStochasticClustered;
+ if (screenParams.size < 0) {
+ screenParams.size = 64;
+ }
+ if (screenParams.dotRadius < 0) {
+ screenParams.dotRadius = 2;
+ }
+ break;
+ case screenUnset:
+ default:
+ // use clustered dithering for resolution >= 300 dpi
+ // (compare to 299.9 to avoid floating point issues)
+ if (hDPI > 299.9 && vDPI > 299.9) {
+ screenParams.type = splashScreenStochasticClustered;
+ if (screenParams.size < 0) {
+ screenParams.size = 64;
+ }
+ if (screenParams.dotRadius < 0) {
+ screenParams.dotRadius = 2;
+ }
+ } else {
+ screenParams.type = splashScreenDispersed;
+ if (screenParams.size < 0) {
+ screenParams.size = 4;
+ }
+ }
+ }
+}
+
+SplashOutputDev::~SplashOutputDev() {
+ int i;
+
+ for (i = 0; i < nT3Fonts; ++i) {
+ delete t3FontCache[i];
+ }
+ if (fontEngine) {
+ delete fontEngine;
+ }
+ if (splash) {
+ delete splash;
+ }
+ if (bitmap) {
+ delete bitmap;
+ }
+ if (textClipPath) {
+ delete textClipPath;
+ }
+}
+
+void SplashOutputDev::startDoc(XRef *xrefA) {
+ int i;
+
+ xref = xrefA;
+ if (fontEngine) {
+ delete fontEngine;
+ }
+ fontEngine = new SplashFontEngine(
+#if HAVE_FREETYPE_H
+ globalParams->getEnableFreeType(),
+ globalParams->getDisableFreeTypeHinting()
+ ? splashFTNoHinting : 0,
+#endif
+ allowAntialias &&
+ globalParams->getAntialias() &&
+ colorMode != splashModeMono1);
+ for (i = 0; i < nT3Fonts; ++i) {
+ delete t3FontCache[i];
+ }
+ nT3Fonts = 0;
+}
+
+void SplashOutputDev::startPage(int pageNum, GfxState *state) {
+ int w, h;
+ double *ctm;
+ SplashCoord mat[6];
+ SplashColor color;
+
+ if (state) {
+ setupScreenParams(state->getHDPI(), state->getVDPI());
+ w = (int)(state->getPageWidth() + 0.5);
+ if (w <= 0) {
+ w = 1;
+ }
+ h = (int)(state->getPageHeight() + 0.5);
+ if (h <= 0) {
+ h = 1;
+ }
+ } else {
+ w = h = 1;
+ }
+ if (splash) {
+ delete splash;
+ splash = NULL;
+ }
+ if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
+ if (bitmap) {
+ delete bitmap;
+ bitmap = NULL;
+ }
+ traceMessage("page bitmap");
+ bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,
+ colorMode != splashModeMono1, bitmapTopDown,
+ NULL);
+ }
+ splash = new Splash(bitmap, vectorAntialias, NULL, &screenParams);
+ splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setEnablePathSimplification(
+ globalParams->getEnablePathSimplification());
+ if (state) {
+ ctm = state->getCTM();
+ mat[0] = (SplashCoord)ctm[0];
+ mat[1] = (SplashCoord)ctm[1];
+ mat[2] = (SplashCoord)ctm[2];
+ mat[3] = (SplashCoord)ctm[3];
+ mat[4] = (SplashCoord)ctm[4];
+ mat[5] = (SplashCoord)ctm[5];
+ splash->setMatrix(mat);
+ }
+ switch (colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ color[0] = 0;
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ color[0] = color[1] = color[2] = 0;
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ color[0] = color[1] = color[2] = color[3] = 0;
+ break;
+#endif
+ }
+ splash->setStrokePattern(new SplashSolidColor(color));
+ splash->setFillPattern(new SplashSolidColor(color));
+ splash->setLineCap(splashLineCapButt);
+ splash->setLineJoin(splashLineJoinMiter);
+ splash->setLineDash(NULL, 0, 0);
+ splash->setMiterLimit(10);
+ splash->setFlatness(1);
+ // the SA parameter supposedly defaults to false, but Acrobat
+ // apparently hardwires it to true
+ splash->setStrokeAdjust(
+ mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
+ splash->clear(paperColor, 0);
+ reverseVideoInvertImages = globalParams->getReverseVideoInvertImages();
+ if (startPageCbk) {
+ (*startPageCbk)(startPageCbkData);
+ }
+}
+
+void SplashOutputDev::endPage() {
+ if (colorMode != splashModeMono1 && !noComposite) {
+ splash->compositeBackground(paperColor);
+ }
+}
+
+void SplashOutputDev::saveState(GfxState *state) {
+ splash->saveState();
+ if (t3GlyphStack && !t3GlyphStack->haveDx) {
+ t3GlyphStack->doNotCache = gTrue;
+ error(errSyntaxWarning, -1,
+ "Save (q) operator before d0/d1 in Type 3 glyph");
+ }
+}
+
+void SplashOutputDev::restoreState(GfxState *state) {
+ splash->restoreState();
+ needFontUpdate = gTrue;
+ if (t3GlyphStack && !t3GlyphStack->haveDx) {
+ t3GlyphStack->doNotCache = gTrue;
+ error(errSyntaxWarning, -1,
+ "Restore (Q) operator before d0/d1 in Type 3 glyph");
+ }
+}
+
+void SplashOutputDev::updateAll(GfxState *state) {
+ updateLineDash(state);
+ updateLineJoin(state);
+ updateLineCap(state);
+ updateLineWidth(state);
+ updateFlatness(state);
+ updateMiterLimit(state);
+ updateStrokeAdjust(state);
+ updateFillColor(state);
+ updateStrokeColor(state);
+ needFontUpdate = gTrue;
+}
+
+void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
+ double m21, double m22,
+ double m31, double m32) {
+ double *ctm;
+ SplashCoord mat[6];
+
+ ctm = state->getCTM();
+ mat[0] = (SplashCoord)ctm[0];
+ mat[1] = (SplashCoord)ctm[1];
+ mat[2] = (SplashCoord)ctm[2];
+ mat[3] = (SplashCoord)ctm[3];
+ mat[4] = (SplashCoord)ctm[4];
+ mat[5] = (SplashCoord)ctm[5];
+ splash->setMatrix(mat);
+}
+
+void SplashOutputDev::updateLineDash(GfxState *state) {
+ double *dashPattern;
+ int dashLength;
+ double dashStart;
+ SplashCoord dash[20];
+ int i;
+
+ state->getLineDash(&dashPattern, &dashLength, &dashStart);
+ if (dashLength > 20) {
+ dashLength = 20;
+ }
+ for (i = 0; i < dashLength; ++i) {
+ dash[i] = (SplashCoord)dashPattern[i];
+ if (dash[i] < 0) {
+ dash[i] = 0;
+ }
+ }
+ splash->setLineDash(dash, dashLength, (SplashCoord)dashStart);
+}
+
+void SplashOutputDev::updateFlatness(GfxState *state) {
+#if 0 // Acrobat ignores the flatness setting, and always renders curves
+ // with a fairly small flatness value
+ splash->setFlatness(state->getFlatness());
+#endif
+}
+
+void SplashOutputDev::updateLineJoin(GfxState *state) {
+ splash->setLineJoin(state->getLineJoin());
+}
+
+void SplashOutputDev::updateLineCap(GfxState *state) {
+ splash->setLineCap(state->getLineCap());
+}
+
+void SplashOutputDev::updateMiterLimit(GfxState *state) {
+ splash->setMiterLimit(state->getMiterLimit());
+}
+
+void SplashOutputDev::updateLineWidth(GfxState *state) {
+ splash->setLineWidth(state->getLineWidth());
+}
+
+void SplashOutputDev::updateStrokeAdjust(GfxState *state) {
+#if 0 // the SA parameter supposedly defaults to false, but Acrobat
+ // apparently hardwires it to true
+ if (state->getStrokeAdjust()) {
+ if (globalParams->getStrokeAdjustMode() == strokeAdjustCAD) {
+ splash->setStrokeAdjust(splashStrokeAdjustCAD);
+ } else {
+ splash->setStrokeAdjust(splashStrokeAdjustNormal);
+ }
+ } else {
+ splash->setStrokeAdjust(splashStrokeAdjustOff);
+ }
+#endif
+}
+
+
+void SplashOutputDev::updateFillColor(GfxState *state) {
+ GfxGray gray;
+ GfxRGB rgb;
+#if SPLASH_CMYK
+ GfxCMYK cmyk;
+#endif
+
+ switch (colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ state->getFillGray(&gray);
+ splash->setFillPattern(getColor(gray));
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ state->getFillRGB(&rgb);
+ splash->setFillPattern(getColor(&rgb));
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ state->getFillCMYK(&cmyk);
+ splash->setFillPattern(getColor(&cmyk));
+ break;
+#endif
+ }
+}
+
+void SplashOutputDev::updateStrokeColor(GfxState *state) {
+ GfxGray gray;
+ GfxRGB rgb;
+#if SPLASH_CMYK
+ GfxCMYK cmyk;
+#endif
+
+ switch (colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ state->getStrokeGray(&gray);
+ splash->setStrokePattern(getColor(gray));
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ state->getStrokeRGB(&rgb);
+ splash->setStrokePattern(getColor(&rgb));
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ state->getStrokeCMYK(&cmyk);
+ splash->setStrokePattern(getColor(&cmyk));
+ break;
+#endif
+ }
+}
+
+
+SplashPattern *SplashOutputDev::getColor(GfxGray gray) {
+ SplashColor color;
+
+ getColor(gray, color);
+ return new SplashSolidColor(color);
+}
+
+SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) {
+ SplashColor color;
+
+ getColor(rgb, color);
+ return new SplashSolidColor(color);
+}
+
+#if SPLASH_CMYK
+SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) {
+ SplashColor color;
+
+ getColor(cmyk, color);
+ return new SplashSolidColor(color);
+}
+#endif
+
+
+void SplashOutputDev::getColor(GfxGray gray, SplashColorPtr color) {
+ if (reverseVideo) {
+ gray = gfxColorComp1 - gray;
+ }
+ color[0] = colToByte(gray);
+}
+
+void SplashOutputDev::getColor(GfxRGB *rgb, SplashColorPtr color) {
+ GfxColorComp r, g, b;
+
+ if (reverseVideo) {
+ r = gfxColorComp1 - rgb->r;
+ g = gfxColorComp1 - rgb->g;
+ b = gfxColorComp1 - rgb->b;
+ } else {
+ r = rgb->r;
+ g = rgb->g;
+ b = rgb->b;
+ }
+ color[0] = colToByte(r);
+ color[1] = colToByte(g);
+ color[2] = colToByte(b);
+}
+
+#if SPLASH_CMYK
+void SplashOutputDev::getColor(GfxCMYK *cmyk, SplashColorPtr color) {
+ color[0] = colToByte(cmyk->c);
+ color[1] = colToByte(cmyk->m);
+ color[2] = colToByte(cmyk->y);
+ color[3] = colToByte(cmyk->k);
+}
+#endif
+
+
+
+void SplashOutputDev::setOverprintMask(GfxState *state,
+ GfxColorSpace *colorSpace,
+ GBool overprintFlag,
+ int overprintMode,
+ GfxColor *singleColor) {
+#if SPLASH_CMYK
+ Guint mask;
+ GfxCMYK cmyk;
+
+ if (overprintFlag && globalParams->getOverprintPreview()) {
+ mask = colorSpace->getOverprintMask();
+ // The OPM (overprintMode) setting is only relevant when the color
+ // space is DeviceCMYK or is "implicitly converted to DeviceCMYK".
+ // Per the PDF spec, this happens with ICCBased color spaces only
+ // if the profile matches the output device.
+ if (singleColor && overprintMode &&
+ colorSpace->getMode() == csDeviceCMYK) {
+ colorSpace->getCMYK(singleColor, &cmyk, state->getRenderingIntent());
+ if (cmyk.c == 0) {
+ mask &= ~1;
+ }
+ if (cmyk.m == 0) {
+ mask &= ~2;
+ }
+ if (cmyk.y == 0) {
+ mask &= ~4;
+ }
+ if (cmyk.k == 0) {
+ mask &= ~8;
+ }
+ }
+ } else {
+ mask = 0xffffffff;
+ }
+ splash->setOverprintMask(mask);
+#endif
+
+}
+
+void SplashOutputDev::updateBlendMode(GfxState *state) {
+ splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]);
+}
+
+void SplashOutputDev::updateFillOpacity(GfxState *state) {
+ splash->setFillAlpha((SplashCoord)state->getFillOpacity());
+}
+
+void SplashOutputDev::updateStrokeOpacity(GfxState *state) {
+ splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity());
+}
+
+void SplashOutputDev::updateRenderingIntent(GfxState *state) {
+ updateFillColor(state);
+ updateStrokeColor(state);
+}
+
+void SplashOutputDev::updateTransfer(GfxState *state) {
+ Function **transfer;
+ Guchar red[256], green[256], blue[256], gray[256];
+ double x, y;
+ int i;
+
+ transfer = state->getTransfer();
+ if (transfer[0] &&
+ transfer[0]->getInputSize() == 1 &&
+ transfer[0]->getOutputSize() == 1) {
+ if (transfer[1] &&
+ transfer[1]->getInputSize() == 1 &&
+ transfer[1]->getOutputSize() == 1 &&
+ transfer[2] &&
+ transfer[2]->getInputSize() == 1 &&
+ transfer[2]->getOutputSize() == 1 &&
+ transfer[3] &&
+ transfer[3]->getInputSize() == 1 &&
+ transfer[3]->getOutputSize() == 1) {
+ for (i = 0; i < 256; ++i) {
+ x = i / 255.0;
+ transfer[0]->transform(&x, &y);
+ red[i] = (Guchar)(y * 255.0 + 0.5);
+ transfer[1]->transform(&x, &y);
+ green[i] = (Guchar)(y * 255.0 + 0.5);
+ transfer[2]->transform(&x, &y);
+ blue[i] = (Guchar)(y * 255.0 + 0.5);
+ transfer[3]->transform(&x, &y);
+ gray[i] = (Guchar)(y * 255.0 + 0.5);
+ }
+ } else {
+ for (i = 0; i < 256; ++i) {
+ x = i / 255.0;
+ transfer[0]->transform(&x, &y);
+ red[i] = green[i] = blue[i] = gray[i] = (Guchar)(y * 255.0 + 0.5);
+ }
+ }
+ } else {
+ for (i = 0; i < 256; ++i) {
+ red[i] = green[i] = blue[i] = gray[i] = (Guchar)i;
+ }
+ }
+ splash->setTransfer(red, green, blue, gray);
+}
+
+void SplashOutputDev::updateFont(GfxState *state) {
+ needFontUpdate = gTrue;
+}
+
+void SplashOutputDev::doUpdateFont(GfxState *state) {
+ GfxFont *gfxFont;
+ GfxFontLoc *fontLoc;
+ GfxFontType fontType;
+ SplashOutFontFileID *id;
+ SplashFontFile *fontFile;
+ int fontNum;
+ FoFiTrueType *ff;
+ FoFiType1C *ffT1C;
+ Ref embRef;
+ Object refObj, strObj;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf;
+ FILE *extFontFile;
+#else
+ GString *tmpFileName, *fileName;
+ FILE *tmpFile;
+#endif
+ char blk[4096];
+ int *codeToGID;
+ CharCodeToUnicode *ctu;
+ double *textMat;
+ double m11, m12, m21, m22, fontSize, oblique;
+ double fsx, fsy, w, fontScaleMin, fontScaleAvg, fontScale;
+ Gushort ww;
+ SplashCoord mat[4];
+ char *name, *start;
+ Unicode uBuf[8];
+ int substIdx, n, code, cmap, cmapPlatform, cmapEncoding, length, i;
+
+ needFontUpdate = gFalse;
+ font = NULL;
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = NULL;
+#else
+ tmpFileName = NULL;
+ fileName = NULL;
+#endif
+ substIdx = -1;
+
+ if (!(gfxFont = state->getFont())) {
+ goto err1;
+ }
+ fontType = gfxFont->getType();
+ if (fontType == fontType3) {
+ goto err1;
+ }
+
+ // 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) > 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;
+
+ } else {
+
+ fontNum = 0;
+
+ if (!(fontLoc = gfxFont->locateFont(xref, gFalse))) {
+ error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ goto err2;
+ }
+
+ // embedded font
+ if (fontLoc->locType == gfxFontLocEmbedded) {
+ gfxFont->getEmbeddedFontID(&embRef);
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = new GString();
+ refObj.initRef(embRef.num, embRef.gen);
+ refObj.fetch(xref, &strObj);
+ refObj.free();
+ if (!strObj.isStream()) {
+ error(errSyntaxError, -1, "Embedded font object is wrong type");
+ strObj.free();
+ delete fontLoc;
+ goto err2;
+ }
+ strObj.streamReset();
+ while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
+ fontBuf->append(blk, n);
+ }
+ strObj.streamClose();
+ strObj.free();
+#else
+ if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {
+ error(errIO, -1, "Couldn't create temporary font file");
+ delete fontLoc;
+ goto err2;
+ }
+ refObj.initRef(embRef.num, embRef.gen);
+ refObj.fetch(xref, &strObj);
+ refObj.free();
+ if (!strObj.isStream()) {
+ error(errSyntaxError, -1, "Embedded font object is wrong type");
+ strObj.free();
+ fclose(tmpFile);
+ delete fontLoc;
+ goto err2;
+ }
+ strObj.streamReset();
+ while ((n = strObj.streamGetBlock(blk, sizeof(blk))) > 0) {
+ fwrite(blk, 1, n, tmpFile);
+ }
+ strObj.streamClose();
+ strObj.free();
+ fclose(tmpFile);
+ fileName = tmpFileName;
+#endif
+
+ // external font
+ } else { // gfxFontLocExternal
+#if LOAD_FONTS_FROM_MEM
+ if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) {
+ error(errSyntaxError, -1, "Couldn't open external font file '{0:t}'",
+ fontLoc->path);
+ delete fontLoc;
+ goto err2;
+ }
+ fontBuf = new GString();
+ while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) {
+ fontBuf->append(blk, n);
+ }
+ fclose(extFontFile);
+#else
+ fileName = fontLoc->path;
+#endif
+ fontNum = fontLoc->fontNum;
+ if (fontLoc->substIdx >= 0) {
+ id->setSubstIdx(fontLoc->substIdx);
+ }
+ if (fontLoc->oblique != 0) {
+ id->setOblique(fontLoc->oblique);
+ }
+ }
+
+ // load the font file
+ switch (fontLoc->fontType) {
+ case fontType1:
+ if (!(fontFile = fontEngine->loadType1Font(
+ id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
+ fileName == tmpFileName,
+#endif
+ (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ delete fontLoc;
+ goto err1;
+ }
+ break;
+ case fontType1C:
+#if LOAD_FONTS_FROM_MEM
+ if ((ffT1C = FoFiType1C::make(fontBuf->getCString(),
+ fontBuf->getLength()))) {
+#else
+ if ((ffT1C = FoFiType1C::load(fileName->getCString()))) {
+#endif
+ codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffT1C);
+ delete ffT1C;
+ } else {
+ codeToGID = NULL;
+ }
+ if (!(fontFile = fontEngine->loadType1CFont(
+ id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
+ fileName == tmpFileName,
+#endif
+ codeToGID,
+ (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ delete fontLoc;
+ goto err1;
+ }
+ break;
+ case fontType1COT:
+ codeToGID = NULL;
+#if LOAD_FONTS_FROM_MEM
+ if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
+ fontNum, gTrue))) {
+#else
+ if ((ff = FoFiTrueType::load(fileName->getCString(),
+ fontNum, gTrue))) {
+#endif
+ if (ff->getCFFBlock(&start, &length) &&
+ (ffT1C = FoFiType1C::make(start, length))) {
+ codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffT1C);
+ delete ffT1C;
+ }
+ delete ff;
+ }
+ if (!(fontFile = fontEngine->loadOpenTypeT1CFont(
+ id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
+ fileName == tmpFileName,
+#endif
+ codeToGID,
+ (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ delete fontLoc;
+ goto err1;
+ }
+ break;
+ case fontTrueType:
+ case fontTrueTypeOT:
+#if LOAD_FONTS_FROM_MEM
+ if ((ff = FoFiTrueType::make(fontBuf->getCString(), fontBuf->getLength(),
+ fontNum))) {
+#else
+ if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+#endif
+ codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
+ n = 256;
+ delete ff;
+ // if we're substituting for a non-TrueType font, we need to mark
+ // all notdef codes as "do not draw" (rather than drawing TrueType
+ // notdef glyphs)
+ if (gfxFont->getType() != fontTrueType &&
+ gfxFont->getType() != fontTrueTypeOT) {
+ for (i = 0; i < 256; ++i) {
+ if (codeToGID[i] == 0) {
+ codeToGID[i] = -1;
+ }
+ }
+ }
+ } else {
+ codeToGID = NULL;
+ n = 0;
+ }
+ if (!(fontFile = fontEngine->loadTrueTypeFont(
+ id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
+ fileName == tmpFileName,
+#endif
+ fontNum, codeToGID, n,
+ gfxFont->getEmbeddedFontName()
+ ? gfxFont->getEmbeddedFontName()->getCString()
+ : (char *)NULL))) {
+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ delete fontLoc;
+ goto err1;
+ }
+ break;
+ case fontCIDType0:
+ case fontCIDType0C:
+ if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
+ n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
+ codeToGID = (int *)gmallocn(n, sizeof(int));
+ memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
+ n * sizeof(int));
+ } else {
+ codeToGID = NULL;
+ n = 0;
+ }
+ if (!(fontFile = fontEngine->loadCIDFont(
+ id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
+ fileName == tmpFileName,
+#endif
+ codeToGID, n))) {
+
+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ delete fontLoc;
+ goto err1;
+ }
+ break;
+ case fontCIDType0COT:
+ codeToGID = NULL;
+ n = 0;
+ if (fontLoc->locType == gfxFontLocEmbedded) {
+ if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
+ n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
+ codeToGID = (int *)gmallocn(n, sizeof(int));
+ memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
+ n * sizeof(int));
+ }
+ } else if (globalParams->getMapExtTrueTypeFontsViaUnicode()) {
+ // create a CID-to-GID mapping, via Unicode
+ if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
+#if LOAD_FONTS_FROM_MEM
+ if ((ff = FoFiTrueType::make(fontBuf->getCString(),
+ fontBuf->getLength(), fontNum))) {
+#else
+ if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+#endif
+ // look for a Unicode cmap
+ for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
+ cmapPlatform = ff->getCmapPlatform(cmap);
+ cmapEncoding = ff->getCmapEncoding(cmap);
+ if ((cmapPlatform == 3 && cmapEncoding == 1) ||
+ (cmapPlatform == 0 && cmapEncoding <= 4)) {
+ break;
+ }
+ }
+ if (cmap < ff->getNumCmaps()) {
+ // map CID -> Unicode -> GID
+ if (ctu->isIdentity()) {
+ n = 65536;
+ } else {
+ n = ctu->getLength();
+ }
+ codeToGID = (int *)gmallocn(n, sizeof(int));
+ for (code = 0; code < n; ++code) {
+ if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
+ codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]);
+ } else {
+ codeToGID[code] = -1;
+ }
+ }
+ }
+ delete ff;
+ }
+ ctu->decRefCnt();
+ } else {
+ error(errSyntaxError, -1,
+ "Couldn't find a mapping to Unicode for font '{0:s}'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ }
+ }
+ if (!(fontFile = fontEngine->loadOpenTypeCFFFont(
+ id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
+ fileName == tmpFileName,
+#endif
+ codeToGID, n))) {
+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ delete fontLoc;
+ goto err1;
+ }
+ break;
+ case fontCIDType2:
+ case fontCIDType2OT:
+ codeToGID = NULL;
+ n = 0;
+ if (fontLoc->locType == gfxFontLocEmbedded) {
+ if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
+ n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
+ codeToGID = (int *)gmallocn(n, sizeof(int));
+ memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
+ n * sizeof(int));
+ }
+ } else if (globalParams->getMapExtTrueTypeFontsViaUnicode() &&
+ !((GfxCIDFont *)gfxFont)->usesIdentityEncoding()) {
+ // create a CID-to-GID mapping, via Unicode
+ if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
+#if LOAD_FONTS_FROM_MEM
+ if ((ff = FoFiTrueType::make(fontBuf->getCString(),
+ fontBuf->getLength(), fontNum))) {
+#else
+ if ((ff = FoFiTrueType::load(fileName->getCString(), fontNum))) {
+#endif
+ // look for a Unicode cmap
+ for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
+ cmapPlatform = ff->getCmapPlatform(cmap);
+ cmapEncoding = ff->getCmapEncoding(cmap);
+ if ((cmapPlatform == 3 && cmapEncoding == 1) ||
+ (cmapPlatform == 0 && cmapEncoding <= 4)) {
+ break;
+ }
+ }
+ if (cmap < ff->getNumCmaps()) {
+ // map CID -> Unicode -> GID
+ if (ctu->isIdentity()) {
+ n = 65536;
+ } else {
+ n = ctu->getLength();
+ }
+ codeToGID = (int *)gmallocn(n, sizeof(int));
+ for (code = 0; code < n; ++code) {
+ if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
+ codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]);
+ } else {
+ codeToGID[code] = -1;
+ }
+ }
+ }
+ delete ff;
+ }
+ ctu->decRefCnt();
+ } else {
+ error(errSyntaxError, -1,
+ "Couldn't find a mapping to Unicode for font '{0:s}'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ }
+ }
+ if (!(fontFile = fontEngine->loadTrueTypeFont(
+ id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fileName->getCString(),
+ fileName == tmpFileName,
+#endif
+ fontNum, codeToGID, n,
+ gfxFont->getEmbeddedFontName()
+ ? gfxFont->getEmbeddedFontName()->getCString()
+ : (char *)NULL))) {
+ error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
+ gfxFont->getName() ? gfxFont->getName()->getCString()
+ : "(unnamed)");
+ delete fontLoc;
+ goto err1;
+ }
+ break;
+ default:
+ // this shouldn't happen
+ delete fontLoc;
+ goto err2;
+ }
+
+ delete fontLoc;
+ }
+
+ // get the font matrix
+ textMat = state->getTextMat();
+ fontSize = state->getFontSize();
+ oblique = ((SplashOutFontFileID *)fontFile->getID())->getOblique();
+ m11 = state->getHorizScaling() * textMat[0];
+ m12 = state->getHorizScaling() * textMat[1];
+ m21 = oblique * m11 + textMat[2];
+ m22 = oblique * m12 + textMat[3];
+ m11 *= fontSize;
+ m12 *= fontSize;
+ m21 *= fontSize;
+ m22 *= fontSize;
+
+ // for substituted fonts: adjust the font matrix -- compare the
+ // widths of letters and digits (A-Z, a-z, 0-9) in the original font
+ // and the substituted font
+ substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();
+ if (substIdx >= 0 && substIdx < 12) {
+ fontScaleMin = 1;
+ fontScaleAvg = 0;
+ n = 0;
+ for (code = 0; code < 256; ++code) {
+ if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
+ name[0] && !name[1] &&
+ ((name[0] >= 'A' && name[0] <= 'Z') ||
+ (name[0] >= 'a' && name[0] <= 'z') ||
+ (name[0] >= '0' && name[0] <= '9'))) {
+ w = ((Gfx8BitFont *)gfxFont)->getWidth((Guchar)code);
+ if (builtinFontSubst[substIdx]->widths->getWidth(name, &ww) &&
+ w > 0.01 && ww > 10) {
+ w /= ww * 0.001;
+ if (w < fontScaleMin) {
+ fontScaleMin = w;
+ }
+ fontScaleAvg += w;
+ ++n;
+ }
+ }
+ }
+ // if real font is narrower than substituted font, reduce the font
+ // size accordingly -- this currently uses a scale factor halfway
+ // between the minimum and average computed scale factors, which
+ // is a bit of a kludge, but seems to produce mostly decent
+ // results
+ if (n) {
+ fontScaleAvg /= n;
+ if (fontScaleAvg < 1) {
+ fontScale = 0.5 * (fontScaleMin + fontScaleAvg);
+ m11 *= fontScale;
+ m12 *= fontScale;
+ }
+ }
+ }
+
+ // create the scaled font
+ mat[0] = m11; mat[1] = m12;
+ mat[2] = m21; mat[3] = m22;
+ font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
+
+#if !LOAD_FONTS_FROM_MEM
+ if (tmpFileName) {
+ delete tmpFileName;
+ }
+#endif
+ return;
+
+ err2:
+ delete id;
+ err1:
+#if LOAD_FONTS_FROM_MEM
+ if (fontBuf) {
+ delete fontBuf;
+ }
+#else
+ if (tmpFileName) {
+ unlink(tmpFileName->getCString());
+ delete tmpFileName;
+ }
+#endif
+ return;
+}
+
+void SplashOutputDev::stroke(GfxState *state) {
+ SplashPath *path;
+
+ if (state->getStrokeColorSpace()->isNonMarking()) {
+ return;
+ }
+ setOverprintMask(state, state->getStrokeColorSpace(),
+ state->getStrokeOverprint(), state->getOverprintMode(),
+ state->getStrokeColor());
+ path = convertPath(state, state->getPath(), gFalse);
+ splash->stroke(path);
+ delete path;
+}
+
+void SplashOutputDev::fill(GfxState *state) {
+ SplashPath *path;
+
+ if (state->getFillColorSpace()->isNonMarking()) {
+ return;
+ }
+ setOverprintMask(state, state->getFillColorSpace(),
+ state->getFillOverprint(), state->getOverprintMode(),
+ state->getFillColor());
+ path = convertPath(state, state->getPath(), gTrue);
+ splash->fill(path, gFalse);
+ delete path;
+}
+
+void SplashOutputDev::eoFill(GfxState *state) {
+ SplashPath *path;
+
+ if (state->getFillColorSpace()->isNonMarking()) {
+ return;
+ }
+ setOverprintMask(state, state->getFillColorSpace(),
+ state->getFillOverprint(), state->getOverprintMode(),
+ state->getFillColor());
+ path = convertPath(state, state->getPath(), gTrue);
+ splash->fill(path, gTrue);
+ delete path;
+}
+
+void SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx,
+ Object *strRef,
+ int paintType, int tilingType,
+ Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep) {
+ 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;
+ double xStepX, xStepY, yStepX, yStepY;
+ double adjXMin, adjYMin;
+ double sx, sy;
+ double clipXMin, clipYMin, clipXMax, clipYMax, clipXC, clipYC;
+ double tx, ty, idet, txMin, tyMin, txMax, tyMax;
+ int tileW, tileH, tileSize;
+ int ixMin, ixMax, iyMin, iyMax, ix, iy, x, y;
+ int i;
+
+ // Notes:
+ // - PTM = pattern matrix = transform from pattern space to default
+ // user space (default for most recent page or form)
+ // - BTM = transform from default user space to device space
+ //
+ // This function is called with:
+ // - mat = PTM * BTM * iCTM = transform from pattern space to
+ // current user space
+
+ // transform the four corners of the pattern bbox from pattern space
+ // to device space and compute the device space bbox
+ state->transform(bbox[0] * mat[0] + bbox[1] * mat[2] + mat[4],
+ bbox[0] * mat[1] + bbox[1] * mat[3] + mat[5],
+ &tx, &ty);
+ tileXMin = tileXMax = tx;
+ tileYMin = tileYMax = ty;
+ state->transform(bbox[2] * mat[0] + bbox[1] * mat[2] + mat[4],
+ bbox[2] * mat[1] + bbox[1] * mat[3] + mat[5],
+ &tx, &ty);
+ if (tx < tileXMin) {
+ tileXMin = tx;
+ } else if (tx > tileXMax) {
+ tileXMax = tx;
+ }
+ if (ty < tileYMin) {
+ tileYMin = ty;
+ } else if (ty > tileYMax) {
+ tileYMax = ty;
+ }
+ state->transform(bbox[2] * mat[0] + bbox[3] * mat[2] + mat[4],
+ bbox[2] * mat[1] + bbox[3] * mat[3] + mat[5],
+ &tx, &ty);
+ if (tx < tileXMin) {
+ tileXMin = tx;
+ } else if (tx > tileXMax) {
+ tileXMax = tx;
+ }
+ if (ty < tileYMin) {
+ tileYMin = ty;
+ } else if (ty > tileYMax) {
+ tileYMax = ty;
+ }
+ state->transform(bbox[0] * mat[0] + bbox[3] * mat[2] + mat[4],
+ bbox[0] * mat[1] + bbox[3] * mat[3] + mat[5],
+ &tx, &ty);
+ if (tx < tileXMin) {
+ tileXMin = tx;
+ } else if (tx > tileXMax) {
+ tileXMax = tx;
+ }
+ if (ty < tileYMin) {
+ tileYMin = ty;
+ } else if (ty > tileYMax) {
+ tileYMax = ty;
+ }
+ if (tileXMin == tileXMax || tileYMin == tileYMax) {
+ return;
+ }
+ tileW = (int)(tileXMax - tileXMin + 0.5);
+ tileH = (int)(tileYMax - tileYMin + 0.5);
+ if (tileW < 1) {
+ tileW = 1;
+ }
+ if (tileH < 1) {
+ tileH = 1;
+ }
+
+ // check for an excessively large tile size
+ tileSize = tileW * tileH;
+ if (tileXMax - tileXMin + 0.5 > (double)INT_MAX ||
+ tileYMax - tileYMin + 0.5 > (double)INT_MAX ||
+ tileW > INT_MAX / tileH ||
+ tileSize > maxTileSize) {
+ mat1[0] = mat[0];
+ mat1[1] = mat[1];
+ mat1[2] = mat[2];
+ mat1[3] = mat[3];
+ for (iy = y0; iy < y1; ++iy) {
+ for (ix = x0; ix < x1; ++ix) {
+ tx = ix * xStep;
+ ty = iy * yStep;
+ mat1[4] = tx * mat[0] + ty * mat[2] + mat[4];
+ mat1[5] = tx * mat[1] + ty * mat[3] + mat[5];
+ gfx->drawForm(strRef, resDict, mat1, bbox);
+ }
+ }
+ return;
+ }
+
+ // transform XStep and YStep to device space
+ state->transformDelta(xStep * mat[0], xStep * mat[1], &xStepX, &xStepY);
+ state->transformDelta(yStep * mat[2], yStep * mat[3], &yStepX, &yStepY);
+
+ // get the clipping bbox (in device space)
+ state->getClipBBox(&clipXMin, &clipYMin, &clipXMax, &clipYMax);
+
+ // compute tiling parameters
+ idet = xStepX * yStepY - yStepX * xStepY;
+ if (tilingType == 2 || idet == 0) {
+ adjXMin = tileXMin;
+ adjYMin = tileYMin;
+ sx = 1;
+ sy = 1;
+ } else {
+ // reposition the pattern origin to the center of the clipping bbox
+ idet = 1 / idet;
+ clipXC = 0.5 * (clipXMin + clipXMax);
+ clipYC = 0.5 * (clipYMin + clipYMax);
+ ix = (int)((yStepX * (tileYMin - clipYC) - (tileXMin - clipXC) * yStepY)
+ * idet + 0.5);
+ iy = (int)((xStepX * (clipYC - tileYMin) - (clipXC - tileXMin) * xStepY)
+ * idet + 0.5);
+ adjXMin = (int)floor(tileXMin + ix * xStepX + iy * yStepX + 0.5);
+ adjYMin = (int)floor(tileYMin + ix * xStepY + iy * yStepY + 0.5);
+ sx = tileW / (tileXMax - tileXMin);
+ sy = tileH / (tileYMax - tileYMin);
+ xStepX = (int)floor(sx * xStepX + 0.5);
+ xStepY = (int)floor(sy * xStepY + 0.5);
+ yStepX = (int)floor(sx * yStepX + 0.5);
+ yStepY = (int)floor(sy * yStepY + 0.5);
+ }
+
+ // compute tile matrix = PTM * BTM * Mtranslate * Mscale * iCTM
+ // = mat * CTM * Mtranslate * Mscale * iCTM
+ ctm = state->getCTM();
+ idet = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+ ictm[0] = ctm[3] * idet;
+ ictm[1] = -ctm[1] * idet;
+ ictm[2] = -ctm[2] * idet;
+ ictm[3] = ctm[0] * idet;
+ ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * idet;
+ ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * idet;
+ // mat * CTM
+ mat1[0] = mat[0] * ctm[0] + mat[1] * ctm[2];
+ mat1[1] = mat[0] * ctm[1] + mat[1] * ctm[3];
+ mat1[2] = mat[2] * ctm[0] + mat[3] * ctm[2];
+ mat1[3] = mat[2] * ctm[1] + mat[3] * ctm[3];
+ mat1[4] = mat[4] * ctm[0] + mat[5] * ctm[2] + ctm[4];
+ mat1[5] = mat[4] * ctm[1] + mat[5] * ctm[3] + ctm[5];
+ // mat * CTM * (Mtranslate * Mscale)
+ mat2[0] = mat1[0] * sx;
+ mat2[1] = mat1[1] * sy;
+ mat2[2] = mat1[2] * sx;
+ mat2[3] = mat1[3] * sy;
+ mat2[4] = mat1[4] * sx - sx * tileXMin;
+ mat2[5] = mat1[5] * sy - sy * tileYMin;
+ // mat * CTM * (Mtranslate * Mscale) * iCTM
+ tileMat[0] = mat2[0] * ictm[0] + mat2[1] * ictm[2];
+ tileMat[1] = mat2[0] * ictm[1] + mat2[1] * ictm[3];
+ tileMat[2] = mat2[2] * ictm[0] + mat2[3] * ictm[2];
+ tileMat[3] = mat2[2] * ictm[1] + mat2[3] * ictm[3];
+ tileMat[4] = mat2[4] * ictm[0] + mat2[5] * ictm[2] + ictm[4];
+ tileMat[5] = mat2[4] * ictm[1] + mat2[5] * ictm[3] + ictm[5];
+
+ // compute tiling range:
+ // - look at the four corners of the clipping bbox
+ // - solve for the (ix,iy) tile position at each corner
+ // - take the min and max values for ix, iy
+ idet = xStepX * yStepY - xStepY * yStepX;
+ if (idet == 0) {
+ return;
+ }
+ idet = 1 / idet;
+ // LL corner
+ tx = idet * (yStepY * (clipXMin - tileW - 1 - adjXMin)
+ - yStepX * (clipYMax + 1 - adjYMin));
+ ty = idet * (xStepX * (clipYMax + 1 - adjYMin)
+ - xStepY * (clipXMin - tileW - 1 - adjXMin));
+ txMin = txMax = tx;
+ tyMin = tyMax = ty;
+ // LR corner
+ tx = idet * (yStepY * (clipXMax + 1 - adjXMin)
+ - yStepX * (clipYMax + 1 - adjYMin));
+ ty = idet * (xStepX * (clipYMax + 1 - adjYMin)
+ - xStepY * (clipXMax + 1 - adjXMin));
+ if (tx < txMin) {
+ txMin = tx;
+ } else if (tx > txMax) {
+ txMax = tx;
+ }
+ if (ty < tyMin) {
+ tyMin = ty;
+ } else if (ty > tyMax) {
+ tyMax = ty;
+ }
+ // UL corner
+ tx = idet * (yStepY * (clipXMin - tileW - 1 - adjXMin)
+ - yStepX * (clipYMin - tileH - 1 - adjYMin));
+ ty = idet * (xStepX * (clipYMin - tileH - 1 - adjYMin)
+ - xStepY * (clipXMin - tileW - 1 - adjXMin));
+ if (tx < txMin) {
+ txMin = tx;
+ } else if (tx > txMax) {
+ txMax = tx;
+ }
+ if (ty < tyMin) {
+ tyMin = ty;
+ } else if (ty > tyMax) {
+ tyMax = ty;
+ }
+ // UR corner
+ tx = idet * (yStepY * (clipXMax + 1 - adjXMin)
+ - yStepX * (clipYMin - tileH - 1 - adjYMin));
+ ty = idet * (xStepX * (clipYMin - tileH - 1 - adjYMin)
+ - xStepY * (clipXMax + 1 - adjXMin));
+ if (tx < txMin) {
+ txMin = tx;
+ } else if (tx > txMax) {
+ txMax = tx;
+ }
+ if (ty < tyMin) {
+ tyMin = ty;
+ } else if (ty > tyMax) {
+ tyMax = ty;
+ }
+ ixMin = (int)floor(txMin);
+ ixMax = (int)ceil(txMax);
+ iyMin = (int)floor(tyMin);
+ iyMax = (int)ceil(tyMax);
+
+ // create a temporary bitmap
+ origBitmap = bitmap;
+ origSplash = splash;
+ traceMessage("tiling pattern bitmap");
+ bitmap = tileBitmap = new SplashBitmap(tileW, tileH, bitmapRowPad,
+ colorMode, gTrue, bitmapTopDown,
+ origBitmap);
+ splash = new Splash(bitmap, vectorAntialias,
+ origSplash->getImageCache(), origSplash->getScreen());
+ for (i = 0; i < splashMaxColorComps; ++i) {
+ 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()]);
+ splash->setEnablePathSimplification(
+ globalParams->getEnablePathSimplification());
+ ++nestCount;
+
+ // copy the fill color (for uncolored tiling patterns)
+ // (and stroke color, to handle buggy PDF files)
+ // -- Acrobat apparently doesn't copy the full state here
+ splash->setFillPattern(origSplash->getFillPattern()->copy());
+ splash->setStrokePattern(origSplash->getStrokePattern()->copy());
+
+ // reset the clip rectangle
+ state->resetDevClipRect(0, 0, tileW, tileH);
+
+ // render the tile
+ gfx->drawForm(strRef, resDict, tileMat, bbox);
+
+ // restore the original bitmap
+ --nestCount;
+ delete splash;
+ bitmap = origBitmap;
+ splash = origSplash;
+ splash->setOverprintMask(0xffffffff);
+
+ // draw the tiles
+ 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);
+ }
+ }
+ } else {
+ 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 {
+ splash->composite(tileBitmap, 0, 0, x, y, tileW, tileH,
+ gFalse, gFalse);
+ }
+ }
+ }
+ }
+
+ gfree(overprintMaskBitmap);
+ delete tileBitmap;
+}
+
+GBool SplashOutputDev::shadedFill(GfxState *state, GfxShading *shading) {
+
+ // generate the bitmap
+ SplashColorMode srcMode;
+ if (colorMode == splashModeMono1) {
+ srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
+ } else {
+ srcMode = colorMode;
+ }
+ 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;
+ }
+
+ // 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);
+ }
+
+ delete tBitmap;
+
+ return gTrue;
+}
+
+void SplashOutputDev::clip(GfxState *state) {
+ SplashPath *path;
+
+ path = convertPath(state, state->getPath(), gTrue);
+ splash->clipToPath(path, gFalse);
+ delete path;
+}
+
+void SplashOutputDev::eoClip(GfxState *state) {
+ SplashPath *path;
+
+ path = convertPath(state, state->getPath(), gTrue);
+ splash->clipToPath(path, gTrue);
+ delete path;
+}
+
+void SplashOutputDev::clipToStrokePath(GfxState *state) {
+ SplashPath *path, *path2;
+
+ path = convertPath(state, state->getPath(), gFalse);
+ path2 = splash->makeStrokePath(path, state->getLineWidth(),
+ state->getLineCap(), state->getLineJoin());
+ delete path;
+ splash->clipToPath(path2, gFalse);
+ delete path2;
+}
+
+SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path,
+ GBool dropEmptySubpaths) {
+ SplashPath *sPath;
+ GfxSubpath *subpath;
+ int n, i, j;
+
+ n = dropEmptySubpaths ? 1 : 0;
+ sPath = new SplashPath();
+ for (i = 0; i < path->getNumSubpaths(); ++i) {
+ subpath = path->getSubpath(i);
+ if (subpath->getNumPoints() > n) {
+ sPath->moveTo((SplashCoord)subpath->getX(0),
+ (SplashCoord)subpath->getY(0));
+ j = 1;
+ while (j < subpath->getNumPoints()) {
+ if (subpath->getCurve(j)) {
+ sPath->curveTo((SplashCoord)subpath->getX(j),
+ (SplashCoord)subpath->getY(j),
+ (SplashCoord)subpath->getX(j+1),
+ (SplashCoord)subpath->getY(j+1),
+ (SplashCoord)subpath->getX(j+2),
+ (SplashCoord)subpath->getY(j+2));
+ j += 3;
+ } else {
+ sPath->lineTo((SplashCoord)subpath->getX(j),
+ (SplashCoord)subpath->getY(j));
+ ++j;
+ }
+ }
+ if (subpath->isClosed()) {
+ sPath->close();
+ }
+ }
+ }
+ return sPath;
+}
+
+void SplashOutputDev::drawChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ double originX, double originY,
+ CharCode code, int nBytes,
+ Unicode *u, int uLen) {
+ SplashPath *path;
+ int render;
+ GBool doFill, doStroke, doClip;
+ SplashStrokeAdjustMode strokeAdjust;
+ double m[4];
+ GBool horiz;
+
+ 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)) {
+ return;
+ }
+ }
+
+ // check for invisible text -- this is used by Acrobat Capture
+ render = state->getRender();
+ if (render == 3) {
+ return;
+ }
+
+ if (needFontUpdate) {
+ doUpdateFont(state);
+ }
+ if (!font) {
+ return;
+ }
+
+ x -= originX;
+ y -= originY;
+
+ doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking();
+ doStroke = ((render & 3) == 1 || (render & 3) == 2) &&
+ !state->getStrokeColorSpace()->isNonMarking();
+ doClip = render & 4;
+
+ path = NULL;
+ if (doStroke || doClip) {
+ if ((path = font->getGlyphPath(code))) {
+ path->offset((SplashCoord)x, (SplashCoord)y);
+ }
+ }
+
+ // don't use stroke adjustment when stroking text -- the results
+ // tend to be ugly (because characters with horizontal upper or
+ // lower edges get misaligned relative to the other characters)
+ strokeAdjust = splashStrokeAdjustOff; // make gcc happy
+ if (doStroke) {
+ strokeAdjust = splash->getStrokeAdjust();
+ splash->setStrokeAdjust(splashStrokeAdjustOff);
+ }
+
+ // fill and stroke
+ if (doFill && doStroke) {
+ if (path) {
+ setOverprintMask(state, state->getFillColorSpace(),
+ state->getFillOverprint(), state->getOverprintMode(),
+ state->getFillColor());
+ splash->fill(path, gFalse);
+ setOverprintMask(state, state->getStrokeColorSpace(),
+ state->getStrokeOverprint(), state->getOverprintMode(),
+ state->getStrokeColor());
+ splash->stroke(path);
+ }
+
+ // fill
+ } else if (doFill) {
+ setOverprintMask(state, state->getFillColorSpace(),
+ state->getFillOverprint(), state->getOverprintMode(),
+ state->getFillColor());
+ splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);
+
+ // stroke
+ } else if (doStroke) {
+ if (path) {
+ setOverprintMask(state, state->getStrokeColorSpace(),
+ state->getStrokeOverprint(), state->getOverprintMode(),
+ state->getStrokeColor());
+ splash->stroke(path);
+ }
+ }
+
+ // clip
+ if (doClip) {
+ if (path) {
+ if (textClipPath) {
+ textClipPath->append(path);
+ } else {
+ textClipPath = path;
+ path = NULL;
+ }
+ }
+ }
+
+ if (doStroke) {
+ splash->setStrokeAdjust(strokeAdjust);
+ }
+
+ if (path) {
+ delete path;
+ }
+}
+
+GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
+ double dx, double dy,
+ CharCode code, Unicode *u, int uLen) {
+ GfxFont *gfxFont;
+ Ref *fontID;
+ double *ctm, *bbox;
+ T3FontCache *t3Font;
+ T3GlyphStack *t3gs;
+ GBool validBBox;
+ double m[4];
+ GBool horiz;
+ double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
+ int render, i, j;
+
+ if (skipHorizText || skipRotatedText) {
+ state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
+ horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
+ fabs(m[2]) < 0.001 && m[3] < 0;
+ if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
+ return gTrue;
+ }
+ }
+
+ // check for invisible text
+ render = state->getRender();
+ if (render == 3 || render == 7) {
+ return gTrue;
+ }
+
+ if (!(gfxFont = state->getFont())) {
+ return gTrue;
+ }
+ fontID = gfxFont->getID();
+ ctm = state->getCTM();
+ state->transform(0, 0, &xt, &yt);
+
+ // is it the first (MRU) font in the cache?
+ if (!(nT3Fonts > 0 &&
+ t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
+
+ // is the font elsewhere in the cache?
+ for (i = 1; i < nT3Fonts; ++i) {
+ if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
+ t3Font = t3FontCache[i];
+ for (j = i; j > 0; --j) {
+ t3FontCache[j] = t3FontCache[j - 1];
+ }
+ t3FontCache[0] = t3Font;
+ break;
+ }
+ }
+ if (i >= nT3Fonts) {
+
+ // create new entry in the font cache
+ if (nT3Fonts < splashOutT3FontCacheSize) {
+ for (j = nT3Fonts; j > 0; --j) {
+ t3FontCache[j] = t3FontCache[j - 1];
+ }
+ } else {
+ for (j = nT3Fonts - 1; j >= 0; --j) {
+ if (t3FontCache[j]->refCount == 0) {
+ break;
+ }
+ }
+ if (j < 0) {
+ error(errSyntaxError, -1, "Type 3 fonts nested too deeply");
+ return gTrue;
+ }
+ delete t3FontCache[j];
+ --nT3Fonts;
+ for (; j > 0; --j) {
+ t3FontCache[j] = t3FontCache[j - 1];
+ }
+ }
+ ++nT3Fonts;
+ bbox = gfxFont->getFontBBox();
+ if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
+ // unspecified bounding box -- just take a guess
+ xMin = xt - 5;
+ xMax = xMin + 30;
+ yMax = yt + 15;
+ yMin = yMax - 45;
+ validBBox = gFalse;
+ } else {
+ state->transform(bbox[0], bbox[1], &x1, &y1);
+ xMin = xMax = x1;
+ yMin = yMax = y1;
+ state->transform(bbox[0], bbox[3], &x1, &y1);
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+ state->transform(bbox[2], bbox[1], &x1, &y1);
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+ state->transform(bbox[2], bbox[3], &x1, &y1);
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+ validBBox = gTrue;
+ }
+ t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
+ (int)floor(xMin - xt) - 2,
+ (int)floor(yMin - yt) - 2,
+ (int)ceil(xMax) - (int)floor(xMin) + 4,
+ (int)ceil(yMax) - (int)floor(yMin) + 4,
+ validBBox,
+ colorMode != splashModeMono1);
+ }
+ }
+ t3Font = t3FontCache[0];
+
+ // is the glyph in the cache?
+ i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
+ for (j = 0; j < t3Font->cacheAssoc; ++j) {
+ if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
+ t3Font->cacheTags[i+j].code == code) {
+ drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j],
+ t3Font->cacheData + (i+j) * t3Font->glyphSize);
+ return 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();
+ t3gs->next = t3GlyphStack;
+ t3GlyphStack = t3gs;
+ t3GlyphStack->code = (Gushort)code;
+ t3GlyphStack->cache = t3Font;
+ t3GlyphStack->cacheTag = NULL;
+ t3GlyphStack->cacheData = NULL;
+ t3GlyphStack->haveDx = gFalse;
+ t3GlyphStack->doNotCache = gFalse;
+#if 1 //~t3-sa
+ t3GlyphStack->savedStrokeAdjust = splash->getStrokeAdjust();
+ splash->setStrokeAdjust(splashStrokeAdjustOff);
+#endif
+
+ return gFalse;
+}
+
+void SplashOutputDev::endType3Char(GfxState *state) {
+ T3GlyphStack *t3gs;
+ double *ctm;
+
+ if (t3GlyphStack->cacheTag) {
+ --nestCount;
+ memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
+ t3GlyphStack->cache->glyphSize);
+ delete bitmap;
+ delete splash;
+ bitmap = t3GlyphStack->origBitmap;
+ colorMode = bitmap->getMode();
+ splash = t3GlyphStack->origSplash;
+ ctm = state->getCTM();
+ state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
+ t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
+ updateCTM(state, 0, 0, 0, 0, 0, 0);
+ drawType3Glyph(state, t3GlyphStack->cache,
+ t3GlyphStack->cacheTag, t3GlyphStack->cacheData);
+ }
+#if 1 //~t3-sa
+ splash->setStrokeAdjust(t3GlyphStack->savedStrokeAdjust);
+#endif
+ t3gs = t3GlyphStack;
+ t3GlyphStack = t3gs->next;
+ --t3gs->cache->refCount;
+ delete t3gs;
+}
+
+void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
+ if (!t3GlyphStack) {
+ error(errSyntaxError, -1,
+ "Encountered d0 operator outside of Type 3 CharProc");
+ return;
+ }
+ t3GlyphStack->haveDx = gTrue;
+}
+
+void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
+ double llx, double lly, double urx, double ury) {
+ double *ctm;
+ T3FontCache *t3Font;
+ SplashColor color;
+ double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
+ int i, j;
+
+ if (!t3GlyphStack) {
+ error(errSyntaxError, -1,
+ "Encountered d1 operator outside of Type 3 CharProc");
+ return;
+ }
+
+ // ignore multiple d0/d1 operators
+ if (t3GlyphStack->haveDx) {
+ return;
+ }
+ t3GlyphStack->haveDx = gTrue;
+ // don't cache if we got a gsave/grestore before the d1
+ if (t3GlyphStack->doNotCache) {
+ return;
+ }
+
+ t3Font = t3GlyphStack->cache;
+
+ // check for a valid bbox
+ state->transform(0, 0, &xt, &yt);
+ state->transform(llx, lly, &x1, &y1);
+ xMin = xMax = x1;
+ yMin = yMax = y1;
+ state->transform(llx, ury, &x1, &y1);
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+ state->transform(urx, lly, &x1, &y1);
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+ state->transform(urx, ury, &x1, &y1);
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+ if (xMin - xt < t3Font->glyphX ||
+ yMin - yt < t3Font->glyphY ||
+ xMax - xt > t3Font->glyphX + t3Font->glyphW ||
+ yMax - yt > t3Font->glyphY + t3Font->glyphH) {
+ if (t3Font->validBBox) {
+ error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph");
+ }
+ return;
+ }
+
+ // allocate a cache entry
+ i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
+ for (j = 0; j < t3Font->cacheAssoc; ++j) {
+ if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
+ t3Font->cacheTags[i+j].mru = 0x8000;
+ t3Font->cacheTags[i+j].code = t3GlyphStack->code;
+ t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
+ t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
+ } else {
+ ++t3Font->cacheTags[i+j].mru;
+ }
+ }
+
+ // save state
+ t3GlyphStack->origBitmap = bitmap;
+ t3GlyphStack->origSplash = splash;
+ ctm = state->getCTM();
+ t3GlyphStack->origCTM4 = ctm[4];
+ t3GlyphStack->origCTM5 = ctm[5];
+
+ // 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,
+ t3GlyphStack->origSplash->getImageCache(),
+ t3GlyphStack->origSplash->getScreen());
+ color[0] = 0;
+ splash->clear(color);
+ 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,
+ t3GlyphStack->origSplash->getImageCache(),
+ t3GlyphStack->origSplash->getScreen());
+ color[0] = 0x00;
+ splash->clear(color);
+ color[0] = 0xff;
+ }
+ splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(t3GlyphStack->origSplash->getStrokeAdjust());
+ splash->setEnablePathSimplification(
+ globalParams->getEnablePathSimplification());
+ copyState(t3GlyphStack->origSplash, gFalse);
+ splash->setFillPattern(new SplashSolidColor(color));
+ splash->setStrokePattern(new SplashSolidColor(color));
+ state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
+ -t3Font->glyphX, -t3Font->glyphY);
+ updateCTM(state, 0, 0, 0, 0, 0, 0);
+ ++nestCount;
+}
+
+void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font,
+ T3FontCacheTag *tag, Guchar *data) {
+ SplashGlyphBitmap glyph;
+
+ setOverprintMask(state, state->getFillColorSpace(),
+ state->getFillOverprint(), state->getOverprintMode(),
+ state->getFillColor());
+ glyph.x = -t3Font->glyphX;
+ glyph.y = -t3Font->glyphY;
+ glyph.w = t3Font->glyphW;
+ glyph.h = t3Font->glyphH;
+ glyph.aa = colorMode != splashModeMono1;
+ glyph.data = data;
+ glyph.freeData = gFalse;
+ splash->fillGlyph(0, 0, &glyph);
+}
+
+void SplashOutputDev::endTextObject(GfxState *state) {
+ if (textClipPath) {
+ splash->clipToPath(textClipPath, gFalse);
+ delete textClipPath;
+ textClipPath = NULL;
+ }
+}
+
+struct SplashOutImageMaskData {
+ ImageStream *imgStr;
+ Guchar invert;
+ int width, height, y;
+};
+
+GBool SplashOutputDev::imageMaskSrc(void *data, Guchar *line) {
+ SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
+ Guchar *p;
+ SplashColorPtr q;
+ int x;
+
+ if (imgMaskData->y == imgMaskData->height ||
+ !(p = imgMaskData->imgStr->getLine())) {
+ memset(line, 0, imgMaskData->width);
+ return gFalse;
+ }
+ for (x = 0, q = line; x < imgMaskData->width; ++x) {
+ *q++ = *p++ ^ imgMaskData->invert;
+ }
+ ++imgMaskData->y;
+ return gTrue;
+}
+
+void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate) {
+ double *ctm;
+ SplashCoord mat[6];
+ SplashOutImageMaskData imgMaskData;
+ GString *imgTag;
+
+ if (state->getFillColorSpace()->isNonMarking()) {
+ return;
+ }
+ setOverprintMask(state, state->getFillColorSpace(),
+ state->getFillOverprint(), state->getOverprintMode(),
+ state->getFillColor());
+
+ ctm = state->getCTM();
+ mat[0] = ctm[0];
+ mat[1] = ctm[1];
+ mat[2] = -ctm[2];
+ mat[3] = -ctm[3];
+ mat[4] = ctm[2] + ctm[4];
+ mat[5] = ctm[3] + ctm[5];
+
+ reduceImageResolution(str, ctm, &width, &height);
+
+ imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
+ imgMaskData.imgStr->reset();
+ imgMaskData.invert = invert ? 0 : 1;
+ imgMaskData.width = width;
+ imgMaskData.height = height;
+ imgMaskData.y = 0;
+
+ imgTag = makeImageTag(ref, gfxRenderingIntentRelativeColorimetric, NULL);
+ splash->fillImageMask(imgTag,
+ &imageMaskSrc, &imgMaskData, width, height, mat,
+ t3GlyphStack != NULL, interpolate);
+
+ if (inlineImg) {
+ while (imgMaskData.y < height) {
+ imgMaskData.imgStr->getLine();
+ ++imgMaskData.y;
+ }
+ }
+
+ delete imgTag;
+ delete imgMaskData.imgStr;
+ str->close();
+}
+
+void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
+ Object *ref, Stream *str,
+ int width, int height,
+ GBool invert,
+ GBool inlineImg,
+ GBool interpolate) {
+ double *ctm;
+ SplashCoord mat[6];
+ SplashOutImageMaskData imgMaskData;
+ SplashBitmap *maskBitmap;
+ Splash *maskSplash;
+ SplashColor maskColor;
+ GString *imgTag;
+
+ ctm = state->getCTM();
+ mat[0] = ctm[0];
+ mat[1] = ctm[1];
+ mat[2] = -ctm[2];
+ mat[3] = -ctm[3];
+ mat[4] = ctm[2] + ctm[4];
+ mat[5] = ctm[3] + ctm[5];
+ reduceImageResolution(str, ctm, &width, &height);
+ imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
+ imgMaskData.imgStr->reset();
+ imgMaskData.invert = invert ? 0 : 1;
+ 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());
+ maskSplash->setStrokeAdjust(
+ mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
+ maskSplash->setEnablePathSimplification(
+ globalParams->getEnablePathSimplification());
+ clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
+ maskColor[0] = 0xff;
+ maskSplash->setFillPattern(new SplashSolidColor(maskColor));
+ imgTag = makeImageTag(ref, gfxRenderingIntentRelativeColorimetric, NULL);
+ maskSplash->fillImageMask(imgTag, &imageMaskSrc, &imgMaskData,
+ width, height, mat, gFalse, interpolate);
+ delete imgTag;
+ delete imgMaskData.imgStr;
+ str->close();
+ delete maskSplash;
+ splash->setSoftMask(maskBitmap);
+}
+
+struct SplashOutImageData {
+ ImageStream *imgStr;
+ GfxImageColorMap *colorMap;
+ GfxRenderingIntent ri;
+ SplashColorPtr lookup;
+ int *maskColors;
+ SplashColorMode colorMode;
+ GBool invert;
+ int width, height, y;
+};
+
+GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
+ Guchar *alphaLine) {
+ SplashOutImageData *imgData = (SplashOutImageData *)data;
+ Guchar *p;
+ SplashColorPtr q, col;
+ int n, x;
+
+ if (imgData->y == imgData->height ||
+ !(p = imgData->imgStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
+ return gFalse;
+ }
+
+ if (imgData->lookup) {
+ switch (imgData->colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
+ *q++ = imgData->lookup[*p];
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
+ col = &imgData->lookup[3 * *p];
+ *q++ = col[0];
+ *q++ = col[1];
+ *q++ = col[2];
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
+ col = &imgData->lookup[4 * *p];
+ *q++ = col[0];
+ *q++ = col[1];
+ *q++ = col[2];
+ *q++ = col[3];
+ }
+ break;
+#endif
+ }
+ } else {
+ switch (imgData->colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ imgData->colorMap->getGrayByteLine(p, colorLine, imgData->width,
+ imgData->ri);
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width,
+ imgData->ri);
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ imgData->colorMap->getCMYKByteLine(p, colorLine, imgData->width,
+ imgData->ri);
+ break;
+#endif
+ }
+ }
+
+ if (imgData->invert) {
+ n = imgData->width * splashColorModeNComps[imgData->colorMode];
+ for (x = 0, p = colorLine; x < n; ++x, ++p) {
+ *p ^= 0xff;
+ }
+ }
+
+ ++imgData->y;
+ return gTrue;
+}
+
+GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
+ Guchar *alphaLine) {
+ SplashOutImageData *imgData = (SplashOutImageData *)data;
+ Guchar *p0, *p, *aq;
+ SplashColorPtr q, col;
+ Guchar alpha;
+ int nComps, x, n, i;
+
+ if (imgData->y == imgData->height ||
+ !(p0 = imgData->imgStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
+ memset(alphaLine, 0, imgData->width);
+ return gFalse;
+ }
+
+ nComps = imgData->colorMap->getNumPixelComps();
+
+ if (imgData->lookup) {
+ switch (imgData->colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ for (x = 0, p = p0, q = colorLine; x < imgData->width; ++x, ++p) {
+ *q++ = imgData->lookup[*p];
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ for (x = 0, p = p0, q = colorLine; x < imgData->width; ++x, ++p) {
+ col = &imgData->lookup[3 * *p];
+ *q++ = col[0];
+ *q++ = col[1];
+ *q++ = col[2];
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ for (x = 0, p = p0, q = colorLine; x < imgData->width; ++x, ++p) {
+ col = &imgData->lookup[4 * *p];
+ *q++ = col[0];
+ *q++ = col[1];
+ *q++ = col[2];
+ *q++ = col[3];
+ }
+ break;
+#endif
+ }
+ } else {
+ switch (imgData->colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ imgData->colorMap->getGrayByteLine(p0, colorLine, imgData->width,
+ imgData->ri);
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ imgData->colorMap->getRGBByteLine(p0, colorLine, imgData->width,
+ imgData->ri);
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ imgData->colorMap->getCMYKByteLine(p0, colorLine, imgData->width,
+ imgData->ri);
+ break;
+#endif
+ }
+ }
+
+ for (x = 0, p = p0, aq = alphaLine; x < imgData->width; ++x, p += nComps) {
+ alpha = 0;
+ for (i = 0; i < nComps; ++i) {
+ if (p[i] < imgData->maskColors[2*i] ||
+ p[i] > imgData->maskColors[2*i+1]) {
+ alpha = 0xff;
+ break;
+ }
+ }
+ *aq++ = alpha;
+ }
+
+ if (imgData->invert) {
+ n = imgData->width * splashColorModeNComps[imgData->colorMode];
+ for (x = 0, p = colorLine; x < n; ++x, ++p) {
+ *p ^= 0xff;
+ }
+ }
+
+ ++imgData->y;
+ return gTrue;
+}
+
+
+void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg,
+ GBool interpolate) {
+ double *ctm;
+ SplashCoord mat[6];
+ SplashOutImageData imgData;
+ SplashColorMode srcMode;
+ SplashImageSource src;
+ GString *imgTag;
+ GfxGray gray;
+ GfxRGB rgb;
+#if SPLASH_CMYK
+ GfxCMYK cmyk;
+#endif
+ Guchar pix;
+ int n, i;
+
+ setOverprintMask(state, colorMap->getColorSpace(),
+ state->getFillOverprint(), state->getOverprintMode(),
+ NULL);
+
+ ctm = state->getCTM();
+ mat[0] = ctm[0];
+ mat[1] = ctm[1];
+ mat[2] = -ctm[2];
+ mat[3] = -ctm[3];
+ mat[4] = ctm[2] + ctm[4];
+ mat[5] = ctm[3] + ctm[5];
+
+ reduceImageResolution(str, ctm, &width, &height);
+
+ imgData.imgStr = new ImageStream(str, width,
+ colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgData.imgStr->reset();
+ imgData.colorMap = colorMap;
+ imgData.ri = state->getRenderingIntent();
+ imgData.maskColors = maskColors;
+ imgData.colorMode = colorMode;
+ imgData.invert = reverseVideo && reverseVideoInvertImages;
+ imgData.width = width;
+ imgData.height = height;
+ imgData.y = 0;
+
+ // special case for one-channel (monochrome/gray/separation) images:
+ // build a lookup table here
+ imgData.lookup = NULL;
+ if (colorMap->getNumPixelComps() == 1) {
+ if (colorMap->getBits() <= 8) {
+ n = 1 << colorMap->getBits();
+ } else {
+ // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit
+ n = 1 << 8;
+ }
+ switch (colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ imgData.lookup = (SplashColorPtr)gmalloc(n);
+ for (i = 0; i < n; ++i) {
+ pix = (Guchar)i;
+ colorMap->getGray(&pix, &gray, state->getRenderingIntent());
+ imgData.lookup[i] = colToByte(gray);
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
+ for (i = 0; i < n; ++i) {
+ pix = (Guchar)i;
+ colorMap->getRGB(&pix, &rgb, state->getRenderingIntent());
+ imgData.lookup[3*i] = colToByte(rgb.r);
+ imgData.lookup[3*i+1] = colToByte(rgb.g);
+ imgData.lookup[3*i+2] = colToByte(rgb.b);
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
+ for (i = 0; i < n; ++i) {
+ pix = (Guchar)i;
+ colorMap->getCMYK(&pix, &cmyk, state->getRenderingIntent());
+ imgData.lookup[4*i] = colToByte(cmyk.c);
+ imgData.lookup[4*i+1] = colToByte(cmyk.m);
+ imgData.lookup[4*i+2] = colToByte(cmyk.y);
+ imgData.lookup[4*i+3] = colToByte(cmyk.k);
+ }
+ break;
+#endif
+ }
+ }
+
+ if (colorMode == splashModeMono1) {
+ srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
+ } else {
+ srcMode = colorMode;
+ }
+ src = maskColors ? &alphaImageSrc : &imageSrc;
+ imgTag = makeImageTag(ref, state->getRenderingIntent(),
+ colorMap->getColorSpace());
+ splash->drawImage(imgTag,
+ src, &imgData, srcMode, maskColors ? gTrue : gFalse,
+ width, height, mat, interpolate);
+ if (inlineImg) {
+ while (imgData.y < height) {
+ imgData.imgStr->getLine();
+ ++imgData.y;
+ }
+ }
+
+ delete imgTag;
+ gfree(imgData.lookup);
+ delete imgData.imgStr;
+ str->close();
+}
+
+struct SplashOutMaskedImageData {
+ ImageStream *imgStr;
+ GfxImageColorMap *colorMap;
+ GfxRenderingIntent ri;
+ SplashBitmap *mask;
+ SplashColorPtr lookup;
+ SplashColorMode colorMode;
+ GBool invert;
+ int width, height, y;
+};
+
+GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
+ Guchar *alphaLine) {
+ SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
+ Guchar *p, *aq;
+ SplashColorPtr q, col;
+ static Guchar bitToByte[2] = {0x00, 0xff};
+ Guchar *maskPtr;
+ int maskShift;
+ int n, x;
+
+ if (imgData->y == imgData->height ||
+ !(p = imgData->imgStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
+ memset(alphaLine, 0, imgData->width);
+ return gFalse;
+ }
+
+ maskPtr = imgData->mask->getDataPtr() +
+ imgData->y * imgData->mask->getRowSize();
+ aq = alphaLine;
+ for (x = 0; x <= imgData->width - 8; x += 8) {
+ aq[0] = bitToByte[(*maskPtr >> 7) & 1];
+ aq[1] = bitToByte[(*maskPtr >> 6) & 1];
+ aq[2] = bitToByte[(*maskPtr >> 5) & 1];
+ aq[3] = bitToByte[(*maskPtr >> 4) & 1];
+ aq[4] = bitToByte[(*maskPtr >> 3) & 1];
+ aq[5] = bitToByte[(*maskPtr >> 2) & 1];
+ aq[6] = bitToByte[(*maskPtr >> 1) & 1];
+ aq[7] = bitToByte[*maskPtr & 1];
+ aq += 8;
+ ++maskPtr;
+ }
+ maskShift = 7;
+ for (; x < imgData->width; ++x) {
+ *aq++ = bitToByte[(*maskPtr >> maskShift) & 1];
+ --maskShift;
+ }
+
+ if (imgData->lookup) {
+ switch (imgData->colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
+ *q++ = imgData->lookup[*p];
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
+ col = &imgData->lookup[3 * *p];
+ *q++ = col[0];
+ *q++ = col[1];
+ *q++ = col[2];
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
+ col = &imgData->lookup[4 * *p];
+ *q++ = col[0];
+ *q++ = col[1];
+ *q++ = col[2];
+ *q++ = col[3];
+ }
+ break;
+#endif
+ }
+ } else {
+ switch (imgData->colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ imgData->colorMap->getGrayByteLine(p, colorLine, imgData->width,
+ imgData->ri);
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ imgData->colorMap->getRGBByteLine(p, colorLine, imgData->width,
+ imgData->ri);
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ imgData->colorMap->getCMYKByteLine(p, colorLine, imgData->width,
+ imgData->ri);
+ break;
+#endif
+ }
+ }
+
+ if (imgData->invert) {
+ n = imgData->width * splashColorModeNComps[imgData->colorMode];
+ for (x = 0, p = colorLine; x < n; ++x, ++p) {
+ *p ^= 0xff;
+ }
+ }
+
+ ++imgData->y;
+ return gTrue;
+}
+
+
+void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
+ Stream *str, int width, int height,
+ GfxImageColorMap *colorMap,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GBool maskInvert, GBool interpolate) {
+ GfxImageColorMap *maskColorMap;
+ Object maskDecode, decodeLow, decodeHigh;
+ double *ctm;
+ SplashCoord mat[6];
+ SplashOutMaskedImageData imgData;
+ SplashOutImageMaskData imgMaskData;
+ SplashColorMode srcMode;
+ SplashBitmap *maskBitmap;
+ Splash *maskSplash;
+ GString *imgTag;
+ SplashColor maskColor;
+ GfxGray gray;
+ GfxRGB rgb;
+#if SPLASH_CMYK
+ GfxCMYK cmyk;
+#endif
+ Guchar pix;
+ int n, i;
+
+ setOverprintMask(state, colorMap->getColorSpace(),
+ state->getFillOverprint(), state->getOverprintMode(),
+ NULL);
+
+ ctm = state->getCTM();
+ reduceImageResolution(str, ctm, &width, &height);
+ reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
+
+ // If the mask is higher resolution than the image, use
+ // drawSoftMaskedImage() instead.
+ if (maskWidth > width || maskHeight > height) {
+ decodeLow.initInt(maskInvert ? 0 : 1);
+ decodeHigh.initInt(maskInvert ? 1 : 0);
+ maskDecode.initArray(xref);
+ maskDecode.arrayAdd(&decodeLow);
+ maskDecode.arrayAdd(&decodeHigh);
+ maskColorMap = new GfxImageColorMap(1, &maskDecode,
+ new GfxDeviceGrayColorSpace());
+ maskDecode.free();
+ drawSoftMaskedImage(state, ref, str, width, height, colorMap,
+ maskRef, maskStr, maskWidth, maskHeight, maskColorMap,
+ NULL, interpolate);
+ delete maskColorMap;
+
+ } else {
+
+ //----- scale the mask image to the same size as the source image
+
+ mat[0] = (SplashCoord)width;
+ mat[1] = 0;
+ mat[2] = 0;
+ mat[3] = (SplashCoord)height;
+ mat[4] = 0;
+ mat[5] = 0;
+ imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
+ imgMaskData.imgStr->reset();
+ imgMaskData.invert = maskInvert ? 0 : 1;
+ 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());
+ maskSplash->setStrokeAdjust(
+ mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
+ maskSplash->setEnablePathSimplification(
+ globalParams->getEnablePathSimplification());
+ maskColor[0] = 0;
+ maskSplash->clear(maskColor);
+ maskColor[0] = 0xff;
+ maskSplash->setFillPattern(new SplashSolidColor(maskColor));
+ // use "glyph mode" here to get the correct scaled size
+ maskSplash->fillImageMask(NULL, &imageMaskSrc, &imgMaskData,
+ maskWidth, maskHeight, mat, gTrue, interpolate);
+ delete imgMaskData.imgStr;
+ maskStr->close();
+ delete maskSplash;
+
+ //----- draw the source image
+
+ mat[0] = ctm[0];
+ mat[1] = ctm[1];
+ mat[2] = -ctm[2];
+ mat[3] = -ctm[3];
+ mat[4] = ctm[2] + ctm[4];
+ mat[5] = ctm[3] + ctm[5];
+
+ imgData.imgStr = new ImageStream(str, width,
+ colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgData.imgStr->reset();
+ imgData.colorMap = colorMap;
+ imgData.ri = state->getRenderingIntent();
+ imgData.mask = maskBitmap;
+ imgData.colorMode = colorMode;
+ imgData.invert = reverseVideo && reverseVideoInvertImages;
+ imgData.width = width;
+ imgData.height = height;
+ imgData.y = 0;
+
+ // special case for one-channel (monochrome/gray/separation) images:
+ // build a lookup table here
+ imgData.lookup = NULL;
+ if (colorMap->getNumPixelComps() == 1) {
+ if (colorMap->getBits() <= 8) {
+ n = 1 << colorMap->getBits();
+ } else {
+ // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit
+ n = 1 << 8;
+ }
+ switch (colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ imgData.lookup = (SplashColorPtr)gmalloc(n);
+ for (i = 0; i < n; ++i) {
+ pix = (Guchar)i;
+ colorMap->getGray(&pix, &gray, state->getRenderingIntent());
+ imgData.lookup[i] = colToByte(gray);
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
+ for (i = 0; i < n; ++i) {
+ pix = (Guchar)i;
+ colorMap->getRGB(&pix, &rgb, state->getRenderingIntent());
+ imgData.lookup[3*i] = colToByte(rgb.r);
+ imgData.lookup[3*i+1] = colToByte(rgb.g);
+ imgData.lookup[3*i+2] = colToByte(rgb.b);
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
+ for (i = 0; i < n; ++i) {
+ pix = (Guchar)i;
+ colorMap->getCMYK(&pix, &cmyk, state->getRenderingIntent());
+ imgData.lookup[4*i] = colToByte(cmyk.c);
+ imgData.lookup[4*i+1] = colToByte(cmyk.m);
+ imgData.lookup[4*i+2] = colToByte(cmyk.y);
+ imgData.lookup[4*i+3] = colToByte(cmyk.k);
+ }
+ break;
+#endif
+ }
+ }
+
+ if (colorMode == splashModeMono1) {
+ srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
+ } else {
+ srcMode = colorMode;
+ }
+ imgTag = makeImageTag(ref, state->getRenderingIntent(),
+ colorMap->getColorSpace());
+ splash->drawImage(imgTag,
+ &maskedImageSrc, &imgData, srcMode, gTrue,
+ width, height, mat, interpolate);
+
+ delete imgTag;
+ delete maskBitmap;
+ gfree(imgData.lookup);
+ delete imgData.imgStr;
+ str->close();
+ }
+}
+
+struct SplashOutSoftMaskMatteImageData {
+ ImageStream *imgStr;
+ ImageStream *maskStr;
+ GfxImageColorMap *colorMap;
+ GfxRenderingIntent ri;
+ Guchar matte[gfxColorMaxComps];
+ SplashColorMode colorMode;
+ GBool invert;
+ int width, height, y;
+};
+
+GBool SplashOutputDev::softMaskMatteImageSrc(void *data,
+ SplashColorPtr colorLine,
+ Guchar *alphaLine) {
+ SplashOutSoftMaskMatteImageData *imgData =
+ (SplashOutSoftMaskMatteImageData *)data;
+ Guchar *p, *ap, *aq;
+ SplashColorPtr q;
+ GfxRGB rgb;
+ GfxGray gray;
+#if SPLASH_CMYK
+ GfxCMYK cmyk;
+#endif
+ Guchar alpha;
+ int nComps, n, x;
+
+ if (imgData->y == imgData->height ||
+ !(p = imgData->imgStr->getLine()) ||
+ !(ap = imgData->maskStr->getLine())) {
+ memset(colorLine, 0,
+ imgData->width * splashColorModeNComps[imgData->colorMode]);
+ memset(alphaLine, 0, imgData->width);
+ return gFalse;
+ }
+
+ nComps = imgData->colorMap->getNumPixelComps();
+
+ for (x = 0, q = colorLine, aq = alphaLine;
+ x < imgData->width;
+ ++x, p += nComps, ++ap) {
+ alpha = *ap;
+ switch (imgData->colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ if (alpha) {
+ imgData->colorMap->getGray(p, &gray, imgData->ri);
+ *q++ = clip255(imgData->matte[0] +
+ (255 * (colToByte(gray) - imgData->matte[0])) / alpha);
+ } else {
+ *q++ = 0;
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ if (alpha) {
+ imgData->colorMap->getRGB(p, &rgb, imgData->ri);
+ *q++ = clip255(imgData->matte[0] +
+ (255 * (colToByte(rgb.r) - imgData->matte[0])) / alpha);
+ *q++ = clip255(imgData->matte[1] +
+ (255 * (colToByte(rgb.g) - imgData->matte[1])) / alpha);
+ *q++ = clip255(imgData->matte[2] +
+ (255 * (colToByte(rgb.b) - imgData->matte[2])) / alpha);
+ } else {
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ if (alpha) {
+ imgData->colorMap->getCMYK(p, &cmyk, imgData->ri);
+ *q++ = clip255(imgData->matte[0] +
+ (255 * (colToByte(cmyk.c) - imgData->matte[0]))
+ / alpha);
+ *q++ = clip255(imgData->matte[1] +
+ (255 * (colToByte(cmyk.m) - imgData->matte[1]))
+ / alpha);
+ *q++ = clip255(imgData->matte[2] +
+ (255 * (colToByte(cmyk.y) - imgData->matte[2]))
+ / alpha);
+ *q++ = clip255(imgData->matte[3] +
+ (255 * (colToByte(cmyk.k) - imgData->matte[3]))
+ / alpha);
+ } else {
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ }
+ break;
+#endif
+ }
+ *aq++ = alpha;
+ }
+
+ if (imgData->invert) {
+ n = imgData->width * splashColorModeNComps[imgData->colorMode];
+ for (x = 0, p = colorLine; x < n; ++x, ++p) {
+ *p ^= 0xff;
+ }
+ }
+
+ ++imgData->y;
+ return gTrue;
+}
+
+
+void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
+ Stream *str, int width, int height,
+ GfxImageColorMap *colorMap,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ double *matte, GBool interpolate) {
+ double *ctm;
+ SplashCoord mat[6];
+ SplashOutImageData imgData;
+ SplashOutImageData imgMaskData;
+ SplashOutSoftMaskMatteImageData matteImgData;
+ GString *imgTag;
+ SplashColorMode srcMode;
+ SplashBitmap *maskBitmap;
+ Splash *maskSplash;
+ GfxColor matteColor;
+ GfxGray gray;
+ GfxRGB rgb;
+#if SPLASH_CMYK
+ GfxCMYK cmyk;
+#endif
+ Guchar pix;
+ int n, i;
+
+ setOverprintMask(state, colorMap->getColorSpace(),
+ state->getFillOverprint(), state->getOverprintMode(),
+ NULL);
+
+ ctm = state->getCTM();
+ mat[0] = ctm[0];
+ mat[1] = ctm[1];
+ mat[2] = -ctm[2];
+ mat[3] = -ctm[3];
+ mat[4] = ctm[2] + ctm[4];
+ mat[5] = ctm[3] + ctm[5];
+
+ if (colorMode == splashModeMono1) {
+ srcMode = splashModeMono8;
+ } else if (colorMode == splashModeBGR8) {
+ srcMode = splashModeRGB8;
+ } else {
+ srcMode = colorMode;
+ }
+
+ //----- handle a preblended image
+
+ if (matte && width == maskWidth && height == maskHeight) {
+
+ // the image and mask must be the same size, so don't call
+ // reduceImageResolution(), which might result in different
+ // reductions (e.g., if the image filter supports resolution
+ // reduction but the mask filter doesn't)
+
+ matteImgData.imgStr = new ImageStream(str, width,
+ colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ matteImgData.imgStr->reset();
+ matteImgData.maskStr = new ImageStream(maskStr, maskWidth,
+ maskColorMap->getNumPixelComps(),
+ maskColorMap->getBits());
+ matteImgData.maskStr->reset();
+ matteImgData.colorMap = colorMap;
+ matteImgData.ri = state->getRenderingIntent();
+ n = colorMap->getNumPixelComps();
+ for (i = 0; i < n; ++i) {
+ matteColor.c[i] = dblToCol(matte[i]);
+ }
+ switch (colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ colorMap->getColorSpace()->getGray(&matteColor, &gray,
+ state->getRenderingIntent());
+ matteImgData.matte[0] = colToByte(gray);
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ colorMap->getColorSpace()->getRGB(&matteColor, &rgb,
+ state->getRenderingIntent());
+ matteImgData.matte[0] = colToByte(rgb.r);
+ matteImgData.matte[1] = colToByte(rgb.g);
+ matteImgData.matte[2] = colToByte(rgb.b);
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ colorMap->getColorSpace()->getCMYK(&matteColor, &cmyk,
+ state->getRenderingIntent());
+ matteImgData.matte[0] = colToByte(cmyk.c);
+ matteImgData.matte[1] = colToByte(cmyk.m);
+ matteImgData.matte[2] = colToByte(cmyk.y);
+ matteImgData.matte[3] = colToByte(cmyk.k);
+ break;
+#endif
+ }
+ //~ could add the matteImgData.lookup special case
+ matteImgData.colorMode = colorMode;
+ matteImgData.invert = reverseVideo && reverseVideoInvertImages;
+ matteImgData.width = width;
+ matteImgData.height = height;
+ matteImgData.y = 0;
+ imgTag = makeImageTag(ref, state->getRenderingIntent(),
+ colorMap->getColorSpace());
+ splash->drawImage(imgTag, &softMaskMatteImageSrc, &matteImgData,
+ srcMode, gTrue, width, height, mat, interpolate);
+ delete imgTag;
+ delete matteImgData.maskStr;
+ delete matteImgData.imgStr;
+ maskStr->close();
+ str->close();
+
+ } else {
+
+ reduceImageResolution(str, ctm, &width, &height);
+ reduceImageResolution(maskStr, ctm, &maskWidth, &maskHeight);
+
+ //----- set up the soft mask
+
+ imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
+ maskColorMap->getNumPixelComps(),
+ maskColorMap->getBits());
+ imgMaskData.imgStr->reset();
+ imgMaskData.colorMap = maskColorMap;
+ imgMaskData.ri = state->getRenderingIntent();
+ imgMaskData.maskColors = NULL;
+ imgMaskData.colorMode = splashModeMono8;
+ imgMaskData.invert = gFalse;
+ imgMaskData.width = maskWidth;
+ imgMaskData.height = maskHeight;
+ imgMaskData.y = 0;
+ if (maskColorMap->getBits() <= 8) {
+ n = 1 << maskColorMap->getBits();
+ } else {
+ // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit
+ n = 1 << 8;
+ }
+ imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
+ for (i = 0; i < n; ++i) {
+ pix = (Guchar)i;
+ maskColorMap->getGray(&pix, &gray, state->getRenderingIntent());
+ imgMaskData.lookup[i] = colToByte(gray);
+ }
+ traceMessage("soft masked image bitmap");
+ maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
+ 1, splashModeMono8, gFalse, gTrue, bitmap);
+ maskSplash = new Splash(maskBitmap, vectorAntialias,
+ splash->getImageCache());
+ maskSplash->setStrokeAdjust(
+ mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
+ maskSplash->setEnablePathSimplification(
+ globalParams->getEnablePathSimplification());
+ clearMaskRegion(state, maskSplash, 0, 0, 1, 1);
+ maskSplash->drawImage(NULL,
+ &imageSrc, &imgMaskData, splashModeMono8, gFalse,
+ maskWidth, maskHeight, mat, interpolate);
+ delete imgMaskData.imgStr;
+ maskStr->close();
+ gfree(imgMaskData.lookup);
+ delete maskSplash;
+ splash->setSoftMask(maskBitmap);
+
+ //----- draw the source image
+
+ imgData.imgStr = new ImageStream(str, width,
+ colorMap->getNumPixelComps(),
+ colorMap->getBits());
+ imgData.imgStr->reset();
+ imgData.colorMap = colorMap;
+ imgData.ri = state->getRenderingIntent();
+ imgData.maskColors = NULL;
+ imgData.colorMode = colorMode;
+ imgData.invert = reverseVideo && reverseVideoInvertImages;
+ imgData.width = width;
+ imgData.height = height;
+ imgData.y = 0;
+
+ // special case for one-channel (monochrome/gray/separation) images:
+ // build a lookup table here
+ imgData.lookup = NULL;
+ if (colorMap->getNumPixelComps() == 1) {
+ if (colorMap->getBits() <= 8) {
+ n = 1 << colorMap->getBits();
+ } else {
+ // GfxImageColorMap and ImageStream compress 16-bit samples to 8-bit
+ n = 1 << 8;
+ }
+ switch (colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ imgData.lookup = (SplashColorPtr)gmalloc(n);
+ for (i = 0; i < n; ++i) {
+ pix = (Guchar)i;
+ colorMap->getGray(&pix, &gray, state->getRenderingIntent());
+ imgData.lookup[i] = colToByte(gray);
+ }
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
+ for (i = 0; i < n; ++i) {
+ pix = (Guchar)i;
+ colorMap->getRGB(&pix, &rgb, state->getRenderingIntent());
+ imgData.lookup[3*i] = colToByte(rgb.r);
+ imgData.lookup[3*i+1] = colToByte(rgb.g);
+ imgData.lookup[3*i+2] = colToByte(rgb.b);
+ }
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
+ for (i = 0; i < n; ++i) {
+ pix = (Guchar)i;
+ colorMap->getCMYK(&pix, &cmyk, state->getRenderingIntent());
+ imgData.lookup[4*i] = colToByte(cmyk.c);
+ imgData.lookup[4*i+1] = colToByte(cmyk.m);
+ imgData.lookup[4*i+2] = colToByte(cmyk.y);
+ imgData.lookup[4*i+3] = colToByte(cmyk.k);
+ }
+ break;
+#endif
+ }
+ }
+
+ imgTag = makeImageTag(ref, state->getRenderingIntent(),
+ colorMap->getColorSpace());
+ splash->drawImage(imgTag,
+ &imageSrc, &imgData, srcMode, gFalse, width, height, mat,
+ interpolate);
+
+ splash->setSoftMask(NULL);
+ delete imgTag;
+ gfree(imgData.lookup);
+ delete imgData.imgStr;
+
+
+ str->close();
+ }
+}
+
+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}_{2:d}",
+ ref->getRefNum(), ref->getRefGen(), (int)ri);
+}
+
+void SplashOutputDev::reduceImageResolution(Stream *str, double *ctm,
+ int *width, int *height) {
+ double sw, sh;
+ int reduction;
+
+ if (str->getKind() == strJPX &&
+ *width >= 256 &&
+ *height >= 256 &&
+ *width * *height > 10000000) {
+ sw = (double)*width / (fabs(ctm[0]) + fabs(ctm[1]));
+ sh = (double)*height / (fabs(ctm[2]) + fabs(ctm[3]));
+ if (sw > 8 && sh > 8) {
+ reduction = 3;
+ } else if (sw > 4 && sh > 4) {
+ reduction = 2;
+ } else if (sw > 2 && sh > 2) {
+ reduction = 1;
+ } else {
+ reduction = 0;
+ }
+ if (reduction > 0) {
+ ((JPXStream *)str)->reduceResolution(reduction);
+ *width >>= reduction;
+ *height >>= reduction;
+ }
+ }
+}
+
+void SplashOutputDev::clearMaskRegion(GfxState *state,
+ Splash *maskSplash,
+ double xMin, double yMin,
+ double xMax, double yMax) {
+ SplashBitmap *maskBitmap;
+ double xxMin, yyMin, xxMax, yyMax, xx, yy;
+ int xxMinI, yyMinI, xxMaxI, yyMaxI, y, n;
+ Guchar *p;
+
+ maskBitmap = maskSplash->getBitmap();
+ xxMin = maskBitmap->getWidth();
+ xxMax = 0;
+ yyMin = maskBitmap->getHeight();
+ yyMax = 0;
+ state->transform(xMin, yMin, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ state->transform(xMin, yMax, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ state->transform(xMax, yMin, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ state->transform(xMax, yMax, &xx, &yy);
+ if (xx < xxMin) { xxMin = xx; }
+ if (xx > xxMax) { xxMax = xx; }
+ if (yy < yyMin) { yyMin = yy; }
+ if (yy > yyMax) { yyMax = yy; }
+ xxMinI = (int)floor(xxMin);
+ if (xxMinI < 0) {
+ xxMinI = 0;
+ }
+ xxMaxI = (int)ceil(xxMax);
+ if (xxMaxI > maskBitmap->getWidth()) {
+ xxMaxI = maskBitmap->getWidth();
+ }
+ yyMinI = (int)floor(yyMin);
+ if (yyMinI < 0) {
+ yyMinI = 0;
+ }
+ yyMaxI = (int)ceil(yyMax);
+ if (yyMaxI > maskBitmap->getHeight()) {
+ yyMaxI = maskBitmap->getHeight();
+ }
+ p = maskBitmap->getDataPtr() + yyMinI * maskBitmap->getRowSize();
+ if (maskBitmap->getMode() == splashModeMono1) {
+ n = (xxMaxI + 7) / 8 - xxMinI / 8;
+ p += xxMinI / 8;
+ } else {
+ n = xxMaxI - xxMinI;
+ p += xxMinI;
+ }
+ if (xxMaxI > xxMinI) {
+ for (y = yyMinI; y < yyMaxI; ++y) {
+ memset(p, 0, n);
+ p += maskBitmap->getRowSize();
+ }
+ }
+}
+
+void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
+ GfxColorSpace *blendingColorSpace,
+ GBool isolated, GBool knockout,
+ GBool forSoftMask) {
+ SplashTransparencyGroup *transpGroup;
+ SplashBitmap *backdropBitmap;
+ SplashColor color;
+ double xMin, yMin, xMax, yMax, x, y;
+ int bw, bh, tx, ty, w, h, i;
+
+ // transform the bbox
+ state->transform(bbox[0], bbox[1], &x, &y);
+ xMin = xMax = x;
+ yMin = yMax = y;
+ state->transform(bbox[0], bbox[3], &x, &y);
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ state->transform(bbox[2], bbox[1], &x, &y);
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ state->transform(bbox[2], bbox[3], &x, &y);
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+
+ // clip the box
+ x = splash->getClip()->getXMin();
+ if (x > xMin) {
+ xMin = x;
+ }
+ x = splash->getClip()->getXMax();
+ if (x < xMax) {
+ xMax = x;
+ }
+ y = splash->getClip()->getYMin();
+ if (y > yMin) {
+ yMin = y;
+ }
+ y = splash->getClip()->getYMax();
+ if (y < yMax) {
+ yMax = y;
+ }
+
+ // convert box coords to integers
+ bw = bitmap->getWidth();
+ bh = bitmap->getHeight();
+ tx = (int)floor(xMin);
+ if (tx < 0) {
+ tx = 0;
+ } else if (tx >= bw) {
+ tx = bw - 1;
+ }
+ ty = (int)floor(yMin);
+ if (ty < 0) {
+ ty = 0;
+ } else if (ty >= bh) {
+ ty = bh - 1;
+ }
+ w = (int)ceil(xMax) - tx + 1;
+ // NB bw and tx are both non-negative, so 'bw - tx' can't overflow
+ if (bw - tx < w) {
+ w = bw - tx;
+ }
+ if (w < 1) {
+ w = 1;
+ }
+ h = (int)ceil(yMax) - ty + 1;
+ // NB bh and ty are both non-negative, so 'bh - ty' can't overflow
+ if (bh - ty < h) {
+ h = bh - ty;
+ }
+ if (h < 1) {
+ h = 1;
+ }
+
+ // push a new stack entry
+ transpGroup = new SplashTransparencyGroup();
+ transpGroup->tx = tx;
+ transpGroup->ty = ty;
+ transpGroup->blendingColorSpace = blendingColorSpace;
+ transpGroup->isolated = isolated;
+ transpGroup->next = transpGroupStack;
+ transpGroupStack = transpGroup;
+
+ // save state
+ transpGroup->origBitmap = bitmap;
+ transpGroup->origSplash = splash;
+
+ //~ this handles the blendingColorSpace arg for soft masks, but
+ //~ not yet for transparency groups
+
+ // switch to the blending color space
+ if (forSoftMask && isolated && !knockout && blendingColorSpace) {
+ if (blendingColorSpace->getMode() == csDeviceGray ||
+ blendingColorSpace->getMode() == csCalGray ||
+ (blendingColorSpace->getMode() == csICCBased &&
+ blendingColorSpace->getNComps() == 1)) {
+ colorMode = splashModeMono8;
+ } else if (blendingColorSpace->getMode() == csDeviceRGB ||
+ blendingColorSpace->getMode() == csCalRGB ||
+ (blendingColorSpace->getMode() == csICCBased &&
+ blendingColorSpace->getNComps() == 3)) {
+ //~ does this need to use BGR8?
+ colorMode = splashModeRGB8;
+#if SPLASH_CMYK
+ } else if (blendingColorSpace->getMode() == csDeviceCMYK ||
+ (blendingColorSpace->getMode() == csICCBased &&
+ blendingColorSpace->getNComps() == 4)) {
+ colorMode = splashModeCMYK8;
+#endif
+ }
+ }
+
+ // create the temporary bitmap
+ traceMessage("t-group bitmap");
+ bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
+ bitmapTopDown, transpGroup->origBitmap);
+ splash = new Splash(bitmap, vectorAntialias,
+ transpGroup->origSplash->getImageCache(),
+ transpGroup->origSplash->getScreen());
+ splash->setMinLineWidth(globalParams->getMinLineWidth());
+ splash->setStrokeAdjust(
+ mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
+ 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) {
+ // 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);
+ }
+ } 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;
+ 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;
+ 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;
+#if 1 //~tmp
+ if (knockout) {
+ splash->setInShading(gTrue);
+ }
+#endif
+ state->shiftCTM(-tx, -ty);
+ updateCTM(state, 0, 0, 0, 0, 0, 0);
+ ++nestCount;
+}
+
+void SplashOutputDev::endTransparencyGroup(GfxState *state) {
+ splash->getModRegion(&transpGroupStack->modXMin, &transpGroupStack->modYMin,
+ &transpGroupStack->modXMax, &transpGroupStack->modYMax);
+
+ // restore state
+ --nestCount;
+ delete splash;
+ bitmap = transpGroupStack->origBitmap;
+ colorMode = bitmap->getMode();
+ splash = transpGroupStack->origSplash;
+ state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty);
+ updateCTM(state, 0, 0, 0, 0, 0, 0);
+}
+
+void SplashOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) {
+ SplashBitmap *tBitmap;
+ SplashTransparencyGroup *transpGroup;
+ GBool isolated;
+ int xSrc, ySrc, xDest, yDest, w, h;
+
+ xSrc = transpGroupStack->modXMin;
+ ySrc = transpGroupStack->modYMin;
+ xDest = transpGroupStack->tx + transpGroupStack->modXMin;
+ yDest = transpGroupStack->ty + transpGroupStack->modYMin;
+ w = transpGroupStack->modXMax - transpGroupStack->modXMin + 1;
+ h = transpGroupStack->modYMax - transpGroupStack->modYMin + 1;
+ tBitmap = transpGroupStack->tBitmap;
+ isolated = transpGroupStack->isolated;
+
+ // paint the transparency group onto the parent bitmap
+ // - the clip path was set in the parent's state)
+ if (xDest < bitmap->getWidth() && yDest < bitmap->getHeight() &&
+ w > 0 && h > 0) {
+ splash->setOverprintMask(0xffffffff);
+ splash->composite(tBitmap, xSrc, ySrc, xDest, yDest, w, h,
+ gFalse, !isolated);
+ }
+
+ // free the temporary backdrop bitmap
+ if (transpGroupStack->backdropBitmap) {
+ delete transpGroupStack->backdropBitmap;
+ }
+
+ // pop the stack
+ transpGroup = transpGroupStack;
+ transpGroupStack = transpGroup->next;
+ delete transpGroup;
+
+ delete tBitmap;
+}
+
+void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
+ GBool alpha, Function *transferFunc,
+ GfxColor *backdropColor) {
+ SplashBitmap *softMask, *tBitmap;
+ Splash *tSplash;
+ SplashTransparencyGroup *transpGroup;
+ SplashColor color;
+ SplashColorPtr p, colorPtr, colorPtr2;
+ Guchar *alphaPtr;
+ GfxGray gray;
+ GfxRGB rgb;
+#if SPLASH_CMYK
+ GfxCMYK cmyk;
+#endif
+ double backdrop, backdrop2, lum, lum2;
+ Guchar lum8;
+ SplashBitmapRowSize rowSize;
+ int tw, th, tNComps, tx, ty, x, y;
+
+ tx = transpGroupStack->tx;
+ ty = transpGroupStack->ty;
+ tBitmap = transpGroupStack->tBitmap;
+
+ // composite with backdrop color
+ backdrop = 0;
+ if (!alpha && tBitmap->getMode() != splashModeMono1) {
+ //~ need to correctly handle the case where no blending color
+ //~ space is given
+ if (transpGroupStack->blendingColorSpace) {
+ tSplash = new Splash(tBitmap, vectorAntialias,
+ transpGroupStack->origSplash->getImageCache(),
+ transpGroupStack->origSplash->getScreen());
+ tSplash->setStrokeAdjust(
+ mapStrokeAdjustMode[globalParams->getStrokeAdjust()]);
+ tSplash->setEnablePathSimplification(
+ globalParams->getEnablePathSimplification());
+ switch (tBitmap->getMode()) {
+ case splashModeMono1:
+ // transparency is not supported in mono1 mode
+ break;
+ case splashModeMono8:
+ transpGroupStack->blendingColorSpace->getGray(
+ backdropColor, &gray, state->getRenderingIntent());
+ backdrop = colToDbl(gray);
+ color[0] = colToByte(gray);
+ tSplash->compositeBackground(color);
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ transpGroupStack->blendingColorSpace->getRGB(
+ backdropColor, &rgb, state->getRenderingIntent());
+ backdrop = 0.3 * colToDbl(rgb.r) +
+ 0.59 * colToDbl(rgb.g) +
+ 0.11 * colToDbl(rgb.b);
+ color[0] = colToByte(rgb.r);
+ color[1] = colToByte(rgb.g);
+ color[2] = colToByte(rgb.b);
+ tSplash->compositeBackground(color);
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ transpGroupStack->blendingColorSpace->getCMYK(
+ backdropColor, &cmyk, state->getRenderingIntent());
+ backdrop = (1 - colToDbl(cmyk.k))
+ - 0.3 * colToDbl(cmyk.c)
+ - 0.59 * colToDbl(cmyk.m)
+ - 0.11 * colToDbl(cmyk.y);
+ if (backdrop < 0) {
+ backdrop = 0;
+ }
+ color[0] = colToByte(cmyk.c);
+ color[1] = colToByte(cmyk.m);
+ color[2] = colToByte(cmyk.y);
+ color[3] = colToByte(cmyk.k);
+ tSplash->compositeBackground(color);
+ break;
+#endif
+ }
+ delete tSplash;
+ }
+ }
+ if (transferFunc) {
+ transferFunc->transform(&backdrop, &backdrop2);
+ } else {
+ backdrop2 = backdrop;
+ }
+
+ traceMessage("soft mask bitmap");
+ softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
+ 1, splashModeMono8, gFalse, gTrue, bitmap);
+ memset(softMask->getDataPtr(), (int)(backdrop2 * 255.0 + 0.5),
+ softMask->getRowSize() * softMask->getHeight());
+ if (tx < softMask->getWidth() && ty < softMask->getHeight()) {
+ p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
+ 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:
+ lum8 = colorPtr2[0];
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ // [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:
+ 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;
+ }
+ p += rowSize;
+ colorPtr += tBitmap->getRowSize();
+ }
+ }
+ }
+ splash->setSoftMask(softMask);
+
+ // free the temporary backdrop bitmap
+ if (transpGroupStack->backdropBitmap) {
+ delete transpGroupStack->backdropBitmap;
+ }
+
+ // pop the stack
+ transpGroup = transpGroupStack;
+ transpGroupStack = transpGroup->next;
+ delete transpGroup;
+
+ delete tBitmap;
+}
+
+void SplashOutputDev::clearSoftMask(GfxState *state) {
+ splash->setSoftMask(NULL);
+}
+
+void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
+ splashColorCopy(paperColor, paperColorA);
+}
+
+int SplashOutputDev::getBitmapWidth() {
+ return bitmap->getWidth();
+}
+
+int SplashOutputDev::getBitmapHeight() {
+ return bitmap->getHeight();
+}
+
+SplashBitmap *SplashOutputDev::takeBitmap() {
+ SplashBitmap *ret;
+
+ ret = bitmap;
+ bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
+ colorMode != splashModeMono1, bitmapTopDown, NULL);
+ return ret;
+}
+
+void SplashOutputDev::getModRegion(int *xMin, int *yMin,
+ int *xMax, int *yMax) {
+ splash->getModRegion(xMin, yMin, xMax, yMax);
+}
+
+void SplashOutputDev::clearModRegion() {
+ splash->clearModRegion();
+}
+
+void SplashOutputDev::setFillColor(int r, int g, int b) {
+ GfxRGB rgb;
+ GfxGray gray;
+#if SPLASH_CMYK
+ GfxCMYK cmyk;
+#endif
+
+ rgb.r = byteToCol((Guchar)r);
+ rgb.g = byteToCol((Guchar)g);
+ rgb.b = byteToCol((Guchar)b);
+ switch (colorMode) {
+ case splashModeMono1:
+ case splashModeMono8:
+ gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5);
+ if (gray > gfxColorComp1) {
+ gray = gfxColorComp1;
+ }
+ splash->setFillPattern(getColor(gray));
+ break;
+ case splashModeRGB8:
+ case splashModeBGR8:
+ splash->setFillPattern(getColor(&rgb));
+ break;
+#if SPLASH_CMYK
+ case splashModeCMYK8:
+ cmyk.c = gfxColorComp1 - rgb.r;
+ cmyk.m = gfxColorComp1 - rgb.g;
+ cmyk.y = gfxColorComp1 - rgb.b;
+ cmyk.k = 0;
+ splash->setFillPattern(getColor(&cmyk));
+ break;
+#endif
+ }
+}
+
+SplashFont *SplashOutputDev::getFont(GString *name, SplashCoord *textMatA) {
+ Ref ref;
+ SplashOutFontFileID *id;
+ GfxFontLoc *fontLoc;
+#if LOAD_FONTS_FROM_MEM
+ GString *fontBuf;
+ FILE *extFontFile;
+ char blk[4096];
+ int n;
+#endif
+ SplashFontFile *fontFile;
+ SplashFont *fontObj;
+ FoFiTrueType *ff;
+ int *codeToGID;
+ Unicode u;
+ SplashCoord textMat[4];
+ SplashCoord oblique;
+ int cmap, cmapPlatform, cmapEncoding, i;
+
+ for (i = 0; i < nBuiltinFonts; ++i) {
+ if (!name->cmp(builtinFonts[i].name)) {
+ break;
+ }
+ }
+ if (i == nBuiltinFonts) {
+ return NULL;
+ }
+ ref.num = i;
+ ref.gen = -1;
+ id = new SplashOutFontFileID(&ref);
+
+ // check the font file cache
+ if ((fontFile = fontEngine->getFontFile(id))) {
+ delete id;
+
+ // load the font file
+ } else {
+ if (!(fontLoc = GfxFont::locateBase14Font(name))) {
+ return NULL;
+ }
+#if LOAD_FONTS_FROM_MEM
+ fontBuf = NULL;
+ if (fontLoc->fontType == fontType1 ||
+ fontLoc->fontType == fontTrueType) {
+ if (!(extFontFile = fopen(fontLoc->path->getCString(), "rb"))) {
+ delete fontLoc;
+ delete id;
+ return NULL;
+ }
+ fontBuf = new GString();
+ while ((n = fread(blk, 1, sizeof(blk), extFontFile)) > 0) {
+ fontBuf->append(blk, n);
+ }
+ fclose(extFontFile);
+ }
+#endif
+ if (fontLoc->fontType == fontType1) {
+ fontFile = fontEngine->loadType1Font(id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fontLoc->path->getCString(),
+ gFalse,
+#endif
+ winAnsiEncoding);
+ } else if (fontLoc->fontType == fontTrueType) {
+#if LOAD_FONTS_FROM_MEM
+ if (!(ff = FoFiTrueType::make(fontBuf->getCString(),
+ fontBuf->getLength(),
+ fontLoc->fontNum))) {
+#else
+ if (!(ff = FoFiTrueType::load(fontLoc->path->getCString(),
+ fontLoc->fontNum))) {
+#endif
+ delete fontLoc;
+ delete id;
+ return NULL;
+ }
+ for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
+ cmapPlatform = ff->getCmapPlatform(cmap);
+ cmapEncoding = ff->getCmapEncoding(cmap);
+ if ((cmapPlatform == 3 && cmapEncoding == 1) ||
+ (cmapPlatform == 0 && cmapEncoding <= 4)) {
+ break;
+ }
+ }
+ if (cmap == ff->getNumCmaps()) {
+ delete ff;
+ delete fontLoc;
+ delete id;
+ return NULL;
+ }
+ codeToGID = (int *)gmallocn(256, sizeof(int));
+ for (i = 0; i < 256; ++i) {
+ codeToGID[i] = 0;
+ if (winAnsiEncoding[i] &&
+ (u = globalParams->mapNameToUnicode(winAnsiEncoding[i]))) {
+ codeToGID[i] = ff->mapCodeToGID(cmap, u);
+ }
+ }
+ delete ff;
+ fontFile = fontEngine->loadTrueTypeFont(id,
+#if LOAD_FONTS_FROM_MEM
+ fontBuf,
+#else
+ fontLoc->path->getCString(),
+ gFalse,
+#endif
+ fontLoc->fontNum,
+ codeToGID, 256, NULL);
+ } else {
+ delete fontLoc;
+ delete id;
+ return NULL;
+ }
+ delete fontLoc;
+ }
+ if (!fontFile) {
+ return NULL;
+ }
+
+ // create the scaled font
+ oblique = (SplashCoord)
+ ((SplashOutFontFileID *)fontFile->getID())->getOblique();
+ textMat[0] = (SplashCoord)textMatA[0];
+ textMat[1] = (SplashCoord)textMatA[1];
+ textMat[2] = oblique * textMatA[0] + textMatA[2];
+ textMat[3] = oblique * textMatA[1] + textMatA[3];
+ fontObj = fontEngine->getFont(fontFile, textMat, splash->getMatrix());
+
+ return fontObj;
+}
+
+// This is called when initializing a temporary Splash object for Type
+// 3 characters and transparency groups. Acrobat apparently copies at
+// least the fill and stroke colors, and the line parameters.
+//~ not sure what else should be copied -- the PDF spec is unclear
+//~ - fill and stroke alpha?
+void SplashOutputDev::copyState(Splash *oldSplash, GBool copyColors) {
+ // cached Type 3 chars set a color, so no need to copy the color here
+ if (copyColors) {
+ splash->setFillPattern(oldSplash->getFillPattern()->copy());
+ splash->setStrokePattern(oldSplash->getStrokePattern()->copy());
+ }
+ splash->setLineDash(oldSplash->getLineDash(),
+ oldSplash->getLineDashLength(),
+ oldSplash->getLineDashPhase());
+ splash->setLineCap(oldSplash->getLineCap());
+ splash->setLineJoin(oldSplash->getLineJoin());
+ splash->setLineWidth(oldSplash->getLineWidth());
+}
+
+#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);
+}
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h
new file mode 100644
index 00000000000..cbe47b61cb7
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/SplashOutputDev.h
@@ -0,0 +1,320 @@
+//========================================================================
+//
+// SplashOutputDev.h
+//
+// Copyright 2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SPLASHOUTPUTDEV_H
+#define SPLASHOUTPUTDEV_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "SplashTypes.h"
+#include "config.h"
+#include "OutputDev.h"
+#include "GfxState.h"
+
+class Gfx8BitFont;
+class SplashBitmap;
+class Splash;
+class SplashPath;
+class SplashPattern;
+class SplashFontEngine;
+class SplashFont;
+class T3FontCache;
+struct T3FontCacheTag;
+struct T3GlyphStack;
+struct SplashTransparencyGroup;
+
+//------------------------------------------------------------------------
+
+// number of Type 3 fonts to cache
+#define splashOutT3FontCacheSize 8
+
+//------------------------------------------------------------------------
+// SplashOutputDev
+//------------------------------------------------------------------------
+
+class SplashOutputDev: public OutputDev {
+public:
+
+ // Constructor.
+ SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA,
+ GBool reverseVideoA, SplashColorPtr paperColorA,
+ GBool bitmapTopDownA = gTrue,
+ GBool allowAntialiasA = gTrue);
+
+
+ // Destructor.
+ virtual ~SplashOutputDev();
+
+ //----- get info about output device
+
+ // Does this device use upside-down coordinates?
+ // (Upside-down means (0,0) is the top left corner of the page.)
+ virtual GBool upsideDown() { return bitmapTopDown ^ bitmapUpsideDown; }
+
+ // Does this device use drawChar() or drawString()?
+ virtual GBool useDrawChar() { return gTrue; }
+
+ // Does this device use tilingPatternFill()? If this returns false,
+ // tiling pattern fills will be reduced to a series of other drawing
+ // operations.
+ virtual GBool useTilingPatternFill() { 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; }
+
+
+
+ //----- initialization and control
+
+ // Start a page.
+ virtual void startPage(int pageNum, GfxState *state);
+
+ // End a page.
+ virtual void endPage();
+
+ //----- save/restore graphics state
+ virtual void saveState(GfxState *state);
+ virtual void restoreState(GfxState *state);
+
+ //----- update graphics state
+ virtual void updateAll(GfxState *state);
+ virtual void updateCTM(GfxState *state, double m11, double m12,
+ double m21, double m22, double m31, double m32);
+ virtual void updateLineDash(GfxState *state);
+ virtual void updateFlatness(GfxState *state);
+ virtual void updateLineJoin(GfxState *state);
+ virtual void updateLineCap(GfxState *state);
+ virtual void updateMiterLimit(GfxState *state);
+ virtual void updateLineWidth(GfxState *state);
+ virtual void updateStrokeAdjust(GfxState *state);
+ virtual void updateFillColor(GfxState *state);
+ virtual void updateStrokeColor(GfxState *state);
+ virtual void updateBlendMode(GfxState *state);
+ virtual void updateFillOpacity(GfxState *state);
+ virtual void updateStrokeOpacity(GfxState *state);
+ virtual void updateRenderingIntent(GfxState *state);
+ virtual void updateTransfer(GfxState *state);
+
+ //----- update text state
+ virtual void updateFont(GfxState *state);
+
+ //----- path painting
+ virtual void stroke(GfxState *state);
+ virtual void fill(GfxState *state);
+ virtual void eoFill(GfxState *state);
+ virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
+ int paintType, int tilingType, Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep);
+ virtual GBool shadedFill(GfxState *state, GfxShading *shading);
+
+ //----- path clipping
+ virtual void clip(GfxState *state);
+ virtual void eoClip(GfxState *state);
+ virtual void clipToStrokePath(GfxState *state);
+
+ //----- text drawing
+ virtual void drawChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ double originX, double originY,
+ CharCode code, int nBytes, Unicode *u, int uLen);
+ virtual GBool beginType3Char(GfxState *state, double x, double y,
+ double dx, double dy,
+ CharCode code, Unicode *u, int uLen);
+ virtual void endType3Char(GfxState *state);
+ virtual void endTextObject(GfxState *state);
+
+ //----- image drawing
+ virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate);
+ virtual void setSoftMaskFromImageMask(GfxState *state,
+ Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg, GBool interpolate);
+ virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg, GBool interpolate);
+ virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ 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,
+ Object *maskRef, Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap,
+ double *matte, GBool interpolate);
+
+ //----- Type 3 font operators
+ virtual void type3D0(GfxState *state, double wx, double wy);
+ virtual void type3D1(GfxState *state, double wx, double wy,
+ double llx, double lly, double urx, double ury);
+
+ //----- transparency groups and soft masks
+ virtual void beginTransparencyGroup(GfxState *state, double *bbox,
+ GfxColorSpace *blendingColorSpace,
+ GBool isolated, GBool knockout,
+ GBool forSoftMask);
+ virtual void endTransparencyGroup(GfxState *state);
+ virtual void paintTransparencyGroup(GfxState *state, double *bbox);
+ virtual void setSoftMask(GfxState *state, double *bbox, GBool alpha,
+ Function *transferFunc, GfxColor *backdropColor);
+ virtual void clearSoftMask(GfxState *state);
+
+ //----- special access
+
+ // Called to indicate that a new PDF document has been loaded.
+ void startDoc(XRef *xrefA);
+
+ void setStartPageCallback(void (*cbk)(void *data), void *data)
+ { startPageCbk = cbk; startPageCbkData = data; }
+
+ void setPaperColor(SplashColorPtr paperColorA);
+
+ GBool isReverseVideo() { return reverseVideo; }
+ void setReverseVideo(GBool reverseVideoA) { reverseVideo = reverseVideoA; }
+
+ // Get the bitmap and its size.
+ SplashBitmap *getBitmap() { return bitmap; }
+ int getBitmapWidth();
+ int getBitmapHeight();
+
+ // Returns the last rasterized bitmap, transferring ownership to the
+ // caller.
+ SplashBitmap *takeBitmap();
+
+ // Set this flag to true to generate an upside-down bitmap (useful
+ // for Windows BMP files).
+ void setBitmapUpsideDown(GBool f) { bitmapUpsideDown = f; }
+
+ // Setting this to true disables the final composite (with the
+ // opaque paper color), resulting in transparent output.
+ void setNoComposite(GBool f) { noComposite = f; }
+
+ // Get the Splash object.
+ Splash *getSplash() { return splash; }
+
+ // Get the modified region.
+ void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax);
+
+ // Clear the modified region.
+ void clearModRegion();
+
+ // Set the Splash fill color.
+ void setFillColor(int r, int g, int b);
+
+ // Get a font object for a Base-14 font, using the Latin-1 encoding.
+ SplashFont *getFont(GString *name, SplashCoord *textMatA);
+
+ SplashFont *getCurrentFont() { return font; }
+
+ // If <skipTextA> is true, don't draw horizontal text.
+ // If <skipRotatedTextA> is true, don't draw rotated (non-horizontal) text.
+ void setSkipText(GBool skipHorizTextA, GBool skipRotatedTextA)
+ { skipHorizText = skipHorizTextA; skipRotatedText = skipRotatedTextA; }
+
+ int getNestCount() { return nestCount; }
+
+
+ // Get the screen parameters.
+ SplashScreenParams *getScreenParams() { return &screenParams; }
+
+private:
+
+ void setupScreenParams(double hDPI, double vDPI);
+ SplashPattern *getColor(GfxGray gray);
+ SplashPattern *getColor(GfxRGB *rgb);
+#if SPLASH_CMYK
+ SplashPattern *getColor(GfxCMYK *cmyk);
+#endif
+ void getColor(GfxGray gray, SplashColorPtr color);
+ void getColor(GfxRGB *rgb, SplashColorPtr color);
+#if SPLASH_CMYK
+ void getColor(GfxCMYK *cmyk, SplashColorPtr color);
+#endif
+ void setOverprintMask(GfxState *state, GfxColorSpace *colorSpace,
+ GBool overprintFlag, int overprintMode,
+ GfxColor *singleColor);
+ SplashPath *convertPath(GfxState *state, GfxPath *path,
+ GBool dropEmptySubpaths);
+ void doUpdateFont(GfxState *state);
+ void drawType3Glyph(GfxState *state, T3FontCache *t3Font,
+ T3FontCacheTag *tag, Guchar *data);
+ static GBool imageMaskSrc(void *data, Guchar *line);
+ static GBool imageSrc(void *data, SplashColorPtr colorLine,
+ Guchar *alphaLine);
+ static GBool alphaImageSrc(void *data, SplashColorPtr line,
+ Guchar *alphaLine);
+ static GBool maskedImageSrc(void *data, SplashColorPtr line,
+ Guchar *alphaLine);
+ static GBool softMaskMatteImageSrc(void *data,
+ SplashColorPtr colorLine,
+ Guchar *alphaLine);
+ GString *makeImageTag(Object *ref, GfxRenderingIntent ri,
+ GfxColorSpace *colorSpace);
+ void reduceImageResolution(Stream *str, double *mat,
+ int *width, int *height);
+ void clearMaskRegion(GfxState *state,
+ Splash *maskSplash,
+ 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;
+ GBool bitmapTopDown;
+ GBool bitmapUpsideDown;
+ GBool noComposite;
+ GBool allowAntialias;
+ GBool vectorAntialias;
+ GBool reverseVideo; // reverse video mode
+ GBool reverseVideoInvertImages;
+ SplashColor paperColor; // paper color
+ SplashScreenParams screenParams;
+ GBool skipHorizText;
+ GBool skipRotatedText;
+
+ XRef *xref; // xref table for current document
+
+ SplashBitmap *bitmap;
+ Splash *splash;
+ SplashFontEngine *fontEngine;
+
+ T3FontCache * // Type 3 font cache
+ t3FontCache[splashOutT3FontCacheSize];
+ int nT3Fonts; // number of valid entries in t3FontCache
+ T3GlyphStack *t3GlyphStack; // Type 3 glyph context stack
+
+ SplashFont *font; // current font
+ GBool needFontUpdate; // set when the font needs to be updated
+ SplashPath *textClipPath; // clipping path built with text object
+
+ SplashTransparencyGroup * // transparency group stack
+ transpGroupStack;
+
+ int nestCount;
+
+ void (*startPageCbk)(void *data);
+ void *startPageCbkData;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream-CCITT.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream-CCITT.h
new file mode 100644
index 00000000000..f82150a79c0
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream-CCITT.h
@@ -0,0 +1,464 @@
+//========================================================================
+//
+// Stream-CCITT.h
+//
+// Tables for CCITT Fax decoding.
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef STREAM_CCITT_H
+#define STREAM_CCITT_H
+
+struct CCITTCode {
+ short bits;
+ short n;
+};
+
+#define ccittEOL -2
+
+//------------------------------------------------------------------------
+// 2D codes
+//------------------------------------------------------------------------
+
+#define twoDimPass 0
+#define twoDimHoriz 1
+#define twoDimVert0 2
+#define twoDimVertR1 3
+#define twoDimVertL1 4
+#define twoDimVertR2 5
+#define twoDimVertL2 6
+#define twoDimVertR3 7
+#define twoDimVertL3 8
+
+// 1-7 bit codes
+static CCITTCode twoDimTab1[128] = {
+ {-1, -1}, {-1, -1}, // 000000x
+ {7, twoDimVertL3}, // 0000010
+ {7, twoDimVertR3}, // 0000011
+ {6, twoDimVertL2}, {6, twoDimVertL2}, // 000010x
+ {6, twoDimVertR2}, {6, twoDimVertR2}, // 000011x
+ {4, twoDimPass}, {4, twoDimPass}, // 0001xxx
+ {4, twoDimPass}, {4, twoDimPass},
+ {4, twoDimPass}, {4, twoDimPass},
+ {4, twoDimPass}, {4, twoDimPass},
+ {3, twoDimHoriz}, {3, twoDimHoriz}, // 001xxxx
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimVertL1}, {3, twoDimVertL1}, // 010xxxx
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertR1}, {3, twoDimVertR1}, // 011xxxx
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {1, twoDimVert0}, {1, twoDimVert0}, // 1xxxxxx
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0}
+};
+
+//------------------------------------------------------------------------
+// white run lengths
+//------------------------------------------------------------------------
+
+// 11-12 bit codes (upper 7 bits are 0)
+static CCITTCode whiteTab1[32] = {
+ {-1, -1}, // 00000
+ {12, ccittEOL}, // 00001
+ {-1, -1}, {-1, -1}, // 0001x
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 001xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 010xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 011xx
+ {11, 1792}, {11, 1792}, // 1000x
+ {12, 1984}, // 10010
+ {12, 2048}, // 10011
+ {12, 2112}, // 10100
+ {12, 2176}, // 10101
+ {12, 2240}, // 10110
+ {12, 2304}, // 10111
+ {11, 1856}, {11, 1856}, // 1100x
+ {11, 1920}, {11, 1920}, // 1101x
+ {12, 2368}, // 11100
+ {12, 2432}, // 11101
+ {12, 2496}, // 11110
+ {12, 2560} // 11111
+};
+
+// 1-9 bit codes
+static CCITTCode whiteTab2[512] = {
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 0000000xx
+ {8, 29}, {8, 29}, // 00000010x
+ {8, 30}, {8, 30}, // 00000011x
+ {8, 45}, {8, 45}, // 00000100x
+ {8, 46}, {8, 46}, // 00000101x
+ {7, 22}, {7, 22}, {7, 22}, {7, 22}, // 0000011xx
+ {7, 23}, {7, 23}, {7, 23}, {7, 23}, // 0000100xx
+ {8, 47}, {8, 47}, // 00001010x
+ {8, 48}, {8, 48}, // 00001011x
+ {6, 13}, {6, 13}, {6, 13}, {6, 13}, // 000011xxx
+ {6, 13}, {6, 13}, {6, 13}, {6, 13},
+ {7, 20}, {7, 20}, {7, 20}, {7, 20}, // 0001000xx
+ {8, 33}, {8, 33}, // 00010010x
+ {8, 34}, {8, 34}, // 00010011x
+ {8, 35}, {8, 35}, // 00010100x
+ {8, 36}, {8, 36}, // 00010101x
+ {8, 37}, {8, 37}, // 00010110x
+ {8, 38}, {8, 38}, // 00010111x
+ {7, 19}, {7, 19}, {7, 19}, {7, 19}, // 0001100xx
+ {8, 31}, {8, 31}, // 00011010x
+ {8, 32}, {8, 32}, // 00011011x
+ {6, 1}, {6, 1}, {6, 1}, {6, 1}, // 000111xxx
+ {6, 1}, {6, 1}, {6, 1}, {6, 1},
+ {6, 12}, {6, 12}, {6, 12}, {6, 12}, // 001000xxx
+ {6, 12}, {6, 12}, {6, 12}, {6, 12},
+ {8, 53}, {8, 53}, // 00100100x
+ {8, 54}, {8, 54}, // 00100101x
+ {7, 26}, {7, 26}, {7, 26}, {7, 26}, // 0010011xx
+ {8, 39}, {8, 39}, // 00101000x
+ {8, 40}, {8, 40}, // 00101001x
+ {8, 41}, {8, 41}, // 00101010x
+ {8, 42}, {8, 42}, // 00101011x
+ {8, 43}, {8, 43}, // 00101100x
+ {8, 44}, {8, 44}, // 00101101x
+ {7, 21}, {7, 21}, {7, 21}, {7, 21}, // 0010111xx
+ {7, 28}, {7, 28}, {7, 28}, {7, 28}, // 0011000xx
+ {8, 61}, {8, 61}, // 00110010x
+ {8, 62}, {8, 62}, // 00110011x
+ {8, 63}, {8, 63}, // 00110100x
+ {8, 0}, {8, 0}, // 00110101x
+ {8, 320}, {8, 320}, // 00110110x
+ {8, 384}, {8, 384}, // 00110111x
+ {5, 10}, {5, 10}, {5, 10}, {5, 10}, // 00111xxxx
+ {5, 10}, {5, 10}, {5, 10}, {5, 10},
+ {5, 10}, {5, 10}, {5, 10}, {5, 10},
+ {5, 10}, {5, 10}, {5, 10}, {5, 10},
+ {5, 11}, {5, 11}, {5, 11}, {5, 11}, // 01000xxxx
+ {5, 11}, {5, 11}, {5, 11}, {5, 11},
+ {5, 11}, {5, 11}, {5, 11}, {5, 11},
+ {5, 11}, {5, 11}, {5, 11}, {5, 11},
+ {7, 27}, {7, 27}, {7, 27}, {7, 27}, // 0100100xx
+ {8, 59}, {8, 59}, // 01001010x
+ {8, 60}, {8, 60}, // 01001011x
+ {9, 1472}, // 010011000
+ {9, 1536}, // 010011001
+ {9, 1600}, // 010011010
+ {9, 1728}, // 010011011
+ {7, 18}, {7, 18}, {7, 18}, {7, 18}, // 0100111xx
+ {7, 24}, {7, 24}, {7, 24}, {7, 24}, // 0101000xx
+ {8, 49}, {8, 49}, // 01010010x
+ {8, 50}, {8, 50}, // 01010011x
+ {8, 51}, {8, 51}, // 01010100x
+ {8, 52}, {8, 52}, // 01010101x
+ {7, 25}, {7, 25}, {7, 25}, {7, 25}, // 0101011xx
+ {8, 55}, {8, 55}, // 01011000x
+ {8, 56}, {8, 56}, // 01011001x
+ {8, 57}, {8, 57}, // 01011010x
+ {8, 58}, {8, 58}, // 01011011x
+ {6, 192}, {6, 192}, {6, 192}, {6, 192}, // 010111xxx
+ {6, 192}, {6, 192}, {6, 192}, {6, 192},
+ {6, 1664}, {6, 1664}, {6, 1664}, {6, 1664}, // 011000xxx
+ {6, 1664}, {6, 1664}, {6, 1664}, {6, 1664},
+ {8, 448}, {8, 448}, // 01100100x
+ {8, 512}, {8, 512}, // 01100101x
+ {9, 704}, // 011001100
+ {9, 768}, // 011001101
+ {8, 640}, {8, 640}, // 01100111x
+ {8, 576}, {8, 576}, // 01101000x
+ {9, 832}, // 011010010
+ {9, 896}, // 011010011
+ {9, 960}, // 011010100
+ {9, 1024}, // 011010101
+ {9, 1088}, // 011010110
+ {9, 1152}, // 011010111
+ {9, 1216}, // 011011000
+ {9, 1280}, // 011011001
+ {9, 1344}, // 011011010
+ {9, 1408}, // 011011011
+ {7, 256}, {7, 256}, {7, 256}, {7, 256}, // 0110111xx
+ {4, 2}, {4, 2}, {4, 2}, {4, 2}, // 0111xxxxx
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3}, // 1000xxxxx
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {5, 128}, {5, 128}, {5, 128}, {5, 128}, // 10010xxxx
+ {5, 128}, {5, 128}, {5, 128}, {5, 128},
+ {5, 128}, {5, 128}, {5, 128}, {5, 128},
+ {5, 128}, {5, 128}, {5, 128}, {5, 128},
+ {5, 8}, {5, 8}, {5, 8}, {5, 8}, // 10011xxxx
+ {5, 8}, {5, 8}, {5, 8}, {5, 8},
+ {5, 8}, {5, 8}, {5, 8}, {5, 8},
+ {5, 8}, {5, 8}, {5, 8}, {5, 8},
+ {5, 9}, {5, 9}, {5, 9}, {5, 9}, // 10100xxxx
+ {5, 9}, {5, 9}, {5, 9}, {5, 9},
+ {5, 9}, {5, 9}, {5, 9}, {5, 9},
+ {5, 9}, {5, 9}, {5, 9}, {5, 9},
+ {6, 16}, {6, 16}, {6, 16}, {6, 16}, // 101010xxx
+ {6, 16}, {6, 16}, {6, 16}, {6, 16},
+ {6, 17}, {6, 17}, {6, 17}, {6, 17}, // 101011xxx
+ {6, 17}, {6, 17}, {6, 17}, {6, 17},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4}, // 1011xxxxx
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5}, // 1100xxxxx
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {6, 14}, {6, 14}, {6, 14}, {6, 14}, // 110100xxx
+ {6, 14}, {6, 14}, {6, 14}, {6, 14},
+ {6, 15}, {6, 15}, {6, 15}, {6, 15}, // 110101xxx
+ {6, 15}, {6, 15}, {6, 15}, {6, 15},
+ {5, 64}, {5, 64}, {5, 64}, {5, 64}, // 11011xxxx
+ {5, 64}, {5, 64}, {5, 64}, {5, 64},
+ {5, 64}, {5, 64}, {5, 64}, {5, 64},
+ {5, 64}, {5, 64}, {5, 64}, {5, 64},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6}, // 1110xxxxx
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7}, // 1111xxxxx
+ {4, 7}, {4, 7}, {4, 7}, {4, 7},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7}
+};
+
+//------------------------------------------------------------------------
+// black run lengths
+//------------------------------------------------------------------------
+
+// 10-13 bit codes (upper 6 bits are 0)
+static CCITTCode blackTab1[128] = {
+ {-1, -1}, {-1, -1}, // 000000000000x
+ {12, ccittEOL}, {12, ccittEOL}, // 000000000001x
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000001xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000010xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000011xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000100xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000101xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000110xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000111xx
+ {11, 1792}, {11, 1792}, {11, 1792}, {11, 1792}, // 00000001000xx
+ {12, 1984}, {12, 1984}, // 000000010010x
+ {12, 2048}, {12, 2048}, // 000000010011x
+ {12, 2112}, {12, 2112}, // 000000010100x
+ {12, 2176}, {12, 2176}, // 000000010101x
+ {12, 2240}, {12, 2240}, // 000000010110x
+ {12, 2304}, {12, 2304}, // 000000010111x
+ {11, 1856}, {11, 1856}, {11, 1856}, {11, 1856}, // 00000001100xx
+ {11, 1920}, {11, 1920}, {11, 1920}, {11, 1920}, // 00000001101xx
+ {12, 2368}, {12, 2368}, // 000000011100x
+ {12, 2432}, {12, 2432}, // 000000011101x
+ {12, 2496}, {12, 2496}, // 000000011110x
+ {12, 2560}, {12, 2560}, // 000000011111x
+ {10, 18}, {10, 18}, {10, 18}, {10, 18}, // 0000001000xxx
+ {10, 18}, {10, 18}, {10, 18}, {10, 18},
+ {12, 52}, {12, 52}, // 000000100100x
+ {13, 640}, // 0000001001010
+ {13, 704}, // 0000001001011
+ {13, 768}, // 0000001001100
+ {13, 832}, // 0000001001101
+ {12, 55}, {12, 55}, // 000000100111x
+ {12, 56}, {12, 56}, // 000000101000x
+ {13, 1280}, // 0000001010010
+ {13, 1344}, // 0000001010011
+ {13, 1408}, // 0000001010100
+ {13, 1472}, // 0000001010101
+ {12, 59}, {12, 59}, // 000000101011x
+ {12, 60}, {12, 60}, // 000000101100x
+ {13, 1536}, // 0000001011010
+ {13, 1600}, // 0000001011011
+ {11, 24}, {11, 24}, {11, 24}, {11, 24}, // 00000010111xx
+ {11, 25}, {11, 25}, {11, 25}, {11, 25}, // 00000011000xx
+ {13, 1664}, // 0000001100100
+ {13, 1728}, // 0000001100101
+ {12, 320}, {12, 320}, // 000000110011x
+ {12, 384}, {12, 384}, // 000000110100x
+ {12, 448}, {12, 448}, // 000000110101x
+ {13, 512}, // 0000001101100
+ {13, 576}, // 0000001101101
+ {12, 53}, {12, 53}, // 000000110111x
+ {12, 54}, {12, 54}, // 000000111000x
+ {13, 896}, // 0000001110010
+ {13, 960}, // 0000001110011
+ {13, 1024}, // 0000001110100
+ {13, 1088}, // 0000001110101
+ {13, 1152}, // 0000001110110
+ {13, 1216}, // 0000001110111
+ {10, 64}, {10, 64}, {10, 64}, {10, 64}, // 0000001111xxx
+ {10, 64}, {10, 64}, {10, 64}, {10, 64}
+};
+
+// 7-12 bit codes (upper 4 bits are 0)
+static CCITTCode blackTab2[192] = {
+ {8, 13}, {8, 13}, {8, 13}, {8, 13}, // 00000100xxxx
+ {8, 13}, {8, 13}, {8, 13}, {8, 13},
+ {8, 13}, {8, 13}, {8, 13}, {8, 13},
+ {8, 13}, {8, 13}, {8, 13}, {8, 13},
+ {11, 23}, {11, 23}, // 00000101000x
+ {12, 50}, // 000001010010
+ {12, 51}, // 000001010011
+ {12, 44}, // 000001010100
+ {12, 45}, // 000001010101
+ {12, 46}, // 000001010110
+ {12, 47}, // 000001010111
+ {12, 57}, // 000001011000
+ {12, 58}, // 000001011001
+ {12, 61}, // 000001011010
+ {12, 256}, // 000001011011
+ {10, 16}, {10, 16}, {10, 16}, {10, 16}, // 0000010111xx
+ {10, 17}, {10, 17}, {10, 17}, {10, 17}, // 0000011000xx
+ {12, 48}, // 000001100100
+ {12, 49}, // 000001100101
+ {12, 62}, // 000001100110
+ {12, 63}, // 000001100111
+ {12, 30}, // 000001101000
+ {12, 31}, // 000001101001
+ {12, 32}, // 000001101010
+ {12, 33}, // 000001101011
+ {12, 40}, // 000001101100
+ {12, 41}, // 000001101101
+ {11, 22}, {11, 22}, // 00000110111x
+ {8, 14}, {8, 14}, {8, 14}, {8, 14}, // 00000111xxxx
+ {8, 14}, {8, 14}, {8, 14}, {8, 14},
+ {8, 14}, {8, 14}, {8, 14}, {8, 14},
+ {8, 14}, {8, 14}, {8, 14}, {8, 14},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10}, // 0000100xxxxx
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11}, // 0000101xxxxx
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {9, 15}, {9, 15}, {9, 15}, {9, 15}, // 000011000xxx
+ {9, 15}, {9, 15}, {9, 15}, {9, 15},
+ {12, 128}, // 000011001000
+ {12, 192}, // 000011001001
+ {12, 26}, // 000011001010
+ {12, 27}, // 000011001011
+ {12, 28}, // 000011001100
+ {12, 29}, // 000011001101
+ {11, 19}, {11, 19}, // 00001100111x
+ {11, 20}, {11, 20}, // 00001101000x
+ {12, 34}, // 000011010010
+ {12, 35}, // 000011010011
+ {12, 36}, // 000011010100
+ {12, 37}, // 000011010101
+ {12, 38}, // 000011010110
+ {12, 39}, // 000011010111
+ {11, 21}, {11, 21}, // 00001101100x
+ {12, 42}, // 000011011010
+ {12, 43}, // 000011011011
+ {10, 0}, {10, 0}, {10, 0}, {10, 0}, // 0000110111xx
+ {7, 12}, {7, 12}, {7, 12}, {7, 12}, // 0000111xxxxx
+ {7, 12}, {7, 12}, {7, 12}, {7, 12},
+ {7, 12}, {7, 12}, {7, 12}, {7, 12},
+ {7, 12}, {7, 12}, {7, 12}, {7, 12},
+ {7, 12}, {7, 12}, {7, 12}, {7, 12},
+ {7, 12}, {7, 12}, {7, 12}, {7, 12},
+ {7, 12}, {7, 12}, {7, 12}, {7, 12},
+ {7, 12}, {7, 12}, {7, 12}, {7, 12}
+};
+
+// 2-6 bit codes
+static CCITTCode blackTab3[64] = {
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 0000xx
+ {6, 9}, // 000100
+ {6, 8}, // 000101
+ {5, 7}, {5, 7}, // 00011x
+ {4, 6}, {4, 6}, {4, 6}, {4, 6}, // 0010xx
+ {4, 5}, {4, 5}, {4, 5}, {4, 5}, // 0011xx
+ {3, 1}, {3, 1}, {3, 1}, {3, 1}, // 010xxx
+ {3, 1}, {3, 1}, {3, 1}, {3, 1},
+ {3, 4}, {3, 4}, {3, 4}, {3, 4}, // 011xxx
+ {3, 4}, {3, 4}, {3, 4}, {3, 4},
+ {2, 3}, {2, 3}, {2, 3}, {2, 3}, // 10xxxx
+ {2, 3}, {2, 3}, {2, 3}, {2, 3},
+ {2, 3}, {2, 3}, {2, 3}, {2, 3},
+ {2, 3}, {2, 3}, {2, 3}, {2, 3},
+ {2, 2}, {2, 2}, {2, 2}, {2, 2}, // 11xxxx
+ {2, 2}, {2, 2}, {2, 2}, {2, 2},
+ {2, 2}, {2, 2}, {2, 2}, {2, 2},
+ {2, 2}, {2, 2}, {2, 2}, {2, 2}
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc
new file mode 100644
index 00000000000..601add89be4
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.cc
@@ -0,0 +1,6000 @@
+//========================================================================
+//
+// Stream.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <limits.h>
+#ifdef _WIN32
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <ctype.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "gfile.h"
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+#include "config.h"
+#include "Error.h"
+#include "Object.h"
+#include "Lexer.h"
+#include "GfxState.h"
+#include "Stream.h"
+#include "JBIG2Stream.h"
+#include "JPXStream.h"
+#include "Stream-CCITT.h"
+
+#ifdef __DJGPP__
+static GBool setDJSYSFLAGS = gFalse;
+#endif
+
+#ifdef VMS
+#ifdef __GNUC__
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+#endif
+
+//------------------------------------------------------------------------
+// Stream (base class)
+//------------------------------------------------------------------------
+
+Stream::Stream() {
+}
+
+Stream::~Stream() {
+}
+
+void Stream::close() {
+}
+
+int Stream::getRawChar() {
+ error(errInternal, -1, "Called getRawChar() on non-predictor stream");
+ return EOF;
+}
+
+int Stream::getBlock(char *buf, int size) {
+ int n, c;
+
+ n = 0;
+ while (n < size) {
+ if ((c = getChar()) == EOF) {
+ break;
+ }
+ buf[n++] = (char)c;
+ }
+ return n;
+}
+
+char *Stream::getLine(char *buf, int size) {
+ int i;
+ int c;
+
+ if (lookChar() == EOF || size < 0)
+ return NULL;
+ for (i = 0; i < size - 1; ++i) {
+ c = getChar();
+ if (c == EOF || c == '\n')
+ break;
+ if (c == '\r') {
+ if ((c = lookChar()) == '\n')
+ getChar();
+ break;
+ }
+ buf[i] = (char)c;
+ }
+ buf[i] = '\0';
+ return buf;
+}
+
+Guint Stream::discardChars(Guint n) {
+ char buf[4096];
+ Guint count, i, j;
+
+ count = 0;
+ while (count < n) {
+ if ((i = n - count) > sizeof(buf)) {
+ i = (Guint)sizeof(buf);
+ }
+ j = (Guint)getBlock(buf, (int)i);
+ count += j;
+ if (j != i) {
+ break;
+ }
+ }
+ return count;
+}
+
+GString *Stream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ return new GString();
+}
+
+Stream *Stream::addFilters(Object *dict, int recursion) {
+ Object obj, obj2;
+ Object params, params2;
+ Stream *str;
+ int i;
+
+ str = this;
+ dict->dictLookup("Filter", &obj, recursion);
+ if (obj.isNull()) {
+ obj.free();
+ dict->dictLookup("F", &obj, recursion);
+ }
+ dict->dictLookup("DecodeParms", &params, recursion);
+ if (params.isNull()) {
+ params.free();
+ dict->dictLookup("DP", &params, recursion);
+ }
+ if (obj.isName()) {
+ str = makeFilter(obj.getName(), str, &params, recursion);
+ } else if (obj.isArray()) {
+ for (i = 0; i < obj.arrayGetLength(); ++i) {
+ obj.arrayGet(i, &obj2, recursion);
+ if (params.isArray())
+ params.arrayGet(i, &params2, recursion);
+ else
+ params2.initNull();
+ if (obj2.isName()) {
+ str = makeFilter(obj2.getName(), str, &params2, recursion);
+ } else {
+ error(errSyntaxError, getPos(), "Bad filter name");
+ str = new EOFStream(str);
+ }
+ obj2.free();
+ params2.free();
+ }
+ } else if (!obj.isNull()) {
+ error(errSyntaxError, getPos(), "Bad 'Filter' attribute in stream");
+ }
+ obj.free();
+ params.free();
+
+ return str;
+}
+
+Stream *Stream::makeFilter(char *name, Stream *str, Object *params,
+ int recursion) {
+ int pred; // parameters
+ int colors;
+ int bits;
+ int early;
+ int encoding;
+ GBool endOfLine, byteAlign, endOfBlock, black;
+ int columns, rows;
+ int colorXform;
+ Object globals, obj;
+
+ if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) {
+ str = new ASCIIHexStream(str);
+ } else if (!strcmp(name, "ASCII85Decode") || !strcmp(name, "A85")) {
+ str = new ASCII85Stream(str);
+ } else if (!strcmp(name, "LZWDecode") || !strcmp(name, "LZW")) {
+ pred = 1;
+ columns = 1;
+ colors = 1;
+ bits = 8;
+ early = 1;
+ if (params->isDict()) {
+ params->dictLookup("Predictor", &obj, recursion);
+ if (obj.isInt())
+ pred = obj.getInt();
+ obj.free();
+ params->dictLookup("Columns", &obj, recursion);
+ if (obj.isInt())
+ columns = obj.getInt();
+ obj.free();
+ params->dictLookup("Colors", &obj, recursion);
+ if (obj.isInt())
+ colors = obj.getInt();
+ obj.free();
+ params->dictLookup("BitsPerComponent", &obj, recursion);
+ if (obj.isInt())
+ bits = obj.getInt();
+ obj.free();
+ params->dictLookup("EarlyChange", &obj, recursion);
+ if (obj.isInt())
+ early = obj.getInt();
+ obj.free();
+ }
+ str = new LZWStream(str, pred, columns, colors, bits, early);
+ } else if (!strcmp(name, "RunLengthDecode") || !strcmp(name, "RL")) {
+ str = new RunLengthStream(str);
+ } else if (!strcmp(name, "CCITTFaxDecode") || !strcmp(name, "CCF")) {
+ encoding = 0;
+ endOfLine = gFalse;
+ byteAlign = gFalse;
+ columns = 1728;
+ rows = 0;
+ endOfBlock = gTrue;
+ black = gFalse;
+ if (params->isDict()) {
+ params->dictLookup("K", &obj, recursion);
+ if (obj.isInt()) {
+ encoding = obj.getInt();
+ }
+ obj.free();
+ params->dictLookup("EndOfLine", &obj, recursion);
+ if (obj.isBool()) {
+ endOfLine = obj.getBool();
+ }
+ obj.free();
+ params->dictLookup("EncodedByteAlign", &obj, recursion);
+ if (obj.isBool()) {
+ byteAlign = obj.getBool();
+ }
+ obj.free();
+ params->dictLookup("Columns", &obj, recursion);
+ if (obj.isInt()) {
+ columns = obj.getInt();
+ }
+ obj.free();
+ params->dictLookup("Rows", &obj, recursion);
+ if (obj.isInt()) {
+ rows = obj.getInt();
+ }
+ obj.free();
+ params->dictLookup("EndOfBlock", &obj, recursion);
+ if (obj.isBool()) {
+ endOfBlock = obj.getBool();
+ }
+ obj.free();
+ params->dictLookup("BlackIs1", &obj, recursion);
+ if (obj.isBool()) {
+ black = obj.getBool();
+ }
+ obj.free();
+ }
+ str = new CCITTFaxStream(str, encoding, endOfLine, byteAlign,
+ columns, rows, endOfBlock, black);
+ } else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) {
+ colorXform = -1;
+ if (params->isDict()) {
+ if (params->dictLookup("ColorTransform", &obj, recursion)->isInt()) {
+ colorXform = obj.getInt();
+ }
+ obj.free();
+ }
+ str = new DCTStream(str, colorXform);
+ } else if (!strcmp(name, "FlateDecode") || !strcmp(name, "Fl")) {
+ pred = 1;
+ columns = 1;
+ colors = 1;
+ bits = 8;
+ if (params->isDict()) {
+ params->dictLookup("Predictor", &obj, recursion);
+ if (obj.isInt())
+ pred = obj.getInt();
+ obj.free();
+ params->dictLookup("Columns", &obj, recursion);
+ if (obj.isInt())
+ columns = obj.getInt();
+ obj.free();
+ params->dictLookup("Colors", &obj, recursion);
+ if (obj.isInt())
+ colors = obj.getInt();
+ obj.free();
+ params->dictLookup("BitsPerComponent", &obj, recursion);
+ if (obj.isInt())
+ bits = obj.getInt();
+ obj.free();
+ }
+ str = new FlateStream(str, pred, columns, colors, bits);
+ } else if (!strcmp(name, "JBIG2Decode")) {
+ if (params->isDict()) {
+ params->dictLookup("JBIG2Globals", &globals, recursion);
+ }
+ str = new JBIG2Stream(str, &globals);
+ globals.free();
+ } else if (!strcmp(name, "JPXDecode")) {
+ str = new JPXStream(str);
+ } else {
+ error(errSyntaxError, getPos(), "Unknown filter '{0:s}'", name);
+ str = new EOFStream(str);
+ }
+ return str;
+}
+
+//------------------------------------------------------------------------
+// BaseStream
+//------------------------------------------------------------------------
+
+BaseStream::BaseStream(Object *dictA) {
+ dict = *dictA;
+}
+
+BaseStream::~BaseStream() {
+ dict.free();
+}
+
+//------------------------------------------------------------------------
+// FilterStream
+//------------------------------------------------------------------------
+
+FilterStream::FilterStream(Stream *strA) {
+ str = strA;
+}
+
+FilterStream::~FilterStream() {
+}
+
+void FilterStream::close() {
+ str->close();
+}
+
+void FilterStream::setPos(GFileOffset pos, int dir) {
+ error(errInternal, -1, "Called setPos() on FilterStream");
+}
+
+//------------------------------------------------------------------------
+// ImageStream
+//------------------------------------------------------------------------
+
+ImageStream::ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA) {
+ int imgLineSize;
+
+ str = strA;
+ width = widthA;
+ nComps = nCompsA;
+ nBits = nBitsA;
+
+ nVals = width * nComps;
+ inputLineSize = (nVals * nBits + 7) >> 3;
+ if (width > INT_MAX / nComps ||
+ nVals > (INT_MAX - 7) / nBits) {
+ // force a call to gmallocn(-1,...), which will throw an exception
+ inputLineSize = -1;
+ }
+ inputLine = (char *)gmallocn(inputLineSize, sizeof(char));
+ if (nBits == 8) {
+ imgLine = (Guchar *)inputLine;
+ } else {
+ if (nBits == 1) {
+ imgLineSize = (nVals + 7) & ~7;
+ } else {
+ imgLineSize = nVals;
+ }
+ imgLine = (Guchar *)gmallocn(imgLineSize, sizeof(Guchar));
+ }
+ imgIdx = nVals;
+}
+
+ImageStream::~ImageStream() {
+ if (imgLine != (Guchar *)inputLine) {
+ gfree(imgLine);
+ }
+ gfree(inputLine);
+}
+
+void ImageStream::reset() {
+ str->reset();
+}
+
+void ImageStream::close() {
+ str->close();
+}
+
+GBool ImageStream::getPixel(Guchar *pix) {
+ int i;
+
+ if (imgIdx >= nVals) {
+ if (!getLine()) {
+ return gFalse;
+ }
+ imgIdx = 0;
+ }
+ for (i = 0; i < nComps; ++i) {
+ pix[i] = imgLine[imgIdx++];
+ }
+ return gTrue;
+}
+
+Guchar *ImageStream::getLine() {
+ Gulong buf, bitMask;
+ int bits;
+ int c;
+ int i;
+ char *p;
+
+ if (str->getBlock(inputLine, inputLineSize) != inputLineSize) {
+ return NULL;
+ }
+ if (nBits == 1) {
+ p = inputLine;
+ for (i = 0; i < nVals; i += 8) {
+ c = *p++;
+ imgLine[i+0] = (Guchar)((c >> 7) & 1);
+ imgLine[i+1] = (Guchar)((c >> 6) & 1);
+ imgLine[i+2] = (Guchar)((c >> 5) & 1);
+ imgLine[i+3] = (Guchar)((c >> 4) & 1);
+ imgLine[i+4] = (Guchar)((c >> 3) & 1);
+ imgLine[i+5] = (Guchar)((c >> 2) & 1);
+ imgLine[i+6] = (Guchar)((c >> 1) & 1);
+ imgLine[i+7] = (Guchar)(c & 1);
+ }
+ } else if (nBits == 8) {
+ // special case: imgLine == inputLine
+ } else if (nBits == 16) {
+ for (i = 0; i < nVals; ++i) {
+ imgLine[i] = (Guchar)inputLine[2*i];
+ }
+ } else {
+ bitMask = (1 << nBits) - 1;
+ buf = 0;
+ bits = 0;
+ p = inputLine;
+ for (i = 0; i < nVals; ++i) {
+ if (bits < nBits) {
+ buf = (buf << 8) | (*p++ & 0xff);
+ bits += 8;
+ }
+ imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask);
+ bits -= nBits;
+ }
+ }
+ return imgLine;
+}
+
+void ImageStream::skipLine() {
+ str->getBlock(inputLine, inputLineSize);
+}
+
+
+//------------------------------------------------------------------------
+// StreamPredictor
+//------------------------------------------------------------------------
+
+StreamPredictor::StreamPredictor(Stream *strA, int predictorA,
+ int widthA, int nCompsA, int nBitsA) {
+ str = strA;
+ predictor = predictorA;
+ width = widthA;
+ nComps = nCompsA;
+ nBits = nBitsA;
+ predLine = NULL;
+ ok = gFalse;
+
+ nVals = width * nComps;
+ pixBytes = (nComps * nBits + 7) >> 3;
+ rowBytes = ((nVals * nBits + 7) >> 3) + pixBytes;
+ if (width <= 0 || nComps <= 0 || nBits <= 0 ||
+ nComps > gfxColorMaxComps ||
+ nBits > 16 ||
+ width >= INT_MAX / nComps || // check for overflow in nVals
+ nVals >= (INT_MAX - 7) / nBits) { // check for overflow in rowBytes
+ return;
+ }
+ predLine = (Guchar *)gmalloc(rowBytes);
+
+ reset();
+
+ ok = gTrue;
+}
+
+StreamPredictor::~StreamPredictor() {
+ gfree(predLine);
+}
+
+void StreamPredictor::reset() {
+ memset(predLine, 0, rowBytes);
+ predIdx = rowBytes;
+}
+
+int StreamPredictor::lookChar() {
+ if (predIdx >= rowBytes) {
+ if (!getNextLine()) {
+ return EOF;
+ }
+ }
+ return predLine[predIdx];
+}
+
+int StreamPredictor::getChar() {
+ if (predIdx >= rowBytes) {
+ if (!getNextLine()) {
+ return EOF;
+ }
+ }
+ return predLine[predIdx++];
+}
+
+int StreamPredictor::getBlock(char *blk, int size) {
+ int n, m;
+
+ n = 0;
+ while (n < size) {
+ if (predIdx >= rowBytes) {
+ if (!getNextLine()) {
+ break;
+ }
+ }
+ m = rowBytes - predIdx;
+ if (m > size - n) {
+ m = size - n;
+ }
+ memcpy(blk + n, predLine + predIdx, m);
+ predIdx += m;
+ n += m;
+ }
+ return n;
+}
+
+GBool StreamPredictor::getNextLine() {
+ int curPred;
+ Guchar upLeftBuf[gfxColorMaxComps * 2 + 1];
+ int left, up, upLeft, p, pa, pb, pc;
+ int c;
+ Gulong inBuf, outBuf, bitMask;
+ int inBits, outBits;
+ int i, j, k, kk;
+
+ // get PNG optimum predictor number
+ if (predictor >= 10) {
+ if ((curPred = str->getRawChar()) == EOF) {
+ return gFalse;
+ }
+ curPred += 10;
+ } else {
+ curPred = predictor;
+ }
+
+ // read the raw line, apply PNG (byte) predictor
+ memset(upLeftBuf, 0, pixBytes + 1);
+ for (i = pixBytes; i < rowBytes; ++i) {
+ for (j = pixBytes; j > 0; --j) {
+ upLeftBuf[j] = upLeftBuf[j-1];
+ }
+ upLeftBuf[0] = predLine[i];
+ if ((c = str->getRawChar()) == EOF) {
+ if (i > pixBytes) {
+ // this ought to return false, but some (broken) PDF files
+ // contain truncated image data, and Adobe apparently reads the
+ // last partial line
+ break;
+ }
+ return gFalse;
+ }
+ switch (curPred) {
+ case 11: // PNG sub
+ predLine[i] = (Guchar)(predLine[i - pixBytes] + c);
+ break;
+ case 12: // PNG up
+ predLine[i] = (Guchar)(predLine[i] + c);
+ break;
+ case 13: // PNG average
+ predLine[i] = (Guchar)(((predLine[i - pixBytes] + predLine[i]) >> 1) + c);
+ break;
+ case 14: // PNG Paeth
+ left = predLine[i - pixBytes];
+ up = predLine[i];
+ upLeft = upLeftBuf[pixBytes];
+ p = left + up - upLeft;
+ if ((pa = p - left) < 0)
+ pa = -pa;
+ if ((pb = p - up) < 0)
+ pb = -pb;
+ if ((pc = p - upLeft) < 0)
+ pc = -pc;
+ if (pa <= pb && pa <= pc)
+ predLine[i] = (Guchar)(left + c);
+ else if (pb <= pc)
+ predLine[i] = (Guchar)(up + c);
+ else
+ predLine[i] = (Guchar)(upLeft + c);
+ break;
+ case 10: // PNG none
+ default: // no predictor or TIFF predictor
+ predLine[i] = (Guchar)c;
+ break;
+ }
+ }
+
+ // apply TIFF (component) predictor
+ if (predictor == 2) {
+ if (nBits == 8) {
+ for (i = pixBytes; i < rowBytes; ++i) {
+ predLine[i] = (Guchar)(predLine[i] + predLine[i - nComps]);
+ }
+ } else if (nBits == 16) {
+ for (i = pixBytes; i < rowBytes; i += 2) {
+ c = ((predLine[i] + predLine[i - 2*nComps]) << 8) +
+ predLine[i + 1] + predLine[i + 1 - 2*nComps];
+ predLine[i] = (Guchar)(c >> 8);
+ predLine[i+1] = (Guchar)(c & 0xff);
+ }
+ } else {
+ memset(upLeftBuf, 0, nComps);
+ bitMask = (1 << nBits) - 1;
+ inBuf = outBuf = 0;
+ inBits = outBits = 0;
+ j = k = pixBytes;
+ for (i = 0; i < width; ++i) {
+ for (kk = 0; kk < nComps; ++kk) {
+ if (inBits < nBits) {
+ inBuf = (inBuf << 8) | (predLine[j++] & 0xff);
+ inBits += 8;
+ }
+ upLeftBuf[kk] = (Guchar)((upLeftBuf[kk] +
+ (inBuf >> (inBits - nBits))) & bitMask);
+ inBits -= nBits;
+ outBuf = (outBuf << nBits) | upLeftBuf[kk];
+ outBits += nBits;
+ if (outBits >= 8) {
+ predLine[k++] = (Guchar)(outBuf >> (outBits - 8));
+ outBits -= 8;
+ }
+ }
+ }
+ if (outBits > 0) {
+ predLine[k++] = (Guchar)((outBuf << (8 - outBits)) +
+ (inBuf & ((1 << (8 - outBits)) - 1)));
+ }
+ }
+ }
+
+ // reset to start of line
+ predIdx = pixBytes;
+
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// SharedFile
+//------------------------------------------------------------------------
+
+class SharedFile {
+public:
+
+ SharedFile(FILE *fA);
+ SharedFile *copy();
+ void free();
+ int readBlock(char *buf, GFileOffset pos, int size);
+ GFileOffset getSize();
+
+private:
+
+ ~SharedFile();
+
+ FILE *f;
+ int refCnt;
+#if MULTITHREADED
+ GMutex mutex;
+#endif
+};
+
+SharedFile::SharedFile(FILE *fA) {
+ f = fA;
+ refCnt = 1;
+#if MULTITHREADED
+ gInitMutex(&mutex);
+#endif
+}
+
+SharedFile::~SharedFile() {
+#if MULTITHREADED
+ gDestroyMutex(&mutex);
+#endif
+}
+
+SharedFile *SharedFile::copy() {
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ ++refCnt;
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+ return this;
+}
+
+void SharedFile::free() {
+ int newCount;
+
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ newCount = --refCnt;
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+ if (newCount == 0) {
+ delete this;
+ }
+}
+
+int SharedFile::readBlock(char *buf, GFileOffset pos, int size) {
+ int n;
+
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ gfseek(f, pos, SEEK_SET);
+ n = (int)fread(buf, 1, size, f);
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+ return n;
+}
+
+GFileOffset SharedFile::getSize() {
+ GFileOffset size;
+
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ gfseek(f, 0, SEEK_END);
+ size = gftell(f);
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+ return size;
+}
+
+//------------------------------------------------------------------------
+// FileStream
+//------------------------------------------------------------------------
+
+FileStream::FileStream(FILE *fA, GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA):
+ BaseStream(dictA) {
+ f = new SharedFile(fA);
+ start = startA;
+ limited = limitedA;
+ length = lengthA;
+ bufPtr = bufEnd = buf;
+ bufPos = start;
+}
+
+FileStream::FileStream(SharedFile *fA, GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA):
+ BaseStream(dictA) {
+ f = fA->copy();
+ start = startA;
+ limited = limitedA;
+ length = lengthA;
+ bufPtr = bufEnd = buf;
+ bufPos = start;
+}
+
+FileStream::~FileStream() {
+ f->free();
+}
+
+Stream *FileStream::copy() {
+ Object dictA;
+
+ dict.copy(&dictA);
+ return new FileStream(f, start, limited, length, &dictA);
+}
+
+Stream *FileStream::makeSubStream(GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA) {
+ return new FileStream(f, startA, limitedA, lengthA, dictA);
+}
+
+void FileStream::reset() {
+ bufPtr = bufEnd = buf;
+ bufPos = start;
+}
+
+int FileStream::getBlock(char *blk, int size) {
+ int n, m;
+
+ n = 0;
+ while (n < size) {
+ if (bufPtr >= bufEnd) {
+ if (!fillBuf()) {
+ break;
+ }
+ }
+ m = (int)(bufEnd - bufPtr);
+ if (m > size - n) {
+ m = size - n;
+ }
+ memcpy(blk + n, bufPtr, m);
+ bufPtr += m;
+ n += m;
+ }
+ return n;
+}
+
+GBool FileStream::fillBuf() {
+ int n;
+
+ bufPos += (int)(bufEnd - buf);
+ bufPtr = bufEnd = buf;
+ if (limited && bufPos >= start + length) {
+ return gFalse;
+ }
+ if (limited && bufPos + fileStreamBufSize > start + length) {
+ n = (int)(start + length - bufPos);
+ } else {
+ n = fileStreamBufSize;
+ }
+ n = f->readBlock(buf, bufPos, n);
+ bufEnd = buf + n;
+ if (bufPtr >= bufEnd) {
+ return gFalse;
+ }
+ return gTrue;
+}
+
+void FileStream::setPos(GFileOffset pos, int dir) {
+ GFileOffset size;
+
+ if (dir >= 0) {
+ bufPos = pos;
+ } else {
+ size = f->getSize();
+ if (pos <= size) {
+ bufPos = size - pos;
+ } else {
+ bufPos = 0;
+ }
+ }
+ bufPtr = bufEnd = buf;
+}
+
+void FileStream::moveStart(int delta) {
+ start += delta;
+ bufPtr = bufEnd = buf;
+ bufPos = start;
+}
+
+//------------------------------------------------------------------------
+// MemStream
+//------------------------------------------------------------------------
+
+MemStream::MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA):
+ BaseStream(dictA) {
+ buf = bufA;
+ start = startA;
+ length = lengthA;
+ bufEnd = buf + start + length;
+ bufPtr = buf + start;
+ needFree = gFalse;
+}
+
+MemStream::~MemStream() {
+ if (needFree) {
+ gfree(buf);
+ }
+}
+
+Stream *MemStream::copy() {
+ Object dictA;
+
+ dict.copy(&dictA);
+ return new MemStream(buf, start, length, &dictA);
+}
+
+Stream *MemStream::makeSubStream(GFileOffset startA, GBool limited,
+ GFileOffset lengthA, Object *dictA) {
+ MemStream *subStr;
+ Guint newStart, newLength;
+
+ if (startA < start) {
+ newStart = start;
+ } else if (startA > start + length) {
+ newStart = start + (int)length;
+ } else {
+ newStart = (int)startA;
+ }
+ if (!limited || newStart + lengthA > start + length) {
+ newLength = start + length - newStart;
+ } else {
+ newLength = (Guint)lengthA;
+ }
+ subStr = new MemStream(buf, newStart, newLength, dictA);
+ return subStr;
+}
+
+void MemStream::reset() {
+ bufPtr = buf + start;
+}
+
+void MemStream::close() {
+}
+
+int MemStream::getBlock(char *blk, int size) {
+ int n;
+
+ if (size <= 0) {
+ return 0;
+ }
+ if (bufEnd - bufPtr < size) {
+ n = (int)(bufEnd - bufPtr);
+ } else {
+ n = size;
+ }
+ memcpy(blk, bufPtr, n);
+ bufPtr += n;
+ return n;
+}
+
+void MemStream::setPos(GFileOffset pos, int dir) {
+ Guint i;
+
+ if (dir >= 0) {
+ i = (Guint)pos;
+ } else {
+ i = (Guint)(start + length - pos);
+ }
+ if (i < start) {
+ i = start;
+ } else if (i > start + length) {
+ i = start + length;
+ }
+ bufPtr = buf + i;
+}
+
+void MemStream::moveStart(int delta) {
+ start += delta;
+ length -= delta;
+ bufPtr = buf + start;
+}
+
+//------------------------------------------------------------------------
+// EmbedStream
+//------------------------------------------------------------------------
+
+EmbedStream::EmbedStream(Stream *strA, Object *dictA,
+ GBool limitedA, GFileOffset lengthA):
+ BaseStream(dictA) {
+ str = strA;
+ limited = limitedA;
+ length = lengthA;
+}
+
+EmbedStream::~EmbedStream() {
+}
+
+Stream *EmbedStream::copy() {
+ Object dictA;
+
+ dict.copy(&dictA);
+ return new EmbedStream(str, &dictA, limited, length);
+}
+
+Stream *EmbedStream::makeSubStream(GFileOffset start, GBool limitedA,
+ GFileOffset lengthA, Object *dictA) {
+ error(errInternal, -1, "Called makeSubStream() on EmbedStream");
+ return NULL;
+}
+
+int EmbedStream::getChar() {
+ if (limited && !length) {
+ return EOF;
+ }
+ --length;
+ return str->getChar();
+}
+
+int EmbedStream::lookChar() {
+ if (limited && !length) {
+ return EOF;
+ }
+ return str->lookChar();
+}
+
+int EmbedStream::getBlock(char *blk, int size) {
+ if (size <= 0) {
+ return 0;
+ }
+ if (limited && length < (Guint)size) {
+ size = (int)length;
+ }
+ length -= size;
+ return str->getBlock(blk, size);
+}
+
+void EmbedStream::setPos(GFileOffset pos, int dir) {
+ error(errInternal, -1, "Called setPos() on EmbedStream");
+}
+
+GFileOffset EmbedStream::getStart() {
+ error(errInternal, -1, "Called getStart() on EmbedStream");
+ return 0;
+}
+
+void EmbedStream::moveStart(int delta) {
+ error(errInternal, -1, "Called moveStart() on EmbedStream");
+}
+
+//------------------------------------------------------------------------
+// ASCIIHexStream
+//------------------------------------------------------------------------
+
+ASCIIHexStream::ASCIIHexStream(Stream *strA):
+ FilterStream(strA) {
+ buf = EOF;
+ eof = gFalse;
+}
+
+ASCIIHexStream::~ASCIIHexStream() {
+ delete str;
+}
+
+Stream *ASCIIHexStream::copy() {
+ return new ASCIIHexStream(str->copy());
+}
+
+void ASCIIHexStream::reset() {
+ str->reset();
+ buf = EOF;
+ eof = gFalse;
+}
+
+int ASCIIHexStream::lookChar() {
+ int c1, c2, x;
+
+ if (buf != EOF)
+ return buf;
+ if (eof) {
+ buf = EOF;
+ return EOF;
+ }
+ do {
+ c1 = str->getChar();
+ } while (isspace(c1));
+ if (c1 == '>') {
+ eof = gTrue;
+ buf = EOF;
+ return buf;
+ }
+ do {
+ c2 = str->getChar();
+ } while (isspace(c2));
+ if (c2 == '>') {
+ eof = gTrue;
+ c2 = '0';
+ }
+ if (c1 >= '0' && c1 <= '9') {
+ x = (c1 - '0') << 4;
+ } else if (c1 >= 'A' && c1 <= 'F') {
+ x = (c1 - 'A' + 10) << 4;
+ } else if (c1 >= 'a' && c1 <= 'f') {
+ x = (c1 - 'a' + 10) << 4;
+ } else if (c1 == EOF) {
+ eof = gTrue;
+ x = 0;
+ } else {
+ error(errSyntaxError, getPos(),
+ "Illegal character <{0:02x}> in ASCIIHex stream", c1);
+ x = 0;
+ }
+ if (c2 >= '0' && c2 <= '9') {
+ x += c2 - '0';
+ } else if (c2 >= 'A' && c2 <= 'F') {
+ x += c2 - 'A' + 10;
+ } else if (c2 >= 'a' && c2 <= 'f') {
+ x += c2 - 'a' + 10;
+ } else if (c2 == EOF) {
+ eof = gTrue;
+ x = 0;
+ } else {
+ error(errSyntaxError, getPos(),
+ "Illegal character <{0:02x}> in ASCIIHex stream", c2);
+ }
+ buf = x & 0xff;
+ return buf;
+}
+
+GString *ASCIIHexStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 2) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ s->append(indent)->append("/ASCIIHexDecode filter\n");
+ return s;
+}
+
+GBool ASCIIHexStream::isBinary(GBool last) {
+ return str->isBinary(gFalse);
+}
+
+//------------------------------------------------------------------------
+// ASCII85Stream
+//------------------------------------------------------------------------
+
+ASCII85Stream::ASCII85Stream(Stream *strA):
+ FilterStream(strA) {
+ index = n = 0;
+ eof = gFalse;
+}
+
+ASCII85Stream::~ASCII85Stream() {
+ delete str;
+}
+
+Stream *ASCII85Stream::copy() {
+ return new ASCII85Stream(str->copy());
+}
+
+void ASCII85Stream::reset() {
+ str->reset();
+ index = n = 0;
+ eof = gFalse;
+}
+
+int ASCII85Stream::lookChar() {
+ int k;
+ Gulong t;
+
+ if (index >= n) {
+ if (eof)
+ return EOF;
+ index = 0;
+ do {
+ c[0] = str->getChar();
+ } while (Lexer::isSpace(c[0]));
+ if (c[0] == '~' || c[0] == EOF) {
+ eof = gTrue;
+ n = 0;
+ return EOF;
+ } else if (c[0] == 'z') {
+ b[0] = b[1] = b[2] = b[3] = 0;
+ n = 4;
+ } else {
+ for (k = 1; k < 5; ++k) {
+ do {
+ c[k] = str->getChar();
+ } while (Lexer::isSpace(c[k]));
+ if (c[k] == '~' || c[k] == EOF)
+ break;
+ }
+ n = k - 1;
+ if (k < 5 && (c[k] == '~' || c[k] == EOF)) {
+ for (++k; k < 5; ++k)
+ c[k] = 0x21 + 84;
+ eof = gTrue;
+ }
+ t = 0;
+ for (k = 0; k < 5; ++k)
+ t = t * 85 + (c[k] - 0x21);
+ for (k = 3; k >= 0; --k) {
+ b[k] = (int)(t & 0xff);
+ t >>= 8;
+ }
+ }
+ }
+ return b[index];
+}
+
+GString *ASCII85Stream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 2) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ s->append(indent)->append("/ASCII85Decode filter\n");
+ return s;
+}
+
+GBool ASCII85Stream::isBinary(GBool last) {
+ return str->isBinary(gFalse);
+}
+
+//------------------------------------------------------------------------
+// LZWStream
+//------------------------------------------------------------------------
+
+LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors,
+ int bits, int earlyA):
+ FilterStream(strA) {
+ if (predictor != 1) {
+ pred = new StreamPredictor(this, predictor, columns, colors, bits);
+ if (!pred->isOk()) {
+ delete pred;
+ pred = NULL;
+ }
+ } else {
+ pred = NULL;
+ }
+ early = earlyA;
+ eof = gFalse;
+ inputBits = 0;
+ clearTable();
+}
+
+LZWStream::~LZWStream() {
+ if (pred) {
+ delete pred;
+ }
+ delete str;
+}
+
+Stream *LZWStream::copy() {
+ if (pred) {
+ return new LZWStream(str->copy(), pred->getPredictor(),
+ pred->getWidth(), pred->getNComps(),
+ pred->getNBits(), early);
+ } else {
+ return new LZWStream(str->copy(), 1, 0, 0, 0, early);
+ }
+}
+
+int LZWStream::getChar() {
+ if (pred) {
+ return pred->getChar();
+ }
+ if (eof) {
+ return EOF;
+ }
+ if (seqIndex >= seqLength) {
+ if (!processNextCode()) {
+ return EOF;
+ }
+ }
+ return seqBuf[seqIndex++];
+}
+
+int LZWStream::lookChar() {
+ if (pred) {
+ return pred->lookChar();
+ }
+ if (eof) {
+ return EOF;
+ }
+ if (seqIndex >= seqLength) {
+ if (!processNextCode()) {
+ return EOF;
+ }
+ }
+ return seqBuf[seqIndex];
+}
+
+int LZWStream::getRawChar() {
+ if (eof) {
+ return EOF;
+ }
+ if (seqIndex >= seqLength) {
+ if (!processNextCode()) {
+ return EOF;
+ }
+ }
+ return seqBuf[seqIndex++];
+}
+
+int LZWStream::getBlock(char *blk, int size) {
+ int n, m;
+
+ if (pred) {
+ return pred->getBlock(blk, size);
+ }
+ if (eof) {
+ return 0;
+ }
+ n = 0;
+ while (n < size) {
+ if (seqIndex >= seqLength) {
+ if (!processNextCode()) {
+ break;
+ }
+ }
+ m = seqLength - seqIndex;
+ if (m > size - n) {
+ m = size - n;
+ }
+ memcpy(blk + n, seqBuf + seqIndex, m);
+ seqIndex += m;
+ n += m;
+ }
+ return n;
+}
+
+void LZWStream::reset() {
+ str->reset();
+ if (pred) {
+ pred->reset();
+ }
+ eof = gFalse;
+ inputBits = 0;
+ clearTable();
+ totalIn = totalOut = 0;
+}
+
+GBool LZWStream::processNextCode() {
+ int code;
+ int nextLength;
+ int i, j;
+
+ // check for EOF
+ if (eof) {
+ return gFalse;
+ }
+
+ // check for eod and clear-table codes
+ start:
+ code = getCode();
+ if (code == EOF || code == 257) {
+ eof = gTrue;
+ return gFalse;
+ }
+ if (code == 256) {
+ clearTable();
+ goto start;
+ }
+ if (nextCode >= 4097) {
+ error(errSyntaxError, getPos(),
+ "Bad LZW stream - expected clear-table code");
+ clearTable();
+ }
+
+ // process the next code
+ nextLength = seqLength + 1;
+ if (code < 256) {
+ seqBuf[0] = (Guchar)code;
+ seqLength = 1;
+ } else if (code < nextCode) {
+ seqLength = table[code].length;
+ for (i = seqLength - 1, j = code; i > 0; --i) {
+ seqBuf[i] = table[j].tail;
+ j = table[j].head;
+ }
+ seqBuf[0] = (Guchar)j;
+ } else if (code == nextCode) {
+ seqBuf[seqLength] = (Guchar)newChar;
+ ++seqLength;
+ } else {
+ error(errSyntaxError, getPos(), "Bad LZW stream - unexpected code");
+ eof = gTrue;
+ return gFalse;
+ }
+ newChar = seqBuf[0];
+ if (first) {
+ first = gFalse;
+ } else {
+ table[nextCode].length = nextLength;
+ table[nextCode].head = prevCode;
+ table[nextCode].tail = (Guchar)newChar;
+ ++nextCode;
+ if (nextCode + early == 512)
+ nextBits = 10;
+ else if (nextCode + early == 1024)
+ nextBits = 11;
+ else if (nextCode + early == 2048)
+ 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;
+
+ return gTrue;
+}
+
+void LZWStream::clearTable() {
+ nextCode = 258;
+ nextBits = 9;
+ seqIndex = seqLength = 0;
+ first = gTrue;
+}
+
+int LZWStream::getCode() {
+ int c;
+ int code;
+
+ while (inputBits < nextBits) {
+ if ((c = str->getChar()) == EOF)
+ return EOF;
+ inputBuf = (inputBuf << 8) | (c & 0xff);
+ inputBits += 8;
+ ++totalIn;
+ }
+ code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1);
+ inputBits -= nextBits;
+ return code;
+}
+
+GString *LZWStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 2 || pred) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ s->append(indent)->append("<< ");
+ if (!early) {
+ s->append("/EarlyChange 0 ");
+ }
+ s->append(">> /LZWDecode filter\n");
+ return s;
+}
+
+GBool LZWStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+//------------------------------------------------------------------------
+// RunLengthStream
+//------------------------------------------------------------------------
+
+RunLengthStream::RunLengthStream(Stream *strA):
+ FilterStream(strA) {
+ bufPtr = bufEnd = buf;
+ eof = gFalse;
+}
+
+RunLengthStream::~RunLengthStream() {
+ delete str;
+}
+
+Stream *RunLengthStream::copy() {
+ return new RunLengthStream(str->copy());
+}
+
+void RunLengthStream::reset() {
+ str->reset();
+ bufPtr = bufEnd = buf;
+ eof = gFalse;
+}
+
+int RunLengthStream::getBlock(char *blk, int size) {
+ int n, m;
+
+ n = 0;
+ while (n < size) {
+ if (bufPtr >= bufEnd) {
+ if (!fillBuf()) {
+ break;
+ }
+ }
+ m = (int)(bufEnd - bufPtr);
+ if (m > size - n) {
+ m = size - n;
+ }
+ memcpy(blk + n, bufPtr, m);
+ bufPtr += m;
+ n += m;
+ }
+ return n;
+}
+
+GString *RunLengthStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 2) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ s->append(indent)->append("/RunLengthDecode filter\n");
+ return s;
+}
+
+GBool RunLengthStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+GBool RunLengthStream::fillBuf() {
+ int c;
+ int n, i;
+
+ if (eof)
+ return gFalse;
+ c = str->getChar();
+ if (c == 0x80 || c == EOF) {
+ eof = gTrue;
+ return gFalse;
+ }
+ if (c < 0x80) {
+ n = c + 1;
+ for (i = 0; i < n; ++i)
+ buf[i] = (char)str->getChar();
+ } else {
+ n = 0x101 - c;
+ c = str->getChar();
+ for (i = 0; i < n; ++i)
+ buf[i] = (char)c;
+ }
+ bufPtr = buf;
+ bufEnd = buf + n;
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// CCITTFaxStream
+//------------------------------------------------------------------------
+
+CCITTFaxStream::CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA,
+ GBool byteAlignA, int columnsA, int rowsA,
+ GBool endOfBlockA, GBool blackA):
+ FilterStream(strA) {
+ encoding = encodingA;
+ endOfLine = endOfLineA;
+ byteAlign = byteAlignA;
+ columns = columnsA;
+ if (columns < 1) {
+ columns = 1;
+ } else if (columns > INT_MAX - 3) {
+ columns = INT_MAX - 3;
+ }
+ rows = rowsA;
+ endOfBlock = endOfBlockA;
+ black = blackA;
+ blackXOR = black ? 0xff : 0x00;
+ // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = columns
+ // ---> max codingLine size = columns + 1
+ // refLine has two extra guard entries at the end
+ // ---> max refLine size = columns + 3
+ codingLine = (int *)gmallocn(columns + 1, sizeof(int));
+ refLine = (int *)gmallocn(columns + 3, sizeof(int));
+
+ eof = gFalse;
+ row = 0;
+ nextLine2D = encoding < 0;
+ inputBits = 0;
+ codingLine[0] = columns;
+ nextCol = columns;
+ a0i = 0;
+ err = gFalse;
+ nErrors = 0;
+}
+
+CCITTFaxStream::~CCITTFaxStream() {
+ delete str;
+ gfree(refLine);
+ gfree(codingLine);
+}
+
+Stream *CCITTFaxStream::copy() {
+ return new CCITTFaxStream(str->copy(), encoding, endOfLine,
+ byteAlign, columns, rows, endOfBlock, black);
+}
+
+void CCITTFaxStream::reset() {
+ int code1;
+
+ str->reset();
+ eof = gFalse;
+ row = 0;
+ nextLine2D = encoding < 0;
+ inputBits = 0;
+ codingLine[0] = columns;
+ nextCol = columns;
+ a0i = 0;
+
+ // skip any initial zero bits and end-of-line marker, and get the 2D
+ // encoding tag
+ while ((code1 = lookBits(12)) == 0) {
+ eatBits(1);
+ }
+ if (code1 == 0x001) {
+ eatBits(12);
+ endOfLine = gTrue;
+ }
+ if (encoding > 0) {
+ nextLine2D = !lookBits(1);
+ eatBits(1);
+ }
+}
+
+int CCITTFaxStream::getChar() {
+ int c, bitsNeeded, bitsAvail, bitsUsed;
+
+ if (nextCol >= columns) {
+ if (eof) {
+ return EOF;
+ }
+ if (!readRow()) {
+ return EOF;
+ }
+ }
+ bitsAvail = codingLine[a0i] - nextCol;
+ if (bitsAvail > 8) {
+ c = (a0i & 1) ? 0x00 : 0xff;
+ } else {
+ c = 0;
+ bitsNeeded = 8;
+ do {
+ bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded;
+ c <<= bitsUsed;
+ if (!(a0i & 1)) {
+ c |= 0xff >> (8 - bitsUsed);
+ }
+ bitsAvail -= bitsUsed;
+ bitsNeeded -= bitsUsed;
+ if (bitsAvail == 0) {
+ if (codingLine[a0i] >= columns) {
+ c <<= bitsNeeded;
+ break;
+ }
+ ++a0i;
+ bitsAvail = codingLine[a0i] - codingLine[a0i - 1];
+ }
+ } while (bitsNeeded > 0);
+ }
+ nextCol += 8;
+ c ^= blackXOR;
+ return c;
+}
+
+int CCITTFaxStream::lookChar() {
+ int c, bitsNeeded, bitsAvail, bitsUsed, i;
+
+ if (nextCol >= columns) {
+ if (eof) {
+ return EOF;
+ }
+ if (!readRow()) {
+ return EOF;
+ }
+ }
+ bitsAvail = codingLine[a0i] - nextCol;
+ if (bitsAvail >= 8) {
+ c = (a0i & 1) ? 0x00 : 0xff;
+ } else {
+ i = a0i;
+ c = 0;
+ bitsNeeded = 8;
+ do {
+ bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded;
+ c <<= bitsUsed;
+ if (!(i & 1)) {
+ c |= 0xff >> (8 - bitsUsed);
+ }
+ bitsAvail -= bitsUsed;
+ bitsNeeded -= bitsUsed;
+ if (bitsAvail == 0) {
+ if (codingLine[i] >= columns) {
+ c <<= bitsNeeded;
+ break;
+ }
+ ++i;
+ bitsAvail = codingLine[i] - codingLine[i - 1];
+ }
+ } while (bitsNeeded > 0);
+ }
+ c ^= blackXOR;
+ return c;
+}
+
+int CCITTFaxStream::getBlock(char *blk, int size) {
+ int bytesRead, bitsAvail, bitsNeeded, bitsUsed, byte, c;
+
+ bytesRead = 0;
+ while (bytesRead < size) {
+ if (nextCol >= columns) {
+ if (eof) {
+ break;
+ }
+ if (!readRow()) {
+ break;
+ }
+ }
+ bitsAvail = codingLine[a0i] - nextCol;
+ byte = (a0i & 1) ? 0x00 : 0xff;
+ if (bitsAvail > 8) {
+ c = byte;
+ bitsAvail -= 8;
+ } else {
+ c = 0;
+ bitsNeeded = 8;
+ do {
+ bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded;
+ c <<= bitsUsed;
+ c |= byte >> (8 - bitsUsed);
+ bitsAvail -= bitsUsed;
+ bitsNeeded -= bitsUsed;
+ if (bitsAvail == 0) {
+ if (codingLine[a0i] >= columns) {
+ c <<= bitsNeeded;
+ break;
+ }
+ ++a0i;
+ bitsAvail = codingLine[a0i] - codingLine[a0i - 1];
+ byte ^= 0xff;
+ }
+ } while (bitsNeeded > 0);
+ }
+ nextCol += 8;
+ blk[bytesRead++] = (char)(c ^ blackXOR);
+ }
+ return bytesRead;
+}
+
+inline void CCITTFaxStream::addPixels(int a1, int blackPixels) {
+ if (a1 > codingLine[a0i]) {
+ if (a1 > columns) {
+ error(errSyntaxError, getPos(),
+ "CCITTFax row is wrong length ({0:d})", a1);
+ err = gTrue;
+ ++nErrors;
+ a1 = columns;
+ }
+ if ((a0i & 1) ^ blackPixels) {
+ ++a0i;
+ }
+ codingLine[a0i] = a1;
+ }
+}
+
+inline void CCITTFaxStream::addPixelsNeg(int a1, int blackPixels) {
+ if (a1 > codingLine[a0i]) {
+ if (a1 > columns) {
+ error(errSyntaxError, getPos(),
+ "CCITTFax row is wrong length ({0:d})", a1);
+ err = gTrue;
+ ++nErrors;
+ a1 = columns;
+ }
+ if ((a0i & 1) ^ blackPixels) {
+ ++a0i;
+ }
+ codingLine[a0i] = a1;
+ } else if (a1 < codingLine[a0i]) {
+ if (a1 < 0) {
+ error(errSyntaxError, getPos(), "Invalid CCITTFax code");
+ err = gTrue;
+ ++nErrors;
+ a1 = 0;
+ }
+ while (a0i > 0 && a1 <= codingLine[a0i - 1]) {
+ --a0i;
+ }
+ codingLine[a0i] = a1;
+ }
+}
+
+GBool CCITTFaxStream::readRow() {
+ int code1, code2, code3;
+ int b1i, blackPixels, i;
+ GBool gotEOL;
+
+ // if at eof just return EOF
+ if (eof) {
+ return gFalse;
+ }
+
+ err = gFalse;
+
+ // 2-D encoding
+ if (nextLine2D) {
+ for (i = 0; codingLine[i] < columns; ++i) {
+ refLine[i] = codingLine[i];
+ }
+ refLine[i++] = columns;
+ refLine[i++] = columns;
+ refLine[i] = columns;
+ codingLine[0] = 0;
+ a0i = 0;
+ b1i = 0;
+ blackPixels = 0;
+ // invariant:
+ // refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1]
+ // <= columns
+ // exception at left edge:
+ // codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible
+ // exception at right edge:
+ // refLine[b1i] = refLine[b1i+1] = columns is possible
+ while (codingLine[a0i] < columns) {
+ code1 = getTwoDimCode();
+ switch (code1) {
+ case twoDimPass:
+ addPixels(refLine[b1i + 1], blackPixels);
+ if (refLine[b1i + 1] < columns) {
+ b1i += 2;
+ }
+ break;
+ case twoDimHoriz:
+ code1 = code2 = 0;
+ if (blackPixels) {
+ do {
+ code1 += code3 = getBlackCode();
+ } while (code3 >= 64);
+ do {
+ code2 += code3 = getWhiteCode();
+ } while (code3 >= 64);
+ } else {
+ do {
+ code1 += code3 = getWhiteCode();
+ } while (code3 >= 64);
+ do {
+ code2 += code3 = getBlackCode();
+ } while (code3 >= 64);
+ }
+ addPixels(codingLine[a0i] + code1, blackPixels);
+ if (codingLine[a0i] < columns) {
+ addPixels(codingLine[a0i] + code2, blackPixels ^ 1);
+ }
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ break;
+ case twoDimVertR3:
+ addPixels(refLine[b1i] + 3, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ ++b1i;
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertR2:
+ addPixels(refLine[b1i] + 2, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ ++b1i;
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertR1:
+ addPixels(refLine[b1i] + 1, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ ++b1i;
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVert0:
+ addPixels(refLine[b1i], blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ ++b1i;
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertL3:
+ addPixelsNeg(refLine[b1i] - 3, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ if (b1i > 0) {
+ --b1i;
+ } else {
+ ++b1i;
+ }
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertL2:
+ addPixelsNeg(refLine[b1i] - 2, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ if (b1i > 0) {
+ --b1i;
+ } else {
+ ++b1i;
+ }
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertL1:
+ addPixelsNeg(refLine[b1i] - 1, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ if (b1i > 0) {
+ --b1i;
+ } else {
+ ++b1i;
+ }
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case EOF:
+ addPixels(columns, 0);
+ err = gTrue;
+ break;
+ default:
+ error(errSyntaxError, getPos(),
+ "Bad 2D code {0:04x} in CCITTFax stream", code1);
+ addPixels(columns, 0);
+ err = gTrue;
+ ++nErrors;
+ break;
+ }
+ }
+
+ // 1-D encoding
+ } else {
+ codingLine[0] = 0;
+ a0i = 0;
+ blackPixels = 0;
+ while (codingLine[a0i] < columns) {
+ code1 = 0;
+ if (blackPixels) {
+ do {
+ code1 += code3 = getBlackCode();
+ } while (code3 >= 64);
+ } else {
+ do {
+ code1 += code3 = getWhiteCode();
+ } while (code3 >= 64);
+ }
+ addPixels(codingLine[a0i] + code1, blackPixels);
+ blackPixels ^= 1;
+ }
+ }
+
+ // check for end-of-line marker, skipping over any extra zero bits
+ // (if EncodedByteAlign is true and EndOfLine is false, there can
+ // be "false" EOL markers -- i.e., if the last n unused bits in
+ // row i are set to zero, and the first 11-n bits in row i+1
+ // happen to be zero -- so we don't look for EOL markers in this
+ // case)
+ gotEOL = gFalse;
+ if (!endOfBlock && row == rows - 1) {
+ eof = gTrue;
+ } else if (endOfLine || !byteAlign) {
+ code1 = lookBits(12);
+ if (endOfLine) {
+ while (code1 != EOF && code1 != 0x001) {
+ eatBits(1);
+ code1 = lookBits(12);
+ }
+ } else {
+ while (code1 == 0) {
+ eatBits(1);
+ code1 = lookBits(12);
+ }
+ }
+ if (code1 == 0x001) {
+ eatBits(12);
+ gotEOL = gTrue;
+ }
+ }
+
+ // byte-align the row
+ // (Adobe apparently doesn't do byte alignment after EOL markers
+ // -- I've seen CCITT image data streams in two different formats,
+ // both with the byteAlign flag set:
+ // 1. xx:x0:01:yy:yy
+ // 2. xx:00:1y:yy:yy
+ // where xx is the previous line, yy is the next line, and colons
+ // separate bytes.)
+ if (byteAlign && !gotEOL) {
+ inputBits &= ~7;
+ }
+
+ // check for end of stream
+ if (lookBits(1) == EOF) {
+ eof = gTrue;
+ }
+
+ // get 2D encoding tag
+ if (!eof && encoding > 0) {
+ nextLine2D = !lookBits(1);
+ eatBits(1);
+ }
+
+ // check for end-of-block marker
+ if (endOfBlock && !endOfLine && byteAlign) {
+ // in this case, we didn't check for an EOL code above, so we
+ // need to check here
+ code1 = lookBits(24);
+ if (code1 == 0x001001) {
+ eatBits(12);
+ gotEOL = gTrue;
+ }
+ }
+ if (endOfBlock && gotEOL) {
+ code1 = lookBits(12);
+ if (code1 == 0x001) {
+ eatBits(12);
+ if (encoding > 0) {
+ lookBits(1);
+ eatBits(1);
+ }
+ if (encoding > 0) {
+ for (i = 0; i < 4; ++i) {
+ code1 = lookBits(12);
+ if (code1 != 0x001) {
+ error(errSyntaxError, getPos(),
+ "Bad RTC code in CCITTFax stream");
+ ++nErrors;
+ }
+ eatBits(12);
+ if (encoding > 0) {
+ lookBits(1);
+ eatBits(1);
+ }
+ }
+ }
+ eof = gTrue;
+ }
+
+ // look for an end-of-line marker after an error -- we only do
+ // this if we know the stream contains end-of-line markers because
+ // the "just plow on" technique tends to work better otherwise
+ } else if (err && endOfLine) {
+ while (1) {
+ code1 = lookBits(13);
+ if (code1 == EOF) {
+ eof = gTrue;
+ return gFalse;
+ }
+ if ((code1 >> 1) == 0x001) {
+ break;
+ }
+ eatBits(1);
+ }
+ eatBits(12);
+ if (encoding > 0) {
+ eatBits(1);
+ nextLine2D = !(code1 & 1);
+ }
+ }
+
+ // corrupt CCITTFax streams can generate huge data expansion -- we
+ // avoid that case by aborting decode after 1000 errors
+ if (nErrors > 1000) {
+ error(errSyntaxError, getPos(), "Too many errors in CCITTFaxStream - aborting decode");
+ eof = gTrue;
+ return gFalse;
+ }
+
+ // set up for output
+ nextCol = 0;
+ a0i = (codingLine[0] > 0) ? 0 : 1;
+
+ ++row;
+
+ return gTrue;
+}
+
+short CCITTFaxStream::getTwoDimCode() {
+ int code;
+ CCITTCode *p;
+ int n;
+
+ code = 0; // make gcc happy
+ if (endOfBlock) {
+ if ((code = lookBits(7)) != EOF) {
+ p = &twoDimTab1[code];
+ if (p->bits > 0) {
+ eatBits(p->bits);
+ return p->n;
+ }
+ }
+ } else {
+ for (n = 1; n <= 7; ++n) {
+ if ((code = lookBits(n)) == EOF) {
+ break;
+ }
+ if (n < 7) {
+ code <<= 7 - n;
+ }
+ p = &twoDimTab1[code];
+ if (p->bits == n) {
+ eatBits(n);
+ return p->n;
+ }
+ }
+ }
+ error(errSyntaxError, getPos(),
+ "Bad two dim code ({0:04x}) in CCITTFax stream", code);
+ ++nErrors;
+ return EOF;
+}
+
+short CCITTFaxStream::getWhiteCode() {
+ short code;
+ CCITTCode *p;
+ int n;
+
+ code = 0; // make gcc happy
+ if (endOfBlock) {
+ code = lookBits(12);
+ if (code == EOF) {
+ return 1;
+ }
+ if ((code >> 5) == 0) {
+ p = &whiteTab1[code];
+ } else {
+ p = &whiteTab2[code >> 3];
+ }
+ if (p->bits > 0) {
+ eatBits(p->bits);
+ return p->n;
+ }
+ } else {
+ for (n = 1; n <= 9; ++n) {
+ code = lookBits(n);
+ if (code == EOF) {
+ return 1;
+ }
+ if (n < 9) {
+ code = (short)(code << (9 - n));
+ }
+ p = &whiteTab2[code];
+ if (p->bits == n) {
+ eatBits(n);
+ return p->n;
+ }
+ }
+ for (n = 11; n <= 12; ++n) {
+ code = lookBits(n);
+ if (code == EOF) {
+ return 1;
+ }
+ if (n < 12) {
+ code = (short)(code << (12 - n));
+ }
+ p = &whiteTab1[code];
+ if (p->bits == n) {
+ eatBits(n);
+ return p->n;
+ }
+ }
+ }
+ error(errSyntaxError, getPos(),
+ "Bad white code ({0:04x}) in CCITTFax stream", code);
+ ++nErrors;
+ // eat a bit and return a positive number so that the caller doesn't
+ // go into an infinite loop
+ eatBits(1);
+ return 1;
+}
+
+short CCITTFaxStream::getBlackCode() {
+ short code;
+ CCITTCode *p;
+ int n;
+
+ code = 0; // make gcc happy
+ if (endOfBlock) {
+ code = lookBits(13);
+ if (code == EOF) {
+ return 1;
+ }
+ if ((code >> 7) == 0) {
+ p = &blackTab1[code];
+ } else if ((code >> 9) == 0 && (code >> 7) != 0) {
+ p = &blackTab2[(code >> 1) - 64];
+ } else {
+ p = &blackTab3[code >> 7];
+ }
+ if (p->bits > 0) {
+ eatBits(p->bits);
+ return p->n;
+ }
+ } else {
+ for (n = 2; n <= 6; ++n) {
+ code = lookBits(n);
+ if (code == EOF) {
+ return 1;
+ }
+ if (n < 6) {
+ code = (short)(code << (6 - n));
+ }
+ p = &blackTab3[code];
+ if (p->bits == n) {
+ eatBits(n);
+ return p->n;
+ }
+ }
+ for (n = 7; n <= 12; ++n) {
+ code = lookBits(n);
+ if (code == EOF) {
+ return 1;
+ }
+ if (n < 12) {
+ code = (short)(code << (12 - n));
+ }
+ if (code >= 64) {
+ p = &blackTab2[code - 64];
+ if (p->bits == n) {
+ eatBits(n);
+ return p->n;
+ }
+ }
+ }
+ for (n = 10; n <= 13; ++n) {
+ code = lookBits(n);
+ if (code == EOF) {
+ return 1;
+ }
+ if (n < 13) {
+ code = (short)(code << (13 - n));
+ }
+ p = &blackTab1[code];
+ if (p->bits == n) {
+ eatBits(n);
+ return p->n;
+ }
+ }
+ }
+ error(errSyntaxError, getPos(),
+ "Bad black code ({0:04x}) in CCITTFax stream", code);
+ ++nErrors;
+ // eat a bit and return a positive number so that the caller doesn't
+ // go into an infinite loop
+ eatBits(1);
+ return 1;
+}
+
+short CCITTFaxStream::lookBits(int n) {
+ int c;
+
+ while (inputBits < n) {
+ if ((c = str->getChar()) == EOF) {
+ if (inputBits == 0) {
+ return EOF;
+ }
+ // near the end of the stream, the caller may ask for more bits
+ // than are available, but there may still be a valid code in
+ // however many bits are available -- we need to return correct
+ // data in this case
+ return (short)((inputBuf << (n - inputBits)) & (0xffffffff >> (32 - n)));
+ }
+ inputBuf = (inputBuf << 8) + c;
+ inputBits += 8;
+ }
+ return (short)((inputBuf >> (inputBits - n)) & (0xffffffff >> (32 - n)));
+}
+
+GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 2) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ s->append(indent)->append("<< ");
+ if (encoding != 0) {
+ s->appendf("/K {0:d} ", encoding);
+ }
+ if (endOfLine) {
+ s->append("/EndOfLine true ");
+ }
+ if (byteAlign) {
+ s->append("/EncodedByteAlign true ");
+ }
+ s->appendf("/Columns {0:d} ", columns);
+ if (rows != 0) {
+ s->appendf("/Rows {0:d} ", rows);
+ }
+ if (!endOfBlock) {
+ s->append("/EndOfBlock false ");
+ }
+ if (black) {
+ s->append("/BlackIs1 true ");
+ }
+ s->append(">> /CCITTFaxDecode filter\n");
+ return s;
+}
+
+GBool CCITTFaxStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+//------------------------------------------------------------------------
+// DCTStream
+//------------------------------------------------------------------------
+
+#if HAVE_JPEGLIB
+
+DCTStream::DCTStream(Stream *strA, GBool colorXformA):
+ FilterStream(strA) {
+ colorXform = colorXformA;
+ lineBuf = NULL;
+ inlineImage = str->isEmbedStream();
+}
+
+DCTStream::~DCTStream() {
+ delete str;
+}
+
+Stream *DCTStream::copy() {
+ return new DCTStream(str->copy(), colorXform);
+}
+
+void DCTStream::reset() {
+ int i;
+
+ lineBuf = NULL;
+ error = gFalse;
+
+ str->reset();
+
+ // initialize the libjpeg decompression object
+ decomp.err = jpeg_std_error(&errorMgr.err);
+ errorMgr.err.error_exit = &errorExit;
+ errorMgr.err.output_message = &errorMessage;
+ if (setjmp(errorMgr.setjmpBuf)) {
+ error = gTrue;
+ return;
+ }
+ jpeg_create_decompress(&decomp);
+
+ // set up the data source manager
+ sourceMgr.src.next_input_byte = NULL;
+ sourceMgr.src.bytes_in_buffer = 0;
+ sourceMgr.src.init_source = &initSourceCbk;
+ sourceMgr.src.fill_input_buffer = &fillInputBufferCbk;
+ sourceMgr.src.skip_input_data = &skipInputDataCbk;
+ sourceMgr.src.resync_to_restart = &jpeg_resync_to_restart;
+ sourceMgr.src.term_source = &termSourceCbk;
+ sourceMgr.str = this;
+ decomp.src = &sourceMgr.src;
+
+ // read the header
+ jpeg_read_header(&decomp, TRUE);
+ jpeg_calc_output_dimensions(&decomp);
+
+ // set up the color transform
+ if (!decomp.saw_Adobe_marker && colorXform >= 0) {
+ if (decomp.num_components == 3) {
+ decomp.jpeg_color_space = colorXform ? JCS_YCbCr : JCS_RGB;
+ decomp.out_color_space = JCS_RGB;
+ decomp.out_color_components = 3;
+ } else if (decomp.num_components == 4) {
+ decomp.jpeg_color_space = colorXform ? JCS_YCCK : JCS_CMYK;
+ decomp.out_color_space = JCS_CMYK;
+ decomp.out_color_components = 4;
+ }
+ }
+
+ // allocate a line buffer
+ if ((lineBufHeight = decomp.rec_outbuf_height) > 4) {
+ lineBufHeight = 4;
+ }
+ lineBuf = (char *)gmallocn(lineBufHeight * decomp.out_color_components,
+ decomp.output_width);
+ for (i = 0; i < lineBufHeight; ++i) {
+ lineBufRows[i] = lineBuf +
+ i * decomp.out_color_components * decomp.output_width;
+ }
+ bufPtr = bufEnd = lineBuf;
+
+ // start up the decompression process
+ jpeg_start_decompress(&decomp);
+}
+
+GBool DCTStream::checkSequentialInterleaved() {
+ //~ this is unimplemented
+ return gTrue;
+}
+
+void DCTStream::close() {
+ // we don't call jpeg_finish_decompress() here because it will report
+ // an error if the full image wasn't read
+ if (setjmp(errorMgr.setjmpBuf)) {
+ goto skip;
+ }
+ jpeg_destroy_decompress(&decomp);
+ skip:
+ gfree(lineBuf);
+ FilterStream::close();
+}
+
+int DCTStream::getChar() {
+ if (error) {
+ return EOF;
+ }
+ if (bufPtr == bufEnd) {
+ if (!fillBuf()) {
+ return EOF;
+ }
+ }
+ return *bufPtr++ & 0xff;
+}
+
+int DCTStream::lookChar() {
+ if (error) {
+ return EOF;
+ }
+ if (bufPtr == bufEnd) {
+ if (!fillBuf()) {
+ return EOF;
+ }
+ }
+ return *bufPtr & 0xff;
+}
+
+int DCTStream::getBlock(char *blk, int size) {
+ int nRead, nAvail, n;
+
+ if (error) {
+ return 0;
+ }
+ nRead = 0;
+ while (nRead < size) {
+ if (bufPtr == bufEnd) {
+ if (!fillBuf()) {
+ break;
+ }
+ }
+ nAvail = bufEnd - bufPtr;
+ n = (nAvail < size - nRead) ? nAvail : size - nRead;
+ memcpy(blk + nRead, bufPtr, n);
+ bufPtr += n;
+ nRead += n;
+ }
+ return nRead;
+}
+
+GBool DCTStream::fillBuf() {
+ int nLines;
+
+ if (setjmp(errorMgr.setjmpBuf)) {
+ error = gTrue;
+ return gFalse;
+ }
+ nLines = jpeg_read_scanlines(&decomp, (JSAMPARRAY)lineBufRows,
+ lineBufHeight);
+ bufPtr = lineBuf;
+ bufEnd = lineBuf +
+ nLines * decomp.out_color_components * decomp.output_width;
+ return nLines > 0;
+}
+
+void DCTStream::errorExit(j_common_ptr d) {
+ DCTErrorMgr *errMgr = (DCTErrorMgr *)d->err;
+ longjmp(errMgr->setjmpBuf, 1);
+}
+
+void DCTStream::errorMessage(j_common_ptr d) {
+#if 0 // for debugging
+ char buf[JMSG_LENGTH_MAX];
+
+ (*d->err->format_message)(d, buf);
+ fprintf(stderr, "%s\n", buf);
+#endif
+}
+
+void DCTStream::initSourceCbk(j_decompress_ptr d) {
+ DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src;
+
+ sourceMgr->src.next_input_byte = NULL;
+ sourceMgr->src.bytes_in_buffer = 0;
+}
+
+boolean DCTStream::fillInputBufferCbk(j_decompress_ptr d) {
+ DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src;
+ int c, n;
+
+ // for inline images, we need to read one byte at a time so we don't
+ // read past the end of the input data
+ if (sourceMgr->str->inlineImage) {
+ c = sourceMgr->str->str->getChar();
+ if (c == EOF) {
+ sourceMgr->buf[0] = (char)0xff;
+ sourceMgr->buf[1] = (char)JPEG_EOI;
+ sourceMgr->src.bytes_in_buffer = 2;
+ } else {
+ sourceMgr->buf[0] = (char)c;
+ sourceMgr->src.bytes_in_buffer = 1;
+ }
+ } else {
+ n = sourceMgr->str->str->getBlock(sourceMgr->buf, dctStreamBufSize);
+ if (n > 0) {
+ sourceMgr->src.bytes_in_buffer = (size_t)n;
+ } else {
+ sourceMgr->buf[0] = (char)0xff;
+ sourceMgr->buf[1] = (char)JPEG_EOI;
+ sourceMgr->src.bytes_in_buffer = 2;
+ }
+ }
+ sourceMgr->src.next_input_byte = (JOCTET *)sourceMgr->buf;
+ return TRUE;
+}
+
+void DCTStream::skipInputDataCbk(j_decompress_ptr d, long numBytes) {
+ DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src;
+
+ if (numBytes > 0) {
+ if ((long)sourceMgr->src.bytes_in_buffer < numBytes) {
+ sourceMgr->str->str->discardChars(
+ (Guint)(numBytes - sourceMgr->src.bytes_in_buffer));
+ sourceMgr->src.bytes_in_buffer = 0;
+ } else {
+ sourceMgr->src.bytes_in_buffer -= numBytes;
+ sourceMgr->src.next_input_byte += numBytes;
+ }
+ }
+}
+
+void DCTStream::termSourceCbk(j_decompress_ptr d) {
+}
+
+#else // HAVE_JPEGLIB
+
+#define idctScaleA 1024
+#define idctScaleB 1138
+#define idctScaleC 1730
+#define idctScaleD 1609
+#define idctScaleE 1264
+#define idctScaleF 1922
+#define idctScaleG 1788
+#define idctScaleH 2923
+#define idctScaleI 2718
+#define idctScaleJ 2528
+
+static int idctScaleMat[64] = {
+ idctScaleA, idctScaleB, idctScaleC, idctScaleD, idctScaleA, idctScaleD, idctScaleC, idctScaleB,
+ idctScaleB, idctScaleE, idctScaleF, idctScaleG, idctScaleB, idctScaleG, idctScaleF, idctScaleE,
+ idctScaleC, idctScaleF, idctScaleH, idctScaleI, idctScaleC, idctScaleI, idctScaleH, idctScaleF,
+ idctScaleD, idctScaleG, idctScaleI, idctScaleJ, idctScaleD, idctScaleJ, idctScaleI, idctScaleG,
+ idctScaleA, idctScaleB, idctScaleC, idctScaleD, idctScaleA, idctScaleD, idctScaleC, idctScaleB,
+ idctScaleD, idctScaleG, idctScaleI, idctScaleJ, idctScaleD, idctScaleJ, idctScaleI, idctScaleG,
+ idctScaleC, idctScaleF, idctScaleH, idctScaleI, idctScaleC, idctScaleI, idctScaleH, idctScaleF,
+ idctScaleB, idctScaleE, idctScaleF, idctScaleG, idctScaleB, idctScaleG, idctScaleF, idctScaleE
+};
+
+// color conversion parameters (16.16 fixed point format)
+#define dctCrToR 91881 // 1.4020
+#define dctCbToG -22553 // -0.3441363
+#define dctCrToG -46802 // -0.71413636
+#define dctCbToB 116130 // 1.772
+
+// The dctClip function clips signed integers to the [0,255] range.
+// To handle valid DCT inputs, this must support an input range of at
+// least [-256,511]. Invalid DCT inputs (e.g., from damaged PDF
+// files) can result in arbitrary values, so we want to mask those
+// out. We round the input range size up to a power of 2 (so we can
+// use a bit mask), which gives us an input range of [-384,639]. The
+// end result is:
+// input output
+// ---------- ------
+// <-384 X invalid inputs -> output is "don't care"
+// -384..-257 0 invalid inputs, clipped
+// -256..-1 0 valid inputs, need to be clipped
+// 0..255 0..255
+// 256..511 255 valid inputs, need to be clipped
+// 512..639 255 invalid inputs, clipped
+// >=512 X invalid inputs -> output is "don't care"
+
+#define dctClipOffset 384
+#define dctClipMask 1023
+static Guchar dctClipData[1024];
+
+static inline void dctClipInit() {
+ static int initDone = 0;
+ int i;
+ if (!initDone) {
+ for (i = -384; i < 0; ++i) {
+ dctClipData[dctClipOffset + i] = 0;
+ }
+ for (i = 0; i < 256; ++i) {
+ dctClipData[dctClipOffset + i] = (Guchar)i;
+ }
+ for (i = 256; i < 639; ++i) {
+ dctClipData[dctClipOffset + i] = 255;
+ }
+ initDone = 1;
+ }
+}
+
+static inline Guchar dctClip(int x) {
+ return dctClipData[(dctClipOffset + x) & dctClipMask];
+}
+
+// zig zag decode map
+static int dctZigZag[64] = {
+ 0,
+ 1, 8,
+ 16, 9, 2,
+ 3, 10, 17, 24,
+ 32, 25, 18, 11, 4,
+ 5, 12, 19, 26, 33, 40,
+ 48, 41, 34, 27, 20, 13, 6,
+ 7, 14, 21, 28, 35, 42, 49, 56,
+ 57, 50, 43, 36, 29, 22, 15,
+ 23, 30, 37, 44, 51, 58,
+ 59, 52, 45, 38, 31,
+ 39, 46, 53, 60,
+ 61, 54, 47,
+ 55, 62,
+ 63
+};
+
+DCTStream::DCTStream(Stream *strA, GBool colorXformA):
+ FilterStream(strA) {
+ int i;
+
+ colorXform = colorXformA;
+ progressive = interleaved = gFalse;
+ width = height = 0;
+ mcuWidth = mcuHeight = 0;
+ numComps = 0;
+ comp = 0;
+ x = y = 0;
+ for (i = 0; i < 4; ++i) {
+ frameBuf[i] = NULL;
+ }
+ rowBuf = NULL;
+ memset(dcHuffTables, 0, sizeof(dcHuffTables));
+ memset(acHuffTables, 0, sizeof(acHuffTables));
+
+ dctClipInit();
+}
+
+DCTStream::~DCTStream() {
+ close();
+ delete str;
+}
+
+Stream *DCTStream::copy() {
+ return new DCTStream(str->copy(), colorXform);
+}
+
+void DCTStream::reset() {
+ int i;
+
+ str->reset();
+
+ progressive = interleaved = gFalse;
+ width = height = 0;
+ numComps = 0;
+ numQuantTables = 0;
+ numDCHuffTables = 0;
+ numACHuffTables = 0;
+ gotJFIFMarker = gFalse;
+ gotAdobeMarker = gFalse;
+ restartInterval = 0;
+
+ if (!readHeader(gTrue)) {
+ // force an EOF condition
+ progressive = gTrue;
+ y = height;
+ return;
+ }
+
+ // compute MCU size
+ if (numComps == 1) {
+ compInfo[0].hSample = compInfo[0].vSample = 1;
+ }
+ mcuWidth = compInfo[0].hSample;
+ mcuHeight = compInfo[0].vSample;
+ for (i = 1; i < numComps; ++i) {
+ if (compInfo[i].hSample > mcuWidth) {
+ mcuWidth = compInfo[i].hSample;
+ }
+ if (compInfo[i].vSample > mcuHeight) {
+ mcuHeight = compInfo[i].vSample;
+ }
+ }
+ mcuWidth *= 8;
+ mcuHeight *= 8;
+
+ // figure out color transform
+ if (colorXform == -1) {
+ if (numComps == 3) {
+ if (gotJFIFMarker) {
+ colorXform = 1;
+ } else if (compInfo[0].id == 82 && compInfo[1].id == 71 &&
+ compInfo[2].id == 66) { // ASCII "RGB"
+ colorXform = 0;
+ } else {
+ colorXform = 1;
+ }
+ } else {
+ colorXform = 0;
+ }
+ }
+
+ if (progressive || !interleaved) {
+
+ // allocate a buffer for the whole image
+ bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
+ bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight;
+ if (bufWidth <= 0 || bufHeight <= 0 ||
+ bufWidth > INT_MAX / bufWidth / (int)sizeof(int)) {
+ error(errSyntaxError, getPos(), "Invalid image size in DCT stream");
+ y = height;
+ return;
+ }
+ for (i = 0; i < numComps; ++i) {
+ frameBuf[i] = (int *)gmallocn(bufWidth * bufHeight, sizeof(int));
+ memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int));
+ }
+
+ // read the image data
+ do {
+ restartMarker = 0xd0;
+ restart();
+ readScan();
+ } while (readHeader(gFalse));
+
+ // decode
+ decodeImage();
+
+ // initialize counters
+ comp = 0;
+ x = 0;
+ y = 0;
+
+ } else {
+
+ if (scanInfo.numComps != numComps) {
+ error(errSyntaxError, getPos(), "Invalid scan in sequential DCT stream");
+ y = height;
+ return;
+ }
+
+ // allocate a buffer for one row of MCUs
+ bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
+ rowBuf = (Guchar *)gmallocn(numComps * mcuHeight, bufWidth);
+ rowBufPtr = rowBufEnd = rowBuf;
+
+ // initialize counters
+ y = -mcuHeight;
+
+ restartMarker = 0xd0;
+ restart();
+ }
+}
+
+GBool DCTStream::checkSequentialInterleaved() {
+ GBool headerOk;
+
+ str->reset();
+
+ progressive = interleaved = gFalse;
+ width = height = 0;
+ numComps = 0;
+ numQuantTables = 0;
+ numDCHuffTables = 0;
+ numACHuffTables = 0;
+ gotJFIFMarker = gFalse;
+ gotAdobeMarker = gFalse;
+ restartInterval = 0;
+
+ headerOk = readHeader(gTrue);
+
+ FilterStream::close();
+
+ return headerOk && !progressive && interleaved;
+}
+
+void DCTStream::close() {
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ gfree(frameBuf[i]);
+ frameBuf[i] = NULL;
+ }
+ gfree(rowBuf);
+ rowBuf = NULL;
+ FilterStream::close();
+}
+
+int DCTStream::getChar() {
+ int c;
+
+ if (progressive || !interleaved) {
+ if (y >= height) {
+ return EOF;
+ }
+ c = frameBuf[comp][y * bufWidth + x];
+ if (++comp == numComps) {
+ comp = 0;
+ if (++x == width) {
+ x = 0;
+ ++y;
+ }
+ }
+ } else {
+ if (rowBufPtr == rowBufEnd) {
+ if (y + mcuHeight >= height) {
+ return EOF;
+ }
+ y += mcuHeight;
+ if (!readMCURow()) {
+ y = height;
+ return EOF;
+ }
+ }
+ c = *rowBufPtr++;
+ }
+ return c;
+}
+
+int DCTStream::lookChar() {
+ if (progressive || !interleaved) {
+ if (y >= height) {
+ return EOF;
+ }
+ return frameBuf[comp][y * bufWidth + x];
+ } else {
+ if (rowBufPtr == rowBufEnd) {
+ if (y + mcuHeight >= height) {
+ return EOF;
+ }
+ if (!readMCURow()) {
+ y = height;
+ return EOF;
+ }
+ }
+ return *rowBufPtr;
+ }
+}
+
+int DCTStream::getBlock(char *blk, int size) {
+ int nRead, nAvail, n;
+
+ if (progressive || !interleaved) {
+ if (y >= height) {
+ return 0;
+ }
+ for (nRead = 0; nRead < size; ++nRead) {
+ blk[nRead] = (char)frameBuf[comp][y * bufWidth + x];
+ if (++comp == numComps) {
+ comp = 0;
+ if (++x == width) {
+ x = 0;
+ ++y;
+ if (y >= height) {
+ ++nRead;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ nRead = 0;
+ while (nRead < size) {
+ if (rowBufPtr == rowBufEnd) {
+ if (y + mcuHeight >= height) {
+ break;
+ }
+ y += mcuHeight;
+ if (!readMCURow()) {
+ y = height;
+ break;
+ }
+ }
+ nAvail = (int)(rowBufEnd - rowBufPtr);
+ n = (nAvail < size - nRead) ? nAvail : size - nRead;
+ memcpy(blk + nRead, rowBufPtr, n);
+ rowBufPtr += n;
+ nRead += n;
+ }
+ }
+ return nRead;
+}
+
+void DCTStream::restart() {
+ int i;
+
+ inputBits = 0;
+ restartCtr = restartInterval;
+ for (i = 0; i < numComps; ++i) {
+ compInfo[i].prevDC = 0;
+ }
+ eobRun = 0;
+}
+
+// Read one row of MCUs from a sequential JPEG stream.
+GBool DCTStream::readMCURow() {
+ int data1[64];
+ Guchar data2[64];
+ Guchar *p1, *p2;
+ int pY, pCb, pCr, pR, pG, pB;
+ int h, v, horiz, vert, hSub, vSub;
+ int x1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i;
+ int c;
+
+ for (cc = 0; cc < numComps; ++cc) {
+ if (scanInfo.dcHuffTable[cc] >= numDCHuffTables ||
+ scanInfo.acHuffTable[cc] >= numACHuffTables) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT data: invalid Huffman table index");
+ return gFalse;
+ }
+ if (compInfo[cc].quantTable > numQuantTables) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT data: invalid quant table index");
+ return gFalse;
+ }
+ }
+
+ for (x1 = 0; x1 < width; x1 += mcuWidth) {
+
+ // deal with restart marker
+ if (restartInterval > 0 && restartCtr == 0) {
+ c = readMarker();
+ if (c != restartMarker) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT data: incorrect restart marker");
+ return gFalse;
+ }
+ if (++restartMarker == 0xd8)
+ restartMarker = 0xd0;
+ restart();
+ }
+
+ // read one MCU
+ for (cc = 0; cc < numComps; ++cc) {
+ h = compInfo[cc].hSample;
+ v = compInfo[cc].vSample;
+ horiz = mcuWidth / h;
+ vert = mcuHeight / v;
+ hSub = horiz / 8;
+ vSub = vert / 8;
+ for (y2 = 0; y2 < mcuHeight; y2 += vert) {
+ for (x2 = 0; x2 < mcuWidth; x2 += horiz) {
+ if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
+ &acHuffTables[scanInfo.acHuffTable[cc]],
+ &compInfo[cc].prevDC,
+ data1)) {
+ return gFalse;
+ }
+ transformDataUnit(quantTables[compInfo[cc].quantTable],
+ data1, data2);
+ if (hSub == 1 && vSub == 1 && x1+x2+8 <= width) {
+ for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+ p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc];
+ p1[0] = data2[i];
+ p1[ numComps] = data2[i+1];
+ p1[2*numComps] = data2[i+2];
+ p1[3*numComps] = data2[i+3];
+ p1[4*numComps] = data2[i+4];
+ p1[5*numComps] = data2[i+5];
+ p1[6*numComps] = data2[i+6];
+ p1[7*numComps] = data2[i+7];
+ }
+ } else if (hSub == 2 && vSub == 2 && x1+x2+16 <= width) {
+ for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
+ p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc];
+ p2 = p1 + width * numComps;
+ p1[0] = p1[numComps] =
+ p2[0] = p2[numComps] = data2[i];
+ p1[2*numComps] = p1[3*numComps] =
+ p2[2*numComps] = p2[3*numComps] = data2[i+1];
+ p1[4*numComps] = p1[5*numComps] =
+ p2[4*numComps] = p2[5*numComps] = data2[i+2];
+ p1[6*numComps] = p1[7*numComps] =
+ p2[6*numComps] = p2[7*numComps] = data2[i+3];
+ p1[8*numComps] = p1[9*numComps] =
+ p2[8*numComps] = p2[9*numComps] = data2[i+4];
+ p1[10*numComps] = p1[11*numComps] =
+ p2[10*numComps] = p2[11*numComps] = data2[i+5];
+ p1[12*numComps] = p1[13*numComps] =
+ p2[12*numComps] = p2[13*numComps] = data2[i+6];
+ p1[14*numComps] = p1[15*numComps] =
+ p2[14*numComps] = p2[15*numComps] = data2[i+7];
+ }
+ } else {
+ p1 = &rowBuf[(y2 * width + (x1+x2)) * numComps + cc];
+ i = 0;
+ for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) {
+ for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
+ for (y5 = 0; y5 < vSub; ++y5) {
+ for (x5 = 0; x5 < hSub && x1+x2+x4+x5 < width; ++x5) {
+ p1[((y4+y5) * width + (x4+x5)) * numComps] = data2[i];
+ }
+ }
+ ++i;
+ }
+ }
+ }
+ }
+ }
+ }
+ --restartCtr;
+ }
+
+ // color space conversion
+ if (colorXform) {
+ // convert YCbCr to RGB
+ if (numComps == 3) {
+ for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 3) {
+ pY = p1[0];
+ pCb = p1[1] - 128;
+ pCr = p1[2] - 128;
+ pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+ p1[0] = dctClip(pR);
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
+ p1[1] = dctClip(pG);
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ p1[2] = dctClip(pB);
+ }
+ // convert YCbCrK to CMYK (K is passed through unchanged)
+ } else if (numComps == 4) {
+ for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 4) {
+ pY = p1[0];
+ pCb = p1[1] - 128;
+ pCr = p1[2] - 128;
+ pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+ p1[0] = (Guchar)(255 - dctClip(pR));
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
+ p1[1] = (Guchar)(255 - dctClip(pG));
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ p1[2] = (Guchar)(255 - dctClip(pB));
+ }
+ }
+ }
+
+ rowBufPtr = rowBuf;
+ if (y + mcuHeight <= height) {
+ rowBufEnd = rowBuf + numComps * width * mcuHeight;
+ } else {
+ rowBufEnd = rowBuf + numComps * width * (height - y);
+ }
+
+ return gTrue;
+}
+
+// Read one scan from a progressive or non-interleaved JPEG stream.
+void DCTStream::readScan() {
+ int data[64];
+ int x1, y1, dx1, dy1, x2, y2, y3, cc, i;
+ int h, v, horiz, vert, vSub;
+ int *p1;
+ int c;
+
+ for (cc = 0; cc < numComps; ++cc) {
+ if (scanInfo.comp[cc] &&
+ (scanInfo.dcHuffTable[cc] >= numDCHuffTables ||
+ ((!progressive || scanInfo.lastCoeff > 0) &&
+ scanInfo.acHuffTable[cc] >= numACHuffTables))) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT data: invalid Huffman table index");
+ return;
+ }
+ if (compInfo[cc].quantTable > numQuantTables) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT data: invalid quant table index");
+ return;
+ }
+ }
+
+ if (scanInfo.numComps == 1) {
+ for (cc = 0; cc < numComps; ++cc) {
+ if (scanInfo.comp[cc]) {
+ break;
+ }
+ }
+ dx1 = mcuWidth / compInfo[cc].hSample;
+ dy1 = mcuHeight / compInfo[cc].vSample;
+ } else {
+ dx1 = mcuWidth;
+ dy1 = mcuHeight;
+ }
+
+ for (y1 = 0; y1 < height; y1 += dy1) {
+ for (x1 = 0; x1 < width; x1 += dx1) {
+
+ // deal with restart marker
+ if (restartInterval > 0 && restartCtr == 0) {
+ c = readMarker();
+ if (c != restartMarker) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT data: incorrect restart marker");
+ return;
+ }
+ if (++restartMarker == 0xd8) {
+ restartMarker = 0xd0;
+ }
+ restart();
+ }
+
+ // read one MCU
+ for (cc = 0; cc < numComps; ++cc) {
+ if (!scanInfo.comp[cc]) {
+ continue;
+ }
+
+ h = compInfo[cc].hSample;
+ v = compInfo[cc].vSample;
+ horiz = mcuWidth / h;
+ vert = mcuHeight / v;
+ vSub = vert / 8;
+ for (y2 = 0; y2 < dy1; y2 += vert) {
+ for (x2 = 0; x2 < dx1; x2 += horiz) {
+
+ // pull out the current values
+ p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+ for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+ data[i] = p1[0];
+ data[i+1] = p1[1];
+ data[i+2] = p1[2];
+ data[i+3] = p1[3];
+ data[i+4] = p1[4];
+ data[i+5] = p1[5];
+ data[i+6] = p1[6];
+ data[i+7] = p1[7];
+ p1 += bufWidth * vSub;
+ }
+
+ // read one data unit
+ if (progressive) {
+ if (!readProgressiveDataUnit(
+ &dcHuffTables[scanInfo.dcHuffTable[cc]],
+ &acHuffTables[scanInfo.acHuffTable[cc]],
+ &compInfo[cc].prevDC,
+ data)) {
+ return;
+ }
+ } else {
+ if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
+ &acHuffTables[scanInfo.acHuffTable[cc]],
+ &compInfo[cc].prevDC,
+ data)) {
+ return;
+ }
+ }
+
+ // add the data unit into frameBuf
+ p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+ for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+ p1[0] = data[i];
+ p1[1] = data[i+1];
+ p1[2] = data[i+2];
+ p1[3] = data[i+3];
+ p1[4] = data[i+4];
+ p1[5] = data[i+5];
+ p1[6] = data[i+6];
+ p1[7] = data[i+7];
+ p1 += bufWidth * vSub;
+ }
+ }
+ }
+ }
+ --restartCtr;
+ }
+ }
+}
+
+// Read one data unit from a sequential JPEG stream.
+GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
+ DCTHuffTable *acHuffTable,
+ int *prevDC, int data[64]) {
+ int run, size, amp;
+ int c;
+ int i, j;
+
+ if ((size = readHuffSym(dcHuffTable)) == 9999) {
+ return gFalse;
+ }
+ if (size > 0) {
+ if ((amp = readAmp(size)) == 9999) {
+ return gFalse;
+ }
+ } else {
+ amp = 0;
+ }
+ data[0] = *prevDC += amp;
+ for (i = 1; i < 64; ++i) {
+ data[i] = 0;
+ }
+ i = 1;
+ while (i < 64) {
+ run = 0;
+ while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30) {
+ run += 0x10;
+ }
+ if (c == 9999) {
+ return gFalse;
+ }
+ if (c == 0x00) {
+ break;
+ } else {
+ run += (c >> 4) & 0x0f;
+ size = c & 0x0f;
+ amp = readAmp(size);
+ if (amp == 9999) {
+ return gFalse;
+ }
+ i += run;
+ if (i < 64) {
+ j = dctZigZag[i++];
+ data[j] = amp;
+ }
+ }
+ }
+ return gTrue;
+}
+
+// Read one data unit from a progressive JPEG stream.
+GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
+ DCTHuffTable *acHuffTable,
+ int *prevDC, int data[64]) {
+ int run, size, amp, bit, c;
+ int i, j, k;
+
+ // get the DC coefficient
+ i = scanInfo.firstCoeff;
+ if (i == 0) {
+ if (scanInfo.ah == 0) {
+ if ((size = readHuffSym(dcHuffTable)) == 9999) {
+ return gFalse;
+ }
+ if (size > 0) {
+ if ((amp = readAmp(size)) == 9999) {
+ return gFalse;
+ }
+ } else {
+ amp = 0;
+ }
+ data[0] += (*prevDC += amp) << scanInfo.al;
+ } else {
+ if ((bit = readBit()) == 9999) {
+ return gFalse;
+ }
+ if (bit) {
+ data[0] += 1 << scanInfo.al;
+ }
+ }
+ ++i;
+ }
+ if (scanInfo.lastCoeff == 0) {
+ return gTrue;
+ }
+
+ // check for an EOB run
+ if (eobRun > 0) {
+ while (i <= scanInfo.lastCoeff) {
+ j = dctZigZag[i++];
+ if (data[j] != 0) {
+ if ((bit = readBit()) == EOF) {
+ return gFalse;
+ }
+ if (bit) {
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
+ }
+ }
+ }
+ --eobRun;
+ return gTrue;
+ }
+
+ // read the AC coefficients
+ while (i <= scanInfo.lastCoeff) {
+ if ((c = readHuffSym(acHuffTable)) == 9999) {
+ return gFalse;
+ }
+
+ // ZRL
+ if (c == 0xf0) {
+ k = 0;
+ while (k < 16 && i <= scanInfo.lastCoeff) {
+ j = dctZigZag[i++];
+ if (data[j] == 0) {
+ ++k;
+ } else {
+ if ((bit = readBit()) == EOF) {
+ return gFalse;
+ }
+ if (bit) {
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
+ }
+ }
+ }
+
+ // EOB run
+ } else if ((c & 0x0f) == 0x00) {
+ j = c >> 4;
+ eobRun = 0;
+ for (k = 0; k < j; ++k) {
+ if ((bit = readBit()) == EOF) {
+ return gFalse;
+ }
+ eobRun = (eobRun << 1) | bit;
+ }
+ eobRun += 1 << j;
+ while (i <= scanInfo.lastCoeff) {
+ j = dctZigZag[i++];
+ if (data[j] != 0) {
+ if ((bit = readBit()) == EOF) {
+ return gFalse;
+ }
+ if (bit) {
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
+ }
+ }
+ }
+ --eobRun;
+ break;
+
+ // zero run and one AC coefficient
+ } else {
+ run = (c >> 4) & 0x0f;
+ size = c & 0x0f;
+ if ((amp = readAmp(size)) == 9999) {
+ return gFalse;
+ }
+ j = 0; // make gcc happy
+ for (k = 0; k <= run && i <= scanInfo.lastCoeff; ++k) {
+ j = dctZigZag[i++];
+ while (data[j] != 0 && i <= scanInfo.lastCoeff) {
+ if ((bit = readBit()) == EOF) {
+ return gFalse;
+ }
+ if (bit) {
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
+ }
+ j = dctZigZag[i++];
+ }
+ }
+ data[j] = amp << scanInfo.al;
+ }
+ }
+
+ return gTrue;
+}
+
+// Decode a progressive JPEG image.
+void DCTStream::decodeImage() {
+ int dataIn[64];
+ Guchar dataOut[64];
+ Gushort *quantTable;
+ int pY, pCb, pCr, pR, pG, pB;
+ int x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i;
+ int h, v, horiz, vert, hSub, vSub;
+ int *p0, *p1, *p2;
+
+ for (y1 = 0; y1 < bufHeight; y1 += mcuHeight) {
+ for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) {
+ for (cc = 0; cc < numComps; ++cc) {
+ quantTable = quantTables[compInfo[cc].quantTable];
+ h = compInfo[cc].hSample;
+ v = compInfo[cc].vSample;
+ horiz = mcuWidth / h;
+ vert = mcuHeight / v;
+ hSub = horiz / 8;
+ vSub = vert / 8;
+ for (y2 = 0; y2 < mcuHeight; y2 += vert) {
+ for (x2 = 0; x2 < mcuWidth; x2 += horiz) {
+
+ // pull out the coded data unit
+ p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+ for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+ dataIn[i] = p1[0];
+ dataIn[i+1] = p1[1];
+ dataIn[i+2] = p1[2];
+ dataIn[i+3] = p1[3];
+ dataIn[i+4] = p1[4];
+ dataIn[i+5] = p1[5];
+ dataIn[i+6] = p1[6];
+ dataIn[i+7] = p1[7];
+ p1 += bufWidth * vSub;
+ }
+
+ // transform
+ transformDataUnit(quantTable, dataIn, dataOut);
+
+ // store back into frameBuf, doing replication for
+ // subsampled components
+ p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+ if (hSub == 1 && vSub == 1) {
+ for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+ p1[0] = dataOut[i] & 0xff;
+ p1[1] = dataOut[i+1] & 0xff;
+ p1[2] = dataOut[i+2] & 0xff;
+ p1[3] = dataOut[i+3] & 0xff;
+ p1[4] = dataOut[i+4] & 0xff;
+ p1[5] = dataOut[i+5] & 0xff;
+ p1[6] = dataOut[i+6] & 0xff;
+ p1[7] = dataOut[i+7] & 0xff;
+ p1 += bufWidth;
+ }
+ } else if (hSub == 2 && vSub == 2) {
+ p2 = p1 + bufWidth;
+ for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
+ p1[0] = p1[1] = p2[0] = p2[1] = dataOut[i] & 0xff;
+ p1[2] = p1[3] = p2[2] = p2[3] = dataOut[i+1] & 0xff;
+ p1[4] = p1[5] = p2[4] = p2[5] = dataOut[i+2] & 0xff;
+ p1[6] = p1[7] = p2[6] = p2[7] = dataOut[i+3] & 0xff;
+ p1[8] = p1[9] = p2[8] = p2[9] = dataOut[i+4] & 0xff;
+ p1[10] = p1[11] = p2[10] = p2[11] = dataOut[i+5] & 0xff;
+ p1[12] = p1[13] = p2[12] = p2[13] = dataOut[i+6] & 0xff;
+ p1[14] = p1[15] = p2[14] = p2[15] = dataOut[i+7] & 0xff;
+ p1 += bufWidth * 2;
+ p2 += bufWidth * 2;
+ }
+ } else {
+ i = 0;
+ for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) {
+ for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
+ p2 = p1 + x4;
+ for (y5 = 0; y5 < vSub; ++y5) {
+ for (x5 = 0; x5 < hSub; ++x5) {
+ p2[x5] = dataOut[i] & 0xff;
+ }
+ p2 += bufWidth;
+ }
+ ++i;
+ }
+ p1 += bufWidth * vSub;
+ }
+ }
+ }
+ }
+ }
+
+ // color space conversion
+ if (colorXform) {
+ // convert YCbCr to RGB
+ if (numComps == 3) {
+ for (y2 = 0; y2 < mcuHeight; ++y2) {
+ p0 = &frameBuf[0][(y1+y2) * bufWidth + x1];
+ p1 = &frameBuf[1][(y1+y2) * bufWidth + x1];
+ p2 = &frameBuf[2][(y1+y2) * bufWidth + x1];
+ for (x2 = 0; x2 < mcuWidth; ++x2) {
+ pY = *p0;
+ pCb = *p1 - 128;
+ pCr = *p2 - 128;
+ pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+ *p0++ = dctClip(pR);
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
+ 32768) >> 16;
+ *p1++ = dctClip(pG);
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ *p2++ = dctClip(pB);
+ }
+ }
+ // convert YCbCrK to CMYK (K is passed through unchanged)
+ } else if (numComps == 4) {
+ for (y2 = 0; y2 < mcuHeight; ++y2) {
+ p0 = &frameBuf[0][(y1+y2) * bufWidth + x1];
+ p1 = &frameBuf[1][(y1+y2) * bufWidth + x1];
+ p2 = &frameBuf[2][(y1+y2) * bufWidth + x1];
+ for (x2 = 0; x2 < mcuWidth; ++x2) {
+ pY = *p0;
+ pCb = *p1 - 128;
+ pCr = *p2 - 128;
+ pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+ *p0++ = 255 - dctClip(pR);
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
+ 32768) >> 16;
+ *p1++ = 255 - dctClip(pG);
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ *p2++ = 255 - dctClip(pB);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// Transform one data unit -- this performs the dequantization and
+// IDCT steps. This IDCT algorithm is taken from:
+// Y. A. Reznik, A. T. Hinds, L. Yu, Z. Ni, and C-X. Zhang,
+// "Efficient fixed-point approximations of the 8x8 inverse discrete
+// cosine transform" (invited paper), Proc. SPIE Vol. 6696, Sep. 24,
+// 2007.
+// which is based on:
+// Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
+// "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
+// IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
+// 988-991.
+// The stage numbers mentioned in the comments refer to Figure 1 in the
+// Loeffler paper.
+void DCTStream::transformDataUnit(Gushort *quantTable,
+ int dataIn[64], Guchar dataOut[64]) {
+ int v0, v1, v2, v3, v4, v5, v6, v7;
+ int t0, t1, t2, t3, t4, t5, t6, t7;
+ int *p, *scale;
+ Gushort *q;
+ int i;
+
+ // dequant; inverse DCT on rows
+ for (i = 0; i < 64; i += 8) {
+ p = dataIn + i;
+ q = quantTable + i;
+ scale = idctScaleMat + i;
+
+ // check for all-zero AC coefficients
+ if (p[1] == 0 && p[2] == 0 && p[3] == 0 &&
+ p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0) {
+ t0 = p[0] * q[0] * scale[0];
+ if (i == 0) {
+ t0 += 1 << 12; // rounding bias
+ }
+ p[0] = t0;
+ p[1] = t0;
+ p[2] = t0;
+ p[3] = t0;
+ p[4] = t0;
+ p[5] = t0;
+ p[6] = t0;
+ p[7] = t0;
+ continue;
+ }
+
+ // stage 4
+ v0 = p[0] * q[0] * scale[0];
+ if (i == 0) {
+ v0 += 1 << 12; // rounding bias
+ }
+ v1 = p[4] * q[4] * scale[4];
+ v2 = p[2] * q[2] * scale[2];
+ v3 = p[6] * q[6] * scale[6];
+ t0 = p[1] * q[1] * scale[1];
+ t1 = p[7] * q[7] * scale[7];
+ v4 = t0 - t1;
+ v7 = t0 + t1;
+ v5 = p[3] * q[3] * scale[3];
+ v6 = p[5] * q[5] * scale[5];
+
+ // stage 3
+ t0 = v0 - v1;
+ v0 = v0 + v1;
+ v1 = t0;
+ t0 = v2 + (v2 >> 5);
+ t1 = t0 >> 2;
+ t2 = t1 + (v2 >> 4); // 41/128 * v2
+ t3 = t0 - t1; // 99/128 * v2
+ t4 = v3 + (v3 >> 5);
+ t5 = t4 >> 2;
+ t6 = t5 + (v3 >> 4); // 41/128 * v3
+ t7 = t4 - t5; // 99/128 * v3
+ v2 = t2 - t7;
+ v3 = t3 + t6;
+ t0 = v4 - v6;
+ v4 = v4 + v6;
+ v6 = t0;
+ t0 = v7 + v5;
+ v5 = v7 - v5;
+ v7 = t0;
+
+ // stage 2
+ t0 = v0 - v3;
+ v0 = v0 + v3;
+ v3 = t0;
+ t0 = v1 - v2;
+ v1 = v1 + v2;
+ v2 = t0;
+ t0 = (v4 >> 9) - v4;
+ t1 = v4 >> 1; // 1/2 * v4
+ t2 = (t0 >> 2) - t0; // 1533/2048 * v4
+ t3 = (v7 >> 9) - v7;
+ t4 = v7 >> 1; // 1/2 * v7
+ t5 = (t3 >> 2) - t3; // 1533/2048 * v7
+ v4 = t2 - t4;
+ v7 = t1 + t5;
+ t0 = (v5 >> 3) - (v5 >> 7);
+ t1 = t0 - (v5 >> 11);
+ t2 = t0 + (t1 >> 1); // 719/4096 * v5
+ t3 = v5 - t0; // 113/256 * v5
+ t4 = (v6 >> 3) - (v6 >> 7);
+ t5 = t4 - (v6 >> 11);
+ t6 = t4 + (t5 >> 1); // 719/4096 * v6
+ t7 = v6 - t4; // 113/256 * v6
+ v5 = t3 - t6;
+ v6 = t2 + t7;
+
+ // stage 1
+ p[0] = v0 + v7;
+ p[7] = v0 - v7;
+ p[1] = v1 + v6;
+ p[6] = v1 - v6;
+ p[2] = v2 + v5;
+ p[5] = v2 - v5;
+ p[3] = v3 + v4;
+ p[4] = v3 - v4;
+ }
+
+ // inverse DCT on columns
+ for (i = 0; i < 8; ++i) {
+ p = dataIn + i;
+
+ // check for all-zero AC coefficients
+ if (p[1*8] == 0 && p[2*8] == 0 && p[3*8] == 0 &&
+ p[4*8] == 0 && p[5*8] == 0 && p[6*8] == 0 && p[7*8] == 0) {
+ t0 = p[0*8];
+ p[1*8] = t0;
+ p[2*8] = t0;
+ p[3*8] = t0;
+ p[4*8] = t0;
+ p[5*8] = t0;
+ p[6*8] = t0;
+ p[7*8] = t0;
+ continue;
+ }
+
+ // stage 4
+ v0 = p[0*8];
+ v1 = p[4*8];
+ v2 = p[2*8];
+ v3 = p[6*8];
+ t0 = p[1*8];
+ t1 = p[7*8];
+ v4 = t0 - t1;
+ v7 = t0 + t1;
+ v5 = p[3*8];
+ v6 = p[5*8];
+
+ // stage 3
+ t0 = v0 - v1;
+ v0 = v0 + v1;
+ v1 = t0;
+ t0 = v2 + (v2 >> 5);
+ t1 = t0 >> 2;
+ t2 = t1 + (v2 >> 4); // 41/128 * v2
+ t3 = t0 - t1; // 99/128 * v2
+ t4 = v3 + (v3 >> 5);
+ t5 = t4 >> 2;
+ t6 = t5 + (v3 >> 4); // 41/128 * v3
+ t7 = t4 - t5; // 99/128 * v3
+ v2 = t2 - t7;
+ v3 = t3 + t6;
+ t0 = v4 - v6;
+ v4 = v4 + v6;
+ v6 = t0;
+ t0 = v7 + v5;
+ v5 = v7 - v5;
+ v7 = t0;
+
+ // stage 2
+ t0 = v0 - v3;
+ v0 = v0 + v3;
+ v3 = t0;
+ t0 = v1 - v2;
+ v1 = v1 + v2;
+ v2 = t0;
+ t0 = (v4 >> 9) - v4;
+ t1 = v4 >> 1; // 1/2 * v4
+ t2 = (t0 >> 2) - t0; // 1533/2048 * v4
+ t3 = (v7 >> 9) - v7;
+ t4 = v7 >> 1; // 1/2 * v7
+ t5 = (t3 >> 2) - t3; // 1533/2048 * v7
+ v4 = t2 - t4;
+ v7 = t1 + t5;
+ t0 = (v5 >> 3) - (v5 >> 7);
+ t1 = t0 - (v5 >> 11);
+ t2 = t0 + (t1 >> 1); // 719/4096 * v5
+ t3 = v5 - t0; // 113/256 * v5
+ t4 = (v6 >> 3) - (v6 >> 7);
+ t5 = t4 - (v6 >> 11);
+ t6 = t4 + (t5 >> 1); // 719/4096 * v6
+ t7 = v6 - t4; // 113/256 * v6
+ v5 = t3 - t6;
+ v6 = t2 + t7;
+
+ // stage 1
+ p[0*8] = v0 + v7;
+ p[7*8] = v0 - v7;
+ p[1*8] = v1 + v6;
+ p[6*8] = v1 - v6;
+ p[2*8] = v2 + v5;
+ p[5*8] = v2 - v5;
+ p[3*8] = v3 + v4;
+ p[4*8] = v3 - v4;
+ }
+
+ // convert to 8-bit integers
+ for (i = 0; i < 64; ++i) {
+ dataOut[i] = dctClip(128 + (dataIn[i] >> 13));
+ }
+}
+
+int DCTStream::readHuffSym(DCTHuffTable *table) {
+ Gushort code;
+ int bit;
+ int codeBits;
+
+ code = 0;
+ codeBits = 0;
+ do {
+ // add a bit to the code
+ if ((bit = readBit()) == EOF) {
+ return 9999;
+ }
+ code = (Gushort)((code << 1) + bit);
+ ++codeBits;
+
+ // look up code
+ if (code < table->firstCode[codeBits]) {
+ break;
+ }
+ if (code - table->firstCode[codeBits] < table->numCodes[codeBits]) {
+ code = (Gushort)(code - table->firstCode[codeBits]);
+ return table->sym[table->firstSym[codeBits] + code];
+ }
+ } while (codeBits < 16);
+
+ error(errSyntaxError, getPos(), "Bad Huffman code in DCT stream");
+ return 9999;
+}
+
+int DCTStream::readAmp(int size) {
+ int amp, bit;
+ int bits;
+
+ amp = 0;
+ for (bits = 0; bits < size; ++bits) {
+ if ((bit = readBit()) == EOF)
+ return 9999;
+ amp = (amp << 1) + bit;
+ }
+ if (amp < (1 << (size - 1)))
+ amp -= (1 << size) - 1;
+ return amp;
+}
+
+int DCTStream::readBit() {
+ int bit;
+ int c, c2;
+
+ if (inputBits == 0) {
+ if ((c = str->getChar()) == EOF)
+ return EOF;
+ if (c == 0xff) {
+ do {
+ c2 = str->getChar();
+ } while (c2 == 0xff);
+ if (c2 != 0x00) {
+ error(errSyntaxError, getPos(), "Bad DCT data: missing 00 after ff");
+ return EOF;
+ }
+ }
+ inputBuf = c;
+ inputBits = 8;
+ }
+ bit = (inputBuf >> (inputBits - 1)) & 1;
+ --inputBits;
+ return bit;
+}
+
+GBool DCTStream::readHeader(GBool frame) {
+ GBool doScan;
+ int n, i;
+ int c = 0;
+
+ // read headers
+ doScan = gFalse;
+ while (!doScan) {
+ c = readMarker();
+ switch (c) {
+ case 0xc0: // SOF0 (sequential)
+ case 0xc1: // SOF1 (extended sequential)
+ if (!frame) {
+ error(errSyntaxError, getPos(),
+ "Invalid DCT marker in scan <{0:02x}>", c);
+ return gFalse;
+ }
+ if (!readBaselineSOF()) {
+ return gFalse;
+ }
+ break;
+ case 0xc2: // SOF2 (progressive)
+ if (!frame) {
+ error(errSyntaxError, getPos(),
+ "Invalid DCT marker in scan <{0:02x}>", c);
+ return gFalse;
+ }
+ if (!readProgressiveSOF()) {
+ return gFalse;
+ }
+ break;
+ case 0xc4: // DHT
+ if (!readHuffmanTables()) {
+ return gFalse;
+ }
+ break;
+ case 0xd8: // SOI
+ if (!frame) {
+ error(errSyntaxError, getPos(),
+ "Invalid DCT marker in scan <{0:02x}>", c);
+ return gFalse;
+ }
+ break;
+ case 0xd9: // EOI
+ return gFalse;
+ case 0xda: // SOS
+ if (!readScanInfo()) {
+ return gFalse;
+ }
+ doScan = gTrue;
+ break;
+ case 0xdb: // DQT
+ if (!readQuantTables()) {
+ return gFalse;
+ }
+ break;
+ case 0xdd: // DRI
+ if (!readRestartInterval()) {
+ return gFalse;
+ }
+ break;
+ case 0xe0: // APP0
+ if (!frame) {
+ error(errSyntaxError, getPos(),
+ "Invalid DCT marker in scan <{0:02x}>", c);
+ return gFalse;
+ }
+ if (!readJFIFMarker()) {
+ return gFalse;
+ }
+ break;
+ case 0xee: // APP14
+ if (!frame) {
+ error(errSyntaxError, getPos(),
+ "Invalid DCT marker in scan <{0:02x}>", c);
+ return gFalse;
+ }
+ if (!readAdobeMarker()) {
+ return gFalse;
+ }
+ break;
+ case EOF:
+ error(errSyntaxError, getPos(), "Bad DCT header");
+ return gFalse;
+ default:
+ // skip APPn / COM / etc.
+ if (c >= 0xe0) {
+ n = read16() - 2;
+ str->discardChars(n);
+ } else {
+ error(errSyntaxError, getPos(), "Unknown DCT marker <{0:02x}>", c);
+ return gFalse;
+ }
+ break;
+ }
+ }
+
+ for (i = 0; i < numComps; ++i) {
+ if (compInfo[i].quantTable >= numQuantTables) {
+ error(errSyntaxError, getPos(), "Invalid DCT quant table selector");
+ return gFalse;
+ }
+ }
+
+ return gTrue;
+}
+
+GBool DCTStream::readBaselineSOF() {
+ int prec;
+ int i;
+ int c;
+
+ read16(); // length
+ prec = str->getChar();
+ height = read16();
+ width = read16();
+ numComps = str->getChar();
+ if (numComps <= 0 || numComps > 4) {
+ error(errSyntaxError, getPos(), "Bad number of components in DCT stream");
+ numComps = 0;
+ return gFalse;
+ }
+ if (prec != 8) {
+ error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec);
+ return gFalse;
+ }
+ for (i = 0; i < numComps; ++i) {
+ compInfo[i].id = str->getChar();
+ c = str->getChar();
+ compInfo[i].hSample = (c >> 4) & 0x0f;
+ compInfo[i].vSample = c & 0x0f;
+ compInfo[i].quantTable = str->getChar();
+ // a sampling factor of 3 is allowed by the spec, but requires
+ // messy upsampling, and appears not to be used in practice
+ if (!(compInfo[i].hSample == 1 ||
+ compInfo[i].hSample == 2 ||
+ compInfo[i].hSample == 4) ||
+ !(compInfo[i].vSample == 1 ||
+ compInfo[i].vSample == 2 ||
+ compInfo[i].vSample == 4)) {
+ error(errSyntaxError, getPos(), "Bad DCT sampling factor");
+ return gFalse;
+ }
+ if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) {
+ error(errSyntaxError, getPos(), "Bad DCT quant table selector");
+ return gFalse;
+ }
+ }
+ progressive = gFalse;
+ return gTrue;
+}
+
+GBool DCTStream::readProgressiveSOF() {
+ int prec;
+ int i;
+ int c;
+
+ read16(); // length
+ prec = str->getChar();
+ height = read16();
+ width = read16();
+ numComps = str->getChar();
+ if (numComps <= 0 || numComps > 4) {
+ error(errSyntaxError, getPos(), "Bad number of components in DCT stream");
+ numComps = 0;
+ return gFalse;
+ }
+ if (prec != 8) {
+ error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec);
+ return gFalse;
+ }
+ for (i = 0; i < numComps; ++i) {
+ compInfo[i].id = str->getChar();
+ c = str->getChar();
+ compInfo[i].hSample = (c >> 4) & 0x0f;
+ compInfo[i].vSample = c & 0x0f;
+ compInfo[i].quantTable = str->getChar();
+ // a sampling factor of 3 is allowed by the spec, but requires
+ // messy upsampling, and appears not to be used in practice
+ if (!(compInfo[i].hSample == 1 ||
+ compInfo[i].hSample == 2 ||
+ compInfo[i].hSample == 4) ||
+ !(compInfo[i].vSample == 1 ||
+ compInfo[i].vSample == 2 ||
+ compInfo[i].vSample == 4)) {
+ error(errSyntaxError, getPos(), "Bad DCT sampling factor");
+ return gFalse;
+ }
+ if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) {
+ error(errSyntaxError, getPos(), "Bad DCT quant table selector");
+ return gFalse;
+ }
+ }
+ progressive = gTrue;
+ return gTrue;
+}
+
+GBool DCTStream::readScanInfo() {
+ int length;
+ int id, c;
+ int i, j;
+
+ length = read16() - 2;
+ scanInfo.numComps = str->getChar();
+ if (scanInfo.numComps <= 0 || scanInfo.numComps > 4) {
+ error(errSyntaxError, getPos(), "Bad number of components in DCT stream");
+ scanInfo.numComps = 0;
+ return gFalse;
+ }
+ --length;
+ if (length != 2 * scanInfo.numComps + 3) {
+ error(errSyntaxError, getPos(), "Bad DCT scan info block");
+ return gFalse;
+ }
+ interleaved = scanInfo.numComps == numComps;
+ for (j = 0; j < numComps; ++j) {
+ scanInfo.comp[j] = gFalse;
+ }
+ for (i = 0; i < scanInfo.numComps; ++i) {
+ id = str->getChar();
+ // some (broken) DCT streams reuse ID numbers, but at least they
+ // keep the components in order, so we check compInfo[i] first to
+ // work around the problem
+ if (id == compInfo[i].id) {
+ j = i;
+ } else {
+ for (j = 0; j < numComps; ++j) {
+ if (id == compInfo[j].id) {
+ break;
+ }
+ }
+ if (j == numComps) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT component ID in scan info block");
+ return gFalse;
+ }
+ }
+ if (scanInfo.comp[j]) {
+ error(errSyntaxError, getPos(),
+ "Invalid DCT component ID in scan info block");
+ return gFalse;
+ }
+ scanInfo.comp[j] = gTrue;
+ c = str->getChar();
+ scanInfo.dcHuffTable[j] = (c >> 4) & 0x0f;
+ scanInfo.acHuffTable[j] = c & 0x0f;
+ }
+ scanInfo.firstCoeff = str->getChar();
+ scanInfo.lastCoeff = str->getChar();
+ if (scanInfo.firstCoeff < 0 || scanInfo.lastCoeff > 63 ||
+ scanInfo.firstCoeff > scanInfo.lastCoeff) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT coefficient numbers in scan info block");
+ return gFalse;
+ }
+ c = str->getChar();
+ scanInfo.ah = (c >> 4) & 0x0f;
+ scanInfo.al = c & 0x0f;
+ return gTrue;
+}
+
+GBool DCTStream::readQuantTables() {
+ int length, prec, i, index;
+
+ length = read16() - 2;
+ while (length > 0) {
+ index = str->getChar();
+ prec = (index >> 4) & 0x0f;
+ index &= 0x0f;
+ if (prec > 1 || index >= 4) {
+ error(errSyntaxError, getPos(), "Bad DCT quantization table");
+ return gFalse;
+ }
+ if (index >= numQuantTables) {
+ numQuantTables = index + 1;
+ }
+ for (i = 0; i < 64; ++i) {
+ if (prec) {
+ quantTables[index][dctZigZag[i]] = (Gushort)read16();
+ } else {
+ quantTables[index][dctZigZag[i]] = (Gushort)str->getChar();
+ }
+ }
+ if (prec) {
+ length -= 129;
+ } else {
+ length -= 65;
+ }
+ }
+ return gTrue;
+}
+
+GBool DCTStream::readHuffmanTables() {
+ DCTHuffTable *tbl;
+ int length;
+ int index;
+ Gushort code;
+ Guchar sym;
+ int i;
+ int c;
+
+ length = read16() - 2;
+ while (length > 0) {
+ index = str->getChar();
+ --length;
+ if ((index & 0x0f) >= 4) {
+ error(errSyntaxError, getPos(), "Bad DCT Huffman table");
+ return gFalse;
+ }
+ if (index & 0x10) {
+ index &= 0x0f;
+ if (index >= numACHuffTables)
+ numACHuffTables = index+1;
+ tbl = &acHuffTables[index];
+ } else {
+ index &= 0x0f;
+ if (index >= numDCHuffTables)
+ numDCHuffTables = index+1;
+ tbl = &dcHuffTables[index];
+ }
+ sym = 0;
+ code = 0;
+ for (i = 1; i <= 16; ++i) {
+ c = str->getChar();
+ tbl->firstSym[i] = sym;
+ tbl->firstCode[i] = code;
+ tbl->numCodes[i] = (Gushort)c;
+ sym = (Guchar)(sym + c);
+ code = (Gushort)((code + c) << 1);
+ }
+ length -= 16;
+ for (i = 0; i < sym; ++i)
+ tbl->sym[i] = (Guchar)str->getChar();
+ length -= sym;
+ }
+ return gTrue;
+}
+
+GBool DCTStream::readRestartInterval() {
+ int length;
+
+ length = read16();
+ if (length != 4) {
+ error(errSyntaxError, getPos(), "Bad DCT restart interval");
+ return gFalse;
+ }
+ restartInterval = read16();
+ return gTrue;
+}
+
+GBool DCTStream::readJFIFMarker() {
+ int length, i;
+ char buf[5];
+ int c;
+
+ length = read16();
+ length -= 2;
+ if (length >= 5) {
+ for (i = 0; i < 5; ++i) {
+ if ((c = str->getChar()) == EOF) {
+ error(errSyntaxError, getPos(), "Bad DCT APP0 marker");
+ return gFalse;
+ }
+ buf[i] = (char)c;
+ }
+ length -= 5;
+ if (!memcmp(buf, "JFIF\0", 5)) {
+ gotJFIFMarker = gTrue;
+ }
+ }
+ while (length > 0) {
+ if (str->getChar() == EOF) {
+ error(errSyntaxError, getPos(), "Bad DCT APP0 marker");
+ return gFalse;
+ }
+ --length;
+ }
+ return gTrue;
+}
+
+GBool DCTStream::readAdobeMarker() {
+ int length, i;
+ char buf[12];
+ int c;
+
+ length = read16();
+ if (length < 14) {
+ goto err;
+ }
+ for (i = 0; i < 12; ++i) {
+ if ((c = str->getChar()) == EOF) {
+ goto err;
+ }
+ buf[i] = (char)c;
+ }
+ if (!strncmp(buf, "Adobe", 5)) {
+ colorXform = buf[11];
+ gotAdobeMarker = gTrue;
+ }
+ for (i = 14; i < length; ++i) {
+ if (str->getChar() == EOF) {
+ goto err;
+ }
+ }
+ return gTrue;
+
+ err:
+ error(errSyntaxError, getPos(), "Bad DCT Adobe APP14 marker");
+ return gFalse;
+}
+
+GBool DCTStream::readTrailer() {
+ int c;
+
+ c = readMarker();
+ if (c != 0xd9) { // EOI
+ error(errSyntaxError, getPos(), "Bad DCT trailer");
+ return gFalse;
+ }
+ return gTrue;
+}
+
+int DCTStream::readMarker() {
+ int c;
+
+ do {
+ do {
+ c = str->getChar();
+ } while (c != 0xff && c != EOF);
+ do {
+ c = str->getChar();
+ } while (c == 0xff);
+ } while (c == 0x00);
+ return c;
+}
+
+int DCTStream::read16() {
+ int c1, c2;
+
+ if ((c1 = str->getChar()) == EOF)
+ return EOF;
+ if ((c2 = str->getChar()) == EOF)
+ return EOF;
+ return (c1 << 8) + c2;
+}
+
+#endif // HAVE_JPEGLIB
+
+GString *DCTStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 2) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ if (okToReadStream && !checkSequentialInterleaved()) {
+ // PostScript does not allow progressive or interleaved JPEG
+ delete s;
+ return NULL;
+ }
+ s->append(indent)->append("<< >> /DCTDecode filter\n");
+ return s;
+}
+
+GBool DCTStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+//------------------------------------------------------------------------
+// FlateStream
+//------------------------------------------------------------------------
+
+int FlateStream::codeLenCodeMap[flateMaxCodeLenCodes] = {
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+FlateDecode FlateStream::lengthDecode[flateMaxLitCodes-257] = {
+ {0, 3},
+ {0, 4},
+ {0, 5},
+ {0, 6},
+ {0, 7},
+ {0, 8},
+ {0, 9},
+ {0, 10},
+ {1, 11},
+ {1, 13},
+ {1, 15},
+ {1, 17},
+ {2, 19},
+ {2, 23},
+ {2, 27},
+ {2, 31},
+ {3, 35},
+ {3, 43},
+ {3, 51},
+ {3, 59},
+ {4, 67},
+ {4, 83},
+ {4, 99},
+ {4, 115},
+ {5, 131},
+ {5, 163},
+ {5, 195},
+ {5, 227},
+ {0, 258},
+ {0, 258},
+ {0, 258}
+};
+
+FlateDecode FlateStream::distDecode[flateMaxDistCodes] = {
+ { 0, 1},
+ { 0, 2},
+ { 0, 3},
+ { 0, 4},
+ { 1, 5},
+ { 1, 7},
+ { 2, 9},
+ { 2, 13},
+ { 3, 17},
+ { 3, 25},
+ { 4, 33},
+ { 4, 49},
+ { 5, 65},
+ { 5, 97},
+ { 6, 129},
+ { 6, 193},
+ { 7, 257},
+ { 7, 385},
+ { 8, 513},
+ { 8, 769},
+ { 9, 1025},
+ { 9, 1537},
+ {10, 2049},
+ {10, 3073},
+ {11, 4097},
+ {11, 6145},
+ {12, 8193},
+ {12, 12289},
+ {13, 16385},
+ {13, 24577}
+};
+
+static FlateCode flateFixedLitCodeTabCodes[512] = {
+ {7, 0x0100},
+ {8, 0x0050},
+ {8, 0x0010},
+ {8, 0x0118},
+ {7, 0x0110},
+ {8, 0x0070},
+ {8, 0x0030},
+ {9, 0x00c0},
+ {7, 0x0108},
+ {8, 0x0060},
+ {8, 0x0020},
+ {9, 0x00a0},
+ {8, 0x0000},
+ {8, 0x0080},
+ {8, 0x0040},
+ {9, 0x00e0},
+ {7, 0x0104},
+ {8, 0x0058},
+ {8, 0x0018},
+ {9, 0x0090},
+ {7, 0x0114},
+ {8, 0x0078},
+ {8, 0x0038},
+ {9, 0x00d0},
+ {7, 0x010c},
+ {8, 0x0068},
+ {8, 0x0028},
+ {9, 0x00b0},
+ {8, 0x0008},
+ {8, 0x0088},
+ {8, 0x0048},
+ {9, 0x00f0},
+ {7, 0x0102},
+ {8, 0x0054},
+ {8, 0x0014},
+ {8, 0x011c},
+ {7, 0x0112},
+ {8, 0x0074},
+ {8, 0x0034},
+ {9, 0x00c8},
+ {7, 0x010a},
+ {8, 0x0064},
+ {8, 0x0024},
+ {9, 0x00a8},
+ {8, 0x0004},
+ {8, 0x0084},
+ {8, 0x0044},
+ {9, 0x00e8},
+ {7, 0x0106},
+ {8, 0x005c},
+ {8, 0x001c},
+ {9, 0x0098},
+ {7, 0x0116},
+ {8, 0x007c},
+ {8, 0x003c},
+ {9, 0x00d8},
+ {7, 0x010e},
+ {8, 0x006c},
+ {8, 0x002c},
+ {9, 0x00b8},
+ {8, 0x000c},
+ {8, 0x008c},
+ {8, 0x004c},
+ {9, 0x00f8},
+ {7, 0x0101},
+ {8, 0x0052},
+ {8, 0x0012},
+ {8, 0x011a},
+ {7, 0x0111},
+ {8, 0x0072},
+ {8, 0x0032},
+ {9, 0x00c4},
+ {7, 0x0109},
+ {8, 0x0062},
+ {8, 0x0022},
+ {9, 0x00a4},
+ {8, 0x0002},
+ {8, 0x0082},
+ {8, 0x0042},
+ {9, 0x00e4},
+ {7, 0x0105},
+ {8, 0x005a},
+ {8, 0x001a},
+ {9, 0x0094},
+ {7, 0x0115},
+ {8, 0x007a},
+ {8, 0x003a},
+ {9, 0x00d4},
+ {7, 0x010d},
+ {8, 0x006a},
+ {8, 0x002a},
+ {9, 0x00b4},
+ {8, 0x000a},
+ {8, 0x008a},
+ {8, 0x004a},
+ {9, 0x00f4},
+ {7, 0x0103},
+ {8, 0x0056},
+ {8, 0x0016},
+ {8, 0x011e},
+ {7, 0x0113},
+ {8, 0x0076},
+ {8, 0x0036},
+ {9, 0x00cc},
+ {7, 0x010b},
+ {8, 0x0066},
+ {8, 0x0026},
+ {9, 0x00ac},
+ {8, 0x0006},
+ {8, 0x0086},
+ {8, 0x0046},
+ {9, 0x00ec},
+ {7, 0x0107},
+ {8, 0x005e},
+ {8, 0x001e},
+ {9, 0x009c},
+ {7, 0x0117},
+ {8, 0x007e},
+ {8, 0x003e},
+ {9, 0x00dc},
+ {7, 0x010f},
+ {8, 0x006e},
+ {8, 0x002e},
+ {9, 0x00bc},
+ {8, 0x000e},
+ {8, 0x008e},
+ {8, 0x004e},
+ {9, 0x00fc},
+ {7, 0x0100},
+ {8, 0x0051},
+ {8, 0x0011},
+ {8, 0x0119},
+ {7, 0x0110},
+ {8, 0x0071},
+ {8, 0x0031},
+ {9, 0x00c2},
+ {7, 0x0108},
+ {8, 0x0061},
+ {8, 0x0021},
+ {9, 0x00a2},
+ {8, 0x0001},
+ {8, 0x0081},
+ {8, 0x0041},
+ {9, 0x00e2},
+ {7, 0x0104},
+ {8, 0x0059},
+ {8, 0x0019},
+ {9, 0x0092},
+ {7, 0x0114},
+ {8, 0x0079},
+ {8, 0x0039},
+ {9, 0x00d2},
+ {7, 0x010c},
+ {8, 0x0069},
+ {8, 0x0029},
+ {9, 0x00b2},
+ {8, 0x0009},
+ {8, 0x0089},
+ {8, 0x0049},
+ {9, 0x00f2},
+ {7, 0x0102},
+ {8, 0x0055},
+ {8, 0x0015},
+ {8, 0x011d},
+ {7, 0x0112},
+ {8, 0x0075},
+ {8, 0x0035},
+ {9, 0x00ca},
+ {7, 0x010a},
+ {8, 0x0065},
+ {8, 0x0025},
+ {9, 0x00aa},
+ {8, 0x0005},
+ {8, 0x0085},
+ {8, 0x0045},
+ {9, 0x00ea},
+ {7, 0x0106},
+ {8, 0x005d},
+ {8, 0x001d},
+ {9, 0x009a},
+ {7, 0x0116},
+ {8, 0x007d},
+ {8, 0x003d},
+ {9, 0x00da},
+ {7, 0x010e},
+ {8, 0x006d},
+ {8, 0x002d},
+ {9, 0x00ba},
+ {8, 0x000d},
+ {8, 0x008d},
+ {8, 0x004d},
+ {9, 0x00fa},
+ {7, 0x0101},
+ {8, 0x0053},
+ {8, 0x0013},
+ {8, 0x011b},
+ {7, 0x0111},
+ {8, 0x0073},
+ {8, 0x0033},
+ {9, 0x00c6},
+ {7, 0x0109},
+ {8, 0x0063},
+ {8, 0x0023},
+ {9, 0x00a6},
+ {8, 0x0003},
+ {8, 0x0083},
+ {8, 0x0043},
+ {9, 0x00e6},
+ {7, 0x0105},
+ {8, 0x005b},
+ {8, 0x001b},
+ {9, 0x0096},
+ {7, 0x0115},
+ {8, 0x007b},
+ {8, 0x003b},
+ {9, 0x00d6},
+ {7, 0x010d},
+ {8, 0x006b},
+ {8, 0x002b},
+ {9, 0x00b6},
+ {8, 0x000b},
+ {8, 0x008b},
+ {8, 0x004b},
+ {9, 0x00f6},
+ {7, 0x0103},
+ {8, 0x0057},
+ {8, 0x0017},
+ {8, 0x011f},
+ {7, 0x0113},
+ {8, 0x0077},
+ {8, 0x0037},
+ {9, 0x00ce},
+ {7, 0x010b},
+ {8, 0x0067},
+ {8, 0x0027},
+ {9, 0x00ae},
+ {8, 0x0007},
+ {8, 0x0087},
+ {8, 0x0047},
+ {9, 0x00ee},
+ {7, 0x0107},
+ {8, 0x005f},
+ {8, 0x001f},
+ {9, 0x009e},
+ {7, 0x0117},
+ {8, 0x007f},
+ {8, 0x003f},
+ {9, 0x00de},
+ {7, 0x010f},
+ {8, 0x006f},
+ {8, 0x002f},
+ {9, 0x00be},
+ {8, 0x000f},
+ {8, 0x008f},
+ {8, 0x004f},
+ {9, 0x00fe},
+ {7, 0x0100},
+ {8, 0x0050},
+ {8, 0x0010},
+ {8, 0x0118},
+ {7, 0x0110},
+ {8, 0x0070},
+ {8, 0x0030},
+ {9, 0x00c1},
+ {7, 0x0108},
+ {8, 0x0060},
+ {8, 0x0020},
+ {9, 0x00a1},
+ {8, 0x0000},
+ {8, 0x0080},
+ {8, 0x0040},
+ {9, 0x00e1},
+ {7, 0x0104},
+ {8, 0x0058},
+ {8, 0x0018},
+ {9, 0x0091},
+ {7, 0x0114},
+ {8, 0x0078},
+ {8, 0x0038},
+ {9, 0x00d1},
+ {7, 0x010c},
+ {8, 0x0068},
+ {8, 0x0028},
+ {9, 0x00b1},
+ {8, 0x0008},
+ {8, 0x0088},
+ {8, 0x0048},
+ {9, 0x00f1},
+ {7, 0x0102},
+ {8, 0x0054},
+ {8, 0x0014},
+ {8, 0x011c},
+ {7, 0x0112},
+ {8, 0x0074},
+ {8, 0x0034},
+ {9, 0x00c9},
+ {7, 0x010a},
+ {8, 0x0064},
+ {8, 0x0024},
+ {9, 0x00a9},
+ {8, 0x0004},
+ {8, 0x0084},
+ {8, 0x0044},
+ {9, 0x00e9},
+ {7, 0x0106},
+ {8, 0x005c},
+ {8, 0x001c},
+ {9, 0x0099},
+ {7, 0x0116},
+ {8, 0x007c},
+ {8, 0x003c},
+ {9, 0x00d9},
+ {7, 0x010e},
+ {8, 0x006c},
+ {8, 0x002c},
+ {9, 0x00b9},
+ {8, 0x000c},
+ {8, 0x008c},
+ {8, 0x004c},
+ {9, 0x00f9},
+ {7, 0x0101},
+ {8, 0x0052},
+ {8, 0x0012},
+ {8, 0x011a},
+ {7, 0x0111},
+ {8, 0x0072},
+ {8, 0x0032},
+ {9, 0x00c5},
+ {7, 0x0109},
+ {8, 0x0062},
+ {8, 0x0022},
+ {9, 0x00a5},
+ {8, 0x0002},
+ {8, 0x0082},
+ {8, 0x0042},
+ {9, 0x00e5},
+ {7, 0x0105},
+ {8, 0x005a},
+ {8, 0x001a},
+ {9, 0x0095},
+ {7, 0x0115},
+ {8, 0x007a},
+ {8, 0x003a},
+ {9, 0x00d5},
+ {7, 0x010d},
+ {8, 0x006a},
+ {8, 0x002a},
+ {9, 0x00b5},
+ {8, 0x000a},
+ {8, 0x008a},
+ {8, 0x004a},
+ {9, 0x00f5},
+ {7, 0x0103},
+ {8, 0x0056},
+ {8, 0x0016},
+ {8, 0x011e},
+ {7, 0x0113},
+ {8, 0x0076},
+ {8, 0x0036},
+ {9, 0x00cd},
+ {7, 0x010b},
+ {8, 0x0066},
+ {8, 0x0026},
+ {9, 0x00ad},
+ {8, 0x0006},
+ {8, 0x0086},
+ {8, 0x0046},
+ {9, 0x00ed},
+ {7, 0x0107},
+ {8, 0x005e},
+ {8, 0x001e},
+ {9, 0x009d},
+ {7, 0x0117},
+ {8, 0x007e},
+ {8, 0x003e},
+ {9, 0x00dd},
+ {7, 0x010f},
+ {8, 0x006e},
+ {8, 0x002e},
+ {9, 0x00bd},
+ {8, 0x000e},
+ {8, 0x008e},
+ {8, 0x004e},
+ {9, 0x00fd},
+ {7, 0x0100},
+ {8, 0x0051},
+ {8, 0x0011},
+ {8, 0x0119},
+ {7, 0x0110},
+ {8, 0x0071},
+ {8, 0x0031},
+ {9, 0x00c3},
+ {7, 0x0108},
+ {8, 0x0061},
+ {8, 0x0021},
+ {9, 0x00a3},
+ {8, 0x0001},
+ {8, 0x0081},
+ {8, 0x0041},
+ {9, 0x00e3},
+ {7, 0x0104},
+ {8, 0x0059},
+ {8, 0x0019},
+ {9, 0x0093},
+ {7, 0x0114},
+ {8, 0x0079},
+ {8, 0x0039},
+ {9, 0x00d3},
+ {7, 0x010c},
+ {8, 0x0069},
+ {8, 0x0029},
+ {9, 0x00b3},
+ {8, 0x0009},
+ {8, 0x0089},
+ {8, 0x0049},
+ {9, 0x00f3},
+ {7, 0x0102},
+ {8, 0x0055},
+ {8, 0x0015},
+ {8, 0x011d},
+ {7, 0x0112},
+ {8, 0x0075},
+ {8, 0x0035},
+ {9, 0x00cb},
+ {7, 0x010a},
+ {8, 0x0065},
+ {8, 0x0025},
+ {9, 0x00ab},
+ {8, 0x0005},
+ {8, 0x0085},
+ {8, 0x0045},
+ {9, 0x00eb},
+ {7, 0x0106},
+ {8, 0x005d},
+ {8, 0x001d},
+ {9, 0x009b},
+ {7, 0x0116},
+ {8, 0x007d},
+ {8, 0x003d},
+ {9, 0x00db},
+ {7, 0x010e},
+ {8, 0x006d},
+ {8, 0x002d},
+ {9, 0x00bb},
+ {8, 0x000d},
+ {8, 0x008d},
+ {8, 0x004d},
+ {9, 0x00fb},
+ {7, 0x0101},
+ {8, 0x0053},
+ {8, 0x0013},
+ {8, 0x011b},
+ {7, 0x0111},
+ {8, 0x0073},
+ {8, 0x0033},
+ {9, 0x00c7},
+ {7, 0x0109},
+ {8, 0x0063},
+ {8, 0x0023},
+ {9, 0x00a7},
+ {8, 0x0003},
+ {8, 0x0083},
+ {8, 0x0043},
+ {9, 0x00e7},
+ {7, 0x0105},
+ {8, 0x005b},
+ {8, 0x001b},
+ {9, 0x0097},
+ {7, 0x0115},
+ {8, 0x007b},
+ {8, 0x003b},
+ {9, 0x00d7},
+ {7, 0x010d},
+ {8, 0x006b},
+ {8, 0x002b},
+ {9, 0x00b7},
+ {8, 0x000b},
+ {8, 0x008b},
+ {8, 0x004b},
+ {9, 0x00f7},
+ {7, 0x0103},
+ {8, 0x0057},
+ {8, 0x0017},
+ {8, 0x011f},
+ {7, 0x0113},
+ {8, 0x0077},
+ {8, 0x0037},
+ {9, 0x00cf},
+ {7, 0x010b},
+ {8, 0x0067},
+ {8, 0x0027},
+ {9, 0x00af},
+ {8, 0x0007},
+ {8, 0x0087},
+ {8, 0x0047},
+ {9, 0x00ef},
+ {7, 0x0107},
+ {8, 0x005f},
+ {8, 0x001f},
+ {9, 0x009f},
+ {7, 0x0117},
+ {8, 0x007f},
+ {8, 0x003f},
+ {9, 0x00df},
+ {7, 0x010f},
+ {8, 0x006f},
+ {8, 0x002f},
+ {9, 0x00bf},
+ {8, 0x000f},
+ {8, 0x008f},
+ {8, 0x004f},
+ {9, 0x00ff}
+};
+
+FlateHuffmanTab FlateStream::fixedLitCodeTab = {
+ flateFixedLitCodeTabCodes, 9
+};
+
+static FlateCode flateFixedDistCodeTabCodes[32] = {
+ {5, 0x0000},
+ {5, 0x0010},
+ {5, 0x0008},
+ {5, 0x0018},
+ {5, 0x0004},
+ {5, 0x0014},
+ {5, 0x000c},
+ {5, 0x001c},
+ {5, 0x0002},
+ {5, 0x0012},
+ {5, 0x000a},
+ {5, 0x001a},
+ {5, 0x0006},
+ {5, 0x0016},
+ {5, 0x000e},
+ {0, 0x0000},
+ {5, 0x0001},
+ {5, 0x0011},
+ {5, 0x0009},
+ {5, 0x0019},
+ {5, 0x0005},
+ {5, 0x0015},
+ {5, 0x000d},
+ {5, 0x001d},
+ {5, 0x0003},
+ {5, 0x0013},
+ {5, 0x000b},
+ {5, 0x001b},
+ {5, 0x0007},
+ {5, 0x0017},
+ {5, 0x000f},
+ {0, 0x0000}
+};
+
+FlateHuffmanTab FlateStream::fixedDistCodeTab = {
+ flateFixedDistCodeTabCodes, 5
+};
+
+FlateStream::FlateStream(Stream *strA, int predictor, int columns,
+ int colors, int bits):
+ FilterStream(strA) {
+ if (predictor != 1) {
+ pred = new StreamPredictor(this, predictor, columns, colors, bits);
+ if (!pred->isOk()) {
+ delete pred;
+ pred = NULL;
+ }
+ } else {
+ pred = NULL;
+ }
+ litCodeTab.codes = NULL;
+ distCodeTab.codes = NULL;
+ memset(buf, 0, flateWindow);
+}
+
+FlateStream::~FlateStream() {
+ if (litCodeTab.codes != fixedLitCodeTab.codes) {
+ gfree(litCodeTab.codes);
+ }
+ if (distCodeTab.codes != fixedDistCodeTab.codes) {
+ gfree(distCodeTab.codes);
+ }
+ if (pred) {
+ delete pred;
+ }
+ delete str;
+}
+
+Stream *FlateStream::copy() {
+ if (pred) {
+ return new FlateStream(str->copy(), pred->getPredictor(),
+ pred->getWidth(), pred->getNComps(),
+ pred->getNBits());
+ } else {
+ return new FlateStream(str->copy(), 1, 0, 0, 0);
+ }
+}
+
+void FlateStream::reset() {
+ int cmf, flg;
+
+ index = 0;
+ remain = 0;
+ codeBuf = 0;
+ codeSize = 0;
+ compressedBlock = gFalse;
+ endOfBlock = gTrue;
+ eof = gTrue;
+
+ str->reset();
+ if (pred) {
+ pred->reset();
+ }
+
+ // read header
+ //~ need to look at window size?
+ endOfBlock = eof = gTrue;
+ cmf = str->getChar();
+ flg = str->getChar();
+ totalIn = 2;
+ totalOut = 0;
+ if (cmf == EOF || flg == EOF)
+ return;
+ if ((cmf & 0x0f) != 0x08) {
+ error(errSyntaxError, getPos(),
+ "Unknown compression method in flate stream");
+ return;
+ }
+ if ((((cmf << 8) + flg) % 31) != 0) {
+ error(errSyntaxError, getPos(), "Bad FCHECK in flate stream");
+ return;
+ }
+ if (flg & 0x20) {
+ error(errSyntaxError, getPos(), "FDICT bit set in flate stream");
+ return;
+ }
+
+ eof = gFalse;
+}
+
+int FlateStream::getChar() {
+ int c;
+
+ if (pred) {
+ return pred->getChar();
+ }
+ while (remain == 0) {
+ if (endOfBlock && eof)
+ return EOF;
+ readSome();
+ }
+ c = buf[index];
+ index = (index + 1) & flateMask;
+ --remain;
+ return c;
+}
+
+int FlateStream::lookChar() {
+ int c;
+
+ if (pred) {
+ return pred->lookChar();
+ }
+ while (remain == 0) {
+ if (endOfBlock && eof)
+ return EOF;
+ readSome();
+ }
+ c = buf[index];
+ return c;
+}
+
+int FlateStream::getRawChar() {
+ int c;
+
+ while (remain == 0) {
+ if (endOfBlock && eof)
+ return EOF;
+ readSome();
+ }
+ c = buf[index];
+ index = (index + 1) & flateMask;
+ --remain;
+ return c;
+}
+
+int FlateStream::getBlock(char *blk, int size) {
+ int n, k;
+
+ if (pred) {
+ return pred->getBlock(blk, size);
+ }
+
+ n = 0;
+ while (n < size) {
+ if (remain == 0) {
+ if (endOfBlock && eof) {
+ break;
+ }
+ readSome();
+ }
+ 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;
+}
+
+GString *FlateStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 3 || pred) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ s->append(indent)->append("<< >> /FlateDecode filter\n");
+ return s;
+}
+
+GBool FlateStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+void FlateStream::readSome() {
+ int code1, code2;
+ int len, dist;
+ int src, dest, n1, n2, n3, i, j, k;
+ int c;
+
+ if (endOfBlock) {
+ if (!startBlock())
+ return;
+ }
+
+ if (compressedBlock) {
+ if ((code1 = getHuffmanCodeWord(&litCodeTab)) == EOF)
+ goto err;
+ if (code1 < 256) {
+ buf[index] = (Guchar)code1;
+ remain = 1;
+ } else if (code1 == 256) {
+ endOfBlock = gTrue;
+ remain = 0;
+ } else {
+ code1 -= 257;
+ code2 = lengthDecode[code1].bits;
+ if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF)
+ goto err;
+ len = lengthDecode[code1].first + code2;
+ if ((code1 = getHuffmanCodeWord(&distCodeTab)) == EOF)
+ goto err;
+ code2 = distDecode[code1].bits;
+ if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF)
+ goto err;
+ dist = distDecode[code1].first + code2;
+ 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;
+ }
+
+ } else {
+ len = (blockLen < flateWindow) ? blockLen : flateWindow;
+ for (i = 0, j = index; i < len; ++i, j = (j + 1) & flateMask) {
+ if ((c = str->getChar()) == EOF) {
+ endOfBlock = eof = gTrue;
+ break;
+ }
+ buf[j] = (Guchar)c;
+ }
+ remain = i;
+ 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:
+ error(errSyntaxError, getPos(), "Unexpected end of file in flate stream");
+ endOfBlock = eof = gTrue;
+ remain = 0;
+}
+
+GBool FlateStream::startBlock() {
+ int blockHdr;
+ int c;
+ int check;
+
+ // free the code tables from the previous block
+ if (litCodeTab.codes != fixedLitCodeTab.codes) {
+ gfree(litCodeTab.codes);
+ }
+ litCodeTab.codes = NULL;
+ if (distCodeTab.codes != fixedDistCodeTab.codes) {
+ gfree(distCodeTab.codes);
+ }
+ distCodeTab.codes = NULL;
+
+ // read block header
+ blockHdr = getCodeWord(3);
+ if (blockHdr & 1)
+ eof = gTrue;
+ blockHdr >>= 1;
+
+ // uncompressed block
+ if (blockHdr == 0) {
+ compressedBlock = gFalse;
+ if ((c = str->getChar()) == EOF)
+ goto err;
+ blockLen = c & 0xff;
+ if ((c = str->getChar()) == EOF)
+ goto err;
+ blockLen |= (c & 0xff) << 8;
+ if ((c = str->getChar()) == EOF)
+ goto err;
+ check = c & 0xff;
+ if ((c = str->getChar()) == EOF)
+ goto err;
+ check |= (c & 0xff) << 8;
+ if (check != (~blockLen & 0xffff))
+ error(errSyntaxError, getPos(),
+ "Bad uncompressed block length in flate stream");
+ codeBuf = 0;
+ codeSize = 0;
+ totalIn += 4;
+
+ // compressed block with fixed codes
+ } else if (blockHdr == 1) {
+ compressedBlock = gTrue;
+ loadFixedCodes();
+
+ // compressed block with dynamic codes
+ } else if (blockHdr == 2) {
+ compressedBlock = gTrue;
+ if (!readDynamicCodes()) {
+ goto err;
+ }
+
+ // unknown block type
+ } else {
+ goto err;
+ }
+
+ endOfBlock = gFalse;
+ return gTrue;
+
+err:
+ error(errSyntaxError, getPos(), "Bad block header in flate stream");
+ endOfBlock = eof = gTrue;
+ return gFalse;
+}
+
+void FlateStream::loadFixedCodes() {
+ litCodeTab.codes = fixedLitCodeTab.codes;
+ litCodeTab.maxLen = fixedLitCodeTab.maxLen;
+ distCodeTab.codes = fixedDistCodeTab.codes;
+ distCodeTab.maxLen = fixedDistCodeTab.maxLen;
+}
+
+GBool FlateStream::readDynamicCodes() {
+ int numCodeLenCodes;
+ int numLitCodes;
+ int numDistCodes;
+ int codeLenCodeLengths[flateMaxCodeLenCodes];
+ FlateHuffmanTab codeLenCodeTab;
+ int len, repeat, code;
+ int i;
+
+ codeLenCodeTab.codes = NULL;
+
+ // read lengths
+ if ((numLitCodes = getCodeWord(5)) == EOF) {
+ goto err;
+ }
+ numLitCodes += 257;
+ if ((numDistCodes = getCodeWord(5)) == EOF) {
+ goto err;
+ }
+ numDistCodes += 1;
+ if ((numCodeLenCodes = getCodeWord(4)) == EOF) {
+ goto err;
+ }
+ numCodeLenCodes += 4;
+ if (numLitCodes > flateMaxLitCodes ||
+ numDistCodes > flateMaxDistCodes ||
+ numCodeLenCodes > flateMaxCodeLenCodes) {
+ goto err;
+ }
+
+ // build the code length code table
+ for (i = 0; i < flateMaxCodeLenCodes; ++i) {
+ codeLenCodeLengths[i] = 0;
+ }
+ for (i = 0; i < numCodeLenCodes; ++i) {
+ if ((codeLenCodeLengths[codeLenCodeMap[i]] = getCodeWord(3)) == -1) {
+ goto err;
+ }
+ }
+ compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab);
+
+ // build the literal and distance code tables
+ len = 0;
+ repeat = 0;
+ i = 0;
+ while (i < numLitCodes + numDistCodes) {
+ if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF) {
+ goto err;
+ }
+ if (code == 16) {
+ if ((repeat = getCodeWord(2)) == EOF) {
+ goto err;
+ }
+ repeat += 3;
+ if (i + repeat > numLitCodes + numDistCodes) {
+ goto err;
+ }
+ for (; repeat > 0; --repeat) {
+ codeLengths[i++] = len;
+ }
+ } else if (code == 17) {
+ if ((repeat = getCodeWord(3)) == EOF) {
+ goto err;
+ }
+ repeat += 3;
+ if (i + repeat > numLitCodes + numDistCodes) {
+ goto err;
+ }
+ len = 0;
+ for (; repeat > 0; --repeat) {
+ codeLengths[i++] = 0;
+ }
+ } else if (code == 18) {
+ if ((repeat = getCodeWord(7)) == EOF) {
+ goto err;
+ }
+ repeat += 11;
+ if (i + repeat > numLitCodes + numDistCodes) {
+ goto err;
+ }
+ len = 0;
+ for (; repeat > 0; --repeat) {
+ codeLengths[i++] = 0;
+ }
+ } else {
+ codeLengths[i++] = len = code;
+ }
+ }
+ compHuffmanCodes(codeLengths, numLitCodes, &litCodeTab);
+ compHuffmanCodes(codeLengths + numLitCodes, numDistCodes, &distCodeTab);
+
+ gfree(codeLenCodeTab.codes);
+ return gTrue;
+
+err:
+ error(errSyntaxError, getPos(), "Bad dynamic code table in flate stream");
+ gfree(codeLenCodeTab.codes);
+ return gFalse;
+}
+
+// Convert an array <lengths> of <n> lengths, in value order, into a
+// Huffman code lookup table.
+void FlateStream::compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab) {
+ int tabSize, len, code, code2, skip, val, i, t;
+
+ // find max code length
+ tab->maxLen = 0;
+ for (val = 0; val < n; ++val) {
+ if (lengths[val] > tab->maxLen) {
+ tab->maxLen = lengths[val];
+ }
+ }
+
+ // allocate the table
+ tabSize = 1 << tab->maxLen;
+ tab->codes = (FlateCode *)gmallocn(tabSize, sizeof(FlateCode));
+
+ // clear the table
+ for (i = 0; i < tabSize; ++i) {
+ tab->codes[i].len = 0;
+ tab->codes[i].val = 0;
+ }
+
+ // build the table
+ for (len = 1, code = 0, skip = 2;
+ len <= tab->maxLen;
+ ++len, code <<= 1, skip <<= 1) {
+ for (val = 0; val < n; ++val) {
+ if (lengths[val] == len) {
+
+ // bit-reverse the code
+ code2 = 0;
+ t = code;
+ for (i = 0; i < len; ++i) {
+ code2 = (code2 << 1) | (t & 1);
+ t >>= 1;
+ }
+
+ // fill in the table entries
+ for (i = code2; i < tabSize; i += skip) {
+ tab->codes[i].len = (Gushort)len;
+ tab->codes[i].val = (Gushort)val;
+ }
+
+ ++code;
+ }
+ }
+ }
+}
+
+int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) {
+ FlateCode *code;
+ int c;
+
+ while (codeSize < tab->maxLen) {
+ if ((c = str->getChar()) == EOF) {
+ break;
+ }
+ codeBuf |= (c & 0xff) << codeSize;
+ codeSize += 8;
+ ++totalIn;
+ }
+ code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)];
+ if (codeSize == 0 || codeSize < code->len || code->len == 0) {
+ return EOF;
+ }
+ codeBuf >>= code->len;
+ codeSize -= code->len;
+ return (int)code->val;
+}
+
+int FlateStream::getCodeWord(int bits) {
+ int c;
+
+ while (codeSize < bits) {
+ if ((c = str->getChar()) == EOF)
+ return EOF;
+ codeBuf |= (c & 0xff) << codeSize;
+ codeSize += 8;
+ ++totalIn;
+ }
+ c = codeBuf & ((1 << bits) - 1);
+ codeBuf >>= bits;
+ codeSize -= bits;
+ return c;
+}
+
+//------------------------------------------------------------------------
+// EOFStream
+//------------------------------------------------------------------------
+
+EOFStream::EOFStream(Stream *strA):
+ FilterStream(strA) {
+}
+
+EOFStream::~EOFStream() {
+ delete str;
+}
+
+Stream *EOFStream::copy() {
+ return new EOFStream(str->copy());
+}
+
+//------------------------------------------------------------------------
+// BufStream
+//------------------------------------------------------------------------
+
+BufStream::BufStream(Stream *strA, int bufSizeA): FilterStream(strA) {
+ bufSize = bufSizeA;
+ buf = (int *)gmallocn(bufSize, sizeof(int));
+}
+
+BufStream::~BufStream() {
+ gfree(buf);
+ delete str;
+}
+
+Stream *BufStream::copy() {
+ return new BufStream(str->copy(), bufSize);
+}
+
+void BufStream::reset() {
+ int i;
+
+ str->reset();
+ for (i = 0; i < bufSize; ++i) {
+ buf[i] = str->getChar();
+ }
+}
+
+int BufStream::getChar() {
+ int c, i;
+
+ c = buf[0];
+ for (i = 1; i < bufSize; ++i) {
+ buf[i-1] = buf[i];
+ }
+ buf[bufSize - 1] = str->getChar();
+ return c;
+}
+
+int BufStream::lookChar() {
+ return buf[0];
+}
+
+int BufStream::lookChar(int idx) {
+ return buf[idx];
+}
+
+GBool BufStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+//------------------------------------------------------------------------
+// FixedLengthEncoder
+//------------------------------------------------------------------------
+
+FixedLengthEncoder::FixedLengthEncoder(Stream *strA, int lengthA):
+ FilterStream(strA) {
+ length = lengthA;
+ count = 0;
+}
+
+FixedLengthEncoder::~FixedLengthEncoder() {
+ if (str->isEncoder())
+ delete str;
+}
+
+Stream *FixedLengthEncoder::copy() {
+ error(errInternal, -1, "Called copy() on FixedLengthEncoder");
+ return NULL;
+}
+
+void FixedLengthEncoder::reset() {
+ str->reset();
+ count = 0;
+}
+
+int FixedLengthEncoder::getChar() {
+ if (length >= 0 && count >= length)
+ return EOF;
+ ++count;
+ return str->getChar();
+}
+
+int FixedLengthEncoder::lookChar() {
+ if (length >= 0 && count >= length)
+ return EOF;
+ return str->getChar();
+}
+
+GBool FixedLengthEncoder::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+//------------------------------------------------------------------------
+// ASCIIHexEncoder
+//------------------------------------------------------------------------
+
+ASCIIHexEncoder::ASCIIHexEncoder(Stream *strA):
+ FilterStream(strA) {
+ bufPtr = bufEnd = buf;
+ lineLen = 0;
+ eof = gFalse;
+}
+
+ASCIIHexEncoder::~ASCIIHexEncoder() {
+ if (str->isEncoder()) {
+ delete str;
+ }
+}
+
+Stream *ASCIIHexEncoder::copy() {
+ error(errInternal, -1, "Called copy() on ASCIIHexEncoder");
+ return NULL;
+}
+
+void ASCIIHexEncoder::reset() {
+ str->reset();
+ bufPtr = bufEnd = buf;
+ lineLen = 0;
+ eof = gFalse;
+}
+
+GBool ASCIIHexEncoder::fillBuf() {
+ static const char *hex = "0123456789abcdef";
+ int c;
+
+ if (eof) {
+ return gFalse;
+ }
+ bufPtr = bufEnd = buf;
+ if ((c = str->getChar()) == EOF) {
+ *bufEnd++ = '>';
+ eof = gTrue;
+ } else {
+ if (lineLen >= 64) {
+ *bufEnd++ = '\n';
+ lineLen = 0;
+ }
+ *bufEnd++ = hex[(c >> 4) & 0x0f];
+ *bufEnd++ = hex[c & 0x0f];
+ lineLen += 2;
+ }
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// ASCII85Encoder
+//------------------------------------------------------------------------
+
+ASCII85Encoder::ASCII85Encoder(Stream *strA):
+ FilterStream(strA) {
+ bufPtr = bufEnd = buf;
+ lineLen = 0;
+ eof = gFalse;
+}
+
+ASCII85Encoder::~ASCII85Encoder() {
+ if (str->isEncoder())
+ delete str;
+}
+
+Stream *ASCII85Encoder::copy() {
+ error(errInternal, -1, "Called copy() on ASCII85Encoder");
+ return NULL;
+}
+
+void ASCII85Encoder::reset() {
+ str->reset();
+ bufPtr = bufEnd = buf;
+ lineLen = 0;
+ eof = gFalse;
+}
+
+GBool ASCII85Encoder::fillBuf() {
+ Guint t;
+ char buf1[5];
+ int c0, c1, c2, c3;
+ int n, i;
+
+ if (eof) {
+ return gFalse;
+ }
+ c0 = str->getChar();
+ c1 = str->getChar();
+ c2 = str->getChar();
+ c3 = str->getChar();
+ bufPtr = bufEnd = buf;
+ if (c3 == EOF) {
+ if (c0 == EOF) {
+ n = 0;
+ t = 0;
+ } else {
+ if (c1 == EOF) {
+ n = 1;
+ t = c0 << 24;
+ } else if (c2 == EOF) {
+ n = 2;
+ t = (c0 << 24) | (c1 << 16);
+ } else {
+ n = 3;
+ t = (c0 << 24) | (c1 << 16) | (c2 << 8);
+ }
+ for (i = 4; i >= 0; --i) {
+ buf1[i] = (char)(t % 85 + 0x21);
+ t /= 85;
+ }
+ for (i = 0; i <= n; ++i) {
+ *bufEnd++ = buf1[i];
+ if (++lineLen == 65) {
+ *bufEnd++ = '\n';
+ lineLen = 0;
+ }
+ }
+ }
+ *bufEnd++ = '~';
+ *bufEnd++ = '>';
+ eof = gTrue;
+ } else {
+ t = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
+ if (t == 0) {
+ *bufEnd++ = 'z';
+ if (++lineLen == 65) {
+ *bufEnd++ = '\n';
+ lineLen = 0;
+ }
+ } else {
+ for (i = 4; i >= 0; --i) {
+ buf1[i] = (char)(t % 85 + 0x21);
+ t /= 85;
+ }
+ for (i = 0; i <= 4; ++i) {
+ *bufEnd++ = buf1[i];
+ if (++lineLen == 65) {
+ *bufEnd++ = '\n';
+ lineLen = 0;
+ }
+ }
+ }
+ }
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// RunLengthEncoder
+//------------------------------------------------------------------------
+
+RunLengthEncoder::RunLengthEncoder(Stream *strA):
+ FilterStream(strA) {
+ bufPtr = bufEnd = nextEnd = buf;
+ eof = gFalse;
+}
+
+RunLengthEncoder::~RunLengthEncoder() {
+ if (str->isEncoder())
+ delete str;
+}
+
+Stream *RunLengthEncoder::copy() {
+ error(errInternal, -1, "Called copy() on RunLengthEncoder");
+ return NULL;
+}
+
+void RunLengthEncoder::reset() {
+ str->reset();
+ bufPtr = bufEnd = nextEnd = buf;
+ eof = gFalse;
+}
+
+//
+// When fillBuf finishes, buf[] looks like this:
+// +-----+--------------+-----------------+--
+// + tag | ... data ... | next 0, 1, or 2 |
+// +-----+--------------+-----------------+--
+// ^ ^ ^
+// bufPtr bufEnd nextEnd
+//
+GBool RunLengthEncoder::fillBuf() {
+ int c, c1, c2;
+ int n;
+
+ // already hit EOF?
+ if (eof)
+ return gFalse;
+
+ // grab two bytes
+ if (nextEnd < bufEnd + 1) {
+ if ((c1 = str->getChar()) == EOF) {
+ eof = gTrue;
+ return gFalse;
+ }
+ } else {
+ c1 = bufEnd[0] & 0xff;
+ }
+ if (nextEnd < bufEnd + 2) {
+ if ((c2 = str->getChar()) == EOF) {
+ eof = gTrue;
+ buf[0] = 0;
+ buf[1] = (char)c1;
+ bufPtr = buf;
+ bufEnd = &buf[2];
+ return gTrue;
+ }
+ } else {
+ c2 = bufEnd[1] & 0xff;
+ }
+
+ // check for repeat
+ c = 0; // make gcc happy
+ if (c1 == c2) {
+ n = 2;
+ while (n < 128 && (c = str->getChar()) == c1)
+ ++n;
+ buf[0] = (char)(257 - n);
+ buf[1] = (char)c1;
+ bufEnd = &buf[2];
+ if (c == EOF) {
+ eof = gTrue;
+ } else if (n < 128) {
+ buf[2] = (char)c;
+ nextEnd = &buf[3];
+ } else {
+ nextEnd = bufEnd;
+ }
+
+ // get up to 128 chars
+ } else {
+ buf[1] = (char)c1;
+ buf[2] = (char)c2;
+ n = 2;
+ while (n < 128) {
+ if ((c = str->getChar()) == EOF) {
+ eof = gTrue;
+ break;
+ }
+ ++n;
+ buf[n] = (char)c;
+ if (buf[n] == buf[n-1])
+ break;
+ }
+ if (buf[n] == buf[n-1]) {
+ buf[0] = (char)(n-2-1);
+ bufEnd = &buf[n-1];
+ nextEnd = &buf[n+1];
+ } else {
+ buf[0] = (char)(n-1);
+ bufEnd = nextEnd = &buf[n+1];
+ }
+ }
+ bufPtr = buf;
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// LZWEncoder
+//------------------------------------------------------------------------
+
+LZWEncoder::LZWEncoder(Stream *strA):
+ FilterStream(strA)
+{
+ inBufStart = 0;
+ inBufLen = 0;
+ outBufLen = 0;
+}
+
+LZWEncoder::~LZWEncoder() {
+ if (str->isEncoder()) {
+ delete str;
+ }
+}
+
+Stream *LZWEncoder::copy() {
+ error(errInternal, -1, "Called copy() on LZWEncoder");
+ return NULL;
+}
+
+void LZWEncoder::reset() {
+ int i;
+
+ str->reset();
+
+ // initialize code table
+ for (i = 0; i < 256; ++i) {
+ table[i].byte = i;
+ table[i].next = NULL;
+ table[i].children = NULL;
+ }
+ nextSeq = 258;
+ codeLen = 9;
+
+ // initialize input buffer
+ inBufLen = str->getBlock((char *)inBuf, sizeof(inBuf));
+ inBufStart = 0;
+
+ // initialize output buffer with a clear-table code
+ outBuf = 256;
+ outBufLen = 9;
+ needEOD = gFalse;
+}
+
+int LZWEncoder::getChar() {
+ int ret;
+
+ if (inBufLen == 0 && !needEOD && outBufLen == 0) {
+ return EOF;
+ }
+ if (outBufLen < 8 && (inBufLen > 0 || needEOD)) {
+ fillBuf();
+ }
+ if (outBufLen >= 8) {
+ ret = (outBuf >> (outBufLen - 8)) & 0xff;
+ outBufLen -= 8;
+ } else {
+ ret = (outBuf << (8 - outBufLen)) & 0xff;
+ outBufLen = 0;
+ }
+ return ret;
+}
+
+int LZWEncoder::lookChar() {
+ if (inBufLen == 0 && !needEOD && outBufLen == 0) {
+ return EOF;
+ }
+ if (outBufLen < 8 && (inBufLen > 0 || needEOD)) {
+ fillBuf();
+ }
+ if (outBufLen >= 8) {
+ return (outBuf >> (outBufLen - 8)) & 0xff;
+ } else {
+ return (outBuf << (8 - outBufLen)) & 0xff;
+ }
+}
+
+// On input, outBufLen < 8.
+// This function generates, at most, 2 12-bit codes
+// --> outBufLen < 8 + 12 + 12 = 32
+void LZWEncoder::fillBuf() {
+ LZWEncoderNode *p0, *p1;
+ int seqLen, code, i;
+
+ if (needEOD) {
+ outBuf = (outBuf << codeLen) | 257;
+ outBufLen += codeLen;
+ needEOD = gFalse;
+ return;
+ }
+
+ // find longest matching sequence (if any)
+ p0 = table + inBuf[inBufStart];
+ seqLen = 1;
+ while (inBufLen > seqLen) {
+ for (p1 = p0->children; p1; p1 = p1->next) {
+ if (p1->byte == inBuf[inBufStart + seqLen]) {
+ break;
+ }
+ }
+ if (!p1) {
+ break;
+ }
+ p0 = p1;
+ ++seqLen;
+ }
+ code = (int)(p0 - table);
+
+ // generate an output code
+ outBuf = (outBuf << codeLen) | code;
+ outBufLen += codeLen;
+
+ // update the table
+ table[nextSeq].byte = seqLen < inBufLen ? inBuf[inBufStart + seqLen] : 0;
+ table[nextSeq].children = NULL;
+ if (table[code].children) {
+ table[nextSeq].next = table[code].children;
+ } else {
+ table[nextSeq].next = NULL;
+ }
+ table[code].children = table + nextSeq;
+ ++nextSeq;
+
+ // update the input buffer
+ inBufStart += seqLen;
+ inBufLen -= seqLen;
+ if (inBufStart >= 4096 && inBufStart + inBufLen == sizeof(inBuf)) {
+ memcpy(inBuf, inBuf + inBufStart, inBufLen);
+ inBufStart = 0;
+ inBufLen += str->getBlock((char *)inBuf + inBufLen,
+ (int)sizeof(inBuf) - inBufLen);
+ }
+
+ // increment codeLen; generate clear-table code
+ if (nextSeq == (1 << codeLen)) {
+ ++codeLen;
+ if (codeLen == 13) {
+ outBuf = (outBuf << 12) | 256;
+ outBufLen += 12;
+ for (i = 0; i < 256; ++i) {
+ table[i].next = NULL;
+ table[i].children = NULL;
+ }
+ nextSeq = 258;
+ codeLen = 9;
+ }
+ }
+
+ // generate EOD next time
+ if (inBufLen == 0) {
+ needEOD = gTrue;
+ }
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h
new file mode 100644
index 00000000000..59cafadc14f
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Stream.h
@@ -0,0 +1,1056 @@
+//========================================================================
+//
+// Stream.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef STREAM_H
+#define STREAM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#if HAVE_JPEGLIB
+#include <jpeglib.h>
+#include <setjmp.h>
+#endif
+#include "gtypes.h"
+#include "gfile.h"
+#include "Object.h"
+
+class BaseStream;
+class SharedFile;
+
+//------------------------------------------------------------------------
+
+enum StreamKind {
+ strFile,
+ strASCIIHex,
+ strASCII85,
+ strLZW,
+ strRunLength,
+ strCCITTFax,
+ strDCT,
+ strFlate,
+ strJBIG2,
+ strJPX,
+ strWeird // internal-use stream types
+};
+
+enum StreamColorSpaceMode {
+ streamCSNone,
+ streamCSDeviceGray,
+ streamCSDeviceRGB,
+ streamCSDeviceCMYK
+};
+
+//------------------------------------------------------------------------
+
+// This is in Stream.h instead of Decrypt.h to avoid really annoying
+// include file dependency loops.
+enum CryptAlgorithm {
+ cryptRC4,
+ cryptAES,
+ cryptAES256
+};
+
+//------------------------------------------------------------------------
+// Stream (base class)
+//------------------------------------------------------------------------
+
+class Stream {
+public:
+
+ // Constructor.
+ Stream();
+
+ // Destructor.
+ virtual ~Stream();
+
+ virtual Stream *copy() = 0;
+
+ // Get kind of stream.
+ virtual StreamKind getKind() = 0;
+
+ virtual GBool isEmbedStream() { return gFalse; }
+
+ // Reset stream to beginning.
+ virtual void reset() = 0;
+
+ // Close down the stream.
+ virtual void close();
+
+ // Get next char from stream.
+ virtual int getChar() = 0;
+
+ // Peek at next char in stream.
+ virtual int lookChar() = 0;
+
+ // Get next char from stream without using the predictor.
+ // This is only used by StreamPredictor.
+ virtual int getRawChar();
+
+ // Get exactly <size> bytes from stream. Returns the number of
+ // bytes read -- the returned count will be less than <size> at EOF.
+ virtual int getBlock(char *blk, int size);
+
+ // Get next line from stream.
+ virtual char *getLine(char *buf, int size);
+
+ // Discard the next <n> bytes from stream. Returns the number of
+ // bytes discarded, which will be less than <n> only if EOF is
+ // reached.
+ virtual Guint discardChars(Guint n);
+
+ // Get current position in file.
+ virtual GFileOffset getPos() = 0;
+
+ // Go to a position in the stream. If <dir> is negative, the
+ // position is from the end of the file; otherwise the position is
+ // from the start of the file.
+ virtual void setPos(GFileOffset pos, int dir = 0) = 0;
+
+ // Get PostScript command for the filter(s).
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream);
+
+ // Does this stream type potentially contain non-printable chars?
+ virtual GBool isBinary(GBool last = gTrue) = 0;
+
+ // Get the BaseStream of this stream.
+ virtual BaseStream *getBaseStream() = 0;
+
+ // Get the stream after the last decoder (this may be a BaseStream
+ // or a DecryptStream).
+ virtual Stream *getUndecodedStream() = 0;
+
+ // Get the dictionary associated with this stream.
+ virtual Dict *getDict() = 0;
+
+ // Is this an encoding filter?
+ virtual GBool isEncoder() { return gFalse; }
+
+ // Get image parameters which are defined by the stream contents.
+ virtual void getImageParams(int *bitsPerComponent,
+ StreamColorSpaceMode *csMode) {}
+
+ // Return the next stream in the "stack".
+ virtual Stream *getNextStream() { return NULL; }
+
+ // Add filters to this stream according to the parameters in <dict>.
+ // Returns the new stream.
+ Stream *addFilters(Object *dict, int recursion = 0);
+
+private:
+
+ Stream *makeFilter(char *name, Stream *str, Object *params, int recursion);
+};
+
+//------------------------------------------------------------------------
+// BaseStream
+//
+// This is the base class for all streams that read directly from a file.
+//------------------------------------------------------------------------
+
+class BaseStream: public Stream {
+public:
+
+ BaseStream(Object *dictA);
+ virtual ~BaseStream();
+ virtual Stream *makeSubStream(GFileOffset start, GBool limited,
+ GFileOffset length, Object *dict) = 0;
+ virtual void setPos(GFileOffset pos, int dir = 0) = 0;
+ virtual GBool isBinary(GBool last = gTrue) { return last; }
+ virtual BaseStream *getBaseStream() { return this; }
+ virtual Stream *getUndecodedStream() { return this; }
+ virtual Dict *getDict() { return dict.getDict(); }
+ virtual GString *getFileName() { return NULL; }
+
+ // Get/set position of first byte of stream within the file.
+ virtual GFileOffset getStart() = 0;
+ virtual void moveStart(int delta) = 0;
+
+protected:
+
+ Object dict;
+};
+
+//------------------------------------------------------------------------
+// FilterStream
+//
+// This is the base class for all streams that filter another stream.
+//------------------------------------------------------------------------
+
+class FilterStream: public Stream {
+public:
+
+ FilterStream(Stream *strA);
+ virtual ~FilterStream();
+ virtual void close();
+ virtual GFileOffset getPos() { return str->getPos(); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual BaseStream *getBaseStream() { return str->getBaseStream(); }
+ virtual Stream *getUndecodedStream() { return str->getUndecodedStream(); }
+ virtual Dict *getDict() { return str->getDict(); }
+ virtual Stream *getNextStream() { return str; }
+
+protected:
+
+ Stream *str;
+};
+
+//------------------------------------------------------------------------
+// ImageStream
+//------------------------------------------------------------------------
+
+class ImageStream {
+public:
+
+ // Create an image stream object for an image with the specified
+ // parameters. Note that these are the actual image parameters,
+ // which may be different from the predictor parameters.
+ ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA);
+
+ ~ImageStream();
+
+ // Reset the stream.
+ void reset();
+
+ // Close down the stream.
+ void close();
+
+ // Gets the next pixel from the stream. <pix> should be able to hold
+ // at least nComps elements. Returns false at end of file.
+ GBool getPixel(Guchar *pix);
+
+ // Returns a pointer to the next line of pixels. Returns NULL at
+ // end of file.
+ Guchar *getLine();
+
+ // Skip an entire line from the image.
+ void skipLine();
+
+private:
+
+ Stream *str; // base stream
+ int width; // pixels per line
+ int nComps; // components per pixel
+ int nBits; // bits per component
+ int nVals; // components per line
+ int inputLineSize; // input line buffer size
+ char *inputLine; // input line buffer
+ Guchar *imgLine; // line buffer
+ int imgIdx; // current index in imgLine
+};
+
+
+//------------------------------------------------------------------------
+// StreamPredictor
+//------------------------------------------------------------------------
+
+class StreamPredictor {
+public:
+
+ // Create a predictor object. Note that the parameters are for the
+ // predictor, and may not match the actual image parameters.
+ StreamPredictor(Stream *strA, int predictorA,
+ int widthA, int nCompsA, int nBitsA);
+
+ ~StreamPredictor();
+
+ GBool isOk() { return ok; }
+
+ void reset();
+
+ int lookChar();
+ int getChar();
+ int getBlock(char *blk, int size);
+
+ int getPredictor() { return predictor; }
+ int getWidth() { return width; }
+ int getNComps() { return nComps; }
+ int getNBits() { return nBits; }
+
+private:
+
+ GBool getNextLine();
+
+ Stream *str; // base stream
+ int predictor; // predictor
+ int width; // pixels per line
+ int nComps; // components per pixel
+ int nBits; // bits per component
+ int nVals; // components per line
+ int pixBytes; // bytes per pixel
+ int rowBytes; // bytes per line
+ Guchar *predLine; // line buffer
+ int predIdx; // current index in predLine
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// FileStream
+//------------------------------------------------------------------------
+
+#define fileStreamBufSize 256
+
+class FileStream: public BaseStream {
+public:
+
+ FileStream(FILE *fA, GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
+ virtual ~FileStream();
+ virtual Stream *copy();
+ virtual Stream *makeSubStream(GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
+ virtual StreamKind getKind() { return strFile; }
+ virtual void reset();
+ virtual int getChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+ virtual int lookChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+ virtual int getBlock(char *blk, int size);
+ virtual GFileOffset getPos() { return bufPos + (int)(bufPtr - buf); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual GFileOffset getStart() { return start; }
+ virtual void moveStart(int delta);
+
+private:
+
+ FileStream(SharedFile *fA, GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
+ GBool fillBuf();
+
+ SharedFile *f;
+ GFileOffset start;
+ GBool limited;
+ GFileOffset length;
+ char buf[fileStreamBufSize];
+ char *bufPtr;
+ char *bufEnd;
+ GFileOffset bufPos;
+};
+
+//------------------------------------------------------------------------
+// MemStream
+//------------------------------------------------------------------------
+
+class MemStream: public BaseStream {
+public:
+
+ MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA);
+ virtual ~MemStream();
+ virtual Stream *copy();
+ virtual Stream *makeSubStream(GFileOffset start, GBool limited,
+ GFileOffset lengthA, Object *dictA);
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual void close();
+ virtual int getChar()
+ { return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; }
+ virtual int lookChar()
+ { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; }
+ virtual int getBlock(char *blk, int size);
+ virtual GFileOffset getPos() { return (GFileOffset)(bufPtr - buf); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual GFileOffset getStart() { return start; }
+ virtual void moveStart(int delta);
+
+private:
+
+ char *buf;
+ Guint start;
+ Guint length;
+ char *bufEnd;
+ char *bufPtr;
+ GBool needFree;
+};
+
+//------------------------------------------------------------------------
+// EmbedStream
+//
+// This is a special stream type used for embedded streams (inline
+// images). It reads directly from the base stream -- after the
+// EmbedStream is deleted, reads from the base stream will proceed where
+// the BaseStream left off. Note that this is very different behavior
+// that creating a new FileStream (using makeSubStream).
+//------------------------------------------------------------------------
+
+class EmbedStream: public BaseStream {
+public:
+
+ EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA);
+ virtual ~EmbedStream();
+ virtual Stream *copy();
+ virtual Stream *makeSubStream(GFileOffset start, GBool limitedA,
+ GFileOffset lengthA, Object *dictA);
+ virtual StreamKind getKind() { return str->getKind(); }
+ virtual GBool isEmbedStream() { return gTrue; }
+ virtual void reset() {}
+ virtual int getChar();
+ virtual int lookChar();
+ virtual int getBlock(char *blk, int size);
+ virtual GFileOffset getPos() { return str->getPos(); }
+ virtual void setPos(GFileOffset pos, int dir = 0);
+ virtual GFileOffset getStart();
+ virtual void moveStart(int delta);
+
+private:
+
+ Stream *str;
+ GBool limited;
+ GFileOffset length;
+};
+
+//------------------------------------------------------------------------
+// ASCIIHexStream
+//------------------------------------------------------------------------
+
+class ASCIIHexStream: public FilterStream {
+public:
+
+ ASCIIHexStream(Stream *strA);
+ virtual ~ASCIIHexStream();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strASCIIHex; }
+ virtual void reset();
+ virtual int getChar()
+ { int c = lookChar(); buf = EOF; return c; }
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ int buf;
+ GBool eof;
+};
+
+//------------------------------------------------------------------------
+// ASCII85Stream
+//------------------------------------------------------------------------
+
+class ASCII85Stream: public FilterStream {
+public:
+
+ ASCII85Stream(Stream *strA);
+ virtual ~ASCII85Stream();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strASCII85; }
+ virtual void reset();
+ virtual int getChar()
+ { int ch = lookChar(); ++index; return ch; }
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ int c[5];
+ int b[4];
+ int index, n;
+ GBool eof;
+};
+
+//------------------------------------------------------------------------
+// LZWStream
+//------------------------------------------------------------------------
+
+class LZWStream: public FilterStream {
+public:
+
+ LZWStream(Stream *strA, int predictor, int columns, int colors,
+ int bits, int earlyA);
+ virtual ~LZWStream();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strLZW; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual int getRawChar();
+ virtual int getBlock(char *blk, int size);
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ StreamPredictor *pred; // predictor
+ int early; // early parameter
+ GBool eof; // true if at eof
+ int inputBuf; // input buffer
+ int inputBits; // number of bits in input buffer
+ struct { // decoding table
+ int length;
+ int head;
+ Guchar tail;
+ } table[4097];
+ int nextCode; // next code to be used
+ int nextBits; // number of bits in next code word
+ int prevCode; // previous code used in stream
+ int newChar; // next char to be added to table
+ Guchar seqBuf[4097]; // buffer for current sequence
+ 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();
+ int getCode();
+};
+
+//------------------------------------------------------------------------
+// RunLengthStream
+//------------------------------------------------------------------------
+
+class RunLengthStream: public FilterStream {
+public:
+
+ RunLengthStream(Stream *strA);
+ virtual ~RunLengthStream();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strRunLength; }
+ virtual void reset();
+ virtual int getChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+ virtual int lookChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+ virtual int getBlock(char *blk, int size);
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ char buf[128]; // buffer
+ char *bufPtr; // next char to read
+ char *bufEnd; // end of buffer
+ GBool eof;
+
+ GBool fillBuf();
+};
+
+//------------------------------------------------------------------------
+// CCITTFaxStream
+//------------------------------------------------------------------------
+
+struct CCITTCodeTable;
+
+class CCITTFaxStream: public FilterStream {
+public:
+
+ CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA,
+ GBool byteAlignA, int columnsA, int rowsA,
+ GBool endOfBlockA, GBool blackA);
+ virtual ~CCITTFaxStream();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strCCITTFax; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual int getBlock(char *blk, int size);
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ int encoding; // 'K' parameter
+ GBool endOfLine; // 'EndOfLine' parameter
+ GBool byteAlign; // 'EncodedByteAlign' parameter
+ int columns; // 'Columns' parameter
+ int rows; // 'Rows' parameter
+ GBool endOfBlock; // 'EndOfBlock' parameter
+ GBool black; // 'BlackIs1' parameter
+ int blackXOR;
+ GBool eof; // true if at eof
+ GBool nextLine2D; // true if next line uses 2D encoding
+ int row; // current row
+ Guint inputBuf; // input buffer
+ int inputBits; // number of bits in input buffer
+ int *codingLine; // coding line changing elements
+ int *refLine; // reference line changing elements
+ int nextCol; // next column to read
+ int a0i; // index into codingLine
+ GBool err; // error on current line
+ int nErrors; // number of errors so far in this stream
+
+ void addPixels(int a1, int blackPixels);
+ void addPixelsNeg(int a1, int blackPixels);
+ GBool readRow();
+ short getTwoDimCode();
+ short getWhiteCode();
+ short getBlackCode();
+ short lookBits(int n);
+ void eatBits(int n) { if ((inputBits -= n) < 0) inputBits = 0; }
+};
+
+//------------------------------------------------------------------------
+// DCTStream
+//------------------------------------------------------------------------
+
+#if HAVE_JPEGLIB
+
+class DCTStream;
+
+#define dctStreamBufSize 4096
+
+struct DCTSourceMgr {
+ jpeg_source_mgr src;
+ DCTStream *str;
+ char buf[dctStreamBufSize];
+};
+
+struct DCTErrorMgr {
+ struct jpeg_error_mgr err;
+ jmp_buf setjmpBuf;
+};
+
+#else // HAVE_JPEGLIB
+
+// DCT component info
+struct DCTCompInfo {
+ int id; // component ID
+ int hSample, vSample; // horiz/vert sampling resolutions
+ int quantTable; // quantization table number
+ int prevDC; // DC coefficient accumulator
+};
+
+struct DCTScanInfo {
+ GBool comp[4]; // comp[i] is set if component i is
+ // included in this scan
+ int numComps; // number of components in the scan
+ int dcHuffTable[4]; // DC Huffman table numbers
+ int acHuffTable[4]; // AC Huffman table numbers
+ int firstCoeff, lastCoeff; // first and last DCT coefficient
+ int ah, al; // successive approximation parameters
+};
+
+// DCT Huffman decoding table
+struct DCTHuffTable {
+ Guchar firstSym[17]; // first symbol for this bit length
+ Gushort firstCode[17]; // first code for this bit length
+ Gushort numCodes[17]; // number of codes of this bit length
+ Guchar sym[256]; // symbols
+};
+
+#endif // HAVE_JPEGLIB
+
+class DCTStream: public FilterStream {
+public:
+
+ DCTStream(Stream *strA, int colorXformA);
+ virtual ~DCTStream();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strDCT; }
+ virtual void reset();
+ virtual void close();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual int getBlock(char *blk, int size);
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream);
+ virtual GBool isBinary(GBool last = gTrue);
+ Stream *getRawStream() { return str; }
+
+private:
+
+ GBool checkSequentialInterleaved();
+
+#if HAVE_JPEGLIB
+
+ int colorXform; // color transform: -1 = unspecified
+ // 0 = none
+ // 1 = YUV/YUVK -> RGB/CMYK
+ jpeg_decompress_struct decomp;
+ DCTErrorMgr errorMgr;
+ DCTSourceMgr sourceMgr;
+ GBool error;
+ char *lineBuf;
+ int lineBufHeight;
+ char *lineBufRows[4];
+ char *bufPtr;
+ char *bufEnd;
+ GBool inlineImage;
+
+ GBool fillBuf();
+ static void errorExit(j_common_ptr d);
+ static void errorMessage(j_common_ptr d);
+ static void initSourceCbk(j_decompress_ptr d);
+ static boolean fillInputBufferCbk(j_decompress_ptr d);
+ static void skipInputDataCbk(j_decompress_ptr d, long numBytes);
+ static void termSourceCbk(j_decompress_ptr d);
+
+#else // HAVE_JPEGLIB
+
+ GBool progressive; // set if in progressive mode
+ GBool interleaved; // set if in interleaved mode
+ int width, height; // image size
+ int mcuWidth, mcuHeight; // size of min coding unit, in data units
+ int bufWidth, bufHeight; // frameBuf size
+ DCTCompInfo compInfo[4]; // info for each component
+ DCTScanInfo scanInfo; // info for the current scan
+ int numComps; // number of components in image
+ int colorXform; // color transform: -1 = unspecified
+ // 0 = none
+ // 1 = YUV/YUVK -> RGB/CMYK
+ GBool gotJFIFMarker; // set if APP0 JFIF marker was present
+ GBool gotAdobeMarker; // set if APP14 Adobe marker was present
+ int restartInterval; // restart interval, in MCUs
+ Gushort quantTables[4][64]; // quantization tables
+ int numQuantTables; // number of quantization tables
+ DCTHuffTable dcHuffTables[4]; // DC Huffman tables
+ DCTHuffTable acHuffTables[4]; // AC Huffman tables
+ int numDCHuffTables; // number of DC Huffman tables
+ int numACHuffTables; // number of AC Huffman tables
+ Guchar *rowBuf;
+ Guchar *rowBufPtr; // current position within rowBuf
+ Guchar *rowBufEnd; // end of valid data in rowBuf
+ int *frameBuf[4]; // buffer for frame (progressive mode)
+ int comp, x, y; // current position within image/MCU
+ int restartCtr; // MCUs left until restart
+ int restartMarker; // next restart marker
+ int eobRun; // number of EOBs left in the current run
+ int inputBuf; // input buffer for variable length codes
+ int inputBits; // number of valid bits in input buffer
+
+ void restart();
+ GBool readMCURow();
+ void readScan();
+ GBool readDataUnit(DCTHuffTable *dcHuffTable,
+ DCTHuffTable *acHuffTable,
+ int *prevDC, int data[64]);
+ GBool readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
+ DCTHuffTable *acHuffTable,
+ int *prevDC, int data[64]);
+ void decodeImage();
+ void transformDataUnit(Gushort *quantTable,
+ int dataIn[64], Guchar dataOut[64]);
+ int readHuffSym(DCTHuffTable *table);
+ int readAmp(int size);
+ int readBit();
+ GBool readHeader(GBool frame);
+ GBool readBaselineSOF();
+ GBool readProgressiveSOF();
+ GBool readScanInfo();
+ GBool readQuantTables();
+ GBool readHuffmanTables();
+ GBool readRestartInterval();
+ GBool readJFIFMarker();
+ GBool readAdobeMarker();
+ GBool readTrailer();
+ int readMarker();
+ int read16();
+
+#endif // HAVE_JPEGLIB
+};
+
+//------------------------------------------------------------------------
+// FlateStream
+//------------------------------------------------------------------------
+
+#define flateWindow 32768 // buffer size
+#define flateMask (flateWindow-1)
+#define flateMaxHuffman 15 // max Huffman code length
+#define flateMaxCodeLenCodes 19 // max # code length codes
+#define flateMaxLitCodes 288 // max # literal codes
+#define flateMaxDistCodes 30 // max # distance codes
+
+// Huffman code table entry
+struct FlateCode {
+ Gushort len; // code length, in bits
+ Gushort val; // value represented by this code
+};
+
+struct FlateHuffmanTab {
+ FlateCode *codes;
+ int maxLen;
+};
+
+// Decoding info for length and distance code words
+struct FlateDecode {
+ int bits; // # extra bits
+ int first; // first length/distance
+};
+
+class FlateStream: public FilterStream {
+public:
+
+ FlateStream(Stream *strA, int predictor, int columns,
+ int colors, int bits);
+ virtual ~FlateStream();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strFlate; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual int getRawChar();
+ virtual int getBlock(char *blk, int size);
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ StreamPredictor *pred; // predictor
+ Guchar buf[flateWindow]; // output data buffer
+ int index; // current index into output buffer
+ int remain; // number valid bytes in output buffer
+ int codeBuf; // input buffer
+ int codeSize; // number of bits in input buffer
+ int // literal and distance code lengths
+ codeLengths[flateMaxLitCodes + flateMaxDistCodes];
+ FlateHuffmanTab litCodeTab; // literal code table
+ FlateHuffmanTab distCodeTab; // distance code table
+ GBool compressedBlock; // set if reading a compressed block
+ 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];
+ static FlateDecode // length decoding info
+ lengthDecode[flateMaxLitCodes-257];
+ static FlateDecode // distance decoding info
+ distDecode[flateMaxDistCodes];
+ static FlateHuffmanTab // fixed literal code table
+ fixedLitCodeTab;
+ static FlateHuffmanTab // fixed distance code table
+ fixedDistCodeTab;
+
+ void readSome();
+ GBool startBlock();
+ void loadFixedCodes();
+ GBool readDynamicCodes();
+ void compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab);
+ int getHuffmanCodeWord(FlateHuffmanTab *tab);
+ int getCodeWord(int bits);
+};
+
+//------------------------------------------------------------------------
+// EOFStream
+//------------------------------------------------------------------------
+
+class EOFStream: public FilterStream {
+public:
+
+ EOFStream(Stream *strA);
+ virtual ~EOFStream();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset() {}
+ virtual int getChar() { return EOF; }
+ virtual int lookChar() { return EOF; }
+ virtual int getBlock(char *blk, int size) { return 0; }
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
+};
+
+//------------------------------------------------------------------------
+// BufStream
+//------------------------------------------------------------------------
+
+class BufStream: public FilterStream {
+public:
+
+ BufStream(Stream *strA, int bufSizeA);
+ virtual ~BufStream();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue);
+
+ int lookChar(int idx);
+
+private:
+
+ int *buf;
+ int bufSize;
+};
+
+//------------------------------------------------------------------------
+// FixedLengthEncoder
+//------------------------------------------------------------------------
+
+class FixedLengthEncoder: public FilterStream {
+public:
+
+ FixedLengthEncoder(Stream *strA, int lengthA);
+ ~FixedLengthEncoder();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue);
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ int length;
+ int count;
+};
+
+//------------------------------------------------------------------------
+// ASCIIHexEncoder
+//------------------------------------------------------------------------
+
+class ASCIIHexEncoder: public FilterStream {
+public:
+
+ ASCIIHexEncoder(Stream *strA);
+ virtual ~ASCIIHexEncoder();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+ virtual int lookChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ char buf[4];
+ char *bufPtr;
+ char *bufEnd;
+ int lineLen;
+ GBool eof;
+
+ GBool fillBuf();
+};
+
+//------------------------------------------------------------------------
+// ASCII85Encoder
+//------------------------------------------------------------------------
+
+class ASCII85Encoder: public FilterStream {
+public:
+
+ ASCII85Encoder(Stream *strA);
+ virtual ~ASCII85Encoder();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+ virtual int lookChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ char buf[8];
+ char *bufPtr;
+ char *bufEnd;
+ int lineLen;
+ GBool eof;
+
+ GBool fillBuf();
+};
+
+//------------------------------------------------------------------------
+// RunLengthEncoder
+//------------------------------------------------------------------------
+
+class RunLengthEncoder: public FilterStream {
+public:
+
+ RunLengthEncoder(Stream *strA);
+ virtual ~RunLengthEncoder();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+ virtual int lookChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ char buf[131];
+ char *bufPtr;
+ char *bufEnd;
+ char *nextEnd;
+ GBool eof;
+
+ GBool fillBuf();
+};
+
+//------------------------------------------------------------------------
+// LZWEncoder
+//------------------------------------------------------------------------
+
+struct LZWEncoderNode {
+ int byte;
+ LZWEncoderNode *next; // next sibling
+ LZWEncoderNode *children; // first child
+};
+
+class LZWEncoder: public FilterStream {
+public:
+
+ LZWEncoder(Stream *strA);
+ virtual ~LZWEncoder();
+ virtual Stream *copy();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream)
+ { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ LZWEncoderNode table[4096];
+ int nextSeq;
+ int codeLen;
+ Guchar inBuf[8192];
+ int inBufStart;
+ int inBufLen;
+ int outBuf;
+ int outBufLen;
+ GBool needEOD;
+
+ void fillBuf();
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc
new file mode 100644
index 00000000000..9ec906c7379
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.cc
@@ -0,0 +1,6348 @@
+//========================================================================
+//
+// TextOutputDev.cc
+//
+// Copyright 1997-2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <math.h>
+#include <limits.h>
+#include <ctype.h>
+#ifdef _WIN32
+#include <fcntl.h> // for O_BINARY
+#include <io.h> // for setmode
+#endif
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "gfile.h"
+#include "config.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "UnicodeMap.h"
+#include "UnicodeRemapping.h"
+#include "UnicodeTypeTable.h"
+#include "GfxState.h"
+#include "Link.h"
+#include "TextOutputDev.h"
+
+//------------------------------------------------------------------------
+// parameters
+//------------------------------------------------------------------------
+
+// Size of bins used for horizontal and vertical profiles is
+// splitPrecisionMul * minFontSize.
+#define splitPrecisionMul 0.05
+
+// Minimum allowed split precision.
+#define minSplitPrecision 0.01
+
+// yMin and yMax (or xMin and xMax for rot=1,3) are adjusted by this
+// fraction of the text height, to allow for slightly overlapping
+// lines (or large ascent/descent values).
+#define ascentAdjustFactor 0
+#define descentAdjustFactor 0.35
+
+// Gaps larger than max{gap} - splitGapSlack * avgFontSize are
+// considered to be equivalent.
+#define splitGapSlack 0.2
+
+// The vertical gap threshold (minimum gap required to split
+// vertically) depends on the (approximate) number of lines in the
+// block:
+// threshold = (max + slope * nLines) * avgFontSize
+// with a min value of vertGapThresholdMin * avgFontSize.
+#define vertGapThresholdMin 0.8
+#define vertGapThresholdMax 3
+#define vertGapThresholdSlope -0.5
+
+// Vertical gap threshold for table mode.
+#define vertGapThresholdTableMin 0.2
+#define vertGapThresholdTableMax 0.5
+#define vertGapThresholdTableSlope -0.02
+
+// A large character has a font size larger than
+// largeCharThreshold * avgFontSize.
+#define largeCharThreshold 1.5
+
+// A block will be split vertically only if the resulting chunk
+// widths are greater than vertSplitChunkThreshold * avgFontSize.
+#define vertSplitChunkThreshold 2
+
+// Max difference in primary,secondary coordinates (as a fraction of
+// the font size) allowed for duplicated text (fake boldface, drop
+// shadows) which is to be discarded.
+#define dupMaxPriDelta 0.1
+#define dupMaxSecDelta 0.2
+
+// 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.
+#define minParagraphIndent 0.5
+
+// If the space between two lines is greater than
+// paragraphSpacingThreshold * avgLineSpacing, start a new paragraph.
+#define paragraphSpacingThreshold 1.25
+
+// If font size changes by at least this much (measured in points)
+// between lines, start a new paragraph.
+#define paragraphFontSizeDelta 1
+
+// Spaces at the start of a line in physical layout mode are this wide
+// (as a multiple of font size).
+#define physLayoutSpaceWidth 0.33
+
+// In simple layout mode, lines are broken at gaps larger than this
+// 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
+
+// Primary axis delta which will cause a line break in raw mode
+// (as a fraction of font size).
+#define rawModeLineDelta 0.5
+
+// Secondary axis delta which will cause a word break in raw mode
+// (as a fraction of font size).
+#define rawModeWordSpacing 0.15
+
+// Secondary axis overlap which will cause a line break in raw mode
+// (as a fraction of font size).
+#define rawModeCharOverlap 0.2
+
+// Max spacing (as a multiple of font size) allowed between the end of
+// a line and a clipped character to be included in that line.
+#define clippedTextMaxWordSpace 0.5
+
+// Max width of underlines (in points).
+#define maxUnderlineWidth 3
+
+// Max horizontal distance between edge of word and start of underline
+// (as a fraction of font size).
+#define underlineSlack 0.2
+
+// Max vertical distance between baseline of word and start of
+// underline (as a fraction of font size).
+#define underlineBaselineSlack 0.2
+
+// Max distance between edge of text and edge of link border (as a
+// fraction of font size).
+#define hyperlinkSlack 0.2
+
+// Text is considered diagonal if abs(tan(angle)) > diagonalThreshold.
+// (Or 1/tan(angle) for 90/270 degrees.)
+#define diagonalThreshold 0.1
+
+// This value is used as the ascent when computing selection
+// 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
+//------------------------------------------------------------------------
+
+class TextChar {
+public:
+
+ TextChar(Unicode cA, int charPosA, int charLenA,
+ double xMinA, double yMinA, double xMaxA, double yMaxA,
+ 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;
+ 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 rotatedA, GBool clippedA, GBool invisibleA,
+ TextFontInfo *fontA, double fontSizeA,
+ double colorRA, double colorGA, double colorBA) {
+ double t;
+
+ c = cA;
+ charPos = charPosA;
+ charLen = charLenA;
+ xMin = xMinA;
+ yMin = yMinA;
+ xMax = xMaxA;
+ yMax = yMaxA;
+ // this can happen with vertical writing mode, or with odd values
+ // for the char/word spacing parameters
+ if (xMin > xMax) {
+ t = xMin; xMin = xMax; xMax = t;
+ }
+ if (yMin > yMax) {
+ t = yMin; yMin = yMax; yMax = t;
+ }
+ // TextPage::findGaps uses integer coordinates, so clip the char
+ // bbox to fit in a 32-bit int (this is generally only a problem in
+ // damaged PDF files)
+ if (xMin < -1e8) {
+ xMin = -1e8;
+ }
+ if (xMax > 1e8) {
+ xMax = 1e8;
+ }
+ if (yMin < -1e8) {
+ yMin = -1e8;
+ }
+ if (yMax > 1e8) {
+ yMax = 1e8;
+ }
+ rot = (Guchar)rotA;
+ rotated = (char)rotatedA;
+ clipped = (char)clippedA;
+ invisible = (char)invisibleA;
+ spaceAfter = (char)gFalse;
+ font = fontA;
+ fontSize = fontSizeA;
+ colorR = colorRA;
+ colorG = colorGA;
+ colorB = colorBA;
+ overlap = gFalse;
+}
+
+int TextChar::cmpX(const void *p1, const void *p2) {
+ const TextChar *ch1 = *(const TextChar **)p1;
+ const TextChar *ch2 = *(const TextChar **)p2;
+
+ if (ch1->xMin < ch2->xMin) {
+ return -1;
+ } else if (ch1->xMin > ch2->xMin) {
+ return 1;
+ } else {
+ return ch1->charPos - ch2->charPos;
+ }
+}
+
+int TextChar::cmpY(const void *p1, const void *p2) {
+ const TextChar *ch1 = *(const TextChar **)p1;
+ const TextChar *ch2 = *(const TextChar **)p2;
+
+ if (ch1->yMin < ch2->yMin) {
+ return -1;
+ } else if (ch1->yMin > ch2->yMin) {
+ return 1;
+ } else {
+ return ch1->charPos - ch2->charPos;
+ }
+}
+
+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
+//------------------------------------------------------------------------
+
+enum TextBlockType {
+ blkVertSplit,
+ blkHorizSplit,
+ blkLeaf
+};
+
+enum TextBlockTag {
+ blkTagMulticolumn,
+ blkTagColumn,
+ blkTagSuperLine,
+ blkTagLine
+};
+
+class TextBlock {
+public:
+
+ TextBlock(TextBlockType typeA, int rotA);
+ ~TextBlock();
+ void addChild(TextBlock *child);
+ void addChild(TextChar *child, GBool updateBox);
+ void prependChild(TextChar *child);
+ void updateBounds(int childIdx);
+
+ TextBlockType type;
+ TextBlockTag tag;
+ int rot;
+ double xMin, yMin, xMax, yMax;
+ GBool smallSplit; // true for blkVertSplit/blkHorizSplit
+ // where the gap size is small
+ GList *children; // for blkLeaf, children are TextWord;
+ // for others, children are TextBlock
+};
+
+TextBlock::TextBlock(TextBlockType typeA, int rotA) {
+ type = typeA;
+ tag = blkTagMulticolumn;
+ rot = rotA;
+ xMin = yMin = xMax = yMax = 0;
+ smallSplit = gFalse;
+ children = new GList();
+}
+
+TextBlock::~TextBlock() {
+ if (type == blkLeaf) {
+ delete children;
+ } else {
+ deleteGList(children, TextBlock);
+ }
+}
+
+void TextBlock::addChild(TextBlock *child) {
+ if (children->getLength() == 0) {
+ xMin = child->xMin;
+ yMin = child->yMin;
+ xMax = child->xMax;
+ yMax = child->yMax;
+ } else {
+ if (child->xMin < xMin) {
+ xMin = child->xMin;
+ }
+ if (child->yMin < yMin) {
+ yMin = child->yMin;
+ }
+ if (child->xMax > xMax) {
+ xMax = child->xMax;
+ }
+ if (child->yMax > yMax) {
+ yMax = child->yMax;
+ }
+ }
+ children->append(child);
+}
+
+void TextBlock::addChild(TextChar *child, GBool updateBox) {
+ if (updateBox) {
+ if (children->getLength() == 0) {
+ xMin = child->xMin;
+ yMin = child->yMin;
+ xMax = child->xMax;
+ yMax = child->yMax;
+ } else {
+ if (child->xMin < xMin) {
+ xMin = child->xMin;
+ }
+ if (child->yMin < yMin) {
+ yMin = child->yMin;
+ }
+ if (child->xMax > xMax) {
+ xMax = child->xMax;
+ }
+ if (child->yMax > yMax) {
+ yMax = child->yMax;
+ }
+ }
+ }
+ children->append(child);
+}
+
+void TextBlock::prependChild(TextChar *child) {
+ if (children->getLength() == 0) {
+ xMin = child->xMin;
+ yMin = child->yMin;
+ xMax = child->xMax;
+ yMax = child->yMax;
+ } else {
+ if (child->xMin < xMin) {
+ xMin = child->xMin;
+ }
+ if (child->yMin < yMin) {
+ yMin = child->yMin;
+ }
+ if (child->xMax > xMax) {
+ xMax = child->xMax;
+ }
+ if (child->yMax > yMax) {
+ yMax = child->yMax;
+ }
+ }
+ children->insert(0, child);
+}
+
+void TextBlock::updateBounds(int childIdx) {
+ TextBlock *child;
+
+ child = (TextBlock *)children->get(childIdx);
+ if (child->xMin < xMin) {
+ xMin = child->xMin;
+ }
+ if (child->yMin < yMin) {
+ yMin = child->yMin;
+ }
+ if (child->xMax > xMax) {
+ xMax = child->xMax;
+ }
+ if (child->yMax > yMax) {
+ yMax = child->yMax;
+ }
+}
+
+//------------------------------------------------------------------------
+// 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
+//------------------------------------------------------------------------
+
+struct TextGap {
+ double x; // center of gap: x for vertical gaps,
+ // y for horizontal gaps
+ double w; // width/height of gap
+};
+
+class TextGaps {
+public:
+
+ TextGaps();
+ ~TextGaps();
+ void addGap(double x, double w);
+ int getLength() { return length; }
+ double getX(int idx) { return gaps[idx].x; }
+ double getW(int idx) { return gaps[idx].w; }
+
+private:
+
+ int length;
+ int size;
+ TextGap *gaps;
+};
+
+TextGaps::TextGaps() {
+ length = 0;
+ size = 16;
+ gaps = (TextGap *)gmallocn(size, sizeof(TextGap));
+}
+
+TextGaps::~TextGaps() {
+ gfree(gaps);
+}
+
+void TextGaps::addGap(double x, double w) {
+ if (length == size) {
+ size *= 2;
+ gaps = (TextGap *)greallocn(gaps, size, sizeof(TextGap));
+ }
+ gaps[length].x = x;
+ gaps[length].w = w;
+ ++length;
+}
+
+//------------------------------------------------------------------------
+// TextSuperLine
+//------------------------------------------------------------------------
+
+class TextSuperLine {
+public:
+
+ TextSuperLine(GList *linesA);
+ ~TextSuperLine();
+
+ GList *lines; // [TextLine]
+ double yMin, yMax;
+ double fontSize;
+};
+
+TextSuperLine::TextSuperLine(GList *linesA) {
+ TextLine *line;
+ int i;
+
+ lines = linesA;
+ yMin = yMax = 0;
+ fontSize = ((TextLine *)lines->get(0))->fontSize;
+ for (i = 0; i < lines->getLength(); ++i) {
+ line = (TextLine *)lines->get(i);
+ if (i == 0 || line->yMin < yMin) {
+ yMin = line->yMin;
+ }
+ if (i == 0 || line->yMax > yMax) {
+ yMax = line->yMax;
+ }
+ }
+}
+
+TextSuperLine::~TextSuperLine() {
+ deleteGList(lines, TextLine);
+}
+
+//------------------------------------------------------------------------
+// TextUnderline
+//------------------------------------------------------------------------
+
+class TextUnderline {
+public:
+
+ TextUnderline(double x0A, double y0A, double x1A, double y1A)
+ { x0 = x0A; y0 = y0A; x1 = x1A; y1 = y1A; horiz = y0 == y1; }
+ ~TextUnderline() {}
+
+ double x0, y0, x1, y1;
+ GBool horiz;
+};
+
+//------------------------------------------------------------------------
+// TextLink
+//------------------------------------------------------------------------
+
+class TextLink {
+public:
+
+ TextLink(double xMinA, double yMinA, double xMaxA, double yMaxA,
+ GString *uriA)
+ { xMin = xMinA; yMin = yMinA; xMax = xMaxA; yMax = yMaxA; uri = uriA; }
+ ~TextLink();
+
+ double xMin, yMin, xMax, yMax;
+ GString *uri;
+};
+
+TextLink::~TextLink() {
+ if (uri) {
+ delete uri;
+ }
+}
+
+//------------------------------------------------------------------------
+// TextOutputControl
+//------------------------------------------------------------------------
+
+TextOutputControl::TextOutputControl() {
+ mode = textOutReadingOrder;
+ fixedPitch = 0;
+ fixedLineSpacing = 0;
+ html = gFalse;
+ clipText = gFalse;
+ discardDiagonalText = gFalse;
+ discardRotatedText = gFalse;
+ discardInvisibleText = gFalse;
+ discardClippedText = gFalse;
+ splitRotatedWords = gFalse;
+ overlapHandling = textOutIgnoreOverlaps;
+ separateLargeChars = gTrue;
+ insertBOM = gFalse;
+ marginLeft = 0;
+ marginRight = 0;
+ marginTop = 0;
+ marginBottom = 0;
+}
+
+
+//------------------------------------------------------------------------
+// TextFontInfo
+//------------------------------------------------------------------------
+
+TextFontInfo::TextFontInfo(GfxState *state) {
+ GfxFont *gfxFont;
+
+ gfxFont = state->getFont();
+ if (gfxFont) {
+ fontID = *gfxFont->getID();
+ ascent = gfxFont->getAscent();
+ descent = gfxFont->getDescent();
+ // "odd" ascent/descent values cause trouble more often than not
+ // (in theory these could be legitimate values for oddly designed
+ // fonts -- but they are more often due to buggy PDF generators)
+ // (values that are too small are a different issue -- those seem
+ // to be more commonly legitimate)
+ if (ascent > 1) {
+ ascent = 0.75;
+ }
+ if (descent < -0.5) {
+ descent = -0.25;
+ }
+ } else {
+ fontID.num = -1;
+ fontID.gen = -1;
+ ascent = 0.75;
+ descent = -0.25;
+ }
+ fontName = (gfxFont && gfxFont->getName()) ? gfxFont->getName()->copy()
+ : (GString *)NULL;
+ flags = gfxFont ? gfxFont->getFlags() : 0;
+ mWidth = 0;
+ if (gfxFont && !gfxFont->isCIDFont()) {
+ char *name;
+ int code;
+ for (code = 0; code < 256; ++code) {
+ if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
+ name[0] == 'm' && name[1] == '\0') {
+ mWidth = ((Gfx8BitFont *)gfxFont)->getWidth((Guchar)code);
+ break;
+ }
+ }
+ }
+}
+
+TextFontInfo::~TextFontInfo() {
+ if (fontName) {
+ delete fontName;
+ }
+}
+
+GBool TextFontInfo::matches(GfxState *state) {
+ Ref id;
+
+ if (state->getFont()) {
+ id = *state->getFont()->getID();
+ } else {
+ id.num = -1;
+ id.gen = -1;
+ }
+ return id.num == fontID.num && id.gen == fontID.gen;
+}
+
+//------------------------------------------------------------------------
+// TextWord
+//------------------------------------------------------------------------
+
+// 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, GBool rotatedA, int dirA, GBool spaceAfterA) {
+ TextChar *ch;
+ int i;
+
+ rot = (char)rotA;
+ rotated = (char)rotatedA;
+ len = lenA;
+ text = (Unicode *)gmallocn(len, sizeof(Unicode));
+ edge = (double *)gmallocn(len + 1, sizeof(double));
+ charPos = (int *)gmallocn(len + 1, sizeof(int));
+ if (rot & 1) {
+ ch = (TextChar *)chars->get(start);
+ xMin = ch->xMin;
+ xMax = ch->xMax;
+ yMin = ch->yMin;
+ ch = (TextChar *)chars->get(start + len - 1);
+ yMax = ch->yMax;
+ } else {
+ ch = (TextChar *)chars->get(start);
+ xMin = ch->xMin;
+ yMin = ch->yMin;
+ yMax = ch->yMax;
+ ch = (TextChar *)chars->get(start + len - 1);
+ xMax = ch->xMax;
+ }
+ for (i = 0; i < len; ++i) {
+ ch = (TextChar *)chars->get(rot >= 2 ? start + len - 1 - i : start + i);
+ text[i] = ch->c;
+ charPos[i] = ch->charPos;
+ if (i == len - 1) {
+ charPos[len] = ch->charPos + ch->charLen;
+ }
+ switch (rot) {
+ case 0:
+ default:
+ edge[i] = ch->xMin;
+ if (i == len - 1) {
+ edge[len] = ch->xMax;
+ }
+ break;
+ case 1:
+ edge[i] = ch->yMin;
+ if (i == len - 1) {
+ edge[len] = ch->yMax;
+ }
+ break;
+ case 2:
+ edge[i] = ch->xMax;
+ if (i == len - 1) {
+ edge[len] = ch->xMin;
+ }
+ break;
+ case 3:
+ edge[i] = ch->yMax;
+ if (i == len - 1) {
+ edge[len] = ch->yMin;
+ }
+ break;
+ }
+ }
+ ch = (TextChar *)chars->get(start);
+ font = ch->font;
+ fontSize = ch->fontSize;
+ dir = (char)dirA;
+ spaceAfter = (char)spaceAfterA;
+ underlined = gFalse;
+ link = NULL;
+ colorR = ch->colorR;
+ colorG = ch->colorG;
+ colorB = ch->colorB;
+ invisible = ch->invisible;
+}
+
+TextWord::TextWord(TextWord *word) {
+ *this = *word;
+ text = (Unicode *)gmallocn(len, sizeof(Unicode));
+ memcpy(text, word->text, len * sizeof(Unicode));
+ edge = (double *)gmallocn(len + 1, sizeof(double));
+ memcpy(edge, word->edge, (len + 1) * sizeof(double));
+ charPos = (int *)gmallocn(len + 1, sizeof(int));
+ memcpy(charPos, word->charPos, (len + 1) * sizeof(int));
+}
+
+TextWord::~TextWord() {
+ gfree(text);
+ gfree(edge);
+ gfree(charPos);
+}
+
+int TextWord::cmpYX(const void *p1, const void *p2) {
+ const TextWord *word1 = *(const TextWord **)p1;
+ const TextWord *word2 = *(const TextWord **)p2;
+ double cmp;
+
+ if ((cmp = word1->yMin - word2->yMin) == 0) {
+ cmp = word1->xMin - word2->xMin;
+ }
+ return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+}
+
+int TextWord::cmpCharPos(const void *p1, const void *p2) {
+ const TextWord *word1 = *(const TextWord **)p1;
+ const TextWord *word2 = *(const TextWord **)p2;
+
+ return word1->charPos[0] - word2->charPos[0];
+}
+
+GString *TextWord::getText() {
+ GString *s;
+ UnicodeMap *uMap;
+ char buf[8];
+ int n, i;
+
+ s = new GString();
+ if (!(uMap = globalParams->getTextEncoding())) {
+ return s;
+ }
+ for (i = 0; i < len; ++i) {
+ n = uMap->mapUnicode(text[i], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ uMap->decRefCnt();
+ return s;
+}
+
+void TextWord::getCharBBox(int charIdx, double *xMinA, double *yMinA,
+ double *xMaxA, double *yMaxA) {
+ if (charIdx < 0 || charIdx >= len) {
+ return;
+ }
+ switch (rot) {
+ case 0:
+ *xMinA = edge[charIdx];
+ *xMaxA = edge[charIdx + 1];
+ *yMinA = yMin;
+ *yMaxA = yMax;
+ break;
+ case 1:
+ *xMinA = xMin;
+ *xMaxA = xMax;
+ *yMinA = edge[charIdx];
+ *yMaxA = edge[charIdx + 1];
+ break;
+ case 2:
+ *xMinA = edge[charIdx + 1];
+ *xMaxA = edge[charIdx];
+ *yMinA = yMin;
+ *yMaxA = yMax;
+ break;
+ case 3:
+ *xMinA = xMin;
+ *xMaxA = xMax;
+ *yMinA = edge[charIdx + 1];
+ *yMaxA = edge[charIdx];
+ break;
+ }
+}
+
+double TextWord::getBaseline() {
+ switch (rot) {
+ case 0:
+ default:
+ return yMax + fontSize * font->descent;
+ case 1:
+ return xMin - fontSize * font->descent;
+ case 2:
+ return yMin - fontSize * font->descent;
+ case 3:
+ return xMax + fontSize * font->descent;
+ }
+}
+
+GString *TextWord::getLinkURI() {
+ return link ? link->uri : (GString *)NULL;
+}
+
+//------------------------------------------------------------------------
+// TextLine
+//------------------------------------------------------------------------
+
+TextLine::TextLine(GList *wordsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA, double fontSizeA) {
+ TextWord *word;
+ int i, j, k;
+
+ words = wordsA;
+ rot = 0;
+ xMin = xMinA;
+ yMin = yMinA;
+ xMax = xMaxA;
+ yMax = yMaxA;
+ fontSize = fontSizeA;
+ px = 0;
+ pw = 0;
+
+ // build the text
+ len = 0;
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ len += word->len;
+ if (word->spaceAfter) {
+ ++len;
+ }
+ }
+ text = (Unicode *)gmallocn(len, sizeof(Unicode));
+ edge = (double *)gmallocn(len + 1, sizeof(double));
+ j = 0;
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ if (i == 0) {
+ rot = word->rot;
+ }
+ for (k = 0; k < word->len; ++k) {
+ text[j] = word->text[k];
+ edge[j] = word->edge[k];
+ ++j;
+ }
+ edge[j] = word->edge[word->len];
+ if (word->spaceAfter) {
+ text[j] = (Unicode)0x0020;
+ ++j;
+ edge[j] = edge[j - 1];
+ }
+ }
+ //~ need to check for other Unicode chars used as hyphens
+ hyphenated = text[len - 1] == (Unicode)'-';
+}
+
+TextLine::~TextLine() {
+ deleteGList(words, TextWord);
+ gfree(text);
+ gfree(edge);
+}
+
+double TextLine::getBaseline() {
+ TextWord *word0;
+
+ word0 = (TextWord *)words->get(0);
+ switch (rot) {
+ case 0:
+ default:
+ return yMax + fontSize * word0->font->descent;
+ case 1:
+ return xMin - fontSize * word0->font->descent;
+ case 2:
+ return yMin - fontSize * word0->font->descent;
+ case 3:
+ return xMax + fontSize * word0->font->descent;
+ }
+}
+
+int TextLine::cmpX(const void *p1, const void *p2) {
+ const TextLine *line1 = *(const TextLine **)p1;
+ const TextLine *line2 = *(const TextLine **)p2;
+
+ if (line1->xMin < line2->xMin) {
+ return -1;
+ } else if (line1->xMin > line2->xMin) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+//------------------------------------------------------------------------
+// TextParagraph
+//------------------------------------------------------------------------
+
+TextParagraph::TextParagraph(GList *linesA, GBool dropCapA) {
+ TextLine *line;
+ int i;
+
+ lines = linesA;
+ dropCap = dropCapA;
+ xMin = yMin = xMax = yMax = 0;
+ for (i = 0; i < lines->getLength(); ++i) {
+ line = (TextLine *)lines->get(i);
+ if (i == 0 || line->xMin < xMin) {
+ xMin = line->xMin;
+ }
+ if (i == 0 || line->yMin < yMin) {
+ yMin = line->yMin;
+ }
+ if (i == 0 || line->xMax > xMax) {
+ xMax = line->xMax;
+ }
+ if (i == 0 || line->yMax > yMax) {
+ yMax = line->yMax;
+ }
+ }
+}
+
+TextParagraph::~TextParagraph() {
+ deleteGList(lines, TextLine);
+}
+
+//------------------------------------------------------------------------
+// TextColumn
+//------------------------------------------------------------------------
+
+TextColumn::TextColumn(GList *paragraphsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA) {
+ paragraphs = paragraphsA;
+ xMin = xMinA;
+ yMin = yMinA;
+ xMax = xMaxA;
+ yMax = yMaxA;
+ px = py = 0;
+ pw = ph = 0;
+}
+
+TextColumn::~TextColumn() {
+ deleteGList(paragraphs, TextParagraph);
+}
+
+int TextColumn::getRotation() {
+ TextParagraph *par;
+ TextLine *line;
+
+ par = (TextParagraph *)paragraphs->get(0);
+ line = (TextLine *)par->getLines()->get(0);
+ return line->getRotation();
+}
+
+int TextColumn::cmpX(const void *p1, const void *p2) {
+ const TextColumn *col1 = *(const TextColumn **)p1;
+ const TextColumn *col2 = *(const TextColumn **)p2;
+
+ if (col1->xMin < col2->xMin) {
+ return -1;
+ } else if (col1->xMin > col2->xMin) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int TextColumn::cmpY(const void *p1, const void *p2) {
+ const TextColumn *col1 = *(const TextColumn **)p1;
+ const TextColumn *col2 = *(const TextColumn **)p2;
+
+ if (col1->yMin < col2->yMin) {
+ return -1;
+ } else if (col1->yMin > col2->yMin) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int TextColumn::cmpPX(const void *p1, const void *p2) {
+ const TextColumn *col1 = *(const TextColumn **)p1;
+ const TextColumn *col2 = *(const TextColumn **)p2;
+
+ if (col1->px < col2->px) {
+ return -1;
+ } else if (col1->px > col2->px) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+//------------------------------------------------------------------------
+// TextWordList
+//------------------------------------------------------------------------
+
+TextWordList::TextWordList(GList *wordsA, GBool primaryLRA) {
+ words = wordsA;
+ primaryLR = primaryLRA;
+}
+
+TextWordList::~TextWordList() {
+ deleteGList(words, TextWord);
+}
+
+int TextWordList::getLength() {
+ return words->getLength();
+}
+
+TextWord *TextWordList::get(int idx) {
+ if (idx < 0 || idx >= words->getLength()) {
+ return NULL;
+ }
+ return (TextWord *)words->get(idx);
+}
+
+//------------------------------------------------------------------------
+// TextPosition
+//------------------------------------------------------------------------
+
+int TextPosition::operator==(TextPosition pos) {
+ return colIdx == pos.colIdx &&
+ parIdx == pos.parIdx &&
+ lineIdx == pos.lineIdx &&
+ charIdx == pos.charIdx;
+}
+
+int TextPosition::operator!=(TextPosition pos) {
+ return colIdx != pos.colIdx ||
+ parIdx != pos.parIdx ||
+ lineIdx != pos.lineIdx ||
+ charIdx != pos.charIdx;
+}
+
+int TextPosition::operator<(TextPosition pos) {
+ return colIdx < pos.colIdx ||
+ (colIdx == pos.colIdx &&
+ (parIdx < pos.parIdx ||
+ (parIdx == pos.parIdx &&
+ (lineIdx < pos.lineIdx ||
+ (lineIdx == pos.lineIdx &&
+ charIdx < pos.charIdx)))));
+}
+
+int TextPosition::operator>(TextPosition pos) {
+ return colIdx > pos.colIdx ||
+ (colIdx == pos.colIdx &&
+ (parIdx > pos.parIdx ||
+ (parIdx == pos.parIdx &&
+ (lineIdx > pos.lineIdx ||
+ (lineIdx == pos.lineIdx &&
+ charIdx > pos.charIdx)))));
+}
+
+//------------------------------------------------------------------------
+// TextPage
+//------------------------------------------------------------------------
+
+TextPage::TextPage(TextOutputControl *controlA) {
+ control = *controlA;
+ remapping = globalParams->getUnicodeRemapping();
+ uBufSize = 16;
+ uBuf = (Unicode *)gmallocn(uBufSize, sizeof(Unicode));
+ pageWidth = pageHeight = 0;
+ charPos = 0;
+ curFont = NULL;
+ curFontSize = 0;
+ curRot = 0;
+ diagonal = gFalse;
+ rotated = gFalse;
+ nTinyChars = 0;
+ actualText = NULL;
+ actualTextLen = 0;
+ actualTextX0 = 0;
+ actualTextY0 = 0;
+ actualTextX1 = 0;
+ actualTextY1 = 0;
+ actualTextNBytes = 0;
+
+ chars = new GList();
+ fonts = new GList();
+
+ underlines = new GList();
+ links = new GList();
+
+ findCols = NULL;
+ lastFindXMin = lastFindYMin = 0;
+ haveLastFind = gFalse;
+
+ problematic = gFalse;
+}
+
+TextPage::~TextPage() {
+ clear();
+ deleteGList(chars, TextChar);
+ deleteGList(fonts, TextFontInfo);
+ deleteGList(underlines, TextUnderline);
+ deleteGList(links, TextLink);
+ if (findCols) {
+ deleteGList(findCols, TextColumn);
+ }
+ gfree(uBuf);
+}
+
+void TextPage::startPage(GfxState *state) {
+ clear();
+ if (state) {
+ pageWidth = state->getPageWidth();
+ pageHeight = state->getPageHeight();
+ } else {
+ pageWidth = pageHeight = 0;
+ }
+}
+
+void TextPage::clear() {
+ pageWidth = pageHeight = 0;
+ charPos = 0;
+ curFont = NULL;
+ curFontSize = 0;
+ curRot = 0;
+ diagonal = gFalse;
+ rotated = gFalse;
+ nTinyChars = 0;
+ gfree(actualText);
+ actualText = NULL;
+ actualTextLen = 0;
+ actualTextNBytes = 0;
+ deleteGList(chars, TextChar);
+ chars = new GList();
+ deleteGList(fonts, TextFontInfo);
+ fonts = new GList();
+ deleteGList(underlines, TextUnderline);
+ underlines = new GList();
+ deleteGList(links, TextLink);
+ links = new GList();
+
+ if (findCols) {
+ deleteGList(findCols, TextColumn);
+ findCols = NULL;
+ }
+ lastFindXMin = lastFindYMin = 0;
+ haveLastFind = gFalse;
+
+ problematic = gFalse;
+}
+
+void TextPage::updateFont(GfxState *state) {
+ GfxFont *gfxFont;
+ double *fm;
+ char *name;
+ int code, mCode, letterCode, anyCode;
+ double w;
+ double m[4], m2[4];
+ int i;
+
+ // get the font info object
+ curFont = NULL;
+ for (i = 0; i < fonts->getLength(); ++i) {
+ curFont = (TextFontInfo *)fonts->get(i);
+ if (curFont->matches(state)) {
+ break;
+ }
+ curFont = NULL;
+ }
+ if (!curFont) {
+ curFont = new TextFontInfo(state);
+ fonts->append(curFont);
+ if (state->getFont() && state->getFont()->problematicForUnicode()) {
+ problematic = gTrue;
+ }
+ }
+
+ // adjust the font size
+ gfxFont = state->getFont();
+ curFontSize = state->getTransformedFontSize();
+ if (gfxFont && gfxFont->getType() == fontType3) {
+ // This is a hack which makes it possible to deal with some Type 3
+ // fonts. The problem is that it's impossible to know what the
+ // base coordinate system used in the font is without actually
+ // rendering the font. This code tries to guess by looking at the
+ // width of the character 'm' (which breaks if the font is a
+ // subset that doesn't contain 'm').
+ mCode = letterCode = anyCode = -1;
+ for (code = 0; code < 256; ++code) {
+ name = ((Gfx8BitFont *)gfxFont)->getCharName(code);
+ if (name && name[0] == 'm' && name[1] == '\0') {
+ mCode = code;
+ }
+ if (letterCode < 0 &&
+ name &&
+ ((name[0] >= 'A' && name[0] <= 'Z') ||
+ (name[0] >= 'a' && name[0] <= 'z')) &&
+ name[1] == '\0') {
+ letterCode = code;
+ }
+ if (anyCode < 0 && name &&
+ ((Gfx8BitFont *)gfxFont)->getWidth((Guchar)code) > 0) {
+ anyCode = code;
+ }
+ }
+ if (mCode >= 0 &&
+ (w = ((Gfx8BitFont *)gfxFont)->getWidth((Guchar)mCode)) > 0) {
+ // 0.6 is a generic average 'm' width -- yes, this is a hack
+ curFontSize *= w / 0.6;
+ } else if (letterCode >= 0 &&
+ (w = ((Gfx8BitFont *)gfxFont)->getWidth((Guchar)letterCode))
+ > 0) {
+ // even more of a hack: 0.5 is a generic letter width
+ curFontSize *= w / 0.5;
+ } else if (anyCode >= 0 &&
+ (w = ((Gfx8BitFont *)gfxFont)->getWidth((Guchar)anyCode)) > 0) {
+ // better than nothing: 0.5 is a generic character width
+ curFontSize *= w / 0.5;
+ }
+ fm = gfxFont->getFontMatrix();
+ if (fm[0] != 0) {
+ curFontSize *= fabs(fm[3] / fm[0]);
+ }
+ }
+
+ // compute the rotation
+ state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
+ if (gfxFont && gfxFont->getType() == fontType3) {
+ fm = gfxFont->getFontMatrix();
+ m2[0] = fm[0] * m[0] + fm[1] * m[2];
+ m2[1] = fm[0] * m[1] + fm[1] * m[3];
+ m2[2] = fm[2] * m[0] + fm[3] * m[2];
+ m2[3] = fm[2] * m[1] + fm[3] * m[3];
+ m[0] = m2[0];
+ m[1] = m2[1];
+ m[2] = m2[2];
+ m[3] = m2[3];
+ }
+ 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 {
+ curRot = 2;
+ }
+ diagonal = fabs(m[1]) > diagonalThreshold * fabs(m[0]);
+ } else {
+ if (m[1] > 0) {
+ curRot = 1;
+ } else {
+ curRot = 3;
+ }
+ 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,
+ double dx, double dy,
+ CharCode c, int nBytes, Unicode *u, int uLen) {
+ double x1, y1, x2, y2, w1, h1, dx2, dy2, ascent, descent, sp;
+ double xMin, yMin, xMax, yMax, xMid, yMid;
+ double clipXMin, clipYMin, clipXMax, clipYMax;
+ GfxRGB rgb;
+ double alpha;
+ GBool clipped, rtl;
+ int uBufLen, i, j;
+
+ // if we're in an ActualText span, save the position info (the
+ // ActualText chars will be added by TextPage::endActualText()).
+ if (actualText) {
+ if (!actualTextNBytes) {
+ actualTextX0 = x;
+ actualTextY0 = y;
+ }
+ actualTextX1 = x + dx;
+ actualTextY1 = y + dy;
+ actualTextNBytes += nBytes;
+ return;
+ }
+
+ // throw away diagonal/rotated chars
+ if ((control.discardDiagonalText && diagonal) ||
+ (control.discardRotatedText && rotated)) {
+ charPos += nBytes;
+ return;
+ }
+
+ // subtract char and word spacing from the dx,dy values
+ sp = state->getCharSpace();
+ if (c == (CharCode)0x20) {
+ sp += state->getWordSpace();
+ }
+ state->textTransformDelta(sp * state->getHorizScaling(), 0, &dx2, &dy2);
+ dx -= dx2;
+ dy -= dy2;
+ state->transformDelta(dx, dy, &w1, &h1);
+
+ // throw away chars that aren't inside the page bounds
+ // (and also do a sanity check on the character size)
+ state->transform(x, y, &x1, &y1);
+ if (x1 + w1 < control.marginLeft ||
+ x1 > pageWidth - control.marginRight ||
+ y1 + h1 < control.marginTop ||
+ y1 > pageHeight - control.marginBottom ||
+ w1 > pageWidth ||
+ h1 > pageHeight) {
+ charPos += nBytes;
+ return;
+ }
+
+ // check the tiny chars limit
+ if (!globalParams->getTextKeepTinyChars() &&
+ fabs(w1) < 3 && fabs(h1) < 3) {
+ if (++nTinyChars > 50000) {
+ charPos += nBytes;
+ return;
+ }
+ }
+
+ // skip space, tab, and non-breaking space characters
+ // (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 =
+ (char)gTrue;
+ }
+ return;
+ }
+
+ // remap Unicode
+ uBufLen = 0;
+ for (i = 0; i < uLen; ++i) {
+ if (uBufSize - uBufLen < 8 && uBufSize < 20000) {
+ uBufSize *= 2;
+ uBuf = (Unicode *)greallocn(uBuf, uBufSize, sizeof(Unicode));
+ }
+ uBufLen += remapping->map(u[i], uBuf + uBufLen, uBufSize - uBufLen);
+ }
+
+ // add the characters
+ if (uBufLen > 0) {
+
+ // handle right-to-left ligatures: if there are multiple Unicode
+ // characters, and they're all right-to-left, insert them in
+ // right-to-left order
+ if (uBufLen > 1) {
+ rtl = gTrue;
+ for (i = 0; i < uBufLen; ++i) {
+ if (!unicodeTypeR(uBuf[i])) {
+ rtl = gFalse;
+ break;
+ }
+ }
+ } else {
+ rtl = gFalse;
+ }
+
+ // compute the bounding box
+ w1 /= uBufLen;
+ h1 /= uBufLen;
+ ascent = curFont->ascent * curFontSize;
+ descent = curFont->descent * curFontSize;
+ for (i = 0; i < uBufLen; ++i) {
+ x2 = x1 + i * w1;
+ y2 = y1 + i * h1;
+ switch (curRot) {
+ case 0:
+ default:
+ xMin = x2;
+ xMax = x2 + w1;
+ yMin = y2 - ascent;
+ yMax = y2 - descent;
+ break;
+ case 1:
+ xMin = x2 + descent;
+ xMax = x2 + ascent;
+ yMin = y2;
+ yMax = y2 + h1;
+ break;
+ case 2:
+ xMin = x2 + w1;
+ xMax = x2;
+ yMin = y2 + descent;
+ yMax = y2 + ascent;
+ break;
+ case 3:
+ xMin = x2 - ascent;
+ xMax = x2 - descent;
+ yMin = y2 + h1;
+ yMax = y2;
+ break;
+ }
+
+ // check for clipping
+ clipped = gFalse;
+ if (control.clipText || control.discardClippedText) {
+ state->getClipBBox(&clipXMin, &clipYMin, &clipXMax, &clipYMax);
+ xMid = 0.5 * (xMin + xMax);
+ yMid = 0.5 * (yMin + yMax);
+ if (xMid < clipXMin || xMid > clipXMax ||
+ yMid < clipYMin || yMid > clipYMax) {
+ clipped = gTrue;
+ }
+ }
+
+ if ((state->getRender() & 3) == 1) {
+ state->getStrokeRGB(&rgb);
+ alpha = state->getStrokeOpacity();
+ } else {
+ state->getFillRGB(&rgb);
+ alpha = state->getFillOpacity();
+ }
+ if (rtl) {
+ j = uBufLen - 1 - i;
+ } else {
+ j = i;
+ }
+ chars->append(new TextChar(uBuf[j], charPos, nBytes,
+ xMin, yMin, xMax, yMax,
+ curRot, rotated, clipped,
+ state->getRender() == 3 || alpha < 0.001,
+ curFont, curFontSize,
+ colToDbl(rgb.r), colToDbl(rgb.g),
+ colToDbl(rgb.b)));
+ }
+ }
+
+ charPos += nBytes;
+}
+
+void TextPage::incCharCount(int nChars) {
+ charPos += nChars;
+}
+
+void TextPage::beginActualText(GfxState *state, Unicode *u, int uLen) {
+ if (actualText) {
+ gfree(actualText);
+ }
+ actualText = (Unicode *)gmallocn(uLen, sizeof(Unicode));
+ memcpy(actualText, u, uLen * sizeof(Unicode));
+ actualTextLen = uLen;
+ actualTextNBytes = 0;
+}
+
+void TextPage::endActualText(GfxState *state) {
+ Unicode *u;
+
+ u = actualText;
+ actualText = NULL; // so we can call TextPage::addChar()
+ if (actualTextNBytes) {
+ // now that we have the position info for all of the text inside
+ // the marked content span, we feed the "ActualText" back through
+ // addChar()
+ addChar(state, actualTextX0, actualTextY0,
+ actualTextX1 - actualTextX0, actualTextY1 - actualTextY0,
+ 0, actualTextNBytes, u, actualTextLen);
+ }
+ gfree(u);
+ actualText = NULL;
+ actualTextLen = 0;
+ actualTextNBytes = gFalse;
+}
+
+void TextPage::addUnderline(double x0, double y0, double x1, double y1) {
+ underlines->append(new TextUnderline(x0, y0, x1, y1));
+}
+
+void TextPage::addLink(double xMin, double yMin, double xMax, double yMax,
+ Link *link) {
+ GString *uri;
+
+ if (link && link->getAction() && link->getAction()->getKind() == actionURI) {
+ uri = ((LinkURI *)link->getAction())->getURI()->copy();
+ links->append(new TextLink(xMin, yMin, xMax, yMax, uri));
+ }
+}
+
+//------------------------------------------------------------------------
+// TextPage: output
+//------------------------------------------------------------------------
+
+void TextPage::write(void *outputStream, TextOutputFunc outputFunc) {
+ UnicodeMap *uMap;
+ char space[8], eol[16], eop[8];
+ int spaceLen, eolLen, eopLen;
+ GBool pageBreaks;
+
+ // get the output encoding
+ if (!(uMap = globalParams->getTextEncoding())) {
+ return;
+ }
+ spaceLen = uMap->mapUnicode(0x20, space, sizeof(space));
+ eolLen = 0; // make gcc happy
+ switch (globalParams->getTextEOL()) {
+ case eolUnix:
+ eolLen = uMap->mapUnicode(0x0a, eol, sizeof(eol));
+ break;
+ case eolDOS:
+ eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
+ eolLen += uMap->mapUnicode(0x0a, eol + eolLen, (int)sizeof(eol) - eolLen);
+ break;
+ case eolMac:
+ eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
+ break;
+ }
+ eopLen = uMap->mapUnicode(0x0c, eop, sizeof(eop));
+ pageBreaks = globalParams->getTextPageBreaks();
+
+ switch (control.mode) {
+ case textOutReadingOrder:
+ writeReadingOrder(outputStream, outputFunc, uMap, space, spaceLen,
+ eol, eolLen);
+ break;
+ case textOutPhysLayout:
+ case textOutTableLayout:
+ writePhysLayout(outputStream, outputFunc, uMap, space, spaceLen,
+ eol, eolLen);
+ break;
+ case textOutSimpleLayout:
+ 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);
+ break;
+ case textOutRawOrder:
+ writeRaw(outputStream, outputFunc, uMap, space, spaceLen,
+ eol, eolLen);
+ break;
+ }
+
+ // end of page
+ if (pageBreaks) {
+ (*outputFunc)(outputStream, eop, eopLen);
+ }
+
+ uMap->decRefCnt();
+}
+
+void TextPage::writeReadingOrder(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextBlock *tree;
+ 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);
+#if 0 //~debug
+ dumpTree(tree);
+#endif
+ if (!tree) {
+ // no text
+ unrotateChars(chars, rot);
+ return;
+ }
+ columns = buildColumns(tree, primaryLR);
+ delete tree;
+ unrotateChars(chars, rot);
+ if (control.html) {
+ rotateUnderlinesAndLinks(rot);
+ generateUnderlinesAndLinks(columns);
+ }
+ if (overlappingChars) {
+ if (overlappingChars->getLength() > 0) {
+ columns->append(buildOverlappingTextColumn(overlappingChars));
+ }
+ deleteGList(overlappingChars, TextChar);
+ }
+#if 0 //~debug
+ dumpColumns(columns);
+#endif
+
+ 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);
+ n = line->len;
+ if (line->hyphenated && lineIdx + 1 < par->lines->getLength()) {
+ --n;
+ }
+ s = new GString();
+ encodeFragment(line->text, n, uMap, primaryLR, s);
+ if (lineIdx + 1 < par->lines->getLength() && !line->hyphenated) {
+ s->append(space, spaceLen);
+ }
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ delete s;
+ }
+ (*outputFunc)(outputStream, eol, eolLen);
+ }
+ (*outputFunc)(outputStream, eol, eolLen);
+ }
+
+ deleteGList(columns, TextColumn);
+}
+
+GList *TextPage::makeColumns() {
+ TextBlock *tree;
+ GList *overlappingChars;
+ GList *columns;
+ GBool primaryLR;
+ int rot;
+
+ if (control.mode == textOutSimple2Layout) {
+ 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(chars);
+ primaryLR = checkPrimaryLR(chars);
+ if ((tree = splitChars(chars))) {
+ columns = buildColumns(tree, primaryLR);
+ delete tree;
+ } else {
+ // no text
+ columns = new GList();
+ }
+ unrotateChars(chars, rot);
+ unrotateColumns(columns, rot);
+ if (control.html) {
+ generateUnderlinesAndLinks(columns);
+ }
+ if (overlappingChars) {
+ if (overlappingChars->getLength() > 0) {
+ columns->append(buildOverlappingTextColumn(overlappingChars));
+ }
+ deleteGList(overlappingChars, TextChar);
+ }
+ }
+ return columns;
+}
+
+// This handles both physical layout and table layout modes.
+void TextPage::writePhysLayout(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextBlock *tree;
+ GString **out;
+ int *outLen;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ GList *overlappingChars;
+ GList *columns;
+ GBool primaryLR;
+ int ph, colIdx, parIdx, lineIdx, rot, y, i;
+
+#if 0 //~debug
+ dumpChars(chars);
+#endif
+#if 0 //~debug
+ dumpUnderlines();
+#endif
+ if (control.overlapHandling != textOutIgnoreOverlaps) {
+ overlappingChars = separateOverlappingText(chars);
+ } else {
+ overlappingChars = NULL;
+ }
+ rot = rotateChars(chars);
+ primaryLR = checkPrimaryLR(chars);
+ tree = splitChars(chars);
+#if 0 //~debug
+ dumpTree(tree);
+#endif
+ if (!tree) {
+ // no text
+ unrotateChars(chars, rot);
+ return;
+ }
+ //~ this doesn't correctly handle the right-to-left case
+ columns = buildColumns(tree, gTrue);
+ delete tree;
+ unrotateChars(chars, rot);
+ if (control.html) {
+ rotateUnderlinesAndLinks(rot);
+ generateUnderlinesAndLinks(columns);
+ }
+ ph = assignPhysLayoutPositions(columns);
+#if 0 //~debug
+ dumpColumns(columns);
+#endif
+
+ out = (GString **)gmallocn(ph, sizeof(GString *));
+ outLen = (int *)gmallocn(ph, sizeof(int));
+ for (i = 0; i < ph; ++i) {
+ out[i] = NULL;
+ outLen[i] = 0;
+ }
+
+ columns->sort(&TextColumn::cmpPX);
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ y = col->py;
+ for (parIdx = 0;
+ parIdx < col->paragraphs->getLength() && y < ph;
+ ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ for (lineIdx = 0;
+ lineIdx < par->lines->getLength() && y < ph;
+ ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ if (!out[y]) {
+ out[y] = new GString();
+ }
+ while (outLen[y] < col->px + line->px) {
+ out[y]->append(space, spaceLen);
+ ++outLen[y];
+ }
+ encodeFragment(line->text, line->len, uMap, primaryLR, out[y]);
+ outLen[y] += line->pw;
+ ++y;
+ }
+ if (parIdx + 1 < col->paragraphs->getLength()) {
+ ++y;
+ }
+ }
+ }
+
+ for (i = 0; i < ph; ++i) {
+ if (out[i]) {
+ (*outputFunc)(outputStream, out[i]->getCString(), out[i]->getLength());
+ delete out[i];
+ }
+ (*outputFunc)(outputStream, eol, eolLen);
+ }
+
+ gfree(out);
+ 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,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextBlock *tree;
+ TextSuperLine *superLine0, *superLine1;
+ TextLine *line;
+ GList *superLines;
+ GString *out;
+ GBool primaryLR;
+ int rot, x, i, j;
+
+#if 0 //~debug
+ dumpChars(chars);
+#endif
+ rot = rotateChars(chars);
+ primaryLR = checkPrimaryLR(chars);
+ tree = splitChars(chars);
+#if 0 //~debug
+ dumpTree(tree);
+#endif
+ if (!tree) {
+ // no text
+ unrotateChars(chars, rot);
+ return;
+ }
+ superLines = new GList();
+ buildSuperLines(tree, superLines);
+ delete tree;
+ unrotateChars(chars, rot);
+ assignSimpleLayoutPositions(superLines, uMap);
+
+ for (i = 0; i < superLines->getLength(); ++i) {
+ superLine0 = (TextSuperLine *)superLines->get(i);
+ out = new GString();
+ x = 0;
+ for (j = 0; j < superLine0->lines->getLength(); ++j) {
+ line = (TextLine *)superLine0->lines->get(j);
+ while (x < line->px) {
+ out->append(space, spaceLen);
+ ++x;
+ }
+ encodeFragment(line->text, line->len, uMap, primaryLR, out);
+ x += line->pw;
+ }
+ (*outputFunc)(outputStream, out->getCString(), out->getLength());
+ delete out;
+ (*outputFunc)(outputStream, eol, eolLen);
+ if (i + 1 < superLines->getLength()) {
+ superLine1 = (TextSuperLine *)superLines->get(i + 1);
+ if (superLine1->yMin - superLine0->yMax > 1.0 * superLine0->fontSize) {
+ (*outputFunc)(outputStream, eol, eolLen);
+ }
+ }
+ }
+
+ 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,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextChar *ch, *ch2;
+ GList *line;
+ GString *s;
+ char buf[8];
+ double pitch, lineSpacing, delta;
+ double yMin0, yShift, xMin0, xShift;
+ double y, x;
+ int rot, n, i, j, k;
+
+ rot = rotateChars(chars);
+ chars->sort(&TextChar::cmpX);
+ // 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
+ if (control.fixedPitch > 0) {
+ pitch = control.fixedPitch;
+ } else {
+ // compute (approximate) character pitch
+ pitch = pageWidth;
+ for (i = 0; i < chars->getLength(); ++i) {
+ ch = (TextChar *)chars->get(i);
+ for (j = i + 1; j < chars->getLength(); ++j) {
+ ch2 = (TextChar *)chars->get(j);
+ if (ch2->yMin + ascentAdjustFactor * (ch2->yMax - ch2->yMin) <
+ ch->yMax - descentAdjustFactor * (ch->yMax - ch->yMin) &&
+ ch->yMin + ascentAdjustFactor * (ch->yMax - ch->yMin) <
+ ch2->yMax - descentAdjustFactor * (ch2->yMax - ch2->yMin)) {
+ delta = fabs(ch2->xMin - ch->xMin);
+ if (delta > 0.01 && delta < pitch) {
+ pitch = delta;
+ }
+ }
+ }
+ }
+ }
+
+ // get line spacing
+ if (control.fixedLineSpacing > 0) {
+ lineSpacing = control.fixedLineSpacing;
+ } else {
+ // compute (approximate) line spacing
+ lineSpacing = pageHeight;
+ i = 0;
+ while (i < chars->getLength()) {
+ ch = (TextChar *)chars->get(i);
+ // look for the first char that does not (substantially)
+ // vertically overlap this one
+ delta = 0;
+ for (++i; delta == 0 && i < chars->getLength(); ++i) {
+ ch2 = (TextChar *)chars->get(i);
+ if (ch2->yMin + ascentAdjustFactor * (ch2->yMax - ch2->yMin) >
+ ch->yMax - descentAdjustFactor * (ch->yMax - ch->yMin)) {
+ delta = ch2->yMin - ch->yMin;
+ }
+ }
+ if (delta > 0 && delta < lineSpacing) {
+ lineSpacing = delta;
+ }
+ }
+ }
+
+ // shift the grid to avoid problems with floating point accuracy --
+ // for fixed line spacing, this avoids problems with
+ // dropping/inserting blank lines
+ if (chars->getLength()) {
+ yMin0 = ((TextChar *)chars->get(0))->yMin;
+ yShift = yMin0 - (int)(yMin0 / lineSpacing + 0.5) * lineSpacing
+ - 0.5 * lineSpacing;
+ } else {
+ yShift = 0;
+ }
+
+ // for each line...
+ i = 0;
+ j = chars->getLength() - 1;
+ for (y = yShift; y < pageHeight; y += lineSpacing) {
+
+ // get the characters in this line
+ line = new GList;
+ while (i < chars->getLength() &&
+ ((TextChar *)chars->get(i))->yMin < y + lineSpacing) {
+ line->append(chars->get(i++));
+ }
+ line->sort(&TextChar::cmpX);
+
+ // shift the grid to avoid problems with floating point accuracy
+ // -- for fixed char spacing, this avoids problems with
+ // dropping/inserting spaces
+ if (line->getLength()) {
+ xMin0 = ((TextChar *)line->get(0))->xMin;
+ xShift = xMin0 - (int)(xMin0 / pitch + 0.5) * pitch - 0.5 * pitch;
+ } else {
+ xShift = 0;
+ }
+
+ // write the line
+ s = new GString();
+ x = xShift;
+ k = 0;
+ while (k < line->getLength()) {
+ ch = (TextChar *)line->get(k);
+ if (ch->xMin < x + pitch) {
+ n = uMap->mapUnicode(ch->c, buf, sizeof(buf));
+ s->append(buf, n);
+ ++k;
+ } else {
+ s->append(space, spaceLen);
+ n = spaceLen;
+ }
+ x += (uMap->isUnicode() ? 1 : n) * pitch;
+ }
+ s->append(eol, eolLen);
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ delete s;
+ delete line;
+ }
+
+ unrotateChars(chars, rot);
+}
+
+void TextPage::writeRaw(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen) {
+ TextChar *ch, *ch2;
+ GString *s;
+ char buf[8];
+ int n, i;
+
+ s = new GString();
+
+ for (i = 0; i < chars->getLength(); ++i) {
+
+ // process one char
+ ch = (TextChar *)chars->get(i);
+ n = uMap->mapUnicode(ch->c, buf, sizeof(buf));
+ s->append(buf, n);
+
+ // check for space or eol
+ if (i+1 < chars->getLength()) {
+ ch2 = (TextChar *)chars->get(i+1);
+ if (ch2->rot != ch->rot) {
+ s->append(eol, eolLen);
+ } else {
+ switch (ch->rot) {
+ case 0:
+ default:
+ if (fabs(ch2->yMin - ch->yMin) > rawModeLineDelta * ch->fontSize ||
+ ch2->xMin - ch->xMax < -rawModeCharOverlap * ch->fontSize) {
+ s->append(eol, eolLen);
+ } else if (ch->spaceAfter ||
+ ch2->xMin - ch->xMax >
+ rawModeWordSpacing * ch->fontSize) {
+ s->append(space, spaceLen);
+ }
+ break;
+ case 1:
+ if (fabs(ch->xMax - ch2->xMax) > rawModeLineDelta * ch->fontSize ||
+ ch2->yMin - ch->yMax < -rawModeCharOverlap * ch->fontSize) {
+ s->append(eol, eolLen);
+ } else if (ch->spaceAfter ||
+ ch2->yMin - ch->yMax >
+ rawModeWordSpacing * ch->fontSize) {
+ s->append(space, spaceLen);
+ }
+ break;
+ case 2:
+ if (fabs(ch->yMax - ch2->yMax) > rawModeLineDelta * ch->fontSize ||
+ ch->xMin - ch2->xMax < -rawModeCharOverlap * ch->fontSize) {
+ s->append(eol, eolLen);
+ } else if (ch->spaceAfter ||
+ ch->xMin - ch2->xMax >
+ rawModeWordSpacing * ch->fontSize) {
+ s->append(space, spaceLen);
+ }
+ break;
+ case 3:
+ if (fabs(ch2->xMin - ch->xMin) > rawModeLineDelta * ch->fontSize ||
+ ch->yMin - ch2->yMax < -rawModeCharOverlap * ch->fontSize) {
+ s->append(eol, eolLen);
+ } else if (ch->spaceAfter ||
+ ch->yMin - ch2->yMax >
+ rawModeWordSpacing * ch->fontSize) {
+ s->append(space, spaceLen);
+ }
+ break;
+ }
+ }
+ } else {
+ s->append(eol, eolLen);
+ }
+
+ if (s->getLength() > 1000) {
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ s->clear();
+ }
+ }
+
+ if (s->getLength() > 0) {
+ (*outputFunc)(outputStream, s->getCString(), s->getLength());
+ }
+ delete s;
+}
+
+void TextPage::encodeFragment(Unicode *text, int len, UnicodeMap *uMap,
+ GBool primaryLR, GString *s) {
+ char lre[8], rle[8], popdf[8], buf[8];
+ int lreLen, rleLen, popdfLen, n;
+ int i, j, k;
+
+ if (uMap->isUnicode()) {
+
+ lreLen = uMap->mapUnicode(0x202a, lre, sizeof(lre));
+ rleLen = uMap->mapUnicode(0x202b, rle, sizeof(rle));
+ popdfLen = uMap->mapUnicode(0x202c, popdf, sizeof(popdf));
+
+ if (primaryLR) {
+
+ i = 0;
+ while (i < len) {
+ // output a left-to-right section
+ for (j = i; j < len && !unicodeTypeR(text[j]); ++j) ;
+ for (k = i; k < j; ++k) {
+ n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ i = j;
+ // output a right-to-left section
+ for (j = i;
+ j < len && !(unicodeTypeL(text[j]) || unicodeTypeNum(text[j]));
+ ++j) ;
+ if (j > i) {
+ s->append(rle, rleLen);
+ for (k = j - 1; k >= i; --k) {
+ n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ s->append(popdf, popdfLen);
+ i = j;
+ }
+ }
+
+ } else {
+
+ // Note: This code treats numeric characters (European and
+ // Arabic/Indic) as left-to-right, which isn't strictly correct
+ // (incurs extra LRE/POPDF pairs), but does produce correct
+ // visual formatting.
+ s->append(rle, rleLen);
+ i = len - 1;
+ while (i >= 0) {
+ // output a right-to-left section
+ for (j = i;
+ j >= 0 && !(unicodeTypeL(text[j]) || unicodeTypeNum(text[j]));
+ --j) ;
+ for (k = i; k > j; --k) {
+ n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ i = j;
+ // output a left-to-right section
+ for (j = i; j >= 0 && !unicodeTypeR(text[j]); --j) ;
+ if (j < i) {
+ s->append(lre, lreLen);
+ for (k = j + 1; k <= i; ++k) {
+ n = uMap->mapUnicode(text[k], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ s->append(popdf, popdfLen);
+ i = j;
+ }
+ }
+ s->append(popdf, popdfLen);
+ }
+
+ } else {
+ for (i = 0; i < len; ++i) {
+ n = uMap->mapUnicode(text[i], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// TextPage: layout analysis
+//------------------------------------------------------------------------
+
+// Determine primary (most common) rotation value. Rotate all chars
+// to that primary rotation.
+int TextPage::rotateChars(GList *charsA) {
+ TextChar *ch;
+ int nChars[4];
+ double xMin, yMin, xMax, yMax, t;
+ int rot, i;
+
+ // determine primary rotation
+ nChars[0] = nChars[1] = nChars[2] = nChars[3] = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ ++nChars[ch->rot];
+ }
+ rot = 0;
+ for (i = 1; i < 4; ++i) {
+ if (nChars[i] > nChars[rot]) {
+ rot = i;
+ }
+ }
+
+ // rotate
+ switch (rot) {
+ case 0:
+ default:
+ break;
+ case 1:
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ 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;
+ ch->rot = (ch->rot + 3) & 3;
+ }
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ break;
+ case 2:
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ 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;
+ ch->rot = (ch->rot + 2) & 3;
+ }
+ break;
+ case 3:
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ 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;
+ ch->rot = (ch->rot + 1) & 3;
+ }
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ break;
+ }
+
+ 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) {
+ TextUnderline *underline;
+ TextLink *link;
+ double xMin, yMin, xMax, yMax;
+ int i;
+
+ switch (rot) {
+ case 0:
+ default:
+ break;
+ case 1:
+ for (i = 0; i < underlines->getLength(); ++i) {
+ underline = (TextUnderline *)underlines->get(i);
+ xMin = underline->y0;
+ xMax = underline->y1;
+ yMin = pageWidth - underline->x1;
+ yMax = pageWidth - underline->x0;
+ underline->x0 = xMin;
+ underline->x1 = xMax;
+ underline->y0 = yMin;
+ underline->y1 = yMax;
+ underline->horiz = !underline->horiz;
+ }
+ for (i = 0; i < links->getLength(); ++i) {
+ link = (TextLink *)links->get(i);
+ xMin = link->yMin;
+ xMax = link->yMax;
+ yMin = pageWidth - link->xMax;
+ yMax = pageWidth - link->xMin;
+ link->xMin = xMin;
+ link->xMax = xMax;
+ link->yMin = yMin;
+ link->yMax = yMax;
+ }
+ break;
+ case 2:
+ for (i = 0; i < underlines->getLength(); ++i) {
+ underline = (TextUnderline *)underlines->get(i);
+ xMin = pageWidth - underline->x1;
+ xMax = pageWidth - underline->x0;
+ yMin = pageHeight - underline->y1;
+ yMax = pageHeight - underline->y0;
+ underline->x0 = xMin;
+ underline->x1 = xMax;
+ underline->y0 = yMin;
+ underline->y1 = yMax;
+ }
+ for (i = 0; i < links->getLength(); ++i) {
+ link = (TextLink *)links->get(i);
+ xMin = pageWidth - link->xMax;
+ xMax = pageWidth - link->xMin;
+ yMin = pageHeight - link->yMax;
+ yMax = pageHeight - link->yMin;
+ link->xMin = xMin;
+ link->xMax = xMax;
+ link->yMin = yMin;
+ link->yMax = yMax;
+ }
+ break;
+ case 3:
+ for (i = 0; i < underlines->getLength(); ++i) {
+ underline = (TextUnderline *)underlines->get(i);
+ xMin = pageHeight - underline->y1;
+ xMax = pageHeight - underline->y0;
+ yMin = underline->x0;
+ yMax = underline->x1;
+ underline->x0 = xMin;
+ underline->x1 = xMax;
+ underline->y0 = yMin;
+ underline->y1 = yMax;
+ underline->horiz = !underline->horiz;
+ }
+ for (i = 0; i < links->getLength(); ++i) {
+ link = (TextLink *)links->get(i);
+ xMin = pageHeight - link->yMax;
+ xMax = pageHeight - link->yMin;
+ yMin = link->xMin;
+ yMax = link->xMax;
+ link->xMin = xMin;
+ link->xMax = xMax;
+ link->yMin = yMin;
+ link->yMax = yMax;
+ }
+ break;
+ }
+}
+
+// Undo the coordinate transform performed by rotateChars().
+void TextPage::unrotateChars(GList *charsA, int rot) {
+ TextChar *ch;
+ double xMin, yMin, xMax, yMax, t;
+ int i;
+
+ switch (rot) {
+ case 0:
+ default:
+ // no transform
+ break;
+ case 1:
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ 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;
+ ch->rot = (ch->rot + 1) & 3;
+ }
+ break;
+ case 2:
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ 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;
+ ch->rot = (ch->rot + 2) & 3;
+ }
+ break;
+ case 3:
+ t = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = t;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ 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;
+ ch->rot = (ch->rot + 3) & 3;
+ }
+ break;
+ }
+}
+
+// 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;
+ TextParagraph *par;
+ TextLine *line;
+ TextWord *word;
+ double xMin, yMin, xMax, yMax;
+ int colIdx, parIdx, lineIdx, wordIdx, i;
+
+ switch (rot) {
+ case 0:
+ default:
+ // no transform
+ break;
+ case 1:
+ // NB: this is called after unrotateChars(), which will have
+ // swapped pageWidth and pageHeight already.
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ 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;
+ line->rot = (line->rot + 1) & 3;
+ if (!(line->rot & 1)) {
+ 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->yMax;
+ xMax = pageWidth - word->yMin;
+ yMin = word->xMin;
+ yMax = word->xMax;
+ word->xMin = xMin;
+ word->xMax = xMax;
+ word->yMin = yMin;
+ word->yMax = yMax;
+ word->rot = (word->rot + 1) & 3;
+ if (!(word->rot & 1)) {
+ for (i = 0; i <= word->len; ++i) {
+ word->edge[i] = pageWidth - word->edge[i];
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 2:
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ 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;
+ line->rot = (line->rot + 2) & 3;
+ if (line->rot & 1) {
+ for (i = 0; i <= line->len; ++i) {
+ line->edge[i] = pageHeight - line->edge[i];
+ }
+ } else {
+ 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;
+ word->rot = (word->rot + 2) & 3;
+ if (word->rot & 1) {
+ for (i = 0; i <= word->len; ++i) {
+ word->edge[i] = pageHeight - word->edge[i];
+ }
+ } else {
+ for (i = 0; i <= word->len; ++i) {
+ word->edge[i] = pageWidth - word->edge[i];
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 3:
+ // NB: this is called after unrotateChars(), which will have
+ // swapped pageWidth and pageHeight already.
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ 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;
+ line->rot = (line->rot + 3) & 3;
+ if (line->rot & 1) {
+ 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;
+ word->rot = (word->rot + 3) & 3;
+ if (word->rot & 1) {
+ for (i = 0; i <= word->len; ++i) {
+ word->edge[i] = pageHeight - word->edge[i];
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+}
+
+void TextPage::unrotateWords(GList *words, int rot) {
+ TextWord *word;
+ double xMin, yMin, xMax, yMax;
+ int i, j;
+
+ switch (rot) {
+ case 0:
+ default:
+ // no transform
+ break;
+ case 1:
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ 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;
+ word->rot = (word->rot + 1) & 3;
+ if (!(word->rot & 1)) {
+ for (j = 0; j <= word->len; ++j) {
+ word->edge[j] = pageWidth - word->edge[j];
+ }
+ }
+ }
+ break;
+ case 2:
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ 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;
+ word->rot = (word->rot + 2) & 3;
+ if (word->rot & 1) {
+ for (j = 0; j <= word->len; ++j) {
+ word->edge[j] = pageHeight - word->edge[j];
+ }
+ } else {
+ for (j = 0; j <= word->len; ++j) {
+ word->edge[j] = pageWidth - word->edge[j];
+ }
+ }
+ }
+ break;
+ case 3:
+ for (i = 0; i < words->getLength(); ++i) {
+ word = (TextWord *)words->get(i);
+ 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;
+ word->rot = (word->rot + 3) & 3;
+ if (word->rot & 1) {
+ for (j = 0; j <= word->len; ++j) {
+ word->edge[j] = pageHeight - word->edge[j];
+ }
+ }
+ }
+ break;
+ }
+}
+
+// Determine the primary text direction (LR vs RL). Returns true for
+// LR, false for RL.
+GBool TextPage::checkPrimaryLR(GList *charsA) {
+ TextChar *ch;
+ int i, lrCount;
+
+ lrCount = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (unicodeTypeL(ch->c)) {
+ ++lrCount;
+ } else if (unicodeTypeR(ch->c)) {
+ --lrCount;
+ }
+ }
+ return lrCount >= 0;
+}
+
+// Remove duplicate characters. The list of chars has been sorted --
+// by x for rot=0,2; by y for rot=1,3.
+void TextPage::removeDuplicates(GList *charsA, int rot) {
+ TextChar *ch, *ch2;
+ double xDelta, yDelta;
+ int i, j;
+
+ if (rot & 1) {
+ i = 0;
+ while (i < charsA->getLength()) {
+ ch = (TextChar *)charsA->get(i);
+ xDelta = dupMaxSecDelta * ch->fontSize;
+ yDelta = dupMaxPriDelta * ch->fontSize;
+ j = i + 1;
+ while (j < charsA->getLength()) {
+ ch2 = (TextChar *)charsA->get(j);
+ if (ch2->yMin - ch->yMin >= yDelta) {
+ break;
+ }
+ if (ch2->c == ch->c &&
+ 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;
+ }
+ charsA->del(j);
+ } else {
+ ++j;
+ }
+ }
+ ++i;
+ }
+ } else {
+ i = 0;
+ while (i < charsA->getLength()) {
+ ch = (TextChar *)charsA->get(i);
+ xDelta = dupMaxPriDelta * ch->fontSize;
+ yDelta = dupMaxSecDelta * ch->fontSize;
+ j = i + 1;
+ while (j < charsA->getLength()) {
+ ch2 = (TextChar *)charsA->get(j);
+ if (ch2->xMin - ch->xMin >= xDelta) {
+ break;
+ }
+ if (ch2->c == ch->c &&
+ 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) {
+ TextBlock *tree[4];
+ TextBlock *blk;
+ GList *chars2, *clippedChars;
+ TextChar *ch;
+ int rot, i;
+
+ // split: build a tree of TextBlocks for each rotation
+ clippedChars = new GList();
+ for (rot = 0; rot < 4; ++rot) {
+ chars2 = new GList();
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (ch->rot == rot &&
+ !(control.discardInvisibleText && ch->invisible) &&
+ !(control.discardClippedText && ch->clipped)) {
+ chars2->append(ch);
+ }
+ }
+ tree[rot] = NULL;
+ if (chars2->getLength() > 0) {
+ chars2->sort((rot & 1) ? &TextChar::cmpY : &TextChar::cmpX);
+ removeDuplicates(chars2, rot);
+ if (control.clipText) {
+ i = 0;
+ while (i < chars2->getLength()) {
+ ch = (TextChar *)chars2->get(i);
+ if (ch->clipped) {
+ ch = (TextChar *)chars2->del(i);
+ clippedChars->append(ch);
+ } else {
+ ++i;
+ }
+ }
+ }
+ if (chars2->getLength() > 0) {
+ tree[rot] = split(chars2, rot);
+ }
+ }
+ delete chars2;
+ }
+
+ // if the page contains no (unclipped) text, just leave an empty
+ // column list
+ if (!tree[0]) {
+ // normally tree[0] is empty only if there is no text at all, but
+ // if the caller didn't do rotation, the rotated trees may be
+ // non-empty, so we need to free them
+ for (rot = 1; rot < 4; ++rot) {
+ if (tree[rot]) {
+ delete tree[rot];
+ }
+ }
+ delete clippedChars;
+ return NULL;
+ }
+
+ // if the main tree is not a multicolumn node, insert one so that
+ // rotated text has somewhere to go
+ if (tree[0]->tag != blkTagMulticolumn) {
+ blk = new TextBlock(blkHorizSplit, 0);
+ blk->addChild(tree[0]);
+ blk->tag = blkTagMulticolumn;
+ tree[0] = blk;
+ }
+
+ // merge non-primary-rotation text into the primary-rotation tree
+ for (rot = 1; rot < 4; ++rot) {
+ if (tree[rot]) {
+ insertIntoTree(tree[rot], tree[0]);
+ tree[rot] = NULL;
+ }
+ }
+
+ if (clippedChars->getLength()) {
+ insertClippedChars(clippedChars, tree[0]);
+ }
+ delete clippedChars;
+
+#if 0 //~debug
+ dumpTree(tree[0]);
+#endif
+
+ return tree[0];
+}
+
+// Generate a tree of TextBlocks, marked as columns, lines, and words.
+TextBlock *TextPage::split(GList *charsA, int rot) {
+ TextBlock *blk;
+ GList *chars2, *chars3;
+ TextGaps *horizGaps, *vertGaps;
+ TextChar *ch;
+ double xMin, yMin, xMax, yMax, avgFontSize;
+ double horizGapSize, vertGapSize, minHorizChunkWidth, minVertChunkWidth;
+ double gap, nLines, vertGapThreshold, minChunk;
+ double largeCharSize;
+ double x0, x1, y0, y1;
+ int nHorizGaps, nVertGaps, nLargeChars;
+ int i;
+ GBool doHorizSplit, doVertSplit, smallSplit;
+
+ //----- find all horizontal and vertical gaps
+
+ horizGaps = new TextGaps();
+ vertGaps = new TextGaps();
+ findGaps(charsA, rot, &xMin, &yMin, &xMax, &yMax, &avgFontSize,
+ horizGaps, vertGaps);
+
+ //----- find the largest horizontal and vertical gaps
+
+ horizGapSize = 0;
+ for (i = 0; i < horizGaps->getLength(); ++i) {
+ gap = horizGaps->getW(i);
+ if (gap > horizGapSize) {
+ horizGapSize = gap;
+ }
+ }
+ vertGapSize = 0;
+ for (i = 0; i < vertGaps->getLength(); ++i) {
+ gap = vertGaps->getW(i);
+ if (gap > vertGapSize) {
+ vertGapSize = gap;
+ }
+ }
+
+ //----- count horiz/vert gaps equivalent to largest gaps
+
+ minHorizChunkWidth = yMax - yMin;
+ nHorizGaps = 0;
+ if (horizGaps->getLength() > 0) {
+ y0 = yMin;
+ for (i = 0; i < horizGaps->getLength(); ++i) {
+ gap = horizGaps->getW(i);
+ if (gap > horizGapSize - splitGapSlack * avgFontSize) {
+ ++nHorizGaps;
+ y1 = horizGaps->getX(i) - 0.5 * gap;
+ if (y1 - y0 < minHorizChunkWidth) {
+ minHorizChunkWidth = y1 - y0;
+ }
+ y0 = y1 + gap;
+ }
+ }
+ y1 = yMax;
+ if (y1 - y0 < minHorizChunkWidth) {
+ minHorizChunkWidth = y1 - y0;
+ }
+ }
+ minVertChunkWidth = xMax - xMin;
+ nVertGaps = 0;
+ if (vertGaps->getLength() > 0) {
+ x0 = xMin;
+ for (i = 0; i < vertGaps->getLength(); ++i) {
+ gap = vertGaps->getW(i);
+ if (gap > vertGapSize - splitGapSlack * avgFontSize) {
+ ++nVertGaps;
+ x1 = vertGaps->getX(i) - 0.5 * gap;
+ if (x1 - x0 < minVertChunkWidth) {
+ minVertChunkWidth = x1 - x0;
+ }
+ x0 = x1 + gap;
+ }
+ }
+ x1 = xMax;
+ if (x1 - x0 < minVertChunkWidth) {
+ minVertChunkWidth = x1 - x0;
+ }
+ }
+
+ //----- compute splitting parameters
+
+ // approximation of number of lines in block
+ if (fabs(avgFontSize) < 0.001) {
+ nLines = 1;
+ } else if (rot & 1) {
+ nLines = (xMax - xMin) / avgFontSize;
+ } else {
+ nLines = (yMax - yMin) / avgFontSize;
+ }
+
+ // compute the minimum allowed vertical gap size
+ // (this is a horizontal gap threshold for rot=1,3
+ if (control.mode == textOutTableLayout) {
+ vertGapThreshold = vertGapThresholdTableMax
+ + vertGapThresholdTableSlope * nLines;
+ if (vertGapThreshold < vertGapThresholdTableMin) {
+ vertGapThreshold = vertGapThresholdTableMin;
+ }
+ } else if (control.mode == textOutSimpleLayout) {
+ vertGapThreshold = simpleLayoutGapThreshold;
+ } else {
+ vertGapThreshold = vertGapThresholdMax + vertGapThresholdSlope * nLines;
+ if (vertGapThreshold < vertGapThresholdMin) {
+ vertGapThreshold = vertGapThresholdMin;
+ }
+ }
+ vertGapThreshold = vertGapThreshold * avgFontSize;
+
+ // compute the minimum allowed chunk width
+ if (control.mode == textOutTableLayout) {
+ minChunk = 0;
+ } else {
+ minChunk = vertSplitChunkThreshold * avgFontSize;
+ }
+
+ // look for large chars
+ // -- this kludge (multiply by 256, convert to int, divide by 256.0)
+ // prevents floating point stability issues on x86 with gcc, where
+ // 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)
+ nLargeChars = 0;
+ 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;
+ }
+ }
+ }
+
+ // figure out which type of split to do
+ doHorizSplit = doVertSplit = gFalse;
+ smallSplit = gFalse;
+ if (rot & 1) {
+ if (control.mode == textOutSimpleLayout) {
+ if (nVertGaps > 0) {
+ doVertSplit = gTrue;
+ } else if (nHorizGaps > 0) {
+ doHorizSplit = gTrue;
+ smallSplit = horizGapSize <= vertGapThreshold;
+ }
+ } else if (nHorizGaps > 0 &&
+ (horizGapSize > vertGapSize ||
+ control.mode == textOutTableLayout) &&
+ horizGapSize > vertGapThreshold &&
+ (minHorizChunkWidth > minChunk ||
+ nVertGaps == 0)) {
+ doHorizSplit = gTrue;
+ } else if (nVertGaps > 0) {
+ doVertSplit = gTrue;
+ } else if (nLargeChars == 0 && nHorizGaps > 0) {
+ doHorizSplit = gTrue;
+ smallSplit = gTrue;
+ }
+ } else {
+ if (control.mode == textOutSimpleLayout) {
+ if (nHorizGaps > 0) {
+ doHorizSplit = gTrue;
+ } else if (nVertGaps > 0) {
+ doVertSplit = gTrue;
+ smallSplit = vertGapSize <= vertGapThreshold;
+ }
+ } else if (nVertGaps > 0 &&
+ (vertGapSize > horizGapSize ||
+ control.mode == textOutTableLayout) &&
+ vertGapSize > vertGapThreshold &&
+ (minVertChunkWidth > minChunk ||
+ nHorizGaps == 0)) {
+ doVertSplit = gTrue;
+ } else if (nHorizGaps > 0) {
+ doHorizSplit = gTrue;
+ } else if (nLargeChars == 0 && nVertGaps > 0) {
+ doVertSplit = gTrue;
+ smallSplit = gTrue;
+ }
+ }
+
+ //----- split the block
+
+ //~ this could use "other content" (vector graphics, rotated text) --
+ //~ presence of other content in a gap means we should definitely split
+
+ // split vertically
+ if (doVertSplit) {
+#if 0 //~debug
+ printf("vert split xMin=%g yMin=%g xMax=%g yMax=%g small=%d\n",
+ xMin, pageHeight - yMax, xMax, pageHeight - yMin, smallSplit);
+ for (i = 0; i < vertGaps->getLength(); ++i) {
+ if (vertGaps->getW(i) > vertGapSize - splitGapSlack * avgFontSize) {
+ printf(" x=%g\n", vertGaps->getX(i));
+ }
+ }
+#endif
+ blk = new TextBlock(blkVertSplit, rot);
+ blk->smallSplit = smallSplit;
+ x0 = xMin - 1;
+ for (i = 0; i < vertGaps->getLength(); ++i) {
+ if (vertGaps->getW(i) > vertGapSize - splitGapSlack * avgFontSize) {
+ x1 = vertGaps->getX(i);
+ chars2 = getChars(charsA, x0, yMin - 1, x1, yMax + 1);
+ blk->addChild(split(chars2, rot));
+ delete chars2;
+ x0 = x1;
+ }
+ }
+ chars2 = getChars(charsA, x0, yMin - 1, xMax + 1, yMax + 1);
+ blk->addChild(split(chars2, rot));
+ delete chars2;
+
+ // split horizontally
+ } else if (doHorizSplit) {
+#if 0 //~debug
+ printf("horiz split xMin=%g yMin=%g xMax=%g yMax=%g small=%d\n",
+ xMin, pageHeight - yMax, xMax, pageHeight - yMin, smallSplit);
+ for (i = 0; i < horizGaps->getLength(); ++i) {
+ if (horizGaps->getW(i) > horizGapSize - splitGapSlack * avgFontSize) {
+ printf(" y=%g\n", pageHeight - horizGaps->getX(i));
+ }
+ }
+#endif
+ blk = new TextBlock(blkHorizSplit, rot);
+ blk->smallSplit = smallSplit;
+ y0 = yMin - 1;
+ for (i = 0; i < horizGaps->getLength(); ++i) {
+ if (horizGaps->getW(i) > horizGapSize - splitGapSlack * avgFontSize) {
+ y1 = horizGaps->getX(i);
+ chars2 = getChars(charsA, xMin - 1, y0, xMax + 1, y1);
+ blk->addChild(split(chars2, rot));
+ delete chars2;
+ y0 = y1;
+ }
+ }
+ chars2 = getChars(charsA, xMin - 1, y0, xMax + 1, yMax + 1);
+ blk->addChild(split(chars2, rot));
+ delete chars2;
+
+ // split into larger and smaller chars
+ } else if (nLargeChars > 0) {
+#if 0 //~debug
+ printf("large char split xMin=%g yMin=%g xMax=%g yMax=%g\n",
+ xMin, pageHeight - yMax, xMax, pageHeight - yMin);
+#endif
+ chars2 = new GList();
+ chars3 = new GList();
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (ch->fontSize > largeCharSize) {
+ chars2->append(ch);
+ } else {
+ chars3->append(ch);
+ }
+ }
+ blk = split(chars3, rot);
+ insertLargeChars(chars2, blk);
+ delete chars2;
+ delete chars3;
+
+ // create a leaf node
+ } else {
+#if 0 //~debug
+ printf("leaf xMin=%g yMin=%g xMax=%g yMax=%g\n",
+ xMin, pageHeight - yMax, xMax, pageHeight - yMin);
+#endif
+ blk = new TextBlock(blkLeaf, rot);
+ for (i = 0; i < charsA->getLength(); ++i) {
+ blk->addChild((TextChar *)charsA->get(i), gTrue);
+ }
+ }
+
+ delete horizGaps;
+ delete vertGaps;
+
+ tagBlock(blk);
+
+ return blk;
+}
+
+// Return the subset of chars inside a rectangle.
+GList *TextPage::getChars(GList *charsA, double xMin, double yMin,
+ double xMax, double yMax) {
+ GList *ret;
+ TextChar *ch;
+ double x, y;
+ int i;
+
+ ret = new GList();
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ // because of {ascent,descent}AdjustFactor, the y coords (or x
+ // coords for rot 1,3) for the gaps will be a little bit tight --
+ // so we use the center of the character here
+ x = 0.5 * (ch->xMin + ch->xMax);
+ y = 0.5 * (ch->yMin + ch->yMax);
+ if (x > xMin && x < xMax && y > yMin && y < yMax) {
+ ret->append(ch);
+ }
+ }
+ return ret;
+}
+
+void TextPage::findGaps(GList *charsA, int rot,
+ double *xMinOut, double *yMinOut,
+ double *xMaxOut, double *yMaxOut,
+ double *avgFontSizeOut,
+ TextGaps *horizGaps, TextGaps *vertGaps) {
+ TextChar *ch;
+ char *horizProfile, *vertProfile;
+ double xMin, yMin, xMax, yMax, w;
+ double minFontSize, avgFontSize, splitPrecision, invSplitPrecision;
+ double ascentAdjust, descentAdjust;
+ int xMinI, yMinI, xMaxI, yMaxI, xMinI2, yMinI2, xMaxI2, yMaxI2;
+ int start, x, y, i;
+
+ //----- compute bbox, min font size, average font size, and split precision
+
+ xMin = yMin = xMax = yMax = 0; // make gcc happy
+ minFontSize = avgFontSize = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ if (i == 0 || ch->xMin < xMin) {
+ xMin = ch->xMin;
+ }
+ if (i == 0 || ch->yMin < yMin) {
+ yMin = ch->yMin;
+ }
+ if (i == 0 || ch->xMax > xMax) {
+ xMax = ch->xMax;
+ }
+ if (i == 0 || ch->yMax > yMax) {
+ yMax = ch->yMax;
+ }
+ avgFontSize += ch->fontSize;
+ if (i == 0 || ch->fontSize < minFontSize) {
+ minFontSize = ch->fontSize;
+ }
+ }
+ avgFontSize /= charsA->getLength();
+ splitPrecision = splitPrecisionMul * minFontSize;
+ if (splitPrecision < minSplitPrecision) {
+ splitPrecision = minSplitPrecision;
+ }
+ invSplitPrecision = 1 / splitPrecision;
+ *xMinOut = xMin;
+ *yMinOut = yMin;
+ *xMaxOut = xMax;
+ *yMaxOut = yMax;
+ *avgFontSizeOut = avgFontSize;
+
+ //----- compute the horizontal and vertical profiles
+
+ if (xMin * invSplitPrecision < 0.5 * INT_MIN ||
+ xMax * invSplitPrecision > 0.5 * INT_MAX ||
+ yMin * invSplitPrecision < 0.5 * INT_MIN ||
+ yMax * invSplitPrecision > 0.5 * INT_MAX) {
+ return;
+ }
+ // add some slack to the array bounds to avoid floating point
+ // precision problems
+ xMinI = (int)floor(xMin * invSplitPrecision) - 1;
+ yMinI = (int)floor(yMin * invSplitPrecision) - 1;
+ xMaxI = (int)floor(xMax * invSplitPrecision) + 1;
+ yMaxI = (int)floor(yMax * invSplitPrecision) + 1;
+ horizProfile = (char *)gmalloc(yMaxI - yMinI + 1);
+ vertProfile = (char *)gmalloc(xMaxI - xMinI + 1);
+ memset(horizProfile, 0, yMaxI - yMinI + 1);
+ memset(vertProfile, 0, xMaxI - xMinI + 1);
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ // yMinI2 and yMaxI2 are adjusted to allow for slightly overlapping lines
+ switch (rot) {
+ case 0:
+ default:
+ xMinI2 = (int)floor(ch->xMin * invSplitPrecision);
+ xMaxI2 = (int)floor(ch->xMax * invSplitPrecision);
+ ascentAdjust = ascentAdjustFactor * (ch->yMax - ch->yMin);
+ yMinI2 = (int)floor((ch->yMin + ascentAdjust) * invSplitPrecision);
+ descentAdjust = descentAdjustFactor * (ch->yMax - ch->yMin);
+ yMaxI2 = (int)floor((ch->yMax - descentAdjust) * invSplitPrecision);
+ break;
+ case 1:
+ descentAdjust = descentAdjustFactor * (ch->xMax - ch->xMin);
+ xMinI2 = (int)floor((ch->xMin + descentAdjust) * invSplitPrecision);
+ ascentAdjust = ascentAdjustFactor * (ch->xMax - ch->xMin);
+ xMaxI2 = (int)floor((ch->xMax - ascentAdjust) * invSplitPrecision);
+ yMinI2 = (int)floor(ch->yMin * invSplitPrecision);
+ yMaxI2 = (int)floor(ch->yMax * invSplitPrecision);
+ break;
+ case 2:
+ xMinI2 = (int)floor(ch->xMin * invSplitPrecision);
+ xMaxI2 = (int)floor(ch->xMax * invSplitPrecision);
+ descentAdjust = descentAdjustFactor * (ch->yMax - ch->yMin);
+ yMinI2 = (int)floor((ch->yMin + descentAdjust) * invSplitPrecision);
+ ascentAdjust = ascentAdjustFactor * (ch->yMax - ch->yMin);
+ yMaxI2 = (int)floor((ch->yMax - ascentAdjust) * invSplitPrecision);
+ break;
+ case 3:
+ ascentAdjust = ascentAdjustFactor * (ch->xMax - ch->xMin);
+ xMinI2 = (int)floor((ch->xMin + ascentAdjust) * invSplitPrecision);
+ descentAdjust = descentAdjustFactor * (ch->xMax - ch->xMin);
+ xMaxI2 = (int)floor((ch->xMax - descentAdjust) * invSplitPrecision);
+ yMinI2 = (int)floor(ch->yMin * invSplitPrecision);
+ yMaxI2 = (int)floor(ch->yMax * invSplitPrecision);
+ break;
+ }
+ for (y = yMinI2; y <= yMaxI2; ++y) {
+ horizProfile[y - yMinI] = 1;
+ }
+ for (x = xMinI2; x <= xMaxI2; ++x) {
+ vertProfile[x - xMinI] = 1;
+ }
+ }
+
+ //----- build the list of horizontal gaps
+
+ for (start = yMinI; start < yMaxI && !horizProfile[start - yMinI]; ++start) ;
+ for (y = start; y < yMaxI; ++y) {
+ if (horizProfile[y - yMinI]) {
+ if (!horizProfile[y + 1 - yMinI]) {
+ start = y;
+ }
+ } else {
+ if (horizProfile[y + 1 - yMinI]) {
+ w = (y - start) * splitPrecision;
+ horizGaps->addGap((start + 1) * splitPrecision + 0.5 * w, w);
+ }
+ }
+ }
+
+ //----- build the list of vertical gaps
+
+ for (start = xMinI; start < xMaxI && !vertProfile[start - xMinI]; ++start) ;
+ for (x = start; x < xMaxI; ++x) {
+ if (vertProfile[x - xMinI]) {
+ if (!vertProfile[x + 1 - xMinI]) {
+ start = x;
+ }
+ } else {
+ if (vertProfile[x + 1 - xMinI]) {
+ w = (x - start) * splitPrecision;
+ vertGaps->addGap((start + 1) * splitPrecision + 0.5 * w, w);
+ }
+ }
+ }
+
+ gfree(horizProfile);
+ gfree(vertProfile);
+}
+
+// Decide whether this block is a line, column, or multiple columns:
+// - all leaf nodes are lines
+// - horiz split nodes whose children are lines or columns are columns
+// - other horiz split nodes are multiple columns
+// - vert split nodes, with small gaps, whose children are lines are lines
+// - other vert split nodes are multiple columns
+// (for rot=1,3: the horiz and vert splits are swapped)
+// In table layout mode:
+// - all leaf nodes are lines
+// - vert split nodes, with small gaps, whose children are lines are lines
+// - everything else is multiple columns
+// In simple layout mode:
+// - all leaf nodes are lines
+// - vert split nodes with small gaps are lines
+// - vert split nodes with large gaps are super-lines
+// - horiz split nodes are columns
+void TextPage::tagBlock(TextBlock *blk) {
+ TextBlock *child;
+ int i;
+
+ if (control.mode == textOutTableLayout) {
+ if (blk->type == blkLeaf) {
+ blk->tag = blkTagLine;
+ } else if (blk->type == ((blk->rot & 1) ? blkHorizSplit : blkVertSplit) &&
+ blk->smallSplit) {
+ blk->tag = blkTagLine;
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ child = (TextBlock *)blk->children->get(i);
+ if (child->tag != blkTagLine) {
+ blk->tag = blkTagMulticolumn;
+ break;
+ }
+ }
+ } else {
+ blk->tag = blkTagMulticolumn;
+ }
+ return;
+ }
+
+ if (control.mode == textOutSimpleLayout) {
+ if (blk->type == blkLeaf) {
+ blk->tag = blkTagLine;
+ } else if (blk->type == ((blk->rot & 1) ? blkHorizSplit : blkVertSplit)) {
+ blk->tag = blk->smallSplit ? blkTagLine : blkTagSuperLine;
+ } else {
+ blk->tag = blkTagColumn;
+ }
+ return;
+ }
+
+ if (blk->type == blkLeaf) {
+ blk->tag = blkTagLine;
+
+ } else {
+ if (blk->type == ((blk->rot & 1) ? blkVertSplit : blkHorizSplit)) {
+ blk->tag = blkTagColumn;
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ child = (TextBlock *)blk->children->get(i);
+ if (child->tag != blkTagColumn && child->tag != blkTagLine) {
+ blk->tag = blkTagMulticolumn;
+ break;
+ }
+ }
+ } else {
+ if (blk->smallSplit) {
+ blk->tag = blkTagLine;
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ child = (TextBlock *)blk->children->get(i);
+ if (child->tag != blkTagLine) {
+ blk->tag = blkTagMulticolumn;
+ break;
+ }
+ }
+ } else {
+ blk->tag = blkTagMulticolumn;
+ }
+ }
+ }
+}
+
+// Insert a list of large characters into a tree.
+void TextPage::insertLargeChars(GList *largeChars, TextBlock *blk) {
+ TextChar *ch, *ch2;
+ GBool singleLine;
+ double minOverlap;
+ int i;
+
+ //~ this currently works only for characters in the primary rotation
+
+ // check to see if the large chars are a single line
+ singleLine = gTrue;
+ for (i = 1; i < largeChars->getLength(); ++i) {
+ ch = (TextChar *)largeChars->get(i-1);
+ ch2 = (TextChar *)largeChars->get(i);
+ minOverlap = 0.5 * (ch->fontSize < ch2->fontSize ? ch->fontSize
+ : ch2->fontSize);
+ if (ch->yMax - ch2->yMin < minOverlap ||
+ ch2->yMax - ch->yMin < minOverlap) {
+ singleLine = gFalse;
+ break;
+ }
+ }
+
+ if (singleLine) {
+ // if the large chars are a single line, prepend them to the first
+ // leaf node in blk
+ insertLargeCharsInFirstLeaf(largeChars, blk);
+ } else {
+ // if the large chars are not a single line, prepend each one to
+ // the appropriate leaf node -- this handles cases like bullets
+ // drawn in a large font, on the left edge of a column
+ for (i = largeChars->getLength() - 1; i >= 0; --i) {
+ ch = (TextChar *)largeChars->get(i);
+ insertLargeCharInLeaf(ch, blk);
+ }
+ }
+}
+
+// Find the first leaf (in depth-first order) in blk, and prepend a
+// list of large chars.
+void TextPage::insertLargeCharsInFirstLeaf(GList *largeChars, TextBlock *blk) {
+ TextChar *ch;
+ int i;
+
+ if (blk->type == blkLeaf) {
+ for (i = largeChars->getLength() - 1; i >= 0; --i) {
+ ch = (TextChar *)largeChars->get(i);
+ blk->prependChild(ch);
+ }
+ } else {
+ insertLargeCharsInFirstLeaf(largeChars, (TextBlock *)blk->children->get(0));
+ blk->updateBounds(0);
+ }
+}
+
+// Find the leaf in <blk> where large char <ch> belongs, and prepend
+// it.
+void TextPage::insertLargeCharInLeaf(TextChar *ch, TextBlock *blk) {
+ TextBlock *child;
+ double y;
+ int i;
+
+ //~ this currently works only for characters in the primary rotation
+
+ //~ this currently just looks down the left edge of blk
+ //~ -- it could be extended to do more
+
+ // estimate the baseline of ch
+ y = ch->yMin + 0.75 * (ch->yMax - ch->yMin);
+
+ if (blk->type == blkLeaf) {
+ blk->prependChild(ch);
+ } else if (blk->type == blkHorizSplit) {
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ child = (TextBlock *)blk->children->get(i);
+ if (y < child->yMax || i == blk->children->getLength() - 1) {
+ insertLargeCharInLeaf(ch, child);
+ blk->updateBounds(i);
+ break;
+ }
+ }
+ } else {
+ insertLargeCharInLeaf(ch, (TextBlock *)blk->children->get(0));
+ blk->updateBounds(0);
+ }
+}
+
+// Merge blk (rot != 0) into primaryTree (rot == 0).
+void TextPage::insertIntoTree(TextBlock *blk, TextBlock *primaryTree) {
+ TextBlock *child;
+
+ // we insert a whole column at a time - so call insertIntoTree
+ // recursively until we get to a column (or line)
+
+ if (blk->tag == blkTagMulticolumn) {
+ while (blk->children->getLength()) {
+ child = (TextBlock *)blk->children->del(0);
+ insertIntoTree(child, primaryTree);
+ }
+ delete blk;
+ } else {
+ insertColumnIntoTree(blk, primaryTree);
+ }
+}
+
+// Insert a column (as an atomic subtree) into tree.
+// Requirement: tree is not a leaf node.
+void TextPage::insertColumnIntoTree(TextBlock *column, TextBlock *tree) {
+ TextBlock *child;
+ int i;
+
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (child->tag == blkTagMulticolumn &&
+ column->xMin >= child->xMin &&
+ column->yMin >= child->yMin &&
+ column->xMax <= child->xMax &&
+ column->yMax <= child->yMax) {
+ insertColumnIntoTree(column, child);
+ tree->tag = blkTagMulticolumn;
+ return;
+ }
+ }
+
+ if (tree->type == blkVertSplit) {
+ if (tree->rot == 1 || tree->rot == 2) {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (column->xMax > 0.5 * (child->xMin + child->xMax)) {
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (column->xMin < 0.5 * (child->xMin + child->xMax)) {
+ break;
+ }
+ }
+ }
+ } else if (tree->type == blkHorizSplit) {
+ if (tree->rot >= 2) {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (column->yMax > 0.5 * (child->yMin + child->yMax)) {
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if (column->yMin < 0.5 * (child->yMin + child->yMax)) {
+ break;
+ }
+ }
+ }
+ } else {
+ // this should never happen
+ return;
+ }
+ tree->children->insert(i, column);
+ tree->tag = blkTagMulticolumn;
+}
+
+// Insert clipped characters back into the TextBlock tree.
+void TextPage::insertClippedChars(GList *clippedChars, TextBlock *tree) {
+ TextChar *ch, *ch2;
+ TextBlock *leaf;
+ double y;
+ int i;
+
+ //~ this currently works only for characters in the primary rotation
+
+ clippedChars->sort(TextChar::cmpX);
+ while (clippedChars->getLength()) {
+ ch = (TextChar *)clippedChars->del(0);
+ if (ch->rot != 0) {
+ continue;
+ }
+ if (!(leaf = findClippedCharLeaf(ch, tree))) {
+ continue;
+ }
+ leaf->addChild(ch, gFalse);
+ i = 0;
+ while (i < clippedChars->getLength()) {
+ ch2 = (TextChar *)clippedChars->get(i);
+ if (ch2->xMin > ch->xMax + clippedTextMaxWordSpace * ch->fontSize) {
+ break;
+ }
+ y = 0.5 * (ch2->yMin + ch2->yMax);
+ if (y > leaf->yMin && y < leaf->yMax) {
+ ch2 = (TextChar *)clippedChars->del(i);
+ leaf->addChild(ch2, gFalse);
+ ch = ch2;
+ } else {
+ ++i;
+ }
+ }
+ }
+}
+
+// Find the leaf in <tree> to which clipped char <ch> can be appended.
+// Returns NULL if there is no appropriate append point.
+TextBlock *TextPage::findClippedCharLeaf(TextChar *ch, TextBlock *tree) {
+ TextBlock *ret, *child;
+ double y;
+ int i;
+
+ //~ this currently works only for characters in the primary rotation
+
+ y = 0.5 * (ch->yMin + ch->yMax);
+ if (tree->type == blkLeaf) {
+ if (tree->rot == 0) {
+ if (y > tree->yMin && y < tree->yMax &&
+ ch->xMin <= tree->xMax + clippedTextMaxWordSpace * ch->fontSize) {
+ return tree;
+ }
+ }
+ } else {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ child = (TextBlock *)tree->children->get(i);
+ if ((ret = findClippedCharLeaf(ch, child))) {
+ return ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+// Convert the tree of TextBlocks into a list of TextColumns.
+GList *TextPage::buildColumns(TextBlock *tree, GBool primaryLR) {
+ GList *columns;
+
+ columns = new GList();
+ buildColumns2(tree, columns, primaryLR);
+ return columns;
+}
+
+void TextPage::buildColumns2(TextBlock *blk, GList *columns, GBool primaryLR) {
+ TextColumn *col;
+ int i;
+
+ switch (blk->tag) {
+ case blkTagSuperLine: // should never happen
+ case blkTagLine:
+ case blkTagColumn:
+ col = buildColumn(blk);
+ columns->append(col);
+ break;
+ case blkTagMulticolumn:
+#if 0 //~tmp
+ if (!primaryLR && blk->type == blkVertSplit) {
+ for (i = blk->children->getLength() - 1; i >= 0; --i) {
+ buildColumns2((TextBlock *)blk->children->get(i), columns, primaryLR);
+ }
+ } else {
+#endif
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ buildColumns2((TextBlock *)blk->children->get(i), columns, primaryLR);
+ }
+#if 0 //~tmp
+ }
+#endif
+ break;
+ }
+}
+
+TextColumn *TextPage::buildColumn(TextBlock *blk) {
+ GList *lines, *parLines;
+ GList *paragraphs;
+ TextLine *line0, *line1;
+ GBool dropCap;
+ double spaceThresh, indent0, indent1, fontSize0, fontSize1;
+ int i;
+
+ lines = new GList();
+ buildLines(blk, lines, gFalse);
+
+ spaceThresh = paragraphSpacingThreshold * getAverageLineSpacing(lines);
+
+ //~ could look for bulleted lists here: look for the case where
+ //~ all out-dented lines start with the same char
+
+ //~ this doesn't handle right-to-left scripts (need to look for indents
+ //~ on the right instead of left, etc.)
+
+ // build the paragraphs
+ paragraphs = new GList();
+ i = 0;
+ while (i < lines->getLength()) {
+
+ // get the first line of the paragraph
+ parLines = new GList();
+ dropCap = gFalse;
+ line0 = (TextLine *)lines->get(i);
+ parLines->append(line0);
+ ++i;
+
+ if (i < lines->getLength()) {
+ line1 = (TextLine *)lines->get(i);
+ indent0 = getLineIndent(line0, blk);
+ indent1 = getLineIndent(line1, blk);
+ fontSize0 = line0->fontSize;
+ fontSize1 = line1->fontSize;
+
+ // inverted indent
+ if (indent1 - indent0 > minParagraphIndent * fontSize0 &&
+ fabs(fontSize0 - fontSize1) <= paragraphFontSizeDelta &&
+ getLineSpacing(line0, line1) <= spaceThresh) {
+ parLines->append(line1);
+ indent0 = indent1;
+ for (++i; i < lines->getLength(); ++i) {
+ line1 = (TextLine *)lines->get(i);
+ indent1 = getLineIndent(line1, blk);
+ fontSize1 = line1->fontSize;
+ if (indent0 - indent1 > minParagraphIndent * fontSize0) {
+ break;
+ }
+ if (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) {
+ break;
+ }
+ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
+ > spaceThresh) {
+ break;
+ }
+ parLines->append(line1);
+ }
+
+ // drop cap
+ } else if (fontSize0 > largeCharThreshold * fontSize1 &&
+ indent1 - indent0 > minParagraphIndent * fontSize1 &&
+ getLineSpacing(line0, line1) < 0) {
+ dropCap = gTrue;
+ parLines->append(line1);
+ fontSize0 = fontSize1;
+ for (++i; i < lines->getLength(); ++i) {
+ line1 = (TextLine *)lines->get(i);
+ indent1 = getLineIndent(line1, blk);
+ if (indent1 - indent0 <= minParagraphIndent * fontSize0) {
+ break;
+ }
+ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
+ > spaceThresh) {
+ break;
+ }
+ parLines->append(line1);
+ }
+ for (; i < lines->getLength(); ++i) {
+ line1 = (TextLine *)lines->get(i);
+ indent1 = getLineIndent(line1, blk);
+ fontSize1 = line1->fontSize;
+ if (indent1 - indent0 > minParagraphIndent * fontSize0) {
+ break;
+ }
+ if (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) {
+ break;
+ }
+ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
+ > spaceThresh) {
+ break;
+ }
+ parLines->append(line1);
+ }
+
+ // regular indent or no indent
+ } else if (fabs(fontSize0 - fontSize1) <= paragraphFontSizeDelta &&
+ getLineSpacing(line0, line1) <= spaceThresh) {
+ parLines->append(line1);
+ indent0 = indent1;
+ for (++i; i < lines->getLength(); ++i) {
+ line1 = (TextLine *)lines->get(i);
+ indent1 = getLineIndent(line1, blk);
+ fontSize1 = line1->fontSize;
+ if (indent1 - indent0 > minParagraphIndent * fontSize0) {
+ break;
+ }
+ if (fabs(fontSize0 - fontSize1) > paragraphFontSizeDelta) {
+ break;
+ }
+ if (getLineSpacing((TextLine *)lines->get(i - 1), line1)
+ > spaceThresh) {
+ break;
+ }
+ parLines->append(line1);
+ }
+ }
+ }
+
+ paragraphs->append(new TextParagraph(parLines, dropCap));
+ }
+
+ delete lines;
+
+ return new TextColumn(paragraphs, blk->xMin, blk->yMin,
+ blk->xMax, blk->yMax);
+}
+
+double TextPage::getLineIndent(TextLine *line, TextBlock *blk) {
+ double indent;
+
+ switch (line->rot) {
+ case 0:
+ default: indent = line->xMin - blk->xMin; break;
+ case 1: indent = line->yMin - blk->yMin; break;
+ case 2: indent = blk->xMax - line->xMax; break;
+ case 3: indent = blk->yMax - line->yMax; break;
+ }
+ return indent;
+}
+
+// Compute average line spacing in column.
+double TextPage::getAverageLineSpacing(GList *lines) {
+ double avg, sp;
+ int n, i;
+
+ avg = 0;
+ n = 0;
+ for (i = 1; i < lines->getLength(); ++i) {
+ sp = getLineSpacing((TextLine *)lines->get(i - 1),
+ (TextLine *)lines->get(i));
+ if (sp > 0) {
+ avg += sp;
+ ++n;
+ }
+ }
+ if (n > 0) {
+ avg /= n;
+ }
+ return avg;
+}
+
+// Compute the space between two lines.
+double TextPage::getLineSpacing(TextLine *line0, TextLine *line1) {
+ double sp;
+
+ switch (line0->rot) {
+ case 0:
+ default: sp = line1->yMin - line0->yMax; break;
+ case 1: sp = line0->xMin - line1->xMax; break;
+ case 2: sp = line0->yMin - line1->yMin; break;
+ case 3: sp = line1->xMin - line1->xMax; break;
+ }
+ return sp;
+}
+
+void TextPage::buildLines(TextBlock *blk, GList *lines,
+ GBool splitSuperLines) {
+ TextLine *line;
+ int i;
+
+ if (blk->tag == blkTagLine ||
+ (blk->tag == blkTagSuperLine && !splitSuperLines)) {
+ line = buildLine(blk);
+ if (blk->rot == 1 || blk->rot == 2) {
+ lines->insert(0, line);
+ } else {
+ lines->append(line);
+ }
+ } else {
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ buildLines((TextBlock *)blk->children->get(i), lines, splitSuperLines);
+ }
+ }
+}
+
+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 rotated, spaceAfter, spaceBefore;
+ int i, j;
+
+ wordSp = computeWordSpacingThreshold(charsA, rot);
+
+ words = new GList();
+ lineFontSize = 0;
+ spaceBefore = gFalse;
+ i = 0;
+ while (i < charsA->getLength()) {
+ 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 = (rot & 1) ? (ch2->yMin - ch->yMax) : (ch2->xMin - ch->xMax);
+ if (sp > wordSp) {
+ spaceAfter = gTrue;
+ break;
+ }
+ // look for significant overlaps, which can happen with clipped
+ // characters (among other things)
+ if (sp < -ch->fontSize) {
+ spaceAfter = gTrue;
+ break;
+ }
+ 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)) {
+ break;
+ }
+ if (!dir && dir2) {
+ dir = dir2;
+ }
+ sp = wordSp - 1;
+ }
+ word = new TextWord(charsA, i, j - i, rot, rotated, dir,
+ (rot >= 2) ? spaceBefore : spaceAfter);
+ spaceBefore = spaceAfter;
+ i = j;
+ if (rot >= 2) {
+ words->insert(0, word);
+ } else {
+ words->append(word);
+ }
+ if (i == 0 || word->fontSize > lineFontSize) {
+ lineFontSize = word->fontSize;
+ }
+ }
+
+ return new TextLine(words, xMin, yMin, xMax, yMax, lineFontSize);
+}
+
+void TextPage::getLineChars(TextBlock *blk, GList *charsA) {
+ int i;
+
+ if (blk->type == blkLeaf) {
+ charsA->append(blk->children);
+ } else {
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ getLineChars((TextBlock *)blk->children->get(i), charsA);
+ }
+ }
+}
+
+// Compute the inter-word spacing threshold for a line of chars.
+// Spaces greater than this threshold will be considered inter-word
+// 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;
+ maxAdjGap = maxSpGap = 0;
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ avgFontSize += ch->fontSize;
+ if (i < charsA->getLength() - 1) {
+ ch2 = (TextChar *)charsA->get(i+1);
+ gap = (rot & 1) ? (ch2->yMin - ch->yMax) : (ch2->xMin - ch->xMax);
+ if (ch->spaceAfter) {
+ if (minSpGap > maxSpGap) {
+ minSpGap = maxSpGap = gap;
+ } else if (gap < minSpGap) {
+ minSpGap = gap;
+ } else if (gap > maxSpGap) {
+ maxSpGap = gap;
+ }
+ } else {
+ if (minAdjGap > maxAdjGap) {
+ minAdjGap = maxAdjGap = gap;
+ } else if (gap < minAdjGap) {
+ minAdjGap = gap;
+ } else if (gap > maxAdjGap) {
+ maxAdjGap = gap;
+ }
+ }
+ if (i == 0 || gap < minGap) {
+ minGap = gap;
+ }
+ if (gap > maxGap) {
+ maxGap = gap;
+ }
+ }
+ }
+ avgFontSize /= charsA->getLength();
+ if (minGap < 0) {
+ minGap = 0;
+ }
+
+ // if spacing is nearly uniform (minGap is close to maxGap), there
+ // are three cases:
+ // (1) if the SpGap and AdjGap values are both available and
+ // sensible, use them
+ // (2) if only the SpGap values are available, meaning that every
+ // character in the line had a space after it, split after every
+ // character
+ // (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 < uniformSp * avgFontSize) {
+ if (minSpGap <= maxSpGap) {
+ if (minAdjGap <= maxAdjGap &&
+ minSpGap - maxAdjGap > 0.01) {
+ return 0.5 * (maxAdjGap + minSpGap);
+ } else if (minAdjGap > maxAdjGap &&
+ maxSpGap - minSpGap < uniformSp * avgFontSize) {
+ return minSpGap - 1;
+ }
+ }
+ return maxGap + 1;
+
+ // if there is some variation in spacing, but it's small, assume
+ // there are some inter-word spaces
+ } else if (maxGap - minGap < wordSp * avgFontSize) {
+ return 0.5 * (minGap + maxGap);
+
+ // if there is a large variation in spacing, use the SpGap/AdjGap
+ // values if they look reasonable, otherwise, assume a reasonable
+ // threshold for inter-word spacing (we can't use something like
+ // 0.5*(minGap+maxGap) here because there can be outliers at the
+ // high end)
+ } else {
+ if (minAdjGap <= maxAdjGap &&
+ minSpGap <= maxSpGap &&
+ minSpGap - maxAdjGap > uniformSp * avgFontSize) {
+ gap = wordSp * avgFontSize;
+ gap2 = 0.5 * (minSpGap - minGap);
+ return minGap + (gap < gap2 ? gap : gap2);
+ } else {
+ return minGap + wordSp * avgFontSize;
+ }
+ }
+}
+
+// Check the characters direction: returns 1 for L or Num; -1 for R; 0
+// for others.
+int TextPage::getCharDirection(TextChar *ch) {
+ if (unicodeTypeL(ch->c) || unicodeTypeNum(ch->c)) {
+ return 1;
+ }
+ if (unicodeTypeR(ch->c)) {
+ return -1;
+ }
+ return 0;
+}
+
+int TextPage::assignPhysLayoutPositions(GList *columns) {
+ assignLinePhysPositions(columns);
+ return assignColumnPhysPositions(columns);
+}
+
+// Assign a physical x coordinate for each TextLine (relative to the
+// containing TextColumn). This also computes TextColumn width and
+// height.
+void TextPage::assignLinePhysPositions(GList *columns) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ UnicodeMap *uMap;
+ int colIdx, parIdx, lineIdx;
+
+ if (!(uMap = globalParams->getTextEncoding())) {
+ return;
+ }
+
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ col->pw = col->ph = 0;
+ 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);
+ computeLinePhysWidth(line, uMap);
+ if (control.fixedPitch > 0) {
+ line->px = (int)((line->xMin - col->xMin) / control.fixedPitch);
+ } else if (fabs(line->fontSize) < 0.001) {
+ line->px = 0;
+ } else {
+ line->px = (int)((line->xMin - col->xMin) /
+ (physLayoutSpaceWidth * line->fontSize));
+ }
+ if (line->px + line->pw > col->pw) {
+ col->pw = line->px + line->pw;
+ }
+ }
+ col->ph += par->lines->getLength();
+ }
+ col->ph += col->paragraphs->getLength() - 1;
+ }
+
+ uMap->decRefCnt();
+}
+
+void TextPage::computeLinePhysWidth(TextLine *line, UnicodeMap *uMap) {
+ char buf[8];
+ int n, i;
+
+ if (uMap->isUnicode()) {
+ line->pw = line->len;
+ } else {
+ line->pw = 0;
+ for (i = 0; i < line->len; ++i) {
+ n = uMap->mapUnicode(line->text[i], buf, sizeof(buf));
+ line->pw += n;
+ }
+ }
+}
+
+// Assign physical x and y coordinates for each TextColumn. Returns
+// the text height (max physical y + 1).
+int TextPage::assignColumnPhysPositions(GList *columns) {
+ TextColumn *col, *col2;
+ double slack, xOverlap, yOverlap;
+ int ph, i, j;
+
+ if (control.mode == textOutTableLayout) {
+ slack = tableCellOverlapSlack;
+ } else {
+ slack = 0;
+ }
+
+ // assign x positions
+ columns->sort(&TextColumn::cmpX);
+ for (i = 0; i < columns->getLength(); ++i) {
+ col = (TextColumn *)columns->get(i);
+ if (control.fixedPitch) {
+ col->px = (int)(col->xMin / control.fixedPitch);
+ } else {
+ col->px = 0;
+ for (j = 0; j < i; ++j) {
+ col2 = (TextColumn *)columns->get(j);
+ xOverlap = col2->xMax - col->xMin;
+ if (xOverlap < slack * (col2->xMax - col2->xMin)) {
+ if (col2->px + col2->pw + 2 > col->px) {
+ col->px = col2->px + col2->pw + 2;
+ }
+ } else {
+ yOverlap = (col->yMax < col2->yMax ? col->yMax : col2->yMax) -
+ (col->yMin > col2->yMin ? col->yMin : col2->yMin);
+ if (yOverlap > 0 && xOverlap < yOverlap) {
+ if (col2->px + col2->pw > col->px) {
+ col->px = col2->px + col2->pw;
+ }
+ } else {
+ if (col2->px > col->px) {
+ col->px = col2->px;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // assign y positions
+ ph = 0;
+ columns->sort(&TextColumn::cmpY);
+ for (i = 0; i < columns->getLength(); ++i) {
+ col = (TextColumn *)columns->get(i);
+ col->py = 0;
+ for (j = 0; j < i; ++j) {
+ col2 = (TextColumn *)columns->get(j);
+ yOverlap = col2->yMax - col->yMin;
+ if (yOverlap < slack * (col2->yMax - col2->yMin)) {
+ if (col2->py + col2->ph + 1 > col->py) {
+ col->py = col2->py + col2->ph + 1;
+ }
+ } else {
+ xOverlap = (col->xMax < col2->xMax ? col->xMax : col2->xMax) -
+ (col->xMin > col2->xMin ? col->xMin : col2->xMin);
+ if (xOverlap > 0 && yOverlap < xOverlap) {
+ if (col2->py + col2->ph > col->py) {
+ col->py = col2->py + col2->ph;
+ }
+ } else {
+ if (col2->py > col->py) {
+ col->py = col2->py;
+ }
+ }
+ }
+ }
+ if (col->py + col->ph > ph) {
+ ph = col->py + col->ph;
+ }
+ }
+
+ return ph;
+}
+
+void TextPage::buildSuperLines(TextBlock *blk, GList *superLines) {
+ GList *lines;
+ int i;
+
+ if (blk->tag == blkTagLine || blk->tag == blkTagSuperLine) {
+ lines = new GList();
+ buildLines(blk, lines, gTrue);
+ superLines->append(new TextSuperLine(lines));
+ } else {
+ for (i = 0; i < blk->children->getLength(); ++i) {
+ buildSuperLines((TextBlock *)blk->children->get(i), superLines);
+ }
+ }
+}
+
+void TextPage::assignSimpleLayoutPositions(GList *superLines,
+ UnicodeMap *uMap) {
+ GList *lines;
+ TextLine *line0, *line1;
+ double xMin, xMax;
+ int px, px2, sp, i, j;
+
+ // build a list of lines and sort by x
+ lines = new GList();
+ for (i = 0; i < superLines->getLength(); ++i) {
+ lines->append(((TextSuperLine *)superLines->get(i))->lines);
+ }
+ lines->sort(&TextLine::cmpX);
+
+ // assign positions
+ xMin = ((TextLine *)lines->get(0))->xMin;
+ for (i = 0; i < lines->getLength(); ++i) {
+ line0 = (TextLine *)lines->get(i);
+ computeLinePhysWidth(line0, uMap);
+ px = 0;
+ xMax = xMin;
+ for (j = 0; j < i; ++j) {
+ line1 = (TextLine *)lines->get(j);
+ if (line0->xMin > line1->xMax) {
+ if (line1->xMax > xMax) {
+ xMax = line1->xMax;
+ }
+ px2 = line1->px + line1->pw;
+ if (px2 > px) {
+ px = px2;
+ }
+ }
+ }
+ sp = (int)((line0->xMin - xMax) / (0.5 * line0->fontSize) + 0.5);
+ if (sp < 1 && xMax > xMin) {
+ sp = 1;
+ }
+ line0->px = px + sp;
+ }
+
+ delete lines;
+}
+
+void TextPage::generateUnderlinesAndLinks(GList *columns) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextWord *word;
+ TextUnderline *underline;
+ TextLink *link;
+ double base, uSlack, ubSlack, hSlack;
+ int colIdx, parIdx, lineIdx, wordIdx, i;
+
+ 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);
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ word = (TextWord *)line->words->get(wordIdx);
+ base = word->getBaseline();
+ uSlack = underlineSlack * word->fontSize;
+ ubSlack = underlineBaselineSlack * word->fontSize;
+ hSlack = hyperlinkSlack * word->fontSize;
+
+ //----- handle underlining
+ for (i = 0; i < underlines->getLength(); ++i) {
+ underline = (TextUnderline *)underlines->get(i);
+ if (underline->horiz) {
+ if (word->rot == 0 || word->rot == 2) {
+ if (fabs(underline->y0 - base) < ubSlack &&
+ underline->x0 < word->xMin + uSlack &&
+ word->xMax - uSlack < underline->x1) {
+ word->underlined = gTrue;
+ }
+ }
+ } else {
+ if (word->rot == 1 || word->rot == 3) {
+ if (fabs(underline->x0 - base) < ubSlack &&
+ underline->y0 < word->yMin + uSlack &&
+ word->yMax - uSlack < underline->y1) {
+ word->underlined = gTrue;
+ }
+ }
+ }
+ }
+
+ //----- handle links
+ for (i = 0; i < links->getLength(); ++i) {
+ link = (TextLink *)links->get(i);
+ if (link->xMin < word->xMin + hSlack &&
+ word->xMax - hSlack < link->xMax &&
+ link->yMin < word->yMin + hSlack &&
+ word->yMax - hSlack < link->yMax) {
+ word->link = link;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// TextPage: access
+//------------------------------------------------------------------------
+
+GBool TextPage::findText(Unicode *s, int len,
+ GBool startAtTop, GBool stopAtBottom,
+ GBool startAtLast, GBool stopAtLast,
+ GBool caseSensitive, GBool backward,
+ GBool wholeWord,
+ double *xMin, double *yMin,
+ double *xMax, double *yMax) {
+ TextColumn *column;
+ TextParagraph *par;
+ TextLine *line;
+ Unicode *s2, *txt;
+ Unicode *p;
+ double xStart, yStart, xStop, yStop;
+ double xMin0, yMin0, xMax0, yMax0;
+ double xMin1, yMin1, xMax1, yMax1;
+ GBool found;
+ int txtSize, m, colIdx, parIdx, lineIdx, i, j, k;
+
+ //~ need to handle right-to-left text
+ //~ - pass primaryLR to buildColumns
+
+ buildFindCols();
+
+ // convert the search string to uppercase
+ if (!caseSensitive) {
+ s2 = (Unicode *)gmallocn(len, sizeof(Unicode));
+ for (i = 0; i < len; ++i) {
+ s2[i] = unicodeToUpper(s[i]);
+ }
+ } else {
+ s2 = s;
+ }
+
+ txt = NULL;
+ txtSize = 0;
+
+ xStart = yStart = xStop = yStop = 0;
+ if (startAtLast && haveLastFind) {
+ xStart = lastFindXMin;
+ yStart = lastFindYMin;
+ } else if (!startAtTop) {
+ xStart = *xMin;
+ yStart = *yMin;
+ }
+ if (stopAtLast && haveLastFind) {
+ xStop = lastFindXMin;
+ yStop = lastFindYMin;
+ } else if (!stopAtBottom) {
+ xStop = *xMax;
+ yStop = *yMax;
+ }
+
+ found = gFalse;
+ xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy
+ xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy
+
+ for (colIdx = backward ? findCols->getLength() - 1 : 0;
+ backward ? colIdx >= 0 : colIdx < findCols->getLength();
+ colIdx += backward ? -1 : 1) {
+ column = (TextColumn *)findCols->get(colIdx);
+
+ // check: is the column above the top limit?
+ if (!startAtTop && (backward ? column->yMin > yStart
+ : column->yMax < yStart)) {
+ continue;
+ }
+
+ // check: is the column below the bottom limit?
+ if (!stopAtBottom && (backward ? column->yMax < yStop
+ : column->yMin > yStop)) {
+ continue;
+ }
+
+ for (parIdx = backward ? column->paragraphs->getLength() - 1 : 0;
+ backward ? parIdx >= 0 : parIdx < column->paragraphs->getLength();
+ parIdx += backward ? -1 : 1) {
+ par = (TextParagraph *)column->paragraphs->get(parIdx);
+
+ // check: is the paragraph above the top limit?
+ if (!startAtTop && (backward ? par->yMin > yStart
+ : par->yMax < yStart)) {
+ continue;
+ }
+
+ // check: is the paragraph below the bottom limit?
+ if (!stopAtBottom && (backward ? par->yMax < yStop
+ : par->yMin > yStop)) {
+ continue;
+ }
+
+ for (lineIdx = backward ? par->lines->getLength() - 1 : 0;
+ backward ? lineIdx >= 0 : lineIdx < par->lines->getLength();
+ lineIdx += backward ? -1 : 1) {
+ line = (TextLine *)par->lines->get(lineIdx);
+
+ // check: is the line above the top limit?
+ if (!startAtTop && (backward ? line->yMin > yStart
+ : line->yMax < yStart)) {
+ continue;
+ }
+
+ // check: is the line below the bottom limit?
+ if (!stopAtBottom && (backward ? line->yMax < yStop
+ : line->yMin > yStop)) {
+ continue;
+ }
+
+ // convert the line to uppercase
+ m = line->len;
+ if (!caseSensitive) {
+ if (m > txtSize) {
+ txt = (Unicode *)greallocn(txt, m, sizeof(Unicode));
+ txtSize = m;
+ }
+ for (k = 0; k < m; ++k) {
+ txt[k] = unicodeToUpper(line->text[k]);
+ }
+ } else {
+ txt = line->text;
+ }
+
+ // search each position in this line
+ j = backward ? m - len : 0;
+ p = txt + j;
+ while (backward ? j >= 0 : j <= m - len) {
+ if (!wholeWord ||
+ ((j == 0 || !unicodeTypeWord(txt[j - 1])) &&
+ (j + len == m || !unicodeTypeWord(txt[j + len])))) {
+
+ // compare the strings
+ for (k = 0; k < len; ++k) {
+ if (p[k] != s2[k]) {
+ break;
+ }
+ }
+
+ // found it
+ if (k == len) {
+ switch (line->rot) {
+ case 0:
+ xMin1 = line->edge[j];
+ xMax1 = line->edge[j + len];
+ yMin1 = line->yMin;
+ yMax1 = line->yMax;
+ break;
+ case 1:
+ xMin1 = line->xMin;
+ xMax1 = line->xMax;
+ yMin1 = line->edge[j];
+ yMax1 = line->edge[j + len];
+ break;
+ case 2:
+ xMin1 = line->edge[j + len];
+ xMax1 = line->edge[j];
+ yMin1 = line->yMin;
+ yMax1 = line->yMax;
+ break;
+ case 3:
+ xMin1 = line->xMin;
+ xMax1 = line->xMax;
+ yMin1 = line->edge[j + len];
+ yMax1 = line->edge[j];
+ break;
+ }
+ if (backward) {
+ if ((startAtTop ||
+ yMin1 < yStart || (yMin1 == yStart && xMin1 < xStart)) &&
+ (stopAtBottom ||
+ yMin1 > yStop || (yMin1 == yStop && xMin1 > xStop))) {
+ if (!found ||
+ yMin1 > yMin0 || (yMin1 == yMin0 && xMin1 > xMin0)) {
+ xMin0 = xMin1;
+ xMax0 = xMax1;
+ yMin0 = yMin1;
+ yMax0 = yMax1;
+ found = gTrue;
+ }
+ }
+ } else {
+ if ((startAtTop ||
+ yMin1 > yStart || (yMin1 == yStart && xMin1 > xStart)) &&
+ (stopAtBottom ||
+ yMin1 < yStop || (yMin1 == yStop && xMin1 < xStop))) {
+ if (!found ||
+ yMin1 < yMin0 || (yMin1 == yMin0 && xMin1 < xMin0)) {
+ xMin0 = xMin1;
+ xMax0 = xMax1;
+ yMin0 = yMin1;
+ yMax0 = yMax1;
+ found = gTrue;
+ }
+ }
+ }
+ }
+ }
+ if (backward) {
+ --j;
+ --p;
+ } else {
+ ++j;
+ ++p;
+ }
+ }
+ }
+ }
+ }
+
+ if (!caseSensitive) {
+ gfree(s2);
+ gfree(txt);
+ }
+
+ if (found) {
+ *xMin = xMin0;
+ *xMax = xMax0;
+ *yMin = yMin0;
+ *yMax = yMax0;
+ lastFindXMin = xMin0;
+ lastFindYMin = yMin0;
+ haveLastFind = gTrue;
+ return gTrue;
+ }
+
+ return gFalse;
+}
+
+GString *TextPage::getText(double xMin, double yMin,
+ double xMax, double yMax, GBool forceEOL) {
+ UnicodeMap *uMap;
+ char space[8], eol[16];
+ int spaceLen, eolLen;
+ GList *chars2;
+ GString **out;
+ int *outLen;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextChar *ch;
+ GBool primaryLR;
+ TextBlock *tree;
+ GList *columns;
+ GString *ret;
+ double xx, yy;
+ int rot, colIdx, parIdx, lineIdx, ph, y, i;
+
+ // get the output encoding
+ if (!(uMap = globalParams->getTextEncoding())) {
+ return NULL;
+ }
+ spaceLen = uMap->mapUnicode(0x20, space, sizeof(space));
+ eolLen = 0; // make gcc happy
+ switch (globalParams->getTextEOL()) {
+ case eolUnix:
+ eolLen = uMap->mapUnicode(0x0a, eol, sizeof(eol));
+ break;
+ case eolDOS:
+ eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
+ eolLen += uMap->mapUnicode(0x0a, eol + eolLen, (int)sizeof(eol) - eolLen);
+ break;
+ case eolMac:
+ eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));
+ break;
+ }
+
+ // get all chars in the rectangle
+ // (i.e., all chars whose center lies inside the rectangle)
+ chars2 = new GList();
+ for (i = 0; i < chars->getLength(); ++i) {
+ ch = (TextChar *)chars->get(i);
+ xx = 0.5 * (ch->xMin + ch->xMax);
+ yy = 0.5 * (ch->yMin + ch->yMax);
+ if (xx > xMin && xx < xMax && yy > yMin && yy < yMax) {
+ chars2->append(ch);
+ }
+ }
+#if 0 //~debug
+ dumpChars(chars2);
+#endif
+
+ rot = rotateChars(chars2);
+ primaryLR = checkPrimaryLR(chars2);
+ tree = splitChars(chars2);
+ if (!tree) {
+ unrotateChars(chars2, rot);
+ delete chars2;
+ return new GString();
+ }
+#if 0 //~debug
+ dumpTree(tree);
+#endif
+ columns = buildColumns(tree, primaryLR);
+ delete tree;
+ ph = assignPhysLayoutPositions(columns);
+#if 0 //~debug
+ dumpColumns(columns);
+#endif
+ unrotateChars(chars2, rot);
+ delete chars2;
+
+ out = (GString **)gmallocn(ph, sizeof(GString *));
+ outLen = (int *)gmallocn(ph, sizeof(int));
+ for (i = 0; i < ph; ++i) {
+ out[i] = NULL;
+ outLen[i] = 0;
+ }
+
+ columns->sort(&TextColumn::cmpPX);
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ y = col->py;
+ for (parIdx = 0;
+ parIdx < col->paragraphs->getLength() && y < ph;
+ ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ for (lineIdx = 0;
+ lineIdx < par->lines->getLength() && y < ph;
+ ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ if (!out[y]) {
+ out[y] = new GString();
+ }
+ while (outLen[y] < col->px + line->px) {
+ out[y]->append(space, spaceLen);
+ ++outLen[y];
+ }
+ encodeFragment(line->text, line->len, uMap, primaryLR, out[y]);
+ outLen[y] += line->pw;
+ ++y;
+ }
+ if (parIdx + 1 < col->paragraphs->getLength()) {
+ ++y;
+ }
+ }
+ }
+
+ ret = new GString();
+ for (i = 0; i < ph; ++i) {
+ if (out[i]) {
+ ret->append(out[i]);
+ delete out[i];
+ }
+ if (ph > 1 || forceEOL) {
+ ret->append(eol, eolLen);
+ }
+ }
+
+ gfree(out);
+ gfree(outLen);
+ deleteGList(columns, TextColumn);
+ uMap->decRefCnt();
+
+ return ret;
+}
+
+GBool TextPage::findCharRange(int pos, int length,
+ double *xMin, double *yMin,
+ double *xMax, double *yMax) {
+ TextChar *ch;
+ double xMin2, yMin2, xMax2, yMax2;
+ GBool first;
+ int i;
+
+ //~ this doesn't correctly handle ranges split across multiple lines
+ //~ (the highlighted region is the bounding box of all the parts of
+ //~ the range)
+
+ xMin2 = yMin2 = xMax2 = yMax2 = 0;
+ first = gTrue;
+ for (i = 0; i < chars->getLength(); ++i) {
+ ch = (TextChar *)chars->get(i);
+ if (ch->charPos >= pos && ch->charPos < pos + length) {
+ if (first || ch->xMin < xMin2) {
+ xMin2 = ch->xMin;
+ }
+ if (first || ch->yMin < yMin2) {
+ yMin2 = ch->yMin;
+ }
+ if (first || ch->xMax > xMax2) {
+ xMax2 = ch->xMax;
+ }
+ if (first || ch->yMax > yMax2) {
+ yMax2 = ch->yMax;
+ }
+ first = gFalse;
+ }
+ }
+ if (first) {
+ return gFalse;
+ }
+ *xMin = xMin2;
+ *yMin = yMin2;
+ *xMax = xMax2;
+ *yMax = yMax2;
+ return gTrue;
+}
+
+GBool TextPage::checkPointInside(double x, double y) {
+ TextColumn *col;
+ int colIdx;
+
+ buildFindCols();
+
+ //~ this doesn't handle RtL, vertical, or rotated text
+ //~ this doesn't handle drop caps
+
+ for (colIdx = 0; colIdx < findCols->getLength(); ++colIdx) {
+ col = (TextColumn *)findCols->get(colIdx);
+ if (col->getRotation() != 0) {
+ continue;
+ }
+ if (x >= col->getXMin() && x <= col->getXMax() &&
+ y >= col->getYMin() && y <= col->getYMax()) {
+ return gTrue;
+ }
+ }
+ return gFalse;
+}
+
+GBool TextPage::findPointInside(double x, double y, TextPosition *pos) {
+ TextColumn *col;
+ int colIdx;
+
+ buildFindCols();
+
+ //~ this doesn't handle RtL, vertical, or rotated text
+ //~ this doesn't handle drop caps
+
+ for (colIdx = 0; colIdx < findCols->getLength(); ++colIdx) {
+ col = (TextColumn *)findCols->get(colIdx);
+ if (col->getRotation() != 0) {
+ continue;
+ }
+ if (x >= col->getXMin() && x <= col->getXMax() &&
+ y >= col->getYMin() && y <= col->getYMax()) {
+ pos->colIdx = colIdx;
+ findPointInColumn(col, x, y, pos);
+ return gTrue;
+ }
+ }
+ return gFalse;
+}
+
+GBool TextPage::findPointNear(double x, double y, TextPosition *pos) {
+ TextColumn *col;
+ double nearestDist, dx, dy;
+ int nearestColIdx, colIdx;
+
+ buildFindCols();
+
+ //~ this doesn't handle RtL, vertical, or rotated text
+ //~ this doesn't handle drop caps
+
+ nearestColIdx = -1;
+ nearestDist = 0;
+ for (colIdx = 0; colIdx < findCols->getLength(); ++colIdx) {
+ col = (TextColumn *)findCols->get(colIdx);
+ if (col->getRotation() != 0) {
+ continue;
+ }
+ if (x < col->getXMin()) {
+ dx = col->getXMin() - x;
+ } else if (x > col->getXMax()) {
+ dx = x - col->getXMax();
+ } else {
+ dx = 0;
+ }
+ if (y < col->getYMin()) {
+ dy = col->getYMin() - y;
+ } else if (y > col->getYMax()) {
+ dy = y - col->getYMax();
+ } else {
+ dy = 0;
+ }
+ if (nearestColIdx < 0 || dx + dy < nearestDist) {
+ nearestColIdx = colIdx;
+ nearestDist = dx + dy;
+ }
+ }
+ if (nearestColIdx < 0) {
+ return gFalse;
+ }
+ pos->colIdx = nearestColIdx;
+ col = (TextColumn *)findCols->get(nearestColIdx);
+ findPointInColumn(col, x, y, pos);
+ return gTrue;
+}
+
+GBool TextPage::findWordPoints(double x, double y,
+ TextPosition *startPos, TextPosition *endPos) {
+ TextPosition pos;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ int startCharIdx, endCharIdx;
+
+ if (!findPointInside(x, y, &pos)) {
+ return gFalse;
+ }
+ col = (TextColumn *)findCols->get(pos.colIdx);
+ par = (TextParagraph *)col->getParagraphs()->get(pos.parIdx);
+ line = (TextLine *)par->getLines()->get(pos.lineIdx);
+
+ for (startCharIdx = pos.charIdx;
+ startCharIdx > 0 && line->text[startCharIdx - 1] != 0x20;
+ --startCharIdx) ;
+ *startPos = pos;
+ startPos->charIdx = startCharIdx;
+
+ for (endCharIdx = pos.charIdx;
+ endCharIdx < line->len && line->text[endCharIdx] != 0x20;
+ ++endCharIdx) ;
+ *endPos = pos;
+ endPos->charIdx = endCharIdx;
+
+ return gTrue;
+}
+
+GBool TextPage::findLinePoints(double x, double y,
+ TextPosition *startPos, TextPosition *endPos) {
+ TextPosition pos;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+
+ if (!findPointInside(x, y, &pos)) {
+ return gFalse;
+ }
+ col = (TextColumn *)findCols->get(pos.colIdx);
+ par = (TextParagraph *)col->getParagraphs()->get(pos.parIdx);
+ line = (TextLine *)par->getLines()->get(pos.lineIdx);
+
+ *startPos = pos;
+ startPos->charIdx = 0;
+
+ *endPos = pos;
+ endPos->charIdx = line->len;
+
+ return gTrue;
+}
+
+// Find the position in [col] corresponding to [x],[y]. The column,
+// [col], was found by findPointInside() or findPointNear().
+void TextPage::findPointInColumn(TextColumn *col, double x, double y,
+ TextPosition *pos) {
+ TextParagraph *par;
+ TextLine *line;
+ GList *pars, *lines;
+ int parIdx, lineIdx, charIdx;
+
+ //~ this doesn't handle RtL, vertical, or rotated text
+ //~ this doesn't handle drop caps
+
+ pars = col->getParagraphs();
+ //~ could use a binary search here
+ for (parIdx = 0; parIdx < pars->getLength() - 1; ++parIdx) {
+ par = (TextParagraph *)pars->get(parIdx);
+ if (y <= par->getYMax()) {
+ break;
+ }
+ }
+ par = (TextParagraph *)pars->get(parIdx);
+
+ lines = par->getLines();
+ //~ could use a binary search here
+ for (lineIdx = 0; lineIdx < lines->getLength() - 1; ++lineIdx) {
+ line = (TextLine *)lines->get(lineIdx);
+ if (y <= line->getYMax()) {
+ break;
+ }
+ }
+ line = (TextLine *)lines->get(lineIdx);
+
+ //~ could use a binary search here
+ for (charIdx = 0; charIdx < line->getLength(); ++charIdx) {
+ if (x <= 0.5 * (line->getEdge(charIdx) + line->getEdge(charIdx + 1))) {
+ break;
+ }
+ }
+
+ pos->parIdx = parIdx;
+ pos->lineIdx = lineIdx;
+ pos->charIdx = charIdx;
+}
+
+void TextPage::convertPosToPointUpper(TextPosition *pos,
+ double *x, double *y) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+
+ buildFindCols();
+ col = (TextColumn *)findCols->get(pos->colIdx);
+ par = (TextParagraph *)col->getParagraphs()->get(pos->parIdx);
+ line = (TextLine *)par->getLines()->get(pos->lineIdx);
+ *x = line->getEdge(pos->charIdx);
+ *y = line->getBaseline() - selectionAscent * line->fontSize;
+}
+
+void TextPage::convertPosToPointLower(TextPosition *pos,
+ double *x, double *y) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+
+ buildFindCols();
+ col = (TextColumn *)findCols->get(pos->colIdx);
+ par = (TextParagraph *)col->getParagraphs()->get(pos->parIdx);
+ line = (TextLine *)par->getLines()->get(pos->lineIdx);
+ *x = line->getEdge(pos->charIdx);
+ *y = line->getYMax();
+}
+
+void TextPage::convertPosToPointLeftEdge(TextPosition *pos,
+ double *x, double *y) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+
+ buildFindCols();
+ col = (TextColumn *)findCols->get(pos->colIdx);
+ par = (TextParagraph *)col->getParagraphs()->get(pos->parIdx);
+ line = (TextLine *)par->getLines()->get(pos->lineIdx);
+ *x = col->getXMin();
+ *y = line->getBaseline() - selectionAscent * line->fontSize;
+}
+
+void TextPage::convertPosToPointRightEdge(TextPosition *pos,
+ double *x, double *y) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+
+ buildFindCols();
+ col = (TextColumn *)findCols->get(pos->colIdx);
+ par = (TextParagraph *)col->getParagraphs()->get(pos->parIdx);
+ line = (TextLine *)par->getLines()->get(pos->lineIdx);
+ *x = col->getXMax();
+ *y = line->getYMax();
+}
+
+void TextPage::getColumnUpperRight(int colIdx, double *x, double *y) {
+ TextColumn *col;
+ TextParagraph *par0;
+ TextLine *line0;
+
+ buildFindCols();
+ col = (TextColumn *)findCols->get(colIdx);
+ *x = col->getXMax();
+ par0 = (TextParagraph *)col->paragraphs->get(0);
+ line0 = (TextLine *)par0->lines->get(0);
+ *y = line0->getBaseline() - selectionAscent * line0->fontSize;
+}
+
+void TextPage::getColumnLowerLeft(int colIdx, double *x, double *y) {
+ TextColumn *col;
+
+ buildFindCols();
+ col = (TextColumn *)findCols->get(colIdx);
+ *x = col->getXMin();
+ *y = col->getYMax();
+}
+
+void TextPage::buildFindCols() {
+ TextBlock *tree;
+ int rot;
+
+ if (findCols) {
+ return;
+ }
+ rot = rotateChars(chars);
+ if ((tree = splitChars(chars))) {
+ findCols = buildColumns(tree, gFalse);
+ delete tree;
+ } else {
+ // no text
+ findCols = new GList();
+ }
+ unrotateChars(chars, rot);
+ unrotateColumns(findCols, rot);
+}
+
+TextWordList *TextPage::makeWordList() {
+ return makeWordListForChars(chars);
+}
+
+TextWordList *TextPage::makeWordListForRect(double xMin, double yMin,
+ double xMax, double yMax) {
+ TextWordList *words;
+ GList *chars2;
+ TextChar *ch;
+ double xx, yy;
+ int i;
+
+ // get all chars in the rectangle
+ // (i.e., all chars whose center lies inside the rectangle)
+ chars2 = new GList();
+ for (i = 0; i < chars->getLength(); ++i) {
+ ch = (TextChar *)chars->get(i);
+ xx = 0.5 * (ch->xMin + ch->xMax);
+ yy = 0.5 * (ch->yMin + ch->yMax);
+ if (xx > xMin && xx < xMax && yy > yMin && yy < yMax) {
+ chars2->append(ch);
+ }
+ }
+
+ words = makeWordListForChars(chars2);
+
+ delete chars2;
+
+ return words;
+}
+
+TextWordList *TextPage::makeWordListForChars(GList *charList) {
+ TextBlock *tree;
+ GList *columns;
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextWord *word;
+ GList *overlappingChars;
+ GList *words;
+ GBool primaryLR;
+ int rot, colIdx, parIdx, lineIdx, wordIdx;
+
+#if 0 //~debug
+ dumpChars(charList);
+#endif
+
+ 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);
+#endif
+ if (!tree) {
+ // no text
+ unrotateChars(charList, rot);
+ return new TextWordList(new GList(), gTrue);
+ }
+ columns = buildColumns(tree, primaryLR);
+#if 0 //~debug
+ dumpColumns(columns, gTrue);
+#endif
+ 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();
+ 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);
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ word = ((TextWord *)line->words->get(wordIdx))->copy();
+ if (wordIdx == line->words->getLength() - 1 &&
+ !line->getHyphenated()) {
+ word->spaceAfter = gTrue;
+ }
+ words->append(word);
+ }
+ }
+ }
+ }
+
+ switch (control.mode) {
+ case textOutReadingOrder:
+ case textOutSimple2Layout:
+ // already in reading order
+ break;
+ case textOutPhysLayout:
+ case textOutSimpleLayout:
+ case textOutTableLayout:
+ case textOutLinePrinter:
+ words->sort(&TextWord::cmpYX);
+ break;
+ case textOutRawOrder:
+ words->sort(&TextWord::cmpCharPos);
+ break;
+ }
+
+ // this has to be done after sorting with cmpYX
+ unrotateWords(words, rot);
+
+ deleteGList(columns, TextColumn);
+
+ return new TextWordList(words, primaryLR);
+}
+
+GBool TextPage::primaryDirectionIsLR() {
+ return checkPrimaryLR(chars);
+}
+
+//------------------------------------------------------------------------
+// TextPage: debug
+//------------------------------------------------------------------------
+
+#if 0 //~debug
+
+void TextPage::dumpChars(GList *charsA) {
+ TextChar *ch;
+ int i;
+
+ for (i = 0; i < charsA->getLength(); ++i) {
+ ch = (TextChar *)charsA->get(i);
+ printf("char: U+%04x '%c' xMin=%g yMin=%g xMax=%g yMax=%g fontSize=%g rot=%d charPos=%d charLen=%d spaceAfter=%d\n",
+ ch->c, ch->c & 0xff, ch->xMin, ch->yMin, ch->xMax, ch->yMax,
+ ch->fontSize, ch->rot, ch->charPos, ch->charLen, ch->spaceAfter);
+ }
+}
+
+void TextPage::dumpTree(TextBlock *tree, int indent) {
+ TextChar *ch;
+ int i;
+
+ printf("%*sblock: type=%s tag=%s small=%d rot=%d xMin=%g yMin=%g xMax=%g yMax=%g\n",
+ indent, "",
+ tree->type == blkLeaf ? "leaf" :
+ tree->type == blkHorizSplit ? "horiz" : "vert",
+ tree->tag == blkTagMulticolumn ? "multicolumn" :
+ 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) {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ ch = (TextChar *)tree->children->get(i);
+ printf("%*schar: '%c' xMin=%g yMin=%g xMax=%g yMax=%g font=%d.%d\n",
+ indent + 2, "", ch->c & 0xff,
+ ch->xMin, ch->yMin, ch->xMax, ch->yMax,
+ ch->font->fontID.num, ch->font->fontID.gen);
+ }
+ } else {
+ for (i = 0; i < tree->children->getLength(); ++i) {
+ dumpTree((TextBlock *)tree->children->get(i), indent + 2);
+ }
+ }
+}
+
+void TextPage::dumpColumns(GList *columns, GBool dumpWords) {
+ TextColumn *col;
+ TextParagraph *par;
+ TextLine *line;
+ TextWord *word;
+ int colIdx, parIdx, lineIdx, wordIdx, i;
+
+ for (colIdx = 0; colIdx < columns->getLength(); ++colIdx) {
+ col = (TextColumn *)columns->get(colIdx);
+ printf("column: xMin=%g yMin=%g xMax=%g yMax=%g px=%d py=%d pw=%d ph=%d\n",
+ col->xMin, col->yMin, col->xMax, col->yMax,
+ col->px, col->py, col->pw, col->ph);
+ for (parIdx = 0; parIdx < col->paragraphs->getLength(); ++parIdx) {
+ par = (TextParagraph *)col->paragraphs->get(parIdx);
+ printf(" paragraph:\n");
+ for (lineIdx = 0; lineIdx < par->lines->getLength(); ++lineIdx) {
+ line = (TextLine *)par->lines->get(lineIdx);
+ printf(" line: xMin=%g yMin=%g xMax=%g yMax=%g px=%d pw=%d rot=%d\n",
+ line->xMin, line->yMin, line->xMax, line->yMax,
+ line->px, line->pw, line->rot);
+ if (dumpWords) {
+ for (wordIdx = 0; wordIdx < line->words->getLength(); ++wordIdx) {
+ 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(" '");
+ for (i = 0; i < word->len; ++i) {
+ printf("%c", word->text[i] & 0xff);
+ }
+ printf("'\n");
+ }
+ } else {
+ printf(" '");
+ for (i = 0; i < line->len; ++i) {
+ printf("%c", line->text[i] & 0xff);
+ }
+ printf("'\n");
+ }
+ }
+ }
+ }
+}
+
+void TextPage::dumpUnderlines() {
+ TextUnderline *u;
+ int i;
+
+ printf("underlines:\n");
+ for (i = 0; i < underlines->getLength(); ++i) {
+ u = (TextUnderline *)underlines->get(i);
+ printf(" horiz=%d x0=%g y0=%g x1=%g y1=%g\n",
+ u->horiz, u->x0, u->y0, u->x1, u->y1);
+ }
+}
+#endif //~debug
+
+//------------------------------------------------------------------------
+// TextOutputDev
+//------------------------------------------------------------------------
+
+static void outputToFile(void *stream, const char *text, int len) {
+ fwrite(text, 1, len, (FILE *)stream);
+}
+
+TextOutputDev::TextOutputDev(char *fileName, TextOutputControl *controlA,
+ GBool append, GBool fileNameIsUTF8) {
+ text = NULL;
+ control = *controlA;
+ ok = gTrue;
+
+ // open file
+ needClose = gFalse;
+ if (fileName) {
+ if (!strcmp(fileName, "-")) {
+ outputStream = stdout;
+#ifdef WIN32
+ // keep DOS from munging the end-of-line characters
+ setmode(fileno(stdout), O_BINARY);
+#endif
+ } 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;
+ }
+ outputFunc = &outputToFile;
+ } else {
+ outputFunc = NULL;
+ outputStream = NULL;
+ }
+
+ // set up text object
+ text = new TextPage(&control);
+ generateBOM();
+}
+
+TextOutputDev::TextOutputDev(TextOutputFunc func, void *stream,
+ TextOutputControl *controlA) {
+ outputFunc = func;
+ outputStream = stream;
+ needClose = gFalse;
+ control = *controlA;
+ text = new TextPage(&control);
+ generateBOM();
+ ok = gTrue;
+}
+
+TextOutputDev::~TextOutputDev() {
+ if (needClose) {
+ fclose((FILE *)outputStream);
+ }
+ if (text) {
+ delete text;
+ }
+}
+
+void TextOutputDev::generateBOM() {
+ UnicodeMap *uMap;
+ char bom[8];
+ int bomLen;
+
+ // insert Unicode BOM
+ if (control.insertBOM && outputStream) {
+ if (!(uMap = globalParams->getTextEncoding())) {
+ return;
+ }
+ bomLen = uMap->mapUnicode(0xfeff, bom, sizeof(bom));
+ uMap->decRefCnt();
+ (*outputFunc)(outputStream, bom, bomLen);
+ }
+}
+
+void TextOutputDev::startPage(int pageNum, GfxState *state) {
+ text->startPage(state);
+}
+
+void TextOutputDev::endPage() {
+ if (outputStream) {
+ text->write(outputStream, outputFunc);
+ }
+}
+
+void TextOutputDev::restoreState(GfxState *state) {
+ text->updateFont(state);
+}
+
+void TextOutputDev::updateFont(GfxState *state) {
+ text->updateFont(state);
+}
+
+void TextOutputDev::beginString(GfxState *state, GString *s) {
+}
+
+void TextOutputDev::endString(GfxState *state) {
+}
+
+void TextOutputDev::drawChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ double originX, double originY,
+ CharCode c, int nBytes, Unicode *u, int uLen) {
+ text->addChar(state, x, y, dx, dy, c, nBytes, u, uLen);
+}
+
+void TextOutputDev::incCharCount(int nChars) {
+ text->incCharCount(nChars);
+}
+
+void TextOutputDev::beginActualText(GfxState *state, Unicode *u, int uLen) {
+ text->beginActualText(state, u, uLen);
+}
+
+void TextOutputDev::endActualText(GfxState *state) {
+ text->endActualText(state);
+}
+
+void TextOutputDev::stroke(GfxState *state) {
+ GfxPath *path;
+ GfxSubpath *subpath;
+ double x[2], y[2], t;
+
+ if (!control.html) {
+ return;
+ }
+ path = state->getPath();
+ if (path->getNumSubpaths() != 1) {
+ return;
+ }
+ subpath = path->getSubpath(0);
+ if (subpath->getNumPoints() != 2) {
+ return;
+ }
+ state->transform(subpath->getX(0), subpath->getY(0), &x[0], &y[0]);
+ state->transform(subpath->getX(1), subpath->getY(1), &x[1], &y[1]);
+
+ // look for a vertical or horizontal line
+ if (x[0] == x[1] || y[0] == y[1]) {
+ if (x[0] > x[1]) {
+ t = x[0]; x[0] = x[1]; x[1] = t;
+ }
+ if (y[0] > y[1]) {
+ t = y[0]; y[0] = y[1]; y[1] = t;
+ }
+ text->addUnderline(x[0], y[0], x[1], y[1]);
+ }
+}
+
+void TextOutputDev::fill(GfxState *state) {
+ GfxPath *path;
+ GfxSubpath *subpath;
+ double x[5], y[5];
+ double rx0, ry0, rx1, ry1, t;
+ int i;
+
+ if (!control.html) {
+ return;
+ }
+ path = state->getPath();
+ if (path->getNumSubpaths() != 1) {
+ return;
+ }
+ subpath = path->getSubpath(0);
+ if (subpath->getNumPoints() != 5) {
+ return;
+ }
+ for (i = 0; i < 5; ++i) {
+ if (subpath->getCurve(i)) {
+ return;
+ }
+ state->transform(subpath->getX(i), subpath->getY(i), &x[i], &y[i]);
+ }
+
+ // look for a rectangle
+ if (x[0] == x[1] && y[1] == y[2] && x[2] == x[3] && y[3] == y[4] &&
+ x[0] == x[4] && y[0] == y[4]) {
+ rx0 = x[0];
+ ry0 = y[0];
+ rx1 = x[2];
+ ry1 = y[1];
+ } else if (y[0] == y[1] && x[1] == x[2] && y[2] == y[3] && x[3] == x[4] &&
+ x[0] == x[4] && y[0] == y[4]) {
+ rx0 = x[0];
+ ry0 = y[0];
+ rx1 = x[1];
+ ry1 = y[2];
+ } else {
+ return;
+ }
+ if (rx1 < rx0) {
+ t = rx0;
+ rx0 = rx1;
+ rx1 = t;
+ }
+ if (ry1 < ry0) {
+ t = ry0;
+ ry0 = ry1;
+ ry1 = t;
+ }
+
+ // skinny horizontal rectangle
+ if (ry1 - ry0 < rx1 - rx0) {
+ if (ry1 - ry0 < maxUnderlineWidth) {
+ ry0 = 0.5 * (ry0 + ry1);
+ text->addUnderline(rx0, ry0, rx1, ry0);
+ }
+
+ // skinny vertical rectangle
+ } else {
+ if (rx1 - rx0 < maxUnderlineWidth) {
+ rx0 = 0.5 * (rx0 + rx1);
+ text->addUnderline(rx0, ry0, rx0, ry1);
+ }
+ }
+}
+
+void TextOutputDev::eoFill(GfxState *state) {
+ if (!control.html) {
+ return;
+ }
+ fill(state);
+}
+
+void TextOutputDev::processLink(Link *link) {
+ double x1, y1, x2, y2;
+ int xMin, yMin, xMax, yMax, x, y;
+
+ if (!control.html) {
+ return;
+ }
+ link->getRect(&x1, &y1, &x2, &y2);
+ cvtUserToDev(x1, y1, &x, &y);
+ xMin = xMax = x;
+ yMin = yMax = y;
+ cvtUserToDev(x1, y2, &x, &y);
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ cvtUserToDev(x2, y1, &x, &y);
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ cvtUserToDev(x2, y2, &x, &y);
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ text->addLink(xMin, yMin, xMax, yMax, link);
+}
+
+GBool TextOutputDev::findText(Unicode *s, int len,
+ GBool startAtTop, GBool stopAtBottom,
+ GBool startAtLast, GBool stopAtLast,
+ GBool caseSensitive, GBool backward,
+ GBool wholeWord,
+ double *xMin, double *yMin,
+ double *xMax, double *yMax) {
+ return text->findText(s, len, startAtTop, stopAtBottom,
+ startAtLast, stopAtLast,
+ caseSensitive, backward, wholeWord,
+ xMin, yMin, xMax, yMax);
+}
+
+GString *TextOutputDev::getText(double xMin, double yMin,
+ double xMax, double yMax) {
+ return text->getText(xMin, yMin, xMax, yMax);
+}
+
+GBool TextOutputDev::findCharRange(int pos, int length,
+ double *xMin, double *yMin,
+ double *xMax, double *yMax) {
+ return text->findCharRange(pos, length, xMin, yMin, xMax, yMax);
+}
+
+TextWordList *TextOutputDev::makeWordList() {
+ return text->makeWordList();
+}
+
+TextWordList *TextOutputDev::makeWordListForRect(double xMin, double yMin,
+ double xMax, double yMax) {
+ return text->makeWordListForRect(xMin, yMin, xMax, yMax);
+}
+
+TextPage *TextOutputDev::takeText() {
+ TextPage *ret;
+
+ ret = text;
+ text = new TextPage(&control);
+ return ret;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h
new file mode 100644
index 00000000000..a1d19c22d7d
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextOutputDev.h
@@ -0,0 +1,779 @@
+//========================================================================
+//
+// TextOutputDev.h
+//
+// Copyright 1997-2012 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef TEXTOUTPUTDEV_H
+#define TEXTOUTPUTDEV_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include "gtypes.h"
+#include "GfxFont.h"
+#include "OutputDev.h"
+
+class GList;
+class UnicodeMap;
+class UnicodeRemapping;
+
+class TextBlock;
+class TextChar;
+class TextGaps;
+class TextLink;
+class TextPage;
+
+//------------------------------------------------------------------------
+
+typedef void (*TextOutputFunc)(void *stream, const char *text, int len);
+
+//------------------------------------------------------------------------
+// TextOutputControl
+//------------------------------------------------------------------------
+
+enum TextOutputMode {
+ textOutReadingOrder, // format into reading order
+ textOutPhysLayout, // maintain original physical layout
+ textOutSimpleLayout, // simple one-column physical layout
+ textOutSimple2Layout, // simple one-column physical layout
+ textOutTableLayout, // similar to PhysLayout, but optimized
+ // for tables
+ textOutLinePrinter, // strict fixed-pitch/height layout
+ textOutRawOrder // keep text in content stream order
+};
+
+enum TextOutputOverlapHandling {
+ textOutIgnoreOverlaps, // no special handling for overlaps
+ textOutAppendOverlaps, // append overlapping text to main text
+ textOutDiscardOverlaps // discard overlapping text
+};
+
+class TextOutputControl {
+public:
+
+ TextOutputControl();
+ ~TextOutputControl() {}
+
+ TextOutputMode mode; // formatting mode
+ double fixedPitch; // if this is non-zero, assume fixed-pitch
+ // characters with this width
+ // (only relevant for PhysLayout, Table,
+ // and LinePrinter modes)
+ double fixedLineSpacing; // fixed line spacing (only relevant for
+ // LinePrinter mode)
+ GBool html; // enable extra processing for HTML
+ GBool clipText; // separate clipped text and add it back
+ // 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
+ marginRight, // discarded
+ marginTop,
+ marginBottom;
+};
+
+//------------------------------------------------------------------------
+// TextFontInfo
+//------------------------------------------------------------------------
+
+class TextFontInfo {
+public:
+
+ TextFontInfo(GfxState *state);
+ ~TextFontInfo();
+
+ GBool matches(GfxState *state);
+
+ // Get the font name (which may be NULL).
+ GString *getFontName() { return fontName; }
+
+ // Get font descriptor flags.
+ GBool isFixedWidth() { return flags & fontFixedWidth; }
+ GBool isSerif() { return flags & fontSerif; }
+ GBool isSymbolic() { return flags & fontSymbolic; }
+ GBool isItalic() { return flags & fontItalic; }
+ GBool isBold() { return flags & fontBold; }
+
+ // Get the width of the 'm' character, if available.
+ double getMWidth() { return mWidth; }
+
+ Ref getFontID() { return fontID; }
+
+private:
+
+ Ref fontID;
+ GString *fontName;
+ int flags;
+ double mWidth;
+ double ascent, descent;
+
+ friend class TextLine;
+ friend class TextPage;
+ friend class TextWord;
+};
+
+//------------------------------------------------------------------------
+// TextWord
+//------------------------------------------------------------------------
+
+class TextWord {
+public:
+
+ TextWord(GList *chars, int start, int lenA,
+ int rotA, GBool rotatedA, int dirA, GBool spaceAfterA);
+ ~TextWord();
+ TextWord *copy() { return new TextWord(this); }
+
+ // Get the TextFontInfo object associated with this word.
+ TextFontInfo *getFontInfo() { return font; }
+
+ int getLength() { return len; }
+ Unicode getChar(int idx) { return text[idx]; }
+ GString *getText();
+ GString *getFontName() { return font->fontName; }
+ void getColor(double *r, double *g, double *b)
+ { *r = colorR; *g = colorG; *b = colorB; }
+ GBool isInvisible() { return invisible; }
+ void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA)
+ { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
+ void getCharBBox(int charIdx, double *xMinA, double *yMinA,
+ 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; }
+ GBool getSpaceAfter() { return spaceAfter; }
+ double getBaseline();
+ GBool isUnderlined() { return underlined; }
+ GString *getLinkURI();
+
+private:
+
+ TextWord(TextWord *word);
+ static int cmpYX(const void *p1, const void *p2);
+ static int cmpCharPos(const void *p1, const void *p2);
+
+ double xMin, xMax; // bounding box x coordinates
+ double yMin, yMax; // bounding box y coordinates
+ Unicode *text; // the text
+ int *charPos; // character position (within content stream)
+ // of each char (plus one extra entry for
+ // the last char)
+ double *edge; // "near" edge x or y coord of each char
+ // (plus one extra entry for the last char)
+ int len; // number of characters
+ TextFontInfo *font; // font information
+ double fontSize; // font size
+ 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;
+};
+
+//------------------------------------------------------------------------
+// TextLine
+//------------------------------------------------------------------------
+
+class TextLine {
+public:
+
+ TextLine(GList *wordsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA, double fontSizeA);
+ ~TextLine();
+
+ double getXMin() { return xMin; }
+ double getYMin() { return yMin; }
+ double getXMax() { return xMax; }
+ double getYMax() { return yMax; }
+ double getBaseline();
+ int getRotation() { return rot; }
+ GList *getWords() { return words; }
+ int getLength() { return len; }
+ double getEdge(int idx) { return edge[idx]; }
+ GBool getHyphenated() { return hyphenated; }
+
+private:
+
+ static int cmpX(const void *p1, const void *p2);
+
+ GList *words; // [TextWord]
+ 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
+ double fontSize; // main (max) font size for this line
+ Unicode *text; // Unicode text of the line, including
+ // spaces between words
+ double *edge; // "near" edge x or y coord of each char
+ // (plus one extra entry for the last char)
+ int len; // number of Unicode chars
+ GBool hyphenated; // set if last char is a hyphen
+ int px; // x offset (in characters, relative to
+ // containing column) in physical layout mode
+ int pw; // line width (in characters) in physical
+ // layout mode
+
+ friend class TextSuperLine;
+ friend class TextPage;
+ friend class TextParagraph;
+};
+
+//------------------------------------------------------------------------
+// TextParagraph
+//------------------------------------------------------------------------
+
+class TextParagraph {
+public:
+
+ TextParagraph(GList *linesA, GBool dropCapA);
+ ~TextParagraph();
+
+ // Get the list of TextLine objects.
+ GList *getLines() { return lines; }
+
+ GBool hasDropCap() { return dropCap; }
+
+ double getXMin() { return xMin; }
+ double getYMin() { return yMin; }
+ double getXMax() { return xMax; }
+ double getYMax() { return yMax; }
+
+private:
+
+ GList *lines; // [TextLine]
+ GBool dropCap; // paragraph starts with a drop capital
+ double xMin, xMax; // bounding box x coordinates
+ double yMin, yMax; // bounding box y coordinates
+
+ friend class TextPage;
+};
+
+//------------------------------------------------------------------------
+// TextColumn
+//------------------------------------------------------------------------
+
+class TextColumn {
+public:
+
+ TextColumn(GList *paragraphsA, double xMinA, double yMinA,
+ double xMaxA, double yMaxA);
+ ~TextColumn();
+
+ // Get the list of TextParagraph objects.
+ GList *getParagraphs() { return paragraphs; }
+
+ double getXMin() { return xMin; }
+ double getYMin() { return yMin; }
+ double getXMax() { return xMax; }
+ double getYMax() { return yMax; }
+
+ int getRotation();
+
+private:
+
+ static int cmpX(const void *p1, const void *p2);
+ static int cmpY(const void *p1, const void *p2);
+ static int cmpPX(const void *p1, const void *p2);
+
+ GList *paragraphs; // [TextParagraph]
+ double xMin, xMax; // bounding box x coordinates
+ double yMin, yMax; // bounding box y coordinates
+ int px, py; // x, y position (in characters) in physical
+ // layout mode
+ int pw, ph; // column width, height (in characters) in
+ // physical layout mode
+
+ friend class TextPage;
+};
+
+//------------------------------------------------------------------------
+// TextWordList
+//------------------------------------------------------------------------
+
+class TextWordList {
+public:
+
+ TextWordList(GList *wordsA, GBool primaryLRA);
+
+ ~TextWordList();
+
+ // Return the number of words on the list.
+ int getLength();
+
+ // Return the <idx>th word from the list.
+ TextWord *get(int idx);
+
+ // Returns true if primary direction is left-to-right, or false if
+ // right-to-left.
+ GBool getPrimaryLR() { return primaryLR; }
+
+private:
+
+ GList *words; // [TextWord]
+ GBool primaryLR;
+};
+
+//------------------------------------------------------------------------
+// TextPosition
+//------------------------------------------------------------------------
+
+// Position within a TextColumn tree. The position is in column
+// [colIdx], paragraph [parIdx], line [lineIdx], before character
+// [charIdx].
+class TextPosition {
+public:
+
+ TextPosition(): colIdx(0), parIdx(0), lineIdx(0), charIdx(0) {}
+ TextPosition(int colIdxA, int parIdxA, int lineIdxA, int charIdxA):
+ colIdx(colIdxA), parIdx(parIdxA), lineIdx(lineIdxA), charIdx(charIdxA) {}
+
+ int operator==(TextPosition pos);
+ int operator!=(TextPosition pos);
+ int operator<(TextPosition pos);
+ int operator>(TextPosition pos);
+
+ int colIdx, parIdx, lineIdx, charIdx;
+};
+
+//------------------------------------------------------------------------
+// TextPage
+//------------------------------------------------------------------------
+
+class TextPage {
+public:
+
+ TextPage(TextOutputControl *controlA);
+ ~TextPage();
+
+ // Write contents of page to a stream.
+ void write(void *outputStream, TextOutputFunc outputFunc);
+
+ // Find a string. If <startAtTop> is true, starts looking at the
+ // top of the page; else if <startAtLast> is true, starts looking
+ // immediately after the last find result; else starts looking at
+ // <xMin>,<yMin>. If <stopAtBottom> is true, stops looking at the
+ // bottom of the page; else if <stopAtLast> is true, stops looking
+ // just before the last find result; else stops looking at
+ // <xMax>,<yMax>.
+ GBool findText(Unicode *s, int len,
+ GBool startAtTop, GBool stopAtBottom,
+ GBool startAtLast, GBool stopAtLast,
+ GBool caseSensitive, GBool backward,
+ GBool wholeWord,
+ double *xMin, double *yMin,
+ double *xMax, double *yMax);
+
+ // Get the text which is inside the specified rectangle. Multi-line
+ // text always includes end-of-line markers at the end of each line.
+ // If <forceEOL> is true, an end-of-line marker will be appended to
+ // single-line text as well.
+ GString *getText(double xMin, double yMin,
+ double xMax, double yMax,
+ GBool forceEOL = gFalse);
+
+ // Find a string by character position and length. If found, sets
+ // the text bounding rectangle and returns true; otherwise returns
+ // false.
+ GBool findCharRange(int pos, int length,
+ double *xMin, double *yMin,
+ double *xMax, double *yMax);
+
+ // Returns true if x,y falls inside a column.
+ GBool checkPointInside(double x, double y);
+
+ // Find a point inside a column. Returns false if x,y fall outside
+ // all columns.
+ GBool findPointInside(double x, double y, TextPosition *pos);
+
+ // Find a point in the nearest column. Returns false only if there
+ // 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);
+
+ // Get the lower point of a TextPosition.
+ void convertPosToPointLower(TextPosition *pos, double *x, double *y);
+
+ // Get the upper left corner of the line containing a TextPosition.
+ void convertPosToPointLeftEdge(TextPosition *pos, double *x, double *y);
+
+ // Get the lower right corner of the line containing a TextPosition.
+ void convertPosToPointRightEdge(TextPosition *pos, double *x, double *y);
+
+ // Get the upper right corner of a column.
+ void getColumnUpperRight(int colIdx, double *x, double *y);
+
+ // Get the lower left corner of a column.
+ void getColumnLowerLeft(int colIdx, double *x, double *y);
+
+ // Create and return a list of TextColumn objects.
+ GList *makeColumns();
+
+ // Get the list of all TextFontInfo objects used on this page.
+ GList *getFonts() { return fonts; }
+
+ // Build a flat word list, in the specified ordering.
+ TextWordList *makeWordList();
+
+ // Build a word list containing only words inside the specified
+ // rectangle.
+ TextWordList *makeWordListForRect(double xMin, double yMin,
+ double xMax, double yMax);
+
+ // Returns true if the primary character direction is left-to-right,
+ // false if it is right-to-left.
+ GBool primaryDirectionIsLR();
+
+ // Returns true if any of the fonts used on this page are likely to
+ // be problematic when converting text to Unicode.
+ GBool problematicForUnicode() { return problematic; }
+
+private:
+
+ void startPage(GfxState *state);
+ void clear();
+ void updateFont(GfxState *state);
+ void addChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ CharCode c, int nBytes, Unicode *u, int uLen);
+ void incCharCount(int nChars);
+ void beginActualText(GfxState *state, Unicode *u, int uLen);
+ void endActualText(GfxState *state);
+ void addUnderline(double x0, double y0, double x1, double y1);
+ void addLink(double xMin, double yMin, double xMax, double yMax,
+ Link *link);
+
+ // output
+ void writeReadingOrder(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
+ void writePhysLayout(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
+ void writeSimpleLayout(void *outputStream,
+ TextOutputFunc outputFunc,
+ 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,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
+ void writeRaw(void *outputStream,
+ TextOutputFunc outputFunc,
+ UnicodeMap *uMap,
+ char *space, int spaceLen,
+ char *eol, int eolLen);
+ void encodeFragment(Unicode *text, int len, UnicodeMap *uMap,
+ GBool primaryLR, GString *s);
+
+ // 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,
+ double xMax, double yMax);
+ void findGaps(GList *charsA, int rot,
+ double *xMinOut, double *yMinOut,
+ double *xMaxOut, double *yMaxOut,
+ double *avgFontSizeOut,
+ TextGaps *horizGaps, TextGaps *vertGaps);
+ void tagBlock(TextBlock *blk);
+ void insertLargeChars(GList *largeChars, TextBlock *blk);
+ void insertLargeCharsInFirstLeaf(GList *largeChars, TextBlock *blk);
+ void insertLargeCharInLeaf(TextChar *ch, TextBlock *blk);
+ void insertIntoTree(TextBlock *subtree, TextBlock *primaryTree);
+ void insertColumnIntoTree(TextBlock *column, TextBlock *tree);
+ void insertClippedChars(GList *clippedChars, TextBlock *tree);
+ TextBlock *findClippedCharLeaf(TextChar *ch, TextBlock *tree);
+ GList *buildColumns(TextBlock *tree, GBool primaryLR);
+ void buildColumns2(TextBlock *blk, GList *columns, GBool primaryLR);
+ TextColumn *buildColumn(TextBlock *tree);
+ double getLineIndent(TextLine *line, TextBlock *blk);
+ double getAverageLineSpacing(GList *lines);
+ double getLineSpacing(TextLine *line0, TextLine *line1);
+ 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);
+ int assignPhysLayoutPositions(GList *columns);
+ void assignLinePhysPositions(GList *columns);
+ void computeLinePhysWidth(TextLine *line, UnicodeMap *uMap);
+ int assignColumnPhysPositions(GList *columns);
+ void buildSuperLines(TextBlock *blk, GList *superLines);
+ void assignSimpleLayoutPositions(GList *superLines, UnicodeMap *uMap);
+ void generateUnderlinesAndLinks(GList *columns);
+ void findPointInColumn(TextColumn *col, double x, double y,
+ TextPosition *pos);
+ void buildFindCols();
+
+ // debug
+#if 0 //~debug
+ void dumpChars(GList *charsA);
+ void dumpTree(TextBlock *tree, int indent = 0);
+ void dumpColumns(GList *columns, GBool dumpWords = gFalse);
+ void dumpUnderlines();
+#endif
+
+ // word list
+ TextWordList *makeWordListForChars(GList *charList);
+
+ TextOutputControl control; // formatting parameters
+
+ UnicodeRemapping *remapping;
+ Unicode *uBuf;
+ int uBufSize;
+
+ double pageWidth, pageHeight; // width and height of current page
+ int charPos; // next character position (within content
+ // stream)
+ TextFontInfo *curFont; // current font
+ double curFontSize; // current font size
+ 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;
+ double actualTextX0,
+ actualTextY0,
+ actualTextX1,
+ actualTextY1;
+ int actualTextNBytes;
+
+ GList *chars; // [TextChar]
+ GList *fonts; // all font info objects used on this
+ // page [TextFontInfo]
+
+ GList *underlines; // [TextUnderline]
+ GList *links; // [TextLink]
+
+ GList *findCols; // text used by the findText**/findPoint**
+ // functions [TextColumn]
+ double lastFindXMin, // coordinates of the last "find" result
+ lastFindYMin;
+ GBool haveLastFind;
+
+ GBool problematic; // true if any of the fonts used on this
+ // page are marked as problematic for
+ // Unicode conversion
+
+ friend class TextOutputDev;
+};
+
+//------------------------------------------------------------------------
+// TextOutputDev
+//------------------------------------------------------------------------
+
+class TextOutputDev: public OutputDev {
+public:
+
+ // Open a text output file. If <fileName> is NULL, no file is
+ // written (this is useful, e.g., for searching text). If
+ // <physLayoutA> is true, the original physical layout of the text
+ // is maintained. If <rawOrder> is true, the text is kept in
+ // content stream order.
+ TextOutputDev(char *fileName, TextOutputControl *controlA,
+ 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
+ // is maintained. If <rawOrder> is true, the text is kept in
+ // content stream order.
+ TextOutputDev(TextOutputFunc func, void *stream,
+ TextOutputControl *controlA);
+
+ // Destructor.
+ virtual ~TextOutputDev();
+
+ // Check if file was successfully created.
+ virtual GBool isOk() { return ok; }
+
+ //---- get info about output device
+
+ // Does this device use upside-down coordinates?
+ // (Upside-down means (0,0) is the top left corner of the page.)
+ virtual GBool upsideDown() { return gTrue; }
+
+ // Does this device use drawChar() or drawString()?
+ virtual GBool useDrawChar() { return gTrue; }
+
+ // Does this device use beginType3Char/endType3Char? Otherwise,
+ // text in Type 3 fonts will be drawn with drawChar/drawString.
+ virtual GBool interpretType3Chars() { return gFalse; }
+
+ // Does this device need non-text content?
+ virtual GBool needNonText() { return gFalse; }
+
+ // Does this device require incCharCount to be called for text on
+ // non-shown layers?
+ virtual GBool needCharCount() { return gTrue; }
+
+ //----- initialization and control
+
+ // Start a page.
+ virtual void startPage(int pageNum, GfxState *state);
+
+ // End a page.
+ virtual void endPage();
+
+ //----- save/restore graphics state
+ virtual void restoreState(GfxState *state);
+
+ //----- update text state
+ virtual void updateFont(GfxState *state);
+
+ //----- text drawing
+ virtual void beginString(GfxState *state, GString *s);
+ virtual void endString(GfxState *state);
+ virtual void drawChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ double originX, double originY,
+ CharCode c, int nBytes, Unicode *u, int uLen);
+ virtual void incCharCount(int nChars);
+ virtual void beginActualText(GfxState *state, Unicode *u, int uLen);
+ virtual void endActualText(GfxState *state);
+
+ //----- path painting
+ virtual void stroke(GfxState *state);
+ virtual void fill(GfxState *state);
+ virtual void eoFill(GfxState *state);
+
+ //----- link borders
+ virtual void processLink(Link *link);
+
+ //----- special access
+
+ // Find a string. If <startAtTop> is true, starts looking at the
+ // top of the page; else if <startAtLast> is true, starts looking
+ // immediately after the last find result; else starts looking at
+ // <xMin>,<yMin>. If <stopAtBottom> is true, stops looking at the
+ // bottom of the page; else if <stopAtLast> is true, stops looking
+ // just before the last find result; else stops looking at
+ // <xMax>,<yMax>.
+ GBool findText(Unicode *s, int len,
+ GBool startAtTop, GBool stopAtBottom,
+ GBool startAtLast, GBool stopAtLast,
+ GBool caseSensitive, GBool backward,
+ GBool wholeWord,
+ double *xMin, double *yMin,
+ double *xMax, double *yMax);
+
+ // Get the text which is inside the specified rectangle.
+ GString *getText(double xMin, double yMin,
+ double xMax, double yMax);
+
+ // Find a string by character position and length. If found, sets
+ // the text bounding rectangle and returns true; otherwise returns
+ // false.
+ GBool findCharRange(int pos, int length,
+ double *xMin, double *yMin,
+ double *xMax, double *yMax);
+
+ // Build a flat word list, in content stream order (if
+ // this->rawOrder is true), physical layout order (if
+ // this->physLayout is true and this->rawOrder is false), or reading
+ // order (if both flags are false).
+ TextWordList *makeWordList();
+
+ // Build a word list containing only words inside the specified
+ // rectangle.
+ TextWordList *makeWordListForRect(double xMin, double yMin,
+ double xMax, double yMax);
+
+ // Returns the TextPage object for the last rasterized page,
+ // transferring ownership to the caller.
+ TextPage *takeText();
+
+ // Turn extra processing for HTML conversion on or off.
+ void enableHTMLExtras(GBool html) { control.html = html; }
+
+private:
+
+ void generateBOM();
+
+ TextOutputFunc outputFunc; // output function
+ void *outputStream; // output stream
+ GBool needClose; // need to close the output file?
+ // (only if outputStream is a FILE*)
+ TextPage *text; // text for the current page
+ TextOutputControl control; // formatting parameters
+ GBool ok; // set up ok?
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc
new file mode 100644
index 00000000000..8e54a1d5471
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.cc
@@ -0,0 +1,213 @@
+//========================================================================
+//
+// TextString.cc
+//
+// Copyright 2011-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "PDFDocEncoding.h"
+#include "UTF8.h"
+#include "TextString.h"
+
+//------------------------------------------------------------------------
+
+TextString::TextString() {
+ u = NULL;
+ len = size = 0;
+}
+
+TextString::TextString(GString *s) {
+ u = NULL;
+ len = size = 0;
+ append(s);
+}
+
+TextString::TextString(TextString *s) {
+ len = size = s->len;
+ if (len) {
+ u = (Unicode *)gmallocn(size, sizeof(Unicode));
+ memcpy(u, s->u, len * sizeof(Unicode));
+ } else {
+ u = NULL;
+ }
+}
+
+TextString::~TextString() {
+ gfree(u);
+}
+
+TextString *TextString::append(Unicode c) {
+ expand(1);
+ u[len] = c;
+ ++len;
+ return this;
+}
+
+TextString *TextString::append(GString *s) {
+ return insert(len, s);
+}
+
+TextString *TextString::insert(int idx, Unicode c) {
+ if (idx >= 0 && idx <= len) {
+ expand(1);
+ if (idx < len) {
+ memmove(u + idx + 1, u + idx, (len - idx) * sizeof(Unicode));
+ }
+ u[idx] = c;
+ ++len;
+ }
+ return this;
+}
+
+TextString *TextString::insert(int idx, Unicode *u2, int n) {
+ if (idx >= 0 && idx <= len) {
+ expand(n);
+ if (idx < len) {
+ memmove(u + idx + n, u + idx, (len - idx) * sizeof(Unicode));
+ }
+ memcpy(u + idx, u2, n * sizeof(Unicode));
+ len += n;
+ }
+ return this;
+}
+
+TextString *TextString::insert(int idx, GString *s) {
+ Unicode uBuf[100];
+ int n, i;
+
+ if (idx >= 0 && idx <= len) {
+ // look for a UTF-16BE BOM
+ if ((s->getChar(0) & 0xff) == 0xfe &&
+ (s->getChar(1) & 0xff) == 0xff) {
+ i = 2;
+ n = 0;
+ while (getUTF16BE(s, &i, uBuf + n)) {
+ ++n;
+ if (n == sizeof(uBuf) / sizeof(Unicode)) {
+ insert(idx, uBuf, n);
+ idx += n;
+ n = 0;
+ }
+ }
+ if (n > 0) {
+ insert(idx, uBuf, n);
+ }
+
+ // look for a UTF-16LE BOM
+ // (technically, this isn't allowed by the PDF spec, but some
+ // PDF files use it)
+ } else if ((s->getChar(0) & 0xff) == 0xff &&
+ (s->getChar(1) & 0xff) == 0xfe) {
+ i = 2;
+ n = 0;
+ while (getUTF16LE(s, &i, uBuf + n)) {
+ ++n;
+ if (n == sizeof(uBuf) / sizeof(Unicode)) {
+ insert(idx, uBuf, n);
+ idx += n;
+ n = 0;
+ }
+ }
+ if (n > 0) {
+ insert(idx, uBuf, n);
+ }
+
+ // look for a UTF-8 BOM
+ } else if ((s->getChar(0) & 0xff) == 0xef &&
+ (s->getChar(1) & 0xff) == 0xbb &&
+ (s->getChar(2) & 0xff) == 0xbf) {
+ i = 3;
+ n = 0;
+ while (getUTF8(s, &i, uBuf + n)) {
+ ++n;
+ if (n == sizeof(uBuf) / sizeof(Unicode)) {
+ insert(idx, uBuf, n);
+ idx += n;
+ n = 0;
+ }
+ }
+ if (n > 0) {
+ insert(idx, uBuf, n);
+ }
+
+ // otherwise, use PDFDocEncoding
+ } else {
+ n = s->getLength();
+ expand(n);
+ if (idx < len) {
+ memmove(u + idx + n, u + idx, (len - idx) * sizeof(Unicode));
+ }
+ for (i = 0; i < n; ++i) {
+ u[idx + i] = pdfDocEncoding[s->getChar(i) & 0xff];
+ }
+ len += n;
+ }
+ }
+ return this;
+}
+
+void TextString::expand(int delta) {
+ int newLen;
+
+ newLen = len + delta;
+ if (delta > INT_MAX - len) {
+ // trigger an out-of-memory error
+ size = -1;
+ } else if (newLen <= size) {
+ return;
+ } else if (size > 0 && size <= INT_MAX / 2 && size*2 >= newLen) {
+ size *= 2;
+ } else {
+ size = newLen;
+ }
+ u = (Unicode *)greallocn(u, size, sizeof(Unicode));
+}
+
+GString *TextString::toPDFTextString() {
+ GString *s;
+ GBool useUnicode;
+ int i;
+
+ useUnicode = gFalse;
+ for (i = 0; i < len; ++i) {
+ if (u[i] >= 0x80) {
+ useUnicode = gTrue;
+ break;
+ }
+ }
+ s = new GString();
+ if (useUnicode) {
+ s->append((char)0xfe);
+ s->append((char)0xff);
+ for (i = 0; i < len; ++i) {
+ s->append((char)(u[i] >> 8));
+ s->append((char)u[i]);
+ }
+ } else {
+ for (i = 0; i < len; ++i) {
+ s->append((char)u[i]);
+ }
+ }
+ return s;
+}
+
+GString *TextString::toUTF8() {
+ GString *s = new GString();
+ for (int i = 0; i < len; ++i) {
+ char buf[8];
+ int n = mapUTF8(u[i], buf, sizeof(buf));
+ s->append(buf, n);
+ }
+ return s;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h b/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h
new file mode 100644
index 00000000000..c296d7d6d7a
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TextString.h
@@ -0,0 +1,71 @@
+//========================================================================
+//
+// TextString.h
+//
+// Copyright 2011-2013 Glyph & Cog, LLC
+//
+// Represents a PDF "text string", which can either be a UTF-16BE
+// string (with a leading byte order marker), or an 8-bit string in
+// PDFDocEncoding.
+//
+//========================================================================
+
+#ifndef TEXTSTRING_H
+#define TEXTSTRING_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "CharTypes.h"
+
+class GString;
+
+//------------------------------------------------------------------------
+
+class TextString {
+public:
+
+ // Create an empty TextString.
+ TextString();
+
+ // Create a TextString from a PDF text string.
+ TextString(GString *s);
+
+ // Copy a TextString.
+ TextString(TextString *s);
+
+ ~TextString();
+
+ // Append a Unicode character or PDF text string to this TextString.
+ TextString *append(Unicode c);
+ TextString *append(GString *s);
+
+ // Insert a Unicode character, sequence of Unicode characters, or
+ // PDF text string in this TextString.
+ TextString *insert(int idx, Unicode c);
+ TextString *insert(int idx, Unicode *u2, int n);
+ TextString *insert(int idx, GString *s);
+
+ // Get the Unicode characters in the TextString.
+ int getLength() { return len; }
+ Unicode *getUnicode() { return u; }
+
+ // Create a PDF text string from a TextString.
+ GString *toPDFTextString();
+
+ // Convert a TextString to UTF-8.
+ GString *toUTF8();
+
+private:
+
+ void expand(int delta);
+
+ Unicode *u; // NB: not null-terminated
+ int len;
+ int size;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TileCache.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TileCache.cc
new file mode 100644
index 00000000000..ff002ed46fc
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TileCache.cc
@@ -0,0 +1,562 @@
+//========================================================================
+//
+// TileCache.cc
+//
+// Copyright 2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "gmempp.h"
+#include "GList.h"
+#include "GMutex.h"
+#ifdef _WIN32
+# include <windows.h>
+#else
+# include <pthread.h>
+# include <unistd.h>
+#endif
+#include "Object.h"
+#include "PDFDoc.h"
+#include "SplashBitmap.h"
+#include "SplashOutputDev.h"
+#include "DisplayState.h"
+#include "GfxState.h"
+#include "TileMap.h"
+#include "TileCache.h"
+
+//------------------------------------------------------------------------
+// CachedTileDesc
+//------------------------------------------------------------------------
+
+enum CachedTileState {
+ cachedTileUnstarted, // worker thread hasn't started
+ // rasterization yet
+ cachedTileStarted, // worker thread is rasterizing the tile
+ cachedTileFinished, // rasterization is done
+ cachedTileCanceled // worker thread should stop rasterizing
+ // and remove this tile from the cache
+};
+
+class CachedTileDesc: public TileDesc {
+public:
+
+ CachedTileDesc(TileDesc *tile):
+ TileDesc(tile->page, tile->rotate, tile->dpi,
+ tile->tx, tile->ty, tile->tw, tile->th),
+ state(cachedTileUnstarted), active(gTrue),
+ bitmap(NULL), freeBitmap(gFalse) {}
+ ~CachedTileDesc();
+
+ CachedTileState state;
+ GBool active;
+ SplashBitmap *bitmap;
+ GBool freeBitmap;
+};
+
+CachedTileDesc::~CachedTileDesc() {
+ if (freeBitmap) {
+ delete bitmap;
+ }
+}
+
+//------------------------------------------------------------------------
+// OS-dependent threading support code
+//
+// NB: This wrapper code is not meant to be general purpose. Pthreads
+// condition objects are not equivalent to Windows event objects, in
+// general.
+//------------------------------------------------------------------------
+
+//-------------------- Windows --------------------
+#ifdef _WIN32
+
+typedef HANDLE GThreadID;
+typedef DWORD (WINAPI *GThreadFunc)(void *);
+#define GThreadReturn DWORD WINAPI
+
+static void gCreateThread(GThreadID *thr, GThreadFunc threadFunc,
+ void *data) {
+ *thr = CreateThread(NULL, 0, threadFunc, data, 0, NULL);
+}
+
+static void gJoinThread(GThreadID thr) {
+ WaitForSingleObject(thr, INFINITE);
+ CloseHandle(thr);
+}
+
+typedef HANDLE GCondition;
+
+static void gInitCondition(GCondition *c) {
+ *c = CreateEvent(NULL, TRUE, FALSE, NULL);
+}
+
+static void gDestroyCondition(GCondition *c) {
+ CloseHandle(*c);
+}
+
+static void gSignalCondition(GCondition *c) {
+ SetEvent(*c);
+}
+
+static void gClearCondition(GCondition *c) {
+ ResetEvent(*c);
+}
+
+static void gWaitCondition(GCondition *c, GMutex *m) {
+ LeaveCriticalSection(m);
+ WaitForSingleObject(*c, INFINITE);
+ EnterCriticalSection(m);
+}
+
+//-------------------- pthreads --------------------
+#else
+
+typedef pthread_t GThreadID;
+typedef void *(*GThreadFunc)(void *);
+#define GThreadReturn void*
+
+static void gCreateThread(GThreadID *thr, GThreadFunc threadFunc,
+ void *data) {
+ pthread_create(thr, NULL, threadFunc, data);
+}
+
+static void gJoinThread(GThreadID thr) {
+ pthread_join(thr, NULL);
+}
+
+typedef pthread_cond_t GCondition;
+
+static void gInitCondition(GCondition *c) {
+ pthread_cond_init(c, NULL);
+}
+
+static void gDestroyCondition(GCondition *c) {
+ pthread_cond_destroy(c);
+}
+
+static void gSignalCondition(GCondition *c) {
+ pthread_cond_broadcast(c);
+}
+
+static void gClearCondition(GCondition *c) {
+}
+
+static void gWaitCondition(GCondition *c, GMutex *m) {
+ pthread_cond_wait(c, m);
+}
+
+#endif
+
+//------------------------------------------------------------------------
+// TileCacheThreadPool
+//------------------------------------------------------------------------
+
+class TileCacheThreadPool {
+public:
+
+ TileCacheThreadPool(TileCache *tileCacheA, int nThreadsA);
+ ~TileCacheThreadPool();
+
+ // Called by the client when one or more jobs have been added to the
+ // queue. This must be called with the mutex unlocked.
+ void jobAdded();
+
+ // Wait for at least one job to be finished. This must be called
+ // with the mutex locked.
+ void waitForFinishedJob();
+
+ // The client should use the mutex to protect the queue data
+ // structure.
+ void lockMutex() { gLockMutex(&mutex); }
+ void unlockMutex() { gUnlockMutex(&mutex); }
+
+private:
+
+ static GThreadReturn threadFunc(void *arg);
+ void worker();
+
+ TileCache *tileCache;
+ int nThreads;
+ GThreadID *threads;
+ GBool quit;
+ GMutex mutex;
+ GCondition cond; // signalled when a job is added to the
+ // queue and when the quit flag is set
+ GCondition finishCond; // signalled whenever a worker thread
+ // finishes rasterizing a tile
+};
+
+TileCacheThreadPool::TileCacheThreadPool(TileCache *tileCacheA,
+ int nThreadsA) {
+ int i;
+
+ tileCache = tileCacheA;
+ nThreads = nThreadsA;
+ quit = gFalse;
+ gInitMutex(&mutex);
+ gInitCondition(&cond);
+ gInitCondition(&finishCond);
+ threads = (GThreadID *)gmallocn(nThreads, sizeof(GThreadID));
+ for (i = 0; i < nThreads; ++i) {
+ gCreateThread(&threads[i], &threadFunc, this);
+ }
+}
+
+TileCacheThreadPool::~TileCacheThreadPool() {
+ int i;
+
+ gLockMutex(&mutex);
+ quit = true;
+ gSignalCondition(&cond);
+ gUnlockMutex(&mutex);
+ for (i = 0; i < nThreads; ++i) {
+ gJoinThread(threads[i]);
+ }
+ gDestroyCondition(&cond);
+ gDestroyCondition(&finishCond);
+ gDestroyMutex(&mutex);
+ gfree(threads);
+}
+
+void TileCacheThreadPool::jobAdded() {
+ gLockMutex(&mutex);
+ gSignalCondition(&cond);
+ gUnlockMutex(&mutex);
+}
+
+GThreadReturn TileCacheThreadPool::threadFunc(void *arg) {
+ ((TileCacheThreadPool *)arg)->worker();
+ return 0;
+}
+
+void TileCacheThreadPool::worker() {
+ CachedTileDesc *ct;
+
+ while (1) {
+ gLockMutex(&mutex);
+ while (!quit && !(ct = tileCache->getUnstartedTile())) {
+ gWaitCondition(&cond, &mutex);
+ }
+ if (quit) {
+ gUnlockMutex(&mutex);
+ break;
+ }
+ if (!tileCache->hasUnstartedTiles()) {
+ gClearCondition(&cond);
+ }
+ gUnlockMutex(&mutex);
+ tileCache->rasterizeTile(ct);
+ gSignalCondition(&finishCond);
+ }
+}
+
+void TileCacheThreadPool::waitForFinishedJob() {
+ gWaitCondition(&finishCond, &mutex);
+}
+
+//------------------------------------------------------------------------
+// TileCache
+//------------------------------------------------------------------------
+
+TileCache::TileCache(DisplayState *stateA) {
+ state = stateA;
+ state->setTileCache(this);
+ cache = new GList();
+ threadPool = new TileCacheThreadPool(this, state->getNWorkerThreads());
+ tileDoneCbk = NULL;
+ tileDoneCbkData = NULL;
+}
+
+TileCache::~TileCache() {
+ flushCache(gFalse);
+ delete threadPool;
+ delete cache;
+}
+
+void TileCache::setActiveTileList(GList *tiles) {
+ TileDesc *tile;
+ CachedTileDesc *ct;
+ int tileIdx, cacheIdx;
+ GBool newTiles;
+
+ threadPool->lockMutex();
+
+ // remove any unstarted tiles not on the new active list;
+ // cancel any started tiles not on the new active list;
+ // mark all other tiles as inactive (active tiles will be marked later)
+ cacheIdx = 0;
+ while (cacheIdx < cache->getLength()) {
+ ct = (CachedTileDesc *)cache->get(cacheIdx);
+ if (ct->state == cachedTileUnstarted && findTile(ct, tiles) < 0) {
+ delete (CachedTileDesc *)cache->del(cacheIdx);
+ } else if (ct->state == cachedTileStarted && findTile(ct, tiles) < 0) {
+ ct->state = cachedTileCanceled;
+ ++cacheIdx;
+ } else {
+ ct->active = gFalse;
+ ++cacheIdx;
+ }
+ }
+
+ // mark cached tiles as active; add any new tiles to the cache
+ newTiles = gFalse;
+ for (tileIdx = 0; tileIdx < tiles->getLength(); ++tileIdx) {
+ tile = (TileDesc *)tiles->get(tileIdx);
+ cacheIdx = findTile(tile, cache);
+ if (cacheIdx >= 0) {
+ ct = (CachedTileDesc *)cache->del(cacheIdx);
+ } else {
+ ct = new CachedTileDesc(tile);
+ newTiles = gTrue;
+ }
+ ct->active = gTrue;
+ cache->insert(0, ct);
+ }
+
+ cleanCache();
+
+ threadPool->unlockMutex();
+
+ if (newTiles) {
+ threadPool->jobAdded();
+ }
+}
+
+SplashBitmap *TileCache::getTileBitmap(TileDesc *tile, GBool *finished) {
+ CachedTileDesc *ct;
+ SplashBitmap *bitmap;
+ int cacheIdx;
+
+ threadPool->lockMutex();
+ cacheIdx = findTile(tile, cache);
+ if (cacheIdx < 0) {
+ threadPool->unlockMutex();
+ return NULL;
+ }
+ ct = (CachedTileDesc *)cache->get(cacheIdx);
+ if (ct->state != cachedTileCanceled) {
+ bitmap = ct->bitmap;
+ } else {
+ bitmap = NULL;
+ }
+ if (finished) {
+ *finished = ct->state == cachedTileFinished;
+ }
+ threadPool->unlockMutex();
+ return bitmap;
+}
+
+void TileCache::paperColorChanged() {
+ flushCache(gFalse);
+}
+
+void TileCache::reverseVideoChanged() {
+ flushCache(gFalse);
+}
+
+void TileCache::optionalContentChanged() {
+ flushCache(gFalse);
+}
+
+void TileCache::docChanged() {
+ flushCache(gTrue);
+}
+
+
+void TileCache::forceRedraw() {
+ flushCache(gFalse);
+}
+
+// Search for <tile> on <tileList>, and return its index if found, or
+// -1 if not found. If <tile> is part of the cache, or if <tileList>
+// is the cache, the caller must have locked the ThreadPool mutex.
+int TileCache::findTile(TileDesc *tile, GList *tileList) {
+ TileDesc *t;
+ int i;
+
+ for (i = 0; i < tileList->getLength(); ++i) {
+ t = (TileDesc *)tileList->get(i);
+ if (t->matches(tile)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+// If there are too many tiles in the cache, remove the least recently
+// used tiles. Never removes active tiles. The caller must have
+// locked the ThreadPool mutex.
+void TileCache::cleanCache() {
+ CachedTileDesc *ct;
+ int n, i;
+
+ // count the number of non-canceled tiles
+ n = 0;
+ for (i = 0; i < cache->getLength(); ++i) {
+ ct = (CachedTileDesc *)cache->get(i);
+ if (ct->state != cachedTileCanceled) {
+ ++n;
+ }
+ }
+
+ // if there are too many non-canceled tiles, remove tiles
+ i = cache->getLength() - 1;
+ while (n > state->getTileCacheSize() && i >= 0) {
+ ct = (CachedTileDesc *)cache->get(i);
+ if (ct->active) {
+ break;
+ }
+ // any non-active tiles with state == cachedTileUnstarted should
+ // already have been removed by setActiveTileList()
+ if (ct->state == cachedTileFinished) {
+ delete (CachedTileDesc *)cache->del(i);
+ --n;
+ --i;
+ } else {
+ --i;
+ }
+ }
+}
+
+// Remove all cached tiles. For tiles that are being rasterized, sets
+// state to canceled. If <wait> is true, this function won't return
+// until the cache is empty, i.e., until all possible users of the
+// PDFDoc are done.
+void TileCache::flushCache(GBool wait) {
+ CachedTileDesc *ct;
+ int i;
+
+ threadPool->lockMutex();
+ i = 0;
+ while (i < cache->getLength()) {
+ ct = (CachedTileDesc *)cache->get(i);
+ switch (ct->state) {
+ case cachedTileUnstarted:
+ case cachedTileFinished:
+ delete (CachedTileDesc *)cache->del(i);
+ break;
+ case cachedTileStarted:
+ ct->state = cachedTileCanceled;
+ ++i;
+ break;
+ case cachedTileCanceled:
+ default:
+ ++i;
+ break;
+ }
+ }
+ if (wait) {
+ while (cache->getLength() > 0) {
+ threadPool->waitForFinishedJob();
+ }
+ }
+ threadPool->unlockMutex();
+}
+
+// Remove a tile from the cache and delete it. This will be called
+// with the TileCacheThreadPool mutex locked.
+void TileCache::removeTile(CachedTileDesc *ct) {
+ int i;
+
+ for (i = 0; i < cache->getLength(); ++i) {
+ if (cache->get(i) == ct) {
+ delete (CachedTileDesc *)cache->del(i);
+ break;
+ }
+ }
+}
+
+// Return true if there are one or more unstarted tiles. This will be
+// called with the TileCacheThreadPool mutex locked.
+GBool TileCache::hasUnstartedTiles() {
+ CachedTileDesc *ct;
+ int i;
+
+ for (i = 0; i < cache->getLength(); ++i) {
+ ct = (CachedTileDesc *)cache->get(i);
+ if (ct->state == cachedTileUnstarted) {
+ return gTrue;
+ }
+ }
+ return gFalse;
+}
+
+// Return the next unstarted tile, changing its state to
+// cachedTileStarted. If there are no unstarted tiles, return NULL.
+// This will be called with the TileCacheThreadPool mutex locked.
+CachedTileDesc *TileCache::getUnstartedTile() {
+ CachedTileDesc *ct;
+ int i;
+
+ for (i = 0; i < cache->getLength(); ++i) {
+ ct = (CachedTileDesc *)cache->get(i);
+ if (ct->state == cachedTileUnstarted) {
+ ct->state = cachedTileStarted;
+ return ct;
+ }
+ }
+ return NULL;
+}
+
+struct TileCacheStartPageInfo {
+ TileCache *tileCache;
+ CachedTileDesc *ct;
+ SplashOutputDev *out;
+};
+
+void TileCache::startPageCbk(void *data) {
+ TileCacheStartPageInfo *info = (TileCacheStartPageInfo *)data;
+
+ info->tileCache->threadPool->lockMutex();
+ info->ct->bitmap = info->out->getBitmap();
+ info->ct->freeBitmap = gFalse;
+ info->tileCache->threadPool->unlockMutex();
+}
+
+// Rasterize <tile>. The state has already been set to
+// cachedTileStarted.
+void TileCache::rasterizeTile(CachedTileDesc *ct) {
+ SplashOutputDev *out;
+ TileCacheStartPageInfo info;
+
+
+ out = new SplashOutputDev(state->getColorMode(), 1, state->getReverseVideo(),
+ state->getPaperColor());
+ info.tileCache = this;
+ info.ct = ct;
+ info.out = out;
+ out->setStartPageCallback(&TileCache::startPageCbk, &info);
+ out->startDoc(state->getDoc()->getXRef());
+ state->getDoc()->displayPageSlice(out, ct->page, ct->dpi, ct->dpi, ct->rotate,
+ gFalse, gTrue, gFalse,
+ ct->tx, ct->ty, ct->tw, ct->th,
+ &abortCheckCbk, ct);
+ if (ct->state == cachedTileCanceled) {
+ threadPool->lockMutex();
+ removeTile(ct);
+ threadPool->unlockMutex();
+ } else {
+ threadPool->lockMutex();
+ ct->bitmap = out->takeBitmap();
+ ct->freeBitmap = gTrue;
+ ct->state = cachedTileFinished;
+ threadPool->unlockMutex();
+ if (tileDoneCbk) {
+ (*tileDoneCbk)(tileDoneCbkData);
+ }
+ }
+ delete out;
+}
+
+
+GBool TileCache::abortCheckCbk(void *data) {
+ CachedTileDesc *ct = (CachedTileDesc *)data;
+ return ct->state == cachedTileCanceled;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TileCache.h b/Build/source/libs/xpdf/xpdf-src/xpdf/TileCache.h
new file mode 100644
index 00000000000..bea58c4c8fc
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TileCache.h
@@ -0,0 +1,78 @@
+//========================================================================
+//
+// TileCache.h
+//
+// Copyright 2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef TILECACHE_H
+#define TILECACHE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "SplashTypes.h"
+
+class SplashBitmap;
+class SplashOutputDev;
+class DisplayState;
+class CachedTileDesc;
+class TileCacheThreadPool;
+class TileDesc;
+
+//------------------------------------------------------------------------
+
+class TileCache {
+public:
+
+ TileCache(DisplayState *stateA);
+ ~TileCache();
+
+ // Set the list of currently displayed tiles (TileDesc objects).
+ void setActiveTileList(GList *tiles);
+
+ // Return the bitmap for a tile. The tile must be on the current
+ // active list. This can return NULL if tile rasterization hasn't
+ // started yet. If <finished> is non-NULL, *<finished> will be set
+ // to true if rasterization of this bitmap is finished, false
+ // otherwise.
+ SplashBitmap *getTileBitmap(TileDesc *tile, GBool *finished);
+
+ // Set a callback to be called whenever a tile rasterization is
+ // finished. NB: this callback will be called from a worker thread.
+ void setTileDoneCbk(void (*cbk)(void *data), void *data)
+ { tileDoneCbk = cbk; tileDoneCbkData = data; }
+
+ void paperColorChanged();
+ void reverseVideoChanged();
+ void optionalContentChanged();
+ void docChanged();
+ void forceRedraw();
+
+private:
+
+ int findTile(TileDesc *tile, GList *tileList);
+ void cleanCache();
+ void flushCache(GBool wait);
+ void removeTile(CachedTileDesc *ct);
+ GBool hasUnstartedTiles();
+ CachedTileDesc *getUnstartedTile();
+ static void startPageCbk(void *data);
+ void rasterizeTile(CachedTileDesc *tile);
+ static GBool abortCheckCbk(void *data);
+
+ DisplayState *state;
+ GList *cache; // [CachedTileDesc]
+ TileCacheThreadPool *threadPool;
+ void (*tileDoneCbk)(void *data);
+ void *tileDoneCbkData;
+
+ friend class TileCacheThreadPool;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TileCompositor.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TileCompositor.cc
new file mode 100644
index 00000000000..70128a4a47f
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TileCompositor.cc
@@ -0,0 +1,452 @@
+//========================================================================
+//
+// TileCompositor.cc
+//
+// Copyright 2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include <math.h>
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "SplashBitmap.h"
+#include "GlobalParams.h"
+#include "DisplayState.h"
+#include "TileMap.h"
+#include "TileCache.h"
+#include "TileCompositor.h"
+
+//------------------------------------------------------------------------
+
+// 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);
+}
+
+//------------------------------------------------------------------------
+
+TileCompositor::TileCompositor(DisplayState *stateA,
+ TileMap *tileMapA,
+ TileCache *tileCacheA) {
+ state = stateA;
+ state->setTileCompositor(this);
+ tileMap = tileMapA;
+ tileCache = tileCacheA;
+ bitmap = NULL;
+ bitmapValid = gFalse;
+}
+
+TileCompositor::~TileCompositor() {
+ delete bitmap;
+}
+
+SplashBitmap *TileCompositor::getBitmap(GBool *finished) {
+ GList *tiles;
+ PlacedTileDesc *tile;
+ SplashBitmap *tileBitmap;
+ GBool allTilesFinished, tileFinished;
+ int xSrc, ySrc, xDest, yDest, w, h, i;
+
+ if (bitmapValid) {
+ *finished = gTrue;
+ return bitmap;
+ }
+ if (!bitmap ||
+ bitmap->getWidth() != state->getWinW() ||
+ bitmap->getHeight() != state->getWinH()) {
+ if (bitmap) {
+ delete bitmap;
+ }
+ bitmap = new SplashBitmap(state->getWinW(), state->getWinH(),
+ state->getBitmapRowPad(), state->getColorMode(),
+ gFalse, gTrue, NULL);
+ }
+ clearBitmap();
+
+ //--- PDF content
+ allTilesFinished = gTrue;
+ tiles = tileMap->getTileList();
+ tileCache->setActiveTileList(tiles);
+ for (i = 0; i < tiles->getLength(); ++i) {
+ tile = (PlacedTileDesc *)tiles->get(i);
+ if (tile->px >= 0) {
+ xSrc = 0;
+ xDest = tile->px;
+ w = tile->tw;
+ } else {
+ xSrc = -tile->px;
+ xDest = 0;
+ w = tile->tw + tile->px;
+ }
+ if (xDest + w > state->getWinW()) {
+ w = state->getWinW() - xDest;
+ }
+ if (tile->py >= 0) {
+ ySrc = 0;
+ yDest = tile->py;
+ h = tile->th;
+ } else {
+ ySrc = -tile->py;
+ yDest = 0;
+ h = tile->th + tile->py;
+ }
+ if (yDest + h > state->getWinH()) {
+ h = state->getWinH() - yDest;
+ }
+ if (w > 0 && h > 0) {
+ if ((tileBitmap = tileCache->getTileBitmap(tile, &tileFinished))) {
+ blit(tileBitmap, xSrc, ySrc,
+ bitmap, xDest, yDest, w, h, !tileFinished);
+ } else {
+ fill(xDest, yDest, w, h, state->getPaperColor());
+ }
+ allTilesFinished &= tileFinished;
+ }
+ }
+
+
+ //--- selection
+ if (state->hasSelection()) {
+ drawSelection();
+ }
+
+ if (finished) {
+ *finished = allTilesFinished;
+ }
+ bitmapValid = allTilesFinished;
+
+ return bitmap;
+}
+
+void TileCompositor::drawSelection() {
+ SelectRect *rect;
+ int x0, y0, x1, y1, t, i;
+
+ for (i = 0; i < state->getNumSelectRects(); ++i) {
+ rect = state->getSelectRect(i);
+ tileMap->cvtUserToWindow(rect->page, rect->x0, rect->y0, &x0, &y0);
+ tileMap->cvtUserToWindow(rect->page, rect->x1, rect->y1, &x1, &y1);
+ if (x0 > x1) {
+ t = x0; x0 = x1; x1 = t;
+ }
+ if (y0 > y1) {
+ t = y0; y0 = y1; y1 = t;
+ }
+ if (x0 < 0) {
+ x0 = 0;
+ } else if (x0 > state->getWinW()) {
+ x0 = state->getWinW();
+ }
+ if (y0 < 0) {
+ y0 = 0;
+ } else if (y0 > state->getWinH()) {
+ y0 = state->getWinH();
+ }
+ if (x1 < 0) {
+ x1 = 0;
+ } else if (x1 > state->getWinW()) {
+ x1 = state->getWinW();
+ }
+ if (y1 < 0) {
+ y1 = 0;
+ } else if (y1 > state->getWinH()) {
+ y1 = state->getWinH();
+ }
+ // NB: in any of the non-continuous modes, if selectPage isn't
+ // displayed, cvtUserToWindow will return 0,0 -- so we need to
+ // check for x0 < x1 and y0 < y1 here
+ if (x0 < x1 && y0 < y1) {
+ applySelection(x0, y0, x1 - x0, y1 - y0, state->getSelectColor());
+ }
+ }
+}
+
+
+void TileCompositor::paperColorChanged() {
+ bitmapValid = gFalse;
+}
+
+void TileCompositor::matteColorChanged() {
+ bitmapValid = gFalse;
+}
+
+void TileCompositor::selectColorChanged() {
+ bitmapValid = gFalse;
+}
+
+void TileCompositor::reverseVideoChanged() {
+ bitmapValid = gFalse;
+}
+
+void TileCompositor::docChanged() {
+ bitmapValid = gFalse;
+}
+
+void TileCompositor::windowSizeChanged() {
+ bitmapValid = gFalse;
+}
+
+void TileCompositor::displayModeChanged() {
+ bitmapValid = gFalse;
+}
+
+void TileCompositor::zoomChanged() {
+ bitmapValid = gFalse;
+}
+
+void TileCompositor::rotateChanged() {
+ bitmapValid = gFalse;
+}
+
+void TileCompositor::scrollPositionChanged() {
+ bitmapValid = gFalse;
+}
+
+void TileCompositor::selectionChanged() {
+ bitmapValid = gFalse;
+}
+
+void TileCompositor::regionsChanged() {
+ bitmapValid = gFalse;
+}
+
+void TileCompositor::optionalContentChanged() {
+ bitmapValid = gFalse;
+}
+
+
+void TileCompositor::forceRedraw() {
+ bitmapValid = gFalse;
+}
+
+// Clear the bitmap to matteColor. This only supports the RGB8 and
+// BGR8 color modes.
+void TileCompositor::clearBitmap() {
+ SplashColorPtr data, row, p;
+ SplashBitmapRowSize rowSize;
+ int w, h, x, y;
+
+ w = bitmap->getWidth();
+ h = bitmap->getHeight();
+ rowSize = bitmap->getRowSize();
+ data = bitmap->getDataPtr();
+
+ switch (state->getColorMode()) {
+ case splashModeRGB8:
+ if (state->getMatteColor()[0] == state->getMatteColor()[1] &&
+ state->getMatteColor()[1] == state->getMatteColor()[2]) {
+ if (rowSize < 0) {
+ memset(data + rowSize * (h - 1), state->getMatteColor()[0],
+ -rowSize * h);
+ } else {
+ memset(data, state->getMatteColor()[0], rowSize * h);
+ }
+ } else {
+ row = data;
+ for (y = 0; y < h; ++y) {
+ p = row;
+ for (x = 0; x < w; ++x) {
+ *p++ = state->getMatteColor()[0];
+ *p++ = state->getMatteColor()[1];
+ *p++ = state->getMatteColor()[2];
+ }
+ row += rowSize;
+ }
+ }
+ break;
+ case splashModeBGR8:
+ if (state->getMatteColor()[0] == state->getMatteColor()[1] &&
+ state->getMatteColor()[1] == state->getMatteColor()[2]) {
+ if (rowSize < 0) {
+ memset(data + rowSize * (h - 1), state->getMatteColor()[0],
+ -rowSize * h);
+ } else {
+ memset(data, state->getMatteColor()[0], rowSize * h);
+ }
+ } else {
+ row = data;
+ for (y = 0; y < h; ++y) {
+ p = row;
+ for (x = 0; x < w; ++x) {
+ *p++ = state->getMatteColor()[2];
+ *p++ = state->getMatteColor()[1];
+ *p++ = state->getMatteColor()[0];
+ }
+ row += rowSize;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+// Copy [xSrc, ySrc, w, h] from srcBitmap to [xDest, yDest] in
+// destBitmap. This only supports the RGB8 and BGR8 color modes.
+// If [compositeWithPaper] is true, composites the source bitmap
+// with the paper color (used for incremental updates).
+void TileCompositor::blit(SplashBitmap *srcBitmap, int xSrc, int ySrc,
+ SplashBitmap *destBitmap, int xDest, int yDest,
+ int w, int h, GBool compositeWithPaper) {
+ SplashColorPtr srcData, destData, srcP, destP;
+ SplashColorPtr paperColor;
+ Guchar *alphaData, *alphaP;
+ Guchar alpha, alpha1;
+ SplashBitmapRowSize srcRowSize, destRowSize;
+ size_t alphaRowSize;
+ int x, y;
+
+ srcData = srcBitmap->getDataPtr();
+ srcRowSize = srcBitmap->getRowSize();
+ destData = destBitmap->getDataPtr();
+ destRowSize = destBitmap->getRowSize();
+
+ if (compositeWithPaper && (alphaData = srcBitmap->getAlphaPtr())) {
+ alphaRowSize = srcBitmap->getAlphaRowSize();
+ paperColor = state->getPaperColor();
+ for (y = 0; y < h; ++y) {
+ destP = &destData[(yDest + y) * destRowSize + 3 * xDest];
+ srcP = &srcData[(ySrc + y) * srcRowSize + 3 * xSrc];
+ alphaP = &alphaData[(ySrc + y) * alphaRowSize + xSrc];
+ for (x = 0; x < w; ++x) {
+ alpha = *alphaP++;
+ //~ this needs to swap paperColor 0/1/2 in BGR8 mode (?)
+ if (alpha == 255) {
+ destP[0] = srcP[0];
+ destP[1] = srcP[1];
+ destP[2] = srcP[2];
+ } else if (alpha == 0) {
+ destP[0] = paperColor[0];
+ destP[1] = paperColor[1];
+ destP[2] = paperColor[2];
+ } else {
+ alpha1 = (Guchar)(255 - alpha);
+ destP[0] = div255(alpha1 * paperColor[0] + alpha * srcP[0]);
+ destP[1] = div255(alpha1 * paperColor[1] + alpha * srcP[1]);
+ destP[2] = div255(alpha1 * paperColor[2] + alpha * srcP[2]);
+ }
+ srcP += 3;
+ destP += 3;
+ }
+ }
+ } else {
+ for (y = 0; y < h; ++y) {
+ destP = &destData[(yDest + y) * destRowSize + 3 * xDest];
+ srcP = &srcData[(ySrc + y) * srcRowSize + 3 * xSrc];
+ memcpy(destP, srcP, 3 * w);
+ }
+ }
+}
+
+// Fill [xDest, yDest, w, h] with color. This only supports the RGB8
+// and BGR8 color modes.
+void TileCompositor::fill(int xDest, int yDest, int w, int h,
+ SplashColorPtr color) {
+ SplashColorPtr data, p;
+ Guchar c0, c1, c2;
+ SplashBitmapRowSize rowSize;
+ int x, y;
+
+ if (xDest < 0) {
+ w += xDest;
+ xDest = 0;
+ }
+ if (xDest + w > state->getWinW()) {
+ w = state->getWinW() - xDest;
+ }
+ if (w <= 0) {
+ return;
+ }
+ if (yDest < 0) {
+ h += yDest;
+ yDest = 0;
+ }
+ if (yDest + h > state->getWinH()) {
+ h = state->getWinH() - yDest;
+ }
+ if (h <= 0) {
+ return;
+ }
+ data = bitmap->getDataPtr();
+ rowSize = bitmap->getRowSize();
+ if (bitmap->getMode() == splashModeRGB8) {
+ c0 = color[0];
+ c1 = color[1];
+ c2 = color[2];
+ } else {
+ c0 = color[2];
+ c1 = color[1];
+ c2 = color[0];
+ }
+ for (y = 0; y < h; ++y) {
+ p = &data[(yDest + y) * rowSize + 3 * xDest];
+ for (x = 0; x < w; ++x) {
+ p[0] = c0;
+ p[1] = c1;
+ p[2] = c2;
+ p += 3;
+ }
+ }
+}
+
+// Apply a selection of <color> to the specified rectangle. This only
+// supports the RGB8 and BGR8 color modes.
+void TileCompositor::applySelection(int xDest, int yDest, int w, int h,
+ SplashColorPtr color) {
+ SplashColorPtr data, p;
+ Guchar c0, c1, c2;
+ SplashBitmapRowSize rowSize;
+ int x, y;
+
+ if (xDest < 0) {
+ w += xDest;
+ xDest = 0;
+ }
+ if (xDest + w > state->getWinW()) {
+ w = state->getWinW() - xDest;
+ }
+ if (w <= 0) {
+ return;
+ }
+ if (yDest < 0) {
+ h += yDest;
+ yDest = 0;
+ }
+ if (yDest + h > state->getWinH()) {
+ h = state->getWinH() - yDest;
+ }
+ if (h <= 0) {
+ return;
+ }
+ data = bitmap->getDataPtr();
+ rowSize = bitmap->getRowSize();
+ if (bitmap->getMode() == splashModeRGB8) {
+ c0 = color[0];
+ c1 = color[1];
+ c2 = color[2];
+ } else {
+ c0 = color[2];
+ c1 = color[1];
+ c2 = color[0];
+ }
+ for (y = 0; y < h; ++y) {
+ p = &data[(yDest + y) * rowSize + 3 * xDest];
+ for (x = 0; x < w; ++x) {
+ p[0] = (Guchar)((3 * p[0] + c0) / 4);
+ p[1] = (Guchar)((3 * p[1] + c1) / 4);
+ p[2] = (Guchar)((3 * p[2] + c2) / 4);
+ p += 3;
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TileCompositor.h b/Build/source/libs/xpdf/xpdf-src/xpdf/TileCompositor.h
new file mode 100644
index 00000000000..aa06c9da7bd
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TileCompositor.h
@@ -0,0 +1,77 @@
+//========================================================================
+//
+// TileCompositor.h
+//
+// Copyright 2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef TILECOMPOSITOR_H
+#define TILECOMPOSITOR_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "SplashTypes.h"
+
+class GList;
+class SplashBitmap;
+class DisplayState;
+class TileCache;
+
+//------------------------------------------------------------------------
+
+class TileCompositor {
+public:
+
+ TileCompositor(DisplayState *stateA,
+ TileMap *tileMapA, TileCache *tileCacheA);
+ ~TileCompositor();
+
+ // Returns the window bitmap. The returned bitmap is owned by the
+ // TileCompositor object (and may be reused) -- the caller should
+ // not modify or free it. If <finished> is is non-NULL, *<finished>
+ // will be set to true if all of the needed tiles are finished,
+ // i.e., if the returned bitmap is complete.
+ SplashBitmap *getBitmap(GBool *finished);
+
+ void paperColorChanged();
+ void matteColorChanged();
+ void selectColorChanged();
+ void reverseVideoChanged();
+ void docChanged();
+ void windowSizeChanged();
+ void displayModeChanged();
+ void zoomChanged();
+ void rotateChanged();
+ void scrollPositionChanged();
+ void selectionChanged();
+ void regionsChanged();
+ void optionalContentChanged();
+ void forceRedraw();
+
+private:
+
+ void clearBitmap();
+ void blit(SplashBitmap *srcBitmap, int xSrc, int ySrc,
+ SplashBitmap *destBitmap, int xDest, int yDest,
+ int w, int h, GBool compositeWithPaper);
+ void fill(int xDest, int yDest, int w, int h,
+ SplashColorPtr color);
+ void drawSelection();
+ void applySelection(int xDest, int yDest, int w, int h,
+ SplashColorPtr color);
+
+ DisplayState *state;
+ TileMap *tileMap;
+ TileCache *tileCache;
+
+ SplashBitmap *bitmap;
+ GBool bitmapValid;
+
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TileMap.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/TileMap.cc
new file mode 100644
index 00000000000..d1a5aea2963
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TileMap.cc
@@ -0,0 +1,1690 @@
+//========================================================================
+//
+// TileMap.cc
+//
+// Copyright 2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "gmempp.h"
+#include "GList.h"
+#include "PDFDoc.h"
+#include "DisplayState.h"
+#include "TileMap.h"
+
+//------------------------------------------------------------------------
+
+// Number of pixels of matte color between pages (above and below each
+// other) in continuous mode.
+#define continuousPageSpacing 3
+
+// Number of pixels of matte color between facing pages (left and
+// right of each other) in side-by-side mode.
+#define sideBySidePageSpacing 3
+
+// Number of pixels of matte color between pages (left and right of
+// each other) in horizontal continuous mode.
+#define horizContinuousPageSpacing 3
+
+//------------------------------------------------------------------------
+
+TileMap::TileMap(DisplayState *stateA) {
+ state = stateA;
+ state->setTileMap(this);
+ pageDPI = NULL;
+ pageW = pageH = NULL;
+ tileW = tileH = NULL;
+ pageBoxW = pageBoxH = NULL;
+ pageX = pageY = NULL;
+ tiles = NULL;
+}
+
+TileMap::~TileMap() {
+ clearPageParams();
+ clearContinuousModeParams();
+ gfree(pageBoxW);
+ gfree(pageBoxH);
+ if (tiles) {
+ deleteGList(tiles, PlacedTileDesc);
+ }
+}
+
+GList *TileMap::getTileList() {
+ double pageDPI1, pageDPI2;
+ int pageW1, pageH1, tileW1, tileH1, pageW2, pageH2, tileW2, tileH2;
+ int offsetX, offsetY, offsetX2;
+ int x0, y0, x1, y1, x, y, tx, ty, tw, th, page;
+
+ if (tiles) {
+ return tiles;
+ }
+
+ tiles = new GList();
+
+ if (!state->getDoc() || !state->getDoc()->getNumPages()) {
+ return tiles;
+ }
+
+ updatePageParams();
+ updateContinuousModeParams();
+
+ switch (state->getDisplayMode()) {
+
+ case displaySingle:
+ page = state->getScrollPage();
+ pageDPI1 = pageDPI[page - 1];
+ pageW1 = pageW[page - 1];
+ pageH1 = pageH[page - 1];
+ tileW1 = tileW[page - 1];
+ tileH1 = tileH[page - 1];
+ if (pageW1 < state->getWinW()) {
+ offsetX = (state->getWinW() - pageW1) / 2;
+ } else {
+ offsetX = 0;
+ }
+ if (pageH1 < state->getWinH()) {
+ offsetY = (state->getWinH() - pageH1) / 2;
+ } else {
+ offsetY = 0;
+ }
+ if ((x0 = state->getScrollX() - offsetX) < 0) {
+ x0 = 0;
+ }
+ if ((y0 = state->getScrollY() - offsetY) < 0) {
+ y0 = 0;
+ }
+ if ((x1 = state->getScrollX() + state->getWinW() - 1 - offsetX) >= pageW1) {
+ x1 = pageW1 - 1;
+ }
+ if ((y1 = state->getScrollY() + state->getWinH() - 1 - offsetY) >= pageH1) {
+ y1 = pageH1 - 1;
+ }
+ for (y = y0 / tileH1; y <= y1 / tileH1; ++y) {
+ for (x = x0 / tileW1; x <= x1 / tileW1; ++x) {
+ tx = x * tileW1;
+ ty = y * tileH1;
+ tw = tileW1;
+ if (tx + tw > pageW1) {
+ tw = pageW1 - tx;
+ }
+ th = tileH1;
+ if (ty + th > pageH1) {
+ th = pageH1 - ty;
+ }
+ tiles->append(new PlacedTileDesc(page, state->getRotate(), pageDPI1,
+ tx, ty, tw, th,
+ tx - state->getScrollX() + offsetX,
+ ty - state->getScrollY() + offsetY));
+ }
+ }
+ break;
+
+ case displayContinuous:
+ if (totalH < state->getWinH()) {
+ offsetY = (state->getWinH() - totalH) / 2;
+ } else {
+ offsetY = 0;
+ }
+ page = findContinuousPage(state->getScrollY());
+ while (page <= state->getDoc()->getNumPages() &&
+ pageY[page - 1] < state->getScrollY() + state->getWinH()) {
+ pageDPI1 = pageDPI[page - 1];
+ pageW1 = pageW[page - 1];
+ pageH1 = pageH[page - 1];
+ tileW1 = tileW[page - 1];
+ tileH1 = tileH[page - 1];
+ if (maxW < state->getWinW()) {
+ offsetX = (state->getWinW() - maxW) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX += (maxW - pageW1) / 2;
+ if ((x0 = state->getScrollX() - offsetX) < 0) {
+ x0 = 0;
+ }
+ if ((y0 = state->getScrollY() - pageY[page - 1] - offsetY) < 0) {
+ y0 = 0;
+ }
+ if ((x1 = state->getScrollX() + state->getWinW() - 1 - offsetX)
+ >= pageW1) {
+ x1 = pageW1 - 1;
+ }
+ if ((y1 = state->getScrollY() - pageY[page - 1]
+ + state->getWinH() - 1 - offsetY)
+ >= pageH1) {
+ y1 = pageH1 - 1;
+ }
+ for (y = y0 / tileH1; y <= y1 / tileH1; ++y) {
+ for (x = x0 / tileW1; x <= x1 / tileW1; ++x) {
+ tx = x * tileW1;
+ ty = y * tileH1;
+ tw = tileW1;
+ if (tx + tw > pageW1) {
+ tw = pageW1 - tx;
+ }
+ th = tileH1;
+ if (ty + th > pageH1) {
+ th = pageH1 - ty;
+ }
+ tiles->append(new PlacedTileDesc(
+ page, state->getRotate(), pageDPI1,
+ tx, ty, tw, th,
+ tx - state->getScrollX() + offsetX,
+ ty - state->getScrollY() + pageY[page - 1]
+ + offsetY));
+ }
+ }
+ ++page;
+ }
+ break;
+
+ case displaySideBySideSingle:
+ page = state->getScrollPage();
+ pageDPI1 = pageDPI[page - 1];
+ pageW1 = pageW[page - 1];
+ pageH1 = pageH[page - 1];
+ tileW1 = tileW[page - 1];
+ tileH1 = tileH[page - 1];
+ if (page + 1 <= state->getDoc()->getNumPages()) {
+ pageDPI2 = pageDPI[page];
+ pageW2 = pageW[page];
+ pageH2 = pageH[page];
+ tileW2 = tileW[page];
+ tileH2 = tileH[page];
+ } else {
+ // display a single page as though there were a blank facing
+ // page of the same size
+ pageDPI2 = pageDPI1;
+ pageW2 = pageW1;
+ pageH2 = pageH1;
+ tileW2 = tileW1;
+ tileH2 = tileH1;
+ }
+ if (pageW1 + sideBySidePageSpacing + pageW2 < state->getWinW()) {
+ offsetX = (state->getWinW() -
+ (pageW1 + sideBySidePageSpacing + pageW2)) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
+ if (pageH1 < state->getWinH() && pageH2 < state->getWinH()) {
+ if (pageH1 > pageH2) {
+ offsetY = (state->getWinH() - pageH1) / 2;
+ } else {
+ offsetY = (state->getWinH() - pageH2) / 2;
+ }
+ } else {
+ offsetY = 0;
+ }
+ // left page
+ if ((x0 = state->getScrollX() - offsetX) < 0) {
+ x0 = 0;
+ }
+ if ((y0 = state->getScrollY() - offsetY) < 0) {
+ y0 = 0;
+ }
+ if ((x1 = state->getScrollX() + state->getWinW() - 1 - offsetX) >= pageW1) {
+ x1 = pageW1 - 1;
+ } else if (x1 < 0) {
+ x1 = -tileW2;
+ }
+ if ((y1 = state->getScrollY() + state->getWinH() - 1 - offsetY) >= pageH1) {
+ y1 = pageH1 - 1;
+ } else if (y1 < 0) {
+ y1 = -tileH2;
+ }
+ for (y = y0 / tileH1; y <= y1 / tileH1; ++y) {
+ for (x = x0 / tileW1; x <= x1 / tileW1; ++x) {
+ tx = x * tileW1;
+ ty = y * tileH1;
+ tw = tileW1;
+ if (tx + tw > pageW1) {
+ tw = pageW1 - tx;
+ }
+ th = tileH1;
+ if (ty + th > pageH1) {
+ th = pageH1 - ty;
+ }
+ tiles->append(new PlacedTileDesc(page,
+ state->getRotate(), pageDPI1,
+ tx, ty, tw, th,
+ tx - state->getScrollX() + offsetX,
+ ty - state->getScrollY() + offsetY));
+ }
+ }
+ // right page
+ if (page + 1 <= state->getDoc()->getNumPages()) {
+ if ((x0 = state->getScrollX() - offsetX2) < 0) {
+ x0 = 0;
+ }
+ if ((y0 = state->getScrollY() - offsetY) < 0) {
+ y0 = 0;
+ }
+ if ((x1 = state->getScrollX() + state->getWinW() - 1 - offsetX2)
+ >= pageW2) {
+ x1 = pageW2 - 1;
+ } else if (x1 < 0) {
+ x1 = -tileW2;
+ }
+ if ((y1 = state->getScrollY() + state->getWinH() - 1 - offsetY)
+ >= pageH2) {
+ y1 = pageH2 - 1;
+ } else if (y1 < 0) {
+ y1 = -tileH2;
+ }
+ for (y = y0 / tileH2; y <= y1 / tileH2; ++y) {
+ for (x = x0 / tileW2; x <= x1 / tileW2; ++x) {
+ tx = x * tileW2;
+ ty = y * tileH2;
+ tw = tileW2;
+ if (tx + tw > pageW2) {
+ tw = pageW2 - tx;
+ }
+ th = tileH2;
+ if (ty + th > pageH2) {
+ th = pageH2 - ty;
+ }
+ tiles->append(new PlacedTileDesc(page + 1,
+ state->getRotate(), pageDPI2,
+ tx, ty, tw, th,
+ tx - state->getScrollX() + offsetX2,
+ ty - state->getScrollY() + offsetY));
+ }
+ }
+ }
+ break;
+
+ case displaySideBySideContinuous:
+ if (totalH < state->getWinH()) {
+ offsetY = (state->getWinH() - totalH) / 2;
+ } else {
+ offsetY = 0;
+ }
+ page = findSideBySideContinuousPage(state->getScrollY());
+ while (page <= state->getDoc()->getNumPages() &&
+ (pageY[page - 1] < state->getScrollY() + state->getWinH() ||
+ (page + 1 <= state->getDoc()->getNumPages() &&
+ pageY[page] < state->getScrollY() + state->getWinH()))) {
+ pageDPI1 = pageDPI[page - 1];
+ pageW1 = pageW[page - 1];
+ pageH1 = pageH[page - 1];
+ tileW1 = tileW[page - 1];
+ tileH1 = tileH[page - 1];
+ if (page + 1 <= state->getDoc()->getNumPages()) {
+ pageDPI2 = pageDPI[page];
+ pageW2 = pageW[page];
+ pageH2 = pageH[page];
+ tileW2 = tileW[page];
+ tileH2 = tileH[page];
+ } else {
+ // display a single page as though there were a blank facing
+ // page of the same size
+ pageDPI2 = pageDPI1;
+ pageW2 = pageW1;
+ pageH2 = pageH1;
+ tileW2 = tileW1;
+ tileH2 = tileH1;
+ }
+ if (maxW + sideBySidePageSpacing + maxW2 < state->getWinW()) {
+ offsetX = (state->getWinW() -
+ (maxW + sideBySidePageSpacing + maxW2)) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX += maxW - pageW1;
+ offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
+ // left page
+ if ((x0 = state->getScrollX() - offsetX) < 0) {
+ x0 = 0;
+ }
+ if ((y0 = state->getScrollY() - pageY[page - 1] - offsetY) < 0) {
+ y0 = 0;
+ }
+ if ((x1 = state->getScrollX() + state->getWinW() - 1 - offsetX)
+ >= pageW1) {
+ x1 = pageW1 - 1;
+ } else if (x1 < 0) {
+ x1 = -tileW2;
+ }
+ if ((y1 = state->getScrollY() - pageY[page - 1]
+ + state->getWinH() - 1 - offsetY)
+ >= pageH1) {
+ y1 = pageH1 - 1;
+ } else if (y1 < 0) {
+ y1 = -tileH2;
+ }
+ for (y = y0 / tileH1; y <= y1 / tileH1; ++y) {
+ for (x = x0 / tileW1; x <= x1 / tileW1; ++x) {
+ tx = x * tileW1;
+ ty = y * tileH1;
+ tw = tileW1;
+ if (tx + tw > pageW1) {
+ tw = pageW1 - tx;
+ }
+ th = tileH1;
+ if (ty + th > pageH1) {
+ th = pageH1 - ty;
+ }
+ tiles->append(new PlacedTileDesc(
+ page, state->getRotate(), pageDPI1,
+ tx, ty, tw, th,
+ tx - state->getScrollX() + offsetX,
+ ty - state->getScrollY() + pageY[page - 1]
+ + offsetY));
+ }
+ }
+ ++page;
+ // right page
+ if (page <= state->getDoc()->getNumPages()) {
+ if ((x0 = state->getScrollX() - offsetX2) < 0) {
+ x0 = 0;
+ }
+ if ((y0 = state->getScrollY() - pageY[page - 1] - offsetY) < 0) {
+ y0 = 0;
+ }
+ if ((x1 = state->getScrollX() + state->getWinW() - 1 - offsetX2)
+ >= pageW2) {
+ x1 = pageW2 - 1;
+ } else if (x1 < 0) {
+ x1 = -tileW2;
+ }
+ if ((y1 = state->getScrollY() - pageY[page - 1]
+ + state->getWinH() - 1 - offsetY)
+ >= pageH2) {
+ y1 = pageH2 - 1;
+ } else if (y1 < 0) {
+ y1 = -tileH2;
+ }
+ for (y = y0 / tileH2; y <= y1 / tileH2; ++y) {
+ for (x = x0 / tileW2; x <= x1 / tileW2; ++x) {
+ tx = x * tileW2;
+ ty = y * tileH2;
+ tw = tileW2;
+ if (tx + tw > pageW2) {
+ tw = pageW2 - tx;
+ }
+ th = tileH2;
+ if (ty + th > pageH2) {
+ th = pageH2 - ty;
+ }
+ tiles->append(new PlacedTileDesc(
+ page, state->getRotate(), pageDPI2,
+ tx, ty, tw, th,
+ tx - state->getScrollX() + offsetX2,
+ ty - state->getScrollY() + pageY[page - 1]
+ + offsetY));
+ }
+ }
+ }
+ ++page;
+ }
+ break;
+
+ case displayHorizontalContinuous:
+ if (totalW < state->getWinW()) {
+ offsetX = (state->getWinW() - totalW) / 2;
+ } else {
+ offsetX = 0;
+ }
+ page = findHorizContinuousPage(state->getScrollX());
+ while (page <= state->getDoc()->getNumPages() &&
+ pageX[page - 1] < state->getScrollX() + state->getWinW()) {
+ pageDPI1 = pageDPI[page - 1];
+ pageW1 = pageW[page - 1];
+ pageH1 = pageH[page - 1];
+ tileW1 = tileW[page - 1];
+ tileH1 = tileH[page - 1];
+ if (maxH < state->getWinH()) {
+ offsetY = (state->getWinH() - maxH) / 2;
+ } else {
+ offsetY = 0;
+ }
+ if ((x0 = state->getScrollX() - pageX[page - 1] - offsetX) < 0) {
+ x0 = 0;
+ }
+ if ((y0 = state->getScrollY() - offsetY) < 0) {
+ y0 = 0;
+ }
+ if ((x1 = state->getScrollX() - pageX[page - 1]
+ + state->getWinW() - 1 - offsetX)
+ >= pageW1) {
+ x1 = pageW1 - 1;
+ }
+ if ((y1 = state->getScrollY() + state->getWinH() - 1 - offsetY)
+ >= pageH1) {
+ y1 = pageH1 - 1;
+ }
+ for (y = y0 / tileH1; y <= y1 / tileH1; ++y) {
+ for (x = x0 / tileW1; x <= x1 / tileW1; ++x) {
+ tx = x * tileW1;
+ ty = y * tileH1;
+ tw = tileW1;
+ if (tx + tw > pageW1) {
+ tw = pageW1 - tx;
+ }
+ th = tileH1;
+ if (ty + th > pageH1) {
+ th = pageH1 - ty;
+ }
+ tiles->append(new PlacedTileDesc(
+ page, state->getRotate(), pageDPI1,
+ tx, ty, tw, th,
+ tx - state->getScrollX() + pageX[page - 1]
+ + offsetX,
+ ty - state->getScrollY() + offsetY));
+ }
+ }
+ ++page;
+ }
+ break;
+ }
+
+ return tiles;
+}
+
+void TileMap::getScrollLimits(int *horizMax, int *vertMax) {
+ int pageW1, pageH1, pageW2, pageH2;
+
+ if (!state->getDoc() || !state->getDoc()->getNumPages()) {
+ *horizMax = *vertMax = 0;
+ return;
+ }
+
+ updatePageParams();
+ updateContinuousModeParams();
+
+ switch (state->getDisplayMode()) {
+ case displaySingle:
+ *horizMax = pageW[state->getScrollPage() - 1];
+ *vertMax = pageH[state->getScrollPage() - 1];
+ break;
+ case displayContinuous:
+ *horizMax = maxW;
+ *vertMax = totalH;
+ break;
+ case displaySideBySideSingle:
+ pageW1 = pageW[state->getScrollPage() - 1];
+ pageH1 = pageH[state->getScrollPage() - 1];
+ if (state->getScrollPage() + 1 <= state->getDoc()->getNumPages()) {
+ pageW2 = pageW[state->getScrollPage()];
+ pageH2 = pageH[state->getScrollPage()];
+ } else {
+ pageW2 = pageW1;
+ pageH2 = pageH1;
+ }
+ *horizMax = pageW1 + sideBySidePageSpacing + pageW2;
+ *vertMax = pageH1 > pageH2 ? pageH1 : pageH2;
+ break;
+ case displaySideBySideContinuous:
+ *horizMax = maxW + sideBySidePageSpacing + maxW2;
+ *vertMax = totalH;
+ break;
+ case displayHorizontalContinuous:
+ *horizMax = totalW;
+ *vertMax = maxH;
+ break;
+ default: // should never happen
+ *horizMax = *vertMax = 0;
+ break;
+ }
+}
+
+GBool TileMap::cvtWindowToUser(int xw, int yw,
+ int *pg, double *xu, double *yu) {
+ GBool ok;
+ int xd, yd;
+
+ if (!state->getDoc() || !state->getDoc()->getNumPages()) {
+ *pg = 0;
+ *xu = *yu = 0;
+ return gFalse;
+ }
+
+ ok = cvtWindowToDev(xw, yw, pg, &xd, &yd);
+ cvtDevToUser(*pg, xd, yd, xu, yu);
+ return ok;
+}
+
+GBool TileMap::cvtWindowToDev(int xw, int yw,
+ int *pg, int *xd, int *yd) {
+ int pageW1, pageH1, pageW2, pageH2, offsetX, offsetX2, offsetY;
+
+ if (!state->getDoc() || !state->getDoc()->getNumPages()) {
+ *pg = 0;
+ *xd = *yd = 0;
+ return gFalse;
+ }
+
+ updatePageParams();
+ updateContinuousModeParams();
+
+ switch (state->getDisplayMode()) {
+
+ case displaySingle:
+ *pg = state->getScrollPage();
+ pageW1 = pageW[*pg - 1];
+ pageH1 = pageH[*pg - 1];
+ if (pageW1 < state->getWinW()) {
+ offsetX = (state->getWinW() - pageW1) / 2;
+ } else {
+ offsetX = 0;
+ }
+ if (pageH1 < state->getWinH()) {
+ offsetY = (state->getWinH() - pageH1) / 2;
+ } else {
+ offsetY = 0;
+ }
+ *xd = xw - offsetX + state->getScrollX();
+ *yd = yw - offsetY + state->getScrollY();
+ return *xd >= 0 && *xd < pageW1 && *yd >= 0 && *yd < pageH1;
+
+ case displayContinuous:
+ if (totalH < state->getWinH()) {
+ offsetY = (state->getWinH() - totalH) / 2;
+ } else {
+ offsetY = 0;
+ }
+ *pg = findContinuousPage(yw - offsetY + state->getScrollY());
+ if (*pg < 1 || *pg > state->getDoc()->getNumPages()) {
+ *pg = 0;
+ *xd = *yd = 0;
+ return gFalse;
+ }
+ pageW1 = pageW[*pg - 1];
+ pageH1 = pageH[*pg - 1];
+ if (maxW < state->getWinW()) {
+ offsetX = (state->getWinW() - maxW) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX += (maxW - pageW1) / 2;
+ *xd = xw - offsetX + state->getScrollX();
+ *yd = yw - offsetY - pageY[*pg - 1] + state->getScrollY();
+ return *xd >= 0 && *xd < pageW1 && *yd >= 0 && *yd < pageH1;
+
+ case displaySideBySideSingle:
+ pageW1 = pageW[state->getScrollPage() - 1];
+ pageH1 = pageH[state->getScrollPage() - 1];
+ if (state->getScrollPage() + 1 <= state->getDoc()->getNumPages()) {
+ pageW2 = pageW[state->getScrollPage()];
+ pageH2 = pageH[state->getScrollPage()];
+ } else {
+ // display a single page as though there were a blank facing
+ // page of the same size
+ pageW2 = pageW1;
+ pageH2 = pageH1;
+ }
+ if (pageW1 + sideBySidePageSpacing + pageW2 < state->getWinW()) {
+ offsetX = (state->getWinW() -
+ (pageW1 + sideBySidePageSpacing + pageW2)) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
+ if (pageH1 < state->getWinH() && pageH2 < state->getWinH()) {
+ if (pageH1 > pageH2) {
+ offsetY = (state->getWinH() - pageH1) / 2;
+ } else {
+ offsetY = (state->getWinH() - pageH2) / 2;
+ }
+ } else {
+ offsetY = 0;
+ }
+ if (xw + state->getScrollX() < offsetX2) {
+ *pg = state->getScrollPage();
+ *xd = xw - offsetX + state->getScrollX();
+ *yd = yw - offsetY + state->getScrollY();
+ return *xd >= 0 && *xd < pageW1 && *yd >= 0 && *yd < pageH1;
+ } else {
+ if (state->getScrollPage() + 1 > state->getDoc()->getNumPages()) {
+ *pg = *xd = *yd = 0;
+ return gFalse;
+ }
+ *pg = state->getScrollPage() + 1;
+ *xd = xw - offsetX2 + state->getScrollX();
+ *yd = yw - offsetY + state->getScrollY();
+ return *xd >= 0 && *xd < pageW2 && *yd >= 0 && *yd < pageH2;
+ }
+
+ case displaySideBySideContinuous:
+ if (totalH < state->getWinH()) {
+ offsetY = (state->getWinH() - totalH) / 2;
+ } else {
+ offsetY = 0;
+ }
+ *pg = findSideBySideContinuousPage(yw - offsetY + state->getScrollY());
+ if (*pg < 1 || *pg > state->getDoc()->getNumPages()) {
+ *pg = 0;
+ *xd = *yd = 0;
+ return gFalse;
+ }
+ pageW1 = pageW[*pg - 1];
+ pageH1 = pageH[*pg - 1];
+ if (*pg + 1 <= state->getDoc()->getNumPages()) {
+ pageW2 = pageW[*pg];
+ pageH2 = pageH[*pg];
+ } else {
+ pageW2 = pageH2 = 0;
+ }
+ if (maxW + sideBySidePageSpacing + maxW2 < state->getWinW()) {
+ offsetX = (state->getWinW() -
+ (maxW + sideBySidePageSpacing + maxW2)) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX += maxW - pageW1;
+ offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
+ if (xw + state->getScrollX() < offsetX2) {
+ *xd = xw - offsetX + state->getScrollX();
+ *yd = yw - offsetY - pageY[*pg - 1] + state->getScrollY();
+ return *xd >= 0 && *xd < pageW1 && *yd >= 0 && *yd < pageH1;
+ } else {
+ if (*pg + 1 > state->getDoc()->getNumPages()) {
+ *pg = *xd = *yd = 0;
+ return false;
+ }
+ ++*pg;
+ *xd = xw - offsetX2 + state->getScrollX();
+ *yd = yw - offsetY - pageY[*pg - 1] + state->getScrollY();
+ return *xd >= 0 && *xd < pageW2 && *yd >= 0 && *yd < pageH2;
+ }
+
+ case displayHorizontalContinuous:
+ if (totalW < state->getWinW()) {
+ offsetX = (state->getWinW() - totalW) / 2;
+ } else {
+ offsetX = 0;
+ }
+ *pg = findHorizContinuousPage(xw - offsetX + state->getScrollX());
+ if (*pg < 1 || *pg > state->getDoc()->getNumPages()) {
+ *pg = 0;
+ *xd = *yd = 0;
+ return gFalse;
+ }
+ pageW1 = pageW[*pg - 1];
+ pageH1 = pageH[*pg - 1];
+ if (maxH < state->getWinH()) {
+ offsetY = (state->getWinH() - maxH) / 2;
+ } else {
+ offsetY = 0;
+ }
+ *xd = xw - offsetX - pageX[*pg - 1] + state->getScrollX();
+ *yd = yw - offsetY + state->getScrollY();
+ return *xd >= 0 && *xd < pageW1 && *yd >= 0 && *yd < pageH1;
+ }
+
+ return gFalse;
+}
+
+GBool TileMap::cvtUserToWindow(int pg, double xu, double yu,
+ int *xw, int *yw) {
+ int xd, yd;
+
+ cvtUserToDev(pg, xu, yu, &xd, &yd);
+ return cvtDevToWindow(pg, xd, yd, xw, yw);
+}
+
+GBool TileMap::cvtDevToWindow(int pg, int xd, int yd,
+ int *xw, int *yw) {
+ int leftPg, pageW1, pageH1, pageW2, pageH2, offsetX, offsetX2, offsetY;
+
+ if (!state->getDoc() ||
+ pg < 1 || pg > state->getDoc()->getNumPages()) {
+ *xw = *yw = 0;
+ return gFalse;
+ }
+
+ updatePageParams();
+ updateContinuousModeParams();
+
+ switch (state->getDisplayMode()) {
+
+ case displaySingle:
+ if (pg != state->getScrollPage()) {
+ *xw = *yw = 0;
+ return gFalse;
+ }
+ pageW1 = pageW[pg - 1];
+ pageH1 = pageH[pg - 1];
+ if (pageW1 < state->getWinW()) {
+ offsetX = (state->getWinW() - pageW1) / 2;
+ } else {
+ offsetX = 0;
+ }
+ if (pageH1 < state->getWinH()) {
+ offsetY = (state->getWinH() - pageH1) / 2;
+ } else {
+ offsetY = 0;
+ }
+ *xw = xd + offsetX - state->getScrollX();
+ *yw = yd + offsetY - state->getScrollY();
+ break;
+
+ case displayContinuous:
+ pageW1 = pageW[pg - 1];
+ pageH1 = pageH[pg - 1];
+ if (maxW < state->getWinW()) {
+ offsetX = (state->getWinW() - maxW) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX += (maxW - pageW1) / 2;
+ if (totalH < state->getWinH()) {
+ offsetY = (state->getWinH() - totalH) / 2;
+ } else {
+ offsetY = 0;
+ }
+ *xw = xd + offsetX - state->getScrollX();
+ *yw = pageY[pg - 1] + yd + offsetY - state->getScrollY();
+ break;
+
+ case displaySideBySideSingle:
+ if (!(pg == state->getScrollPage() ||
+ (pg == state->getScrollPage() + 1 &&
+ state->getScrollPage() + 1 <= state->getDoc()->getNumPages()))) {
+ *xw = *yw = 0;
+ return gFalse;
+ }
+ pageW1 = pageW[state->getScrollPage() - 1];
+ pageH1 = pageH[state->getScrollPage() - 1];
+ if (state->getScrollPage() + 1 <= state->getDoc()->getNumPages()) {
+ pageW2 = pageW[state->getScrollPage()];
+ pageH2 = pageH[state->getScrollPage()];
+ } else {
+ // display a single page as though there were a blank facing
+ // page of the same size
+ pageW2 = pageW1;
+ pageH2 = pageH1;
+ }
+ if (pageW1 + sideBySidePageSpacing + pageW2 < state->getWinW()) {
+ offsetX = (state->getWinW() -
+ (pageW1 + sideBySidePageSpacing + pageW2)) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
+ if (pageH1 < state->getWinH() && pageH2 < state->getWinH()) {
+ if (pageH1 > pageH2) {
+ offsetY = (state->getWinH() - pageH1) / 2;
+ } else {
+ offsetY = (state->getWinH() - pageH2) / 2;
+ }
+ } else {
+ offsetY = 0;
+ }
+ if (pg == state->getScrollPage()) {
+ *xw = xd + offsetX - state->getScrollX();
+ *yw = yd + offsetY - state->getScrollY();
+ } else {
+ *xw = xd + offsetX2 - state->getScrollX();
+ *yw = yd + offsetY - state->getScrollY();
+ }
+ break;
+
+ case displaySideBySideContinuous:
+ leftPg = (pg - 1) | 1;
+ pageW1 = pageW[leftPg - 1];
+ pageH1 = pageH[leftPg - 1];
+ if (maxW + sideBySidePageSpacing + maxW2 < state->getWinW()) {
+ offsetX = (state->getWinW() -
+ (maxW + sideBySidePageSpacing + maxW2)) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX += maxW - pageW1;
+ offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
+ if (totalH < state->getWinH()) {
+ offsetY = (state->getWinH() - totalH) / 2;
+ } else {
+ offsetY = 0;
+ }
+ if (pg == leftPg) {
+ *xw = xd + offsetX - state->getScrollX();
+ } else {
+ *xw = xd + offsetX2 - state->getScrollX();
+ }
+ *yw = pageY[pg - 1] + yd + offsetY - state->getScrollY();
+ break;
+
+ case displayHorizontalContinuous:
+ if (totalW < state->getWinW()) {
+ offsetX = (state->getWinW() - totalW) / 2;
+ } else {
+ offsetX = 0;
+ }
+ if (maxH < state->getWinH()) {
+ offsetY = (state->getWinH() - maxH) / 2;
+ } else {
+ offsetY = 0;
+ }
+ *xw = pageX[pg - 1] + xd + offsetX - state->getScrollX();
+ *yw = yd + offsetY - state->getScrollY();
+ break;
+ }
+
+ return gTrue;
+}
+
+void TileMap::cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd) {
+ double m[6];
+
+ if (!state->getDoc() ||
+ pg < 1 || pg > state->getDoc()->getNumPages()) {
+ *xd = *yd = 0;
+ return;
+ }
+
+ computePageMatrix(pg, m);
+ *xd = (int)(xu * m[0] + yu * m[2] + m[4] + 0.5);
+ *yd = (int)(xu * m[1] + yu * m[3] + m[5] + 0.5);
+}
+
+void TileMap::cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu) {
+ double m[6], im[6];
+
+ if (!state->getDoc() ||
+ pg < 1 || pg > state->getDoc()->getNumPages()) {
+ *xu = *yu = 0;
+ return;
+ }
+
+ computePageMatrix(pg, m);
+ invertMatrix(m, im);
+ *xu = xd * im[0] + yd * im[2] + im[4];
+ *yu = xd * im[1] + yd * im[3] + im[5];
+}
+
+void TileMap::getWindowPageRange(int x, int y, int w, int h,
+ int *firstPage, int *lastPage) {
+ GList *tiles;
+ PlacedTileDesc *tile;
+ int i;
+
+ if (!state->getDoc() || !state->getDoc()->getNumPages()) {
+ *firstPage = *lastPage = 0;
+ return;
+ }
+ *firstPage = state->getDoc()->getNumPages();
+ *lastPage = 0;
+ tiles = getTileList();
+ for (i = 0; i < tiles->getLength(); ++i) {
+ tile = (PlacedTileDesc *)tiles->get(i);
+ if (tile->px < x + w &&
+ tile->px + tile->tw > x &&
+ tile->py < y + h &&
+ tile->py + tile->th > y) {
+ if (tile->page < *firstPage) {
+ *firstPage = tile->page;
+ }
+ if (tile->page > *lastPage) {
+ *lastPage = tile->page;
+ }
+ }
+ }
+}
+
+int TileMap::getPageTopY(int page) {
+ if (!state->getDoc() || !state->getDoc()->getNumPages()) {
+ return 0;
+ }
+
+ updateContinuousModeParams();
+
+ switch (state->getDisplayMode()) {
+ case displaySingle:
+ case displaySideBySideSingle:
+ case displayHorizontalContinuous:
+ default:
+ return 0;
+ case displayContinuous:
+ case displaySideBySideContinuous:
+ return pageY[page - 1];
+ }
+}
+
+int TileMap::getPageBottomY(int page) {
+ if (!state->getDoc() || !state->getDoc()->getNumPages()) {
+ return 0;
+ }
+
+ updatePageParams();
+ updateContinuousModeParams();
+
+ switch (state->getDisplayMode()) {
+ case displaySingle:
+ case displaySideBySideSingle:
+ case displayHorizontalContinuous:
+ default:
+ return pageH[page - 1] - state->getWinH();
+ case displayContinuous:
+ case displaySideBySideContinuous:
+ return pageY[page - 1] + pageH[page - 1] - state->getWinH();
+ }
+}
+
+int TileMap::getPageLeftX(int page) {
+ int leftPage, rightPage, pageW1, pageW2, offsetX, offsetX2;
+
+ if (!state->getDoc() || !state->getDoc()->getNumPages()) {
+ return 0;
+ }
+
+ updatePageParams();
+ updateContinuousModeParams();
+
+ switch (state->getDisplayMode()) {
+ case displaySingle:
+ default:
+ return 0;
+ case displayContinuous:
+ return (maxW - pageW[page - 1]) / 2;
+ case displaySideBySideSingle:
+ leftPage = ((page - 1) & ~1) + 1;
+ rightPage = leftPage + 1;
+ pageW1 = pageW[leftPage - 1];
+ if (rightPage <= state->getDoc()->getNumPages()) {
+ pageW2 = pageW[rightPage - 1];
+ } else {
+ // display a single page as though there were a blank facing
+ // page of the same size
+ pageW2 = pageW1;
+ }
+ if (pageW1 + sideBySidePageSpacing + pageW2 < state->getWinW()) {
+ offsetX = (state->getWinW() -
+ (pageW1 + sideBySidePageSpacing + pageW2)) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
+ return (page == leftPage) ? offsetX : offsetX2;
+ case displaySideBySideContinuous:
+ leftPage = ((page - 1) & ~1) + 1;
+ rightPage = leftPage + 1;
+ pageW1 = pageW[leftPage - 1];
+ if (maxW + sideBySidePageSpacing + maxW2 < state->getWinW()) {
+ offsetX = (state->getWinW() -
+ (maxW + sideBySidePageSpacing + maxW2)) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX += maxW - pageW1;
+ offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
+ return (page == leftPage) ? offsetX : offsetX2;
+ case displayHorizontalContinuous:
+ return pageX[page - 1];
+ }
+}
+
+int TileMap::getPageRightX(int page) {
+ int leftPage, rightPage, pageW1, pageW2, offsetX, offsetX2;
+
+ if (!state->getDoc() || !state->getDoc()->getNumPages()) {
+ return 0;
+ }
+
+ updatePageParams();
+ updateContinuousModeParams();
+
+ switch (state->getDisplayMode()) {
+ case displaySingle:
+ default:
+ return pageW[page - 1] - state->getWinW();
+ case displayContinuous:
+ return (maxW + pageW[page - 1]) / 2 - state->getWinW();
+ case displaySideBySideSingle:
+ leftPage = ((page - 1) & ~1) + 1;
+ rightPage = leftPage + 1;
+ pageW1 = pageW[leftPage - 1];
+ if (rightPage <= state->getDoc()->getNumPages()) {
+ pageW2 = pageW[rightPage - 1];
+ } else {
+ // display a single page as though there were a blank facing
+ // page of the same size
+ pageW2 = pageW1;
+ }
+ if (pageW1 + sideBySidePageSpacing + pageW2 < state->getWinW()) {
+ offsetX = (state->getWinW() -
+ (pageW1 + sideBySidePageSpacing + pageW2)) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
+ return (page == leftPage) ? offsetX + pageW1 - state->getWinW()
+ : offsetX2 + pageW2 - state->getWinW();
+ case displaySideBySideContinuous:
+ leftPage = ((page - 1) & ~1) + 1;
+ rightPage = leftPage + 1;
+ pageW1 = pageW[leftPage - 1];
+ if (rightPage <= state->getDoc()->getNumPages()) {
+ pageW2 = pageW[rightPage - 1];
+ } else {
+ // display a single page as though there were a blank facing
+ // page of the same size
+ pageW2 = pageW1;
+ }
+ if (maxW + sideBySidePageSpacing + maxW2 < state->getWinW()) {
+ offsetX = (state->getWinW() -
+ (maxW + sideBySidePageSpacing + maxW2)) / 2;
+ } else {
+ offsetX = 0;
+ }
+ offsetX += maxW - pageW1;
+ offsetX2 = offsetX + pageW1 + sideBySidePageSpacing;
+ return (page == leftPage) ? offsetX + pageW1 - state->getWinW()
+ : offsetX2 + pageW2 - state->getWinW();
+ case displayHorizontalContinuous:
+ return pageX[page - 1] + pageW[page - 1] - state->getWinW();
+ }
+}
+
+int TileMap::getFirstPage() {
+ updateContinuousModeParams();
+ switch (state->getDisplayMode()) {
+ case displaySingle:
+ default:
+ return state->getScrollPage();
+ case displayContinuous:
+ return findContinuousPage(state->getScrollY());
+ case displaySideBySideSingle:
+ return state->getScrollPage();
+ case displaySideBySideContinuous:
+ return findSideBySideContinuousPage(state->getScrollY());
+ case displayHorizontalContinuous:
+ return findHorizContinuousPage(state->getScrollX());
+ }
+}
+
+int TileMap::getFirstPageTop() {
+ int page;
+
+ updateContinuousModeParams();
+ switch (state->getDisplayMode()) {
+ case displaySingle:
+ default:
+ return state->getScrollPage();
+ case displayContinuous:
+ page = findContinuousPage(state->getScrollY());
+ if (page < state->getDoc()->getNumPages() &&
+ pageY[page - 1] < state->getScrollY()) {
+ return page + 1;
+ } else {
+ return page;
+ }
+ case displaySideBySideSingle:
+ return state->getScrollPage();
+ case displaySideBySideContinuous:
+ page = findSideBySideContinuousPage(state->getScrollY());
+ if (page < state->getDoc()->getNumPages() &&
+ pageY[page - 1] < state->getScrollY()) {
+ return page + 1;
+ } else {
+ return page;
+ }
+ case displayHorizontalContinuous:
+ page = findHorizContinuousPage(state->getScrollX());
+ if (page < state->getDoc()->getNumPages() &&
+ pageX[page - 1] < state->getScrollX()) {
+ return page + 1;
+ } else {
+ return page;
+ }
+ }
+}
+
+int TileMap::getMidPage() {
+ int wx, wy, pg, x, y;
+
+ wx = state->getWinW() / 2;
+ wy = state->getWinH() / 2;
+ if (!cvtWindowToDev(wx, wy, &pg, &x, &y)) {
+ if (state->getDisplayMode() == displayContinuous) {
+ wy += continuousPageSpacing;
+ } else if (state->getDisplayMode() == displaySideBySideContinuous) {
+ wx += sideBySidePageSpacing;
+ wy += continuousPageSpacing;
+ } else if (state->getDisplayMode() == displayHorizontalContinuous) {
+ wx += horizContinuousPageSpacing;
+ } else {
+ return state->getScrollPage();
+ }
+ if (!cvtWindowToDev(wx, wy, &pg, &x, &y)) {
+ return 1;
+ }
+ }
+ return pg;
+}
+
+int TileMap::getLastPage() {
+ int pg, x, y, n;
+
+ switch (state->getDisplayMode()) {
+ case displaySingle:
+ default:
+ return state->getScrollPage();
+ case displayContinuous:
+ if (!cvtWindowToDev(state->getWinW() / 2, state->getWinH() - 1,
+ &pg, &x, &y)) {
+ return state->getDoc()->getNumPages();
+ }
+ return pg;
+ case displaySideBySideSingle:
+ pg = state->getScrollPage() + 1;
+ n = state->getDoc()->getNumPages();
+ if (pg > n) {
+ pg = n;
+ }
+ return pg;
+ case displaySideBySideContinuous:
+ if (!cvtWindowToDev(state->getWinW() / 2, state->getWinH() - 1,
+ &pg, &x, &y)) {
+ return state->getScrollPage();
+ }
+ pg = ((pg - 1) & ~1) + 2;
+ n = state->getDoc()->getNumPages();
+ if (pg > n) {
+ pg = n;
+ }
+ return pg;
+ case displayHorizontalContinuous:
+ x = state->getWinW() - 1;
+ y = state->getWinH() / 2;
+ if (!cvtWindowToDev(state->getWinW() - 1, state->getWinH() / 2,
+ &pg, &x, &y)) {
+ return state->getDoc()->getNumPages();
+ }
+ return pg;
+ }
+}
+
+double TileMap::getDPI(int page) {
+ if (page < 1 || page > state->getDoc()->getNumPages()) {
+ return 0;
+ }
+ updatePageParams();
+ return pageDPI[page - 1];
+}
+
+double TileMap::getPageBoxWidth(int page) {
+ return pageBoxW[page - 1];
+}
+
+double TileMap::getPageBoxHeight(int page) {
+ return pageBoxH[page - 1];
+}
+
+int TileMap::getContinuousPageSpacing() {
+ return continuousPageSpacing;
+}
+
+int TileMap::getSideBySidePageSpacing() {
+ return sideBySidePageSpacing;
+}
+
+int TileMap::getHorizContinuousPageSpacing() {
+ return horizContinuousPageSpacing;
+}
+
+void TileMap::docChanged() {
+ PDFDoc *doc;
+ int nPages, pg, rot;
+
+ doc = state->getDoc();
+
+ if (doc) {
+ nPages = doc->getNumPages();
+ } else {
+ nPages = 0;
+ }
+ pageBoxW = (double *)greallocn(pageBoxW, nPages, sizeof(double));
+ pageBoxH = (double *)greallocn(pageBoxH, nPages, sizeof(double));
+ for (pg = 1; pg <= nPages; ++pg) {
+ rot = doc->getPageRotate(pg);
+ if (rot == 0 || rot == 180) {
+ pageBoxW[pg - 1] = doc->getPageCropWidth(pg);
+ pageBoxH[pg - 1] = doc->getPageCropHeight(pg);
+ } else {
+ pageBoxW[pg - 1] = doc->getPageCropHeight(pg);
+ pageBoxH[pg - 1] = doc->getPageCropWidth(pg);
+ }
+ }
+
+ clearPageParams();
+ clearContinuousModeParams();
+ if (tiles) {
+ deleteGList(tiles, PlacedTileDesc);
+ tiles = NULL;
+ }
+}
+
+void TileMap::windowSizeChanged() {
+ clearPageParams();
+ clearContinuousModeParams();
+ if (tiles) {
+ deleteGList(tiles, PlacedTileDesc);
+ tiles = NULL;
+ }
+}
+
+void TileMap::displayModeChanged() {
+ clearPageParams();
+ clearContinuousModeParams();
+ if (tiles) {
+ deleteGList(tiles, PlacedTileDesc);
+ tiles = NULL;
+ }
+}
+
+void TileMap::zoomChanged() {
+ clearPageParams();
+ clearContinuousModeParams();
+ if (tiles) {
+ deleteGList(tiles, PlacedTileDesc);
+ tiles = NULL;
+ }
+}
+
+void TileMap::rotateChanged() {
+ clearPageParams();
+ clearContinuousModeParams();
+ if (tiles) {
+ deleteGList(tiles, PlacedTileDesc);
+ tiles = NULL;
+ }
+}
+
+void TileMap::scrollPositionChanged() {
+ if (tiles) {
+ deleteGList(tiles, PlacedTileDesc);
+ tiles = NULL;
+ }
+}
+
+
+void TileMap::forceRedraw() {
+ clearPageParams();
+ clearContinuousModeParams();
+ if (tiles) {
+ deleteGList(tiles, PlacedTileDesc);
+ tiles = NULL;
+ }
+}
+
+void TileMap::clearPageParams() {
+ gfree(pageDPI);
+ gfree(pageW);
+ gfree(pageH);
+ gfree(tileW);
+ gfree(tileH);
+ pageDPI = NULL;
+ pageW = pageH = NULL;
+ tileW = tileH = NULL;
+}
+
+void TileMap::updatePageParams() {
+ double rotPageBoxW, rotPageBoxW2, rotPageBoxH, rotPageBoxH2, rotPageBoxHMax;
+ double hDPI, vDPI, dpi;
+ int page, otherPage, nxTiles, nyTiles;
+
+ //--- check to see if the continuous mode params have already been updated
+ if (pageDPI) {
+ return;
+ }
+
+ //--- allocate memory
+ pageDPI = (double *)gmallocn(state->getDoc()->getNumPages(), sizeof(double));
+ pageW = (int *)gmallocn(state->getDoc()->getNumPages(), sizeof(int));
+ pageH = (int *)gmallocn(state->getDoc()->getNumPages(), sizeof(int));
+ tileW = (int *)gmallocn(state->getDoc()->getNumPages(), sizeof(int));
+ tileH = (int *)gmallocn(state->getDoc()->getNumPages(), sizeof(int));
+
+ for (page = 1; page <= state->getDoc()->getNumPages(); ++page) {
+
+ //--- special handling for side-by-side modes
+ if (state->displayModeIsSideBySide()) {
+
+ // rotate the page boxes
+ if (page & 1) {
+ otherPage = page + 1;
+ if (otherPage >= state->getDoc()->getNumPages()) {
+ otherPage = page;
+ }
+ } else {
+ otherPage = page - 1;
+ if (otherPage < 1) {
+ otherPage = page;
+ }
+ }
+ if (state->getRotate() == 0 || state->getRotate() == 180) {
+ rotPageBoxW = pageBoxW[page - 1];
+ rotPageBoxW2 = pageBoxW[otherPage - 1];
+ rotPageBoxH = pageBoxH[page - 1];
+ rotPageBoxH2 = pageBoxH[otherPage - 1];
+ } else {
+ rotPageBoxW = pageBoxH[page - 1];
+ rotPageBoxW2 = pageBoxH[otherPage - 1];
+ rotPageBoxH = pageBoxW[page - 1];
+ rotPageBoxH2 = pageBoxW[otherPage - 1];
+ }
+ rotPageBoxHMax = (rotPageBoxH > rotPageBoxH2) ? rotPageBoxH
+ : rotPageBoxH2;
+
+ // compute resolution
+ if (state->getZoom() == zoomPage) {
+ hDPI = ((state->getWinW() - sideBySidePageSpacing) /
+ (rotPageBoxW + rotPageBoxW2)) * 72.0;
+ vDPI = (state->getWinH() / rotPageBoxHMax) * 72.0;
+ dpi = hDPI < vDPI ? hDPI : vDPI;
+ // allow for some floating point jitter
+ dpi -= 0.01;
+ } else if (state->getZoom() == zoomWidth) {
+ dpi = ((state->getWinW() - sideBySidePageSpacing) /
+ (rotPageBoxW + rotPageBoxW2)) * 72.0;
+ // allow for some floating point jitter
+ dpi -= 0.01;
+ } else if (state->getZoom() == zoomHeight) {
+ dpi = (state->getWinH() / rotPageBoxHMax) * 72.0;
+ // allow for some floating point jitter
+ dpi -= 0.01;
+ } else {
+ dpi = 0.01 * state->getZoom() * 72.0;
+ }
+
+ //--- all other (non-side-by-side) modes
+ } else {
+
+ // rotate the page boxes
+ if (state->getRotate() == 0 || state->getRotate() == 180) {
+ rotPageBoxW = pageBoxW[page - 1];
+ rotPageBoxH = pageBoxH[page - 1];
+ } else {
+ rotPageBoxW = pageBoxH[page - 1];
+ rotPageBoxH = pageBoxW[page - 1];
+ }
+
+ // compute resolution
+ if (state->getZoom() == zoomPage) {
+ hDPI = (state->getWinW() / rotPageBoxW) * 72.0;
+ vDPI = (state->getWinH() / rotPageBoxH) * 72.0;
+ dpi = hDPI < vDPI ? hDPI : vDPI;
+ // allow for some floating point jitter
+ dpi -= 0.01;
+ } else if (state->getZoom() == zoomWidth) {
+ dpi = (state->getWinW() / rotPageBoxW) * 72.0;
+ // allow for some floating point jitter
+ dpi -= 0.01;
+ } else if (state->getZoom() == zoomHeight) {
+ dpi = (state->getWinH() / rotPageBoxH) * 72.0;
+ // allow for some floating point jitter
+ dpi -= 0.01;
+ } else {
+ dpi = 0.01 * state->getZoom() * 72.0;
+ }
+
+ }
+ pageDPI[page - 1] = dpi;
+
+ // compute bitmap size
+ pageW[page - 1] = (int)((rotPageBoxW * dpi / 72.0) + 0.5);
+ if (pageW[page - 1] < 1) {
+ pageW[page - 1] = 1;
+ }
+ pageH[page - 1] = (int)((rotPageBoxH * dpi / 72.0) + 0.5);
+ if (pageH[page - 1] < 1) {
+ pageH[page - 1] = 1;
+ }
+
+ // compute tile size
+ // (tile width and height are rounded up -- the bottom and right
+ // tiles may be slightly smaller than the computed size)
+ if (pageW[page - 1] <= state->getMaxTileWidth()) {
+ nxTiles = 1;
+ tileW[page - 1] = pageW[page - 1];
+ } else {
+ nxTiles = (pageW[page - 1] + state->getMaxTileWidth() - 1)
+ / state->getMaxTileWidth();
+ tileW[page - 1] = (pageW[page - 1] + nxTiles - 1) / nxTiles;
+ }
+ if (pageH[page - 1] <= state->getMaxTileHeight()) {
+ nyTiles = 1;
+ tileH[page - 1] = pageH[page - 1];
+ } else {
+ nyTiles = (pageH[page - 1] + state->getMaxTileHeight() - 1)
+ / state->getMaxTileHeight();
+ tileH[page - 1] = (pageH[page - 1] + nyTiles - 1) / nyTiles;
+ }
+
+ }
+}
+
+void TileMap::clearContinuousModeParams() {
+ gfree(pageX);
+ pageX = pageY = NULL;
+}
+
+void TileMap::updateContinuousModeParams() {
+ int page, pageW1, pageH1, pageW2, pageH2, x, y;
+
+ // check to see if the continuous mode params have already been updated
+ if (pageX) {
+ return;
+ }
+
+ updatePageParams();
+
+ switch (state->getDisplayMode()) {
+ case displayContinuous:
+ if (!pageX) {
+ pageX = pageY = (int *)gmallocn(state->getDoc()->getNumPages(),
+ sizeof(int));
+ }
+ y = 0;
+ maxW = 0;
+ for (page = 1; page <= state->getDoc()->getNumPages(); ++page) {
+ pageY[page - 1] = y;
+ y += pageH[page - 1] + continuousPageSpacing;
+ if (page == 1 || pageW[page - 1] > maxW) {
+ maxW = pageW[page - 1];
+ }
+ }
+ totalH = y - continuousPageSpacing;
+ break;
+ case displaySideBySideContinuous:
+ if (!pageX) {
+ pageX = pageY = (int *)gmallocn(state->getDoc()->getNumPages(),
+ sizeof(int));
+ }
+ y = 0;
+ maxW = maxW2 = 0;
+ for (page = 1; page <= state->getDoc()->getNumPages(); page += 2) {
+ pageW1 = pageW[page - 1];
+ pageH1 = pageH[page - 1];
+ if (page + 1 <= state->getDoc()->getNumPages()) {
+ pageW2 = pageW[page];
+ pageH2 = pageH[page];
+ } else {
+ pageW2 = pageW1;
+ pageH2 = pageH1;
+ }
+ pageY[page - 1] = y;
+ if (page == 1 || pageW1 > maxW) {
+ maxW = pageW1;
+ }
+ if (page + 1 <= state->getDoc()->getNumPages()) {
+ pageY[page] = y;
+ }
+ if (pageW2 > maxW2) {
+ maxW2 = pageW2;
+ }
+ y += (pageH1 > pageH2) ? pageH1 : pageH2;
+ y += continuousPageSpacing;
+ }
+ totalH = y - continuousPageSpacing;
+ break;
+ case displayHorizontalContinuous:
+ if (!pageX) {
+ pageX = pageY = (int *)gmallocn(state->getDoc()->getNumPages(),
+ sizeof(int));
+ }
+ x = 0;
+ maxH = 0;
+ for (page = 1; page <= state->getDoc()->getNumPages(); ++page) {
+ pageX[page - 1] = x;
+ x += pageW[page - 1] + horizContinuousPageSpacing;
+ if (page == 1 || pageH[page - 1] > maxH) {
+ maxH = pageH[page - 1];
+ }
+ }
+ totalW = x - horizContinuousPageSpacing;
+ break;
+ default:
+ break;
+ }
+}
+
+void TileMap::computePageMatrix(int page, double *m) {
+ PDFRectangle *cropBox;
+ double px1, py1, px2, py2, k;
+ int rotate;
+
+ updatePageParams();
+ cropBox = state->getDoc()->getCatalog()->getPage(page)->getCropBox();
+ px1 = cropBox->x1;
+ py1 = cropBox->y1;
+ px2 = cropBox->x2;
+ py2 = cropBox->y2;
+ k = pageDPI[page - 1] / 72.0;
+ rotate = state->getRotate() +
+ state->getDoc()->getCatalog()->getPage(page)->getRotate();
+ if (rotate > 360) {
+ rotate -= 360;
+ }
+ switch (rotate) {
+ case 0:
+ default:
+ m[0] = k;
+ m[1] = 0;
+ m[2] = 0;
+ m[3] = -k;
+ m[4] = -k * px1;
+ m[5] = k * py2;
+ break;
+ case 90:
+ m[0] = 0;
+ m[1] = k;
+ m[2] = k;
+ m[3] = 0;
+ m[4] = -k * py1;
+ m[5] = -k * px1;
+ break;
+ case 180:
+ m[0] = -k;
+ m[1] = 0;
+ m[2] = 0;
+ m[3] = k;
+ m[4] = k * px2;
+ m[5] = -k * py1;
+ break;
+ case 270:
+ m[0] = 0;
+ m[1] = -k;
+ m[2] = -k;
+ m[3] = 0;
+ m[4] = k * py2;
+ m[5] = k * px2;
+ break;
+ }
+}
+
+void TileMap::invertMatrix(double *m, double *im) {
+ double det;
+
+ det = 1 / (m[0] * m[3] - m[1] * m[2]);
+ im[0] = m[3] * det;
+ im[1] = -m[1] * det;
+ im[2] = -m[2] * det;
+ im[3] = m[0] * det;
+ im[4] = (m[2] * m[5] - m[3] * m[4]) * det;
+ im[5] = (m[1] * m[4] - m[0] * m[5]) * det;
+}
+
+int TileMap::findContinuousPage(int y) {
+ int a, b, m;
+
+ if (y < pageY[0]) {
+ return 0;
+ }
+ if (y >= totalH) {
+ return state->getDoc()->getNumPages() + 1;
+ }
+ a = -1;
+ b = state->getDoc()->getNumPages();
+ // invariant: pageY[a] < y < pageY[b]
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (y > pageY[m] - continuousPageSpacing) {
+ a = m;
+ } else if (y < pageY[m] - continuousPageSpacing) {
+ b = m;
+ } else {
+ return m + 1;
+ }
+ }
+ return a + 1;
+}
+
+int TileMap::findSideBySideContinuousPage(int y) {
+ int a, b, m;
+
+ if (y < pageY[0]) {
+ return 0;
+ }
+ if (y >= totalH) {
+ return (state->getDoc()->getNumPages() + 2) & ~1;
+ }
+ a = -2;
+ b = (state->getDoc()->getNumPages() + 1) & ~1;
+ // invariant: pageY[a] < y < pageY[b]
+ while (b - a > 2) {
+ m = ((a + b) / 2) & ~1;
+ if (y > pageY[m] - continuousPageSpacing) {
+ a = m;
+ } else if (y < pageY[m] - continuousPageSpacing) {
+ b = m;
+ } else {
+ return m + 1;
+ }
+ }
+ return a + 1;
+}
+
+int TileMap::findHorizContinuousPage(int x) {
+ int a, b, m;
+
+ if (x < pageX[0]) {
+ return 0;
+ }
+ if (x >= totalW) {
+ return state->getDoc()->getNumPages() + 1;
+ }
+ a = -1;
+ b = state->getDoc()->getNumPages();
+ // invariant: pageX[a] < x < pageX[b]
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (x > pageX[m] - horizContinuousPageSpacing) {
+ a = m;
+ } else if (x < pageX[m] - horizContinuousPageSpacing) {
+ b = m;
+ } else {
+ return m + 1;
+ }
+ }
+ return a + 1;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/TileMap.h b/Build/source/libs/xpdf/xpdf-src/xpdf/TileMap.h
new file mode 100644
index 00000000000..222877c5050
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/TileMap.h
@@ -0,0 +1,247 @@
+//========================================================================
+//
+// TileMap.h
+//
+// Copyright 2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef TILEMAP_H
+#define TILEMAP_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class GList;
+class DisplayState;
+
+//------------------------------------------------------------------------
+
+// Tile descriptor: this is all of the information needed to specify a
+// tile.
+class TileDesc {
+public:
+
+ TileDesc(int pageA, int rotateA, double dpiA,
+ int txA, int tyA, int twA, int thA):
+ page(pageA), rotate(rotateA), dpi(dpiA),
+ tx(txA), ty(tyA), tw(twA), th(thA) {}
+
+ GBool matches(TileDesc *tile) {
+ return page == tile->page && rotate == tile->rotate &&
+ dpi == tile->dpi &&
+ tx == tile->tx && ty == tile->ty &&
+ tw == tile->tw && th == tile->th;
+ }
+
+ int page; // page number
+ int rotate; // rotation
+ double dpi; // tile resolution
+ int tx, ty; // origin of tile, relative to top left
+ // corner of page
+ int tw, th; // tile size
+};
+
+//------------------------------------------------------------------------
+
+// A TileDesc plus the tile's location in the display.
+class PlacedTileDesc: public TileDesc {
+public:
+
+ PlacedTileDesc(int pageA, int rotateA, double dpiA,
+ int txA, int tyA, int twA, int thA,
+ int pxA, int pyA):
+ TileDesc(pageA, rotateA, dpiA, txA, tyA, twA, thA), px(pxA), py(pyA) {}
+
+ int px, py; // position, in window coordinates, of
+ // tile origin
+};
+
+//------------------------------------------------------------------------
+
+// Tile map: this maps window position to tile, and vice versa; and
+// computes the list of tiles needed for the current display.
+class TileMap {
+public:
+
+ TileMap(DisplayState *stateA);
+ ~TileMap();
+
+ // Returns a list of PlacedTileDesc objects describing the tiles
+ // needed for the current display. The returned list is owned by
+ // the TileMap object (and may be reused) -- the caller should not
+ // modify or free it.
+ GList *getTileList();
+
+ // Return the max values for the horizontal and vertical scrollbars.
+ // Scroll thumbs should be winW and winH.
+ void getScrollLimits(int *horizMax, int *vertMax);
+
+ // Coordinate conversion functions.
+ // - user space: per-page, as defined by PDF file; unit = point
+ // - device space: (0,0) is upper-left corner of a page; unit = pixel
+ // - window space: (0,0) is upper-left corner of drawing area; unit = pixel
+ // Conversions to window space return false if the specified page
+ // isn't visible (in non-continuous modes). Conversions from window
+ // space return false if the point isn't on a page. Otherwise,
+ // these functions do not do bounds-checking or any clipping to
+ // window or page coordinates.
+ GBool cvtWindowToUser(int xw, int yw,
+ int *pg, double *xu, double *yu);
+ GBool cvtWindowToDev(int xw, int yw,
+ int *pg, int *xd, int *yd);
+ GBool cvtUserToWindow(int pg, double xu, double yu,
+ int *xw, int *yw);
+ GBool cvtDevToWindow(int pg, int xd, int yd,
+ int *xw, int *yw);
+ void cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd);
+ void cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu);
+
+ // Return the range of pages intersected by a rectangle in window
+ // coordinates. E.g., these are the pages that will need to be
+ // redrawn for a redraw event covering that rectangle.
+ void getWindowPageRange(int x, int y, int w, int h,
+ int *firstPage, int *lastPage);
+
+ // Return the scrollY setting that would place the top of <page> at
+ // the top of the window. (In non-continuous modes, this just
+ // returns zero.)
+ int getPageTopY(int page);
+
+ // Return the scrollY setting that would place the bottom of <page>
+ // at the bottom of the window.
+ int getPageBottomY(int page);
+
+ // Return the scrollX setting that would place the left side of
+ // <page> at the left side of the window. (In non-continuous modes,
+ // this just returns zero.)
+ int getPageLeftX(int page);
+
+ // Return the scrollX setting that would place the right side of
+ // <page> at the right side of the window.
+ int getPageRightX(int page);
+
+ // In continuous modes: return the lowest page number visible
+ // anywhere in the window; in non-continuous modes: return the
+ // current page.
+ int getFirstPage();
+
+ // In continuous modes: return the lowest page number whose top (or
+ // left) edge is visible in the window (or if no top/left edge is
+ // visilble, the page after the single visible page); in
+ // non-continuous modes: return the current page.
+ int getFirstPageTop();
+
+ // In continuous modes: return the page number at the center of the
+ // window; in non-continuous modes: return the current page.
+ int getMidPage();
+
+ // In continuous modes: return the highest page number visible
+ // anywhere in the window; in non-continuous modes: return the
+ // current page.
+ int getLastPage();
+
+ // Get the resolution at which the specified will be rasterized.
+ double getDPI(int page);
+
+ // Get the width and height of a page, in points.
+ double getPageBoxWidth(int page);
+ double getPageBoxHeight(int page);
+
+ // Get the number of pixels (vertically) between adjacent pages in
+ // continuous / side-by-side continuous mode.
+ int getContinuousPageSpacing();
+
+ // Get the number of pixels (horizontally) between facing pages in
+ // the side-by-side modes.
+ int getSideBySidePageSpacing();
+
+ // Get the number of pixels (horizontally) between adjacent pages in
+ // horizontal continuous mode.
+ int getHorizContinuousPageSpacing();
+
+ void docChanged();
+ void windowSizeChanged();
+ void displayModeChanged();
+ void zoomChanged();
+ void rotateChanged();
+ void scrollPositionChanged();
+ void forceRedraw();
+
+private:
+
+ // Clear the pageDPI, pageW, pageH, tileW, and tileH arrays.
+ void clearPageParams();
+
+ // Update pageDPI, pageW, pageH, tileW, tileH (if needed).
+ void updatePageParams();
+
+ // Clear the pageX/pageY array.
+ void clearContinuousModeParams();
+
+ // Update pageX, pageY, maxW, maxW2, maxH, totalW, totalH.
+ void updateContinuousModeParams();
+
+ // Compute the user-to-device transform matrix for the specified
+ // page.
+ void computePageMatrix(int page, double *m);
+
+ // Invert a matrix.
+ void invertMatrix(double *m, double *im);
+
+ // Return the page number at y. If y falls in a gap between pages,
+ // return the next page. If y falls after the last page, return
+ // numPages + 1.
+ int findContinuousPage(int y);
+
+ // Return the left-side page number at y. If y falls in a gap between
+ // page pairs, return the next page. If y falls after the last page,
+ // return numPages + 1 (or numPages + 2 if numPages is odd).
+ int findSideBySideContinuousPage(int y);
+
+ // Return the page number at x. If x falls in a gap between pages,
+ // return the next page. If x falls after the last page, return
+ // numPages + 1.
+ int findHorizContinuousPage(int x);
+
+ DisplayState *state;
+
+ double *pageBoxW, *pageBoxH; // page sizes, in points
+
+ // Page parameters:
+ // pageDPI[pg-1] = rasterization resolution
+ // pageW[pg-1], pageH[pg-1] = size of rasterized page
+ // tileW[pg-1], tileH[pg-1] = size of tiles
+ double *pageDPI;
+ int *pageW, *pageH;
+ int *tileW, *tileH;
+
+ // In displayContinuous mode:
+ // pageY[pg-1] = top edge of page
+ // maxW = max page width
+ // totalH = total page height (including gaps)
+ // In displaySideBySideContinuous mode:
+ // pageY[pg-1] = top edge of page-pair
+ // pageY[pg] = top edge of page-pair (if pg+1 <= nPages)
+ // maxW = max left-side page width
+ // maxW2 = max right-side page width
+ // totalH = total page-pair height (including gaps)
+ // In displayHorizontalContinuous mode:
+ // pageX[pg-1] = left edge of page
+ // maxH = max page height
+ // totalW = total page width (including gaps)
+ // Only one of pageX or pageY is used at a time, so a single array
+ // is allocated, i.e., pageX = pageY.
+ int *pageX, *pageY;
+ int maxW, maxW2, maxH;
+ int totalW, totalH;
+
+ GList *tiles;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/UTF8.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/UTF8.cc
new file mode 100644
index 00000000000..9ee01425094
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/UTF8.cc
@@ -0,0 +1,183 @@
+//========================================================================
+//
+// UTF8.cc
+//
+// Copyright 2001-2017 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include "UTF8.h"
+
+int mapUTF8(Unicode u, char *buf, int bufSize) {
+ if (u <= 0x0000007f) {
+ if (bufSize < 1) {
+ return 0;
+ }
+ buf[0] = (char)u;
+ return 1;
+ } else if (u <= 0x000007ff) {
+ if (bufSize < 2) {
+ return 0;
+ }
+ buf[0] = (char)(0xc0 + (u >> 6));
+ buf[1] = (char)(0x80 + (u & 0x3f));
+ return 2;
+ } else if (u <= 0x0000ffff) {
+ if (bufSize < 3) {
+ return 0;
+ }
+ buf[0] = (char)(0xe0 + (u >> 12));
+ buf[1] = (char)(0x80 + ((u >> 6) & 0x3f));
+ buf[2] = (char)(0x80 + (u & 0x3f));
+ return 3;
+ } else if (u <= 0x0010ffff) {
+ if (bufSize < 4) {
+ return 0;
+ }
+ buf[0] = (char)(0xf0 + (u >> 18));
+ buf[1] = (char)(0x80 + ((u >> 12) & 0x3f));
+ buf[2] = (char)(0x80 + ((u >> 6) & 0x3f));
+ buf[3] = (char)(0x80 + (u & 0x3f));
+ return 4;
+ } else {
+ return 0;
+ }
+}
+
+int mapUCS2(Unicode u, char *buf, int bufSize) {
+ if (u <= 0xffff) {
+ if (bufSize < 2) {
+ return 0;
+ }
+ buf[0] = (char)((u >> 8) & 0xff);
+ buf[1] = (char)(u & 0xff);
+ return 2;
+ } else {
+ return 0;
+ }
+}
+
+GBool getUTF8(GString *s, int *i, Unicode *u) {
+ Guchar c0, c1, c2, c3, c4, c5;
+
+ if (*i >= s->getLength()) {
+ return gFalse;
+ }
+ c0 = (Guchar)s->getChar((*i)++);
+ if (c0 < 0x80) {
+ *u = (Unicode)c0;
+ } else if (c0 < 0xe0) {
+ if (*i < s->getLength() &&
+ ((c1 = (Guchar)s->getChar(*i)) & 0xc0) == 0x80) {
+ *i += 1;
+ *u = (Unicode)(((c0 & 0x1f) << 6) |
+ (c1 & 0x3f));
+ } else {
+ *u = (Unicode)c0;
+ }
+ } else if (c0 < 0xf0) {
+ if (*i < s->getLength() - 1 &&
+ ((c1 = (Guchar)s->getChar(*i)) & 0xc0) == 0x80 &&
+ ((c2 = (Guchar)s->getChar(*i + 1)) & 0xc0) == 0x80) {
+ *i += 2;
+ *u = (Unicode)(((c0 & 0x0f) << 12) |
+ ((c1 & 0x3f) << 6) |
+ (c2 & 0x3f));
+ } else {
+ *u = (Unicode)c0;
+ }
+ } else if (c0 < 0xf8) {
+ if (*i < s->getLength() - 2 &&
+ ((c1 = (Guchar)s->getChar(*i)) & 0xc0) == 0x80 &&
+ ((c2 = (Guchar)s->getChar(*i + 1)) & 0xc0) == 0x80 &&
+ ((c3 = (Guchar)s->getChar(*i + 2)) & 0xc0) == 0x80) {
+ *i += 3;
+ *u = (Unicode)(((c0 & 0x07) << 18) |
+ ((c1 & 0x3f) << 12) |
+ ((c2 & 0x3f) << 6) |
+ (c3 & 0x3f));
+ } else {
+ *u = (Unicode)c0;
+ }
+ } else if (c0 < 0xfc) {
+ if (*i < s->getLength() - 3 &&
+ ((c1 = (Guchar)s->getChar(*i)) & 0xc0) == 0x80 &&
+ ((c2 = (Guchar)s->getChar(*i + 1)) & 0xc0) == 0x80 &&
+ ((c3 = (Guchar)s->getChar(*i + 2)) & 0xc0) == 0x80 &&
+ ((c4 = (Guchar)s->getChar(*i + 3)) & 0xc0) == 0x80) {
+ *i += 4;
+ *u = (Unicode)(((c0 & 0x03) << 24) |
+ ((c1 & 0x3f) << 18) |
+ ((c2 & 0x3f) << 12) |
+ ((c3 & 0x3f) << 6) |
+ (c4 & 0x3f));
+ } else {
+ *u = (Unicode)c0;
+ }
+ } else if (c0 < 0xfe) {
+ if (*i < s->getLength() - 4 &&
+ ((c1 = (Guchar)s->getChar(*i)) & 0xc0) == 0x80 &&
+ ((c2 = (Guchar)s->getChar(*i + 1)) & 0xc0) == 0x80 &&
+ ((c3 = (Guchar)s->getChar(*i + 2)) & 0xc0) == 0x80 &&
+ ((c4 = (Guchar)s->getChar(*i + 3)) & 0xc0) == 0x80 &&
+ ((c5 = (Guchar)s->getChar(*i + 4)) & 0xc0) == 0x80) {
+ *i += 5;
+ *u = (Unicode)(((c0 & 0x01) << 30) |
+ ((c1 & 0x3f) << 24) |
+ ((c2 & 0x3f) << 18) |
+ ((c3 & 0x3f) << 12) |
+ ((c4 & 0x3f) << 6) |
+ (c5 & 0x3f));
+ } else {
+ *u = (Unicode)c0;
+ }
+ } else {
+ *u = (Unicode)c0;
+ }
+ return gTrue;
+}
+
+GBool getUTF16BE(GString *s, int *i, Unicode *u) {
+ int w0, w1;
+
+ if (*i >= s->getLength() - 1) {
+ return gFalse;
+ }
+ w0 = ((s->getChar(*i) & 0xff) << 8) | (s->getChar(*i + 1) & 0xff);
+ *i += 2;
+ if (w0 < 0xd800 || w0 >= 0xe000) {
+ *u = (Unicode)w0;
+ } else {
+ if (*i < s->getLength() - 1) {
+ w1 = ((s->getChar(*i) & 0xff) << 8) | (s->getChar(*i + 1) & 0xff);
+ *i += 2;
+ *u = 0x10000 + ((w0 - 0xd800) << 10) + (w1 - 0xdc00);
+ } else {
+ *u = (Unicode)w0;
+ }
+ }
+ return gTrue;
+}
+
+GBool getUTF16LE(GString *s, int *i, Unicode *u) {
+ int w0, w1;
+
+ if (*i >= s->getLength() - 1) {
+ return gFalse;
+ }
+ w0 = (s->getChar(*i) & 0xff) | ((s->getChar(*i + 1) & 0xff) << 8);
+ *i += 2;
+ if (w0 < 0xd800 || w0 >= 0xe000) {
+ *u = (Unicode)w0;
+ } else {
+ if (*i < s->getLength() - 1) {
+ w1 = (s->getChar(*i) & 0xff) | ((s->getChar(*i + 1) & 0xff) << 8);
+ *i += 2;
+ *u = 0x10000 + ((w0 - 0xd800) << 10) + (w1 - 0xdc00);
+ } else {
+ *u = (Unicode)w0;
+ }
+ }
+ return gTrue;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/UTF8.h b/Build/source/libs/xpdf/xpdf-src/xpdf/UTF8.h
new file mode 100644
index 00000000000..1f29030096f
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/UTF8.h
@@ -0,0 +1,42 @@
+//========================================================================
+//
+// UTF8.h
+//
+// Copyright 2001-2017 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef UTF8_H
+#define UTF8_H
+
+#include <aconf.h>
+
+#include "gtypes.h"
+#include "GString.h"
+#include "CharTypes.h"
+
+// Convert [u] to UTF-8 in [buf]. Returns the number of bytes written
+// to [buf]. If [u] requires more then [bufSize] bytes in UTF-8,
+// writes nothing and returns 0.
+extern int mapUTF8(Unicode u, char *buf, int bufSize);
+
+// Convert [u] to UCS-2BE in [buf]. Returns the number of bytes
+// written to [buf]. If [u] requires more then [bufSize] bytes in
+// UCS-2, writes nothing and returns 0.
+extern int mapUCS2(Unicode u, char *buf, int bufSize);
+
+// Parse one UTF-8 character from [s], starting at *[i]. Writes the
+// character to *[u], updates *[i] to point to the next available byte
+// in [s], and returns true. At end of string: writes nothing to *[u]
+// or *[i] and returns false. For an invalid UTF-8 character: sets
+// *[u] to the next byte, advances *[i] by one (to avoid infinite
+// loops), and returns true.
+extern GBool getUTF8(GString *s, int *i, Unicode *u);
+
+// Same as getUTF8, but for UTF-16BE.
+extern GBool getUTF16BE(GString *s, int *i, Unicode *u);
+
+// Same as getUTF8, but for UTF-16LE.
+extern GBool getUTF16LE(GString *s, int *i, Unicode *u);
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeMap.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeMap.cc
new file mode 100644
index 00000000000..833d6fc2f0c
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeMap.cc
@@ -0,0 +1,281 @@
+//========================================================================
+//
+// UnicodeMap.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "gfile.h"
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "UnicodeMap.h"
+
+//------------------------------------------------------------------------
+
+#define maxExtCode 16
+
+struct UnicodeMapExt {
+ Unicode u; // Unicode char
+ char code[maxExtCode];
+ Guint nBytes;
+};
+
+//------------------------------------------------------------------------
+
+UnicodeMap *UnicodeMap::parse(GString *encodingNameA) {
+ FILE *f;
+ UnicodeMap *map;
+ UnicodeMapRange *range;
+ UnicodeMapExt *eMap;
+ int size, eMapsSize;
+ char buf[256];
+ int line, nBytes, i, x;
+ char *tok1, *tok2, *tok3;
+
+ if (!(f = globalParams->getUnicodeMapFile(encodingNameA))) {
+ error(errSyntaxError, -1,
+ "Couldn't find unicodeMap file for the '{0:t}' encoding",
+ encodingNameA);
+ return NULL;
+ }
+
+ map = new UnicodeMap(encodingNameA->copy());
+
+ size = 8;
+ map->ranges = (UnicodeMapRange *)gmallocn(size, sizeof(UnicodeMapRange));
+ eMapsSize = 0;
+
+ line = 1;
+ while (getLine(buf, sizeof(buf), f)) {
+ if ((tok1 = strtok(buf, " \t\r\n")) &&
+ (tok2 = strtok(NULL, " \t\r\n"))) {
+ if (!(tok3 = strtok(NULL, " \t\r\n"))) {
+ tok3 = tok2;
+ tok2 = tok1;
+ }
+ nBytes = (int)strlen(tok3) / 2;
+ if (nBytes <= 4) {
+ if (map->len == size) {
+ size *= 2;
+ map->ranges = (UnicodeMapRange *)
+ greallocn(map->ranges, size, sizeof(UnicodeMapRange));
+ }
+ range = &map->ranges[map->len];
+ sscanf(tok1, "%x", &range->start);
+ sscanf(tok2, "%x", &range->end);
+ sscanf(tok3, "%x", &range->code);
+ range->nBytes = nBytes;
+ ++map->len;
+ } else if (tok2 == tok1) {
+ if (map->eMapsLen == eMapsSize) {
+ eMapsSize += 16;
+ map->eMaps = (UnicodeMapExt *)
+ greallocn(map->eMaps, eMapsSize, sizeof(UnicodeMapExt));
+ }
+ eMap = &map->eMaps[map->eMapsLen];
+ sscanf(tok1, "%x", &eMap->u);
+ for (i = 0; i < nBytes; ++i) {
+ sscanf(tok3 + i*2, "%2x", &x);
+ eMap->code[i] = (char)x;
+ }
+ eMap->nBytes = nBytes;
+ ++map->eMapsLen;
+ } else {
+ error(errSyntaxError, -1,
+ "Bad line ({0:d}) in unicodeMap file for the '{1:t}' encoding",
+ line, encodingNameA);
+ }
+ } else {
+ error(errSyntaxError, -1,
+ "Bad line ({0:d}) in unicodeMap file for the '{1:t}' encoding",
+ line, encodingNameA);
+ }
+ ++line;
+ }
+
+ fclose(f);
+
+ return map;
+}
+
+UnicodeMap::UnicodeMap(GString *encodingNameA) {
+ encodingName = encodingNameA;
+ unicodeOut = gFalse;
+ kind = unicodeMapUser;
+ ranges = NULL;
+ len = 0;
+ eMaps = NULL;
+ eMapsLen = 0;
+ refCnt = 1;
+}
+
+UnicodeMap::UnicodeMap(const char *encodingNameA, GBool unicodeOutA,
+ UnicodeMapRange *rangesA, int lenA) {
+ encodingName = new GString(encodingNameA);
+ unicodeOut = unicodeOutA;
+ kind = unicodeMapResident;
+ ranges = rangesA;
+ len = lenA;
+ eMaps = NULL;
+ eMapsLen = 0;
+ refCnt = 1;
+}
+
+UnicodeMap::UnicodeMap(const char *encodingNameA, GBool unicodeOutA,
+ UnicodeMapFunc funcA) {
+ encodingName = new GString(encodingNameA);
+ unicodeOut = unicodeOutA;
+ kind = unicodeMapFunc;
+ func = funcA;
+ eMaps = NULL;
+ eMapsLen = 0;
+ refCnt = 1;
+}
+
+UnicodeMap::~UnicodeMap() {
+ delete encodingName;
+ if (kind == unicodeMapUser && ranges) {
+ gfree(ranges);
+ }
+ if (eMaps) {
+ gfree(eMaps);
+ }
+}
+
+void UnicodeMap::incRefCnt() {
+#if MULTITHREADED
+ gAtomicIncrement(&refCnt);
+#else
+ ++refCnt;
+#endif
+}
+
+void UnicodeMap::decRefCnt() {
+ GBool done;
+
+#if MULTITHREADED
+ done = gAtomicDecrement(&refCnt) == 0;
+#else
+ done = --refCnt == 0;
+#endif
+ if (done) {
+ delete this;
+ }
+}
+
+GBool UnicodeMap::match(GString *encodingNameA) {
+ return !encodingName->cmp(encodingNameA);
+}
+
+int UnicodeMap::mapUnicode(Unicode u, char *buf, int bufSize) {
+ int a, b, m, n, i, j;
+ Guint code;
+
+ if (kind == unicodeMapFunc) {
+ return (*func)(u, buf, bufSize);
+ }
+
+ a = 0;
+ b = len;
+ if (u >= ranges[a].start) {
+ // invariant: ranges[a].start <= u < ranges[b].start
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (u >= ranges[m].start) {
+ a = m;
+ } else if (u < ranges[m].start) {
+ b = m;
+ }
+ }
+ if (u <= ranges[a].end) {
+ n = ranges[a].nBytes;
+ if (n > bufSize) {
+ return 0;
+ }
+ code = ranges[a].code + (u - ranges[a].start);
+ for (i = n - 1; i >= 0; --i) {
+ buf[i] = (char)(code & 0xff);
+ code >>= 8;
+ }
+ return n;
+ }
+ }
+
+ for (i = 0; i < eMapsLen; ++i) {
+ if (eMaps[i].u == u) {
+ n = eMaps[i].nBytes;
+ for (j = 0; j < n; ++j) {
+ buf[j] = eMaps[i].code[j];
+ }
+ return n;
+ }
+ }
+
+ return 0;
+}
+
+//------------------------------------------------------------------------
+
+UnicodeMapCache::UnicodeMapCache() {
+ int i;
+
+ for (i = 0; i < unicodeMapCacheSize; ++i) {
+ cache[i] = NULL;
+ }
+}
+
+UnicodeMapCache::~UnicodeMapCache() {
+ int i;
+
+ for (i = 0; i < unicodeMapCacheSize; ++i) {
+ if (cache[i]) {
+ cache[i]->decRefCnt();
+ }
+ }
+}
+
+UnicodeMap *UnicodeMapCache::getUnicodeMap(GString *encodingName) {
+ UnicodeMap *map;
+ int i, j;
+
+ if (cache[0] && cache[0]->match(encodingName)) {
+ cache[0]->incRefCnt();
+ return cache[0];
+ }
+ for (i = 1; i < unicodeMapCacheSize; ++i) {
+ if (cache[i] && cache[i]->match(encodingName)) {
+ map = cache[i];
+ for (j = i; j >= 1; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = map;
+ map->incRefCnt();
+ return map;
+ }
+ }
+ if ((map = UnicodeMap::parse(encodingName))) {
+ if (cache[unicodeMapCacheSize - 1]) {
+ cache[unicodeMapCacheSize - 1]->decRefCnt();
+ }
+ for (j = unicodeMapCacheSize - 1; j >= 1; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = map;
+ map->incRefCnt();
+ return map;
+ }
+ return NULL;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeMap.h b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeMap.h
new file mode 100644
index 00000000000..a15d628b4cc
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeMap.h
@@ -0,0 +1,124 @@
+//========================================================================
+//
+// UnicodeMap.h
+//
+// Mapping from Unicode to an encoding.
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef UNICODEMAP_H
+#define UNICODEMAP_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "CharTypes.h"
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+class GString;
+
+//------------------------------------------------------------------------
+
+enum UnicodeMapKind {
+ unicodeMapUser, // read from a file
+ unicodeMapResident, // static list of ranges
+ unicodeMapFunc // function pointer
+};
+
+typedef int (*UnicodeMapFunc)(Unicode u, char *buf, int bufSize);
+
+struct UnicodeMapRange {
+ Unicode start, end; // range of Unicode chars
+ Guint code, nBytes; // first output code
+};
+
+struct UnicodeMapExt;
+
+//------------------------------------------------------------------------
+
+class UnicodeMap {
+public:
+
+ // Create the UnicodeMap specified by <encodingName>. Sets the
+ // initial reference count to 1. Returns NULL on failure.
+ static UnicodeMap *parse(GString *encodingNameA);
+
+ // Create a resident UnicodeMap.
+ UnicodeMap(const char *encodingNameA, GBool unicodeOutA,
+ UnicodeMapRange *rangesA, int lenA);
+
+ // Create a resident UnicodeMap that uses a function instead of a
+ // list of ranges.
+ UnicodeMap(const char *encodingNameA, GBool unicodeOutA,
+ UnicodeMapFunc funcA);
+
+ ~UnicodeMap();
+
+ void incRefCnt();
+ void decRefCnt();
+
+ GString *getEncodingName() { return encodingName; }
+
+ GBool isUnicode() { return unicodeOut; }
+
+ // Return true if this UnicodeMap matches the specified
+ // <encodingNameA>.
+ GBool match(GString *encodingNameA);
+
+ // Map Unicode to the target encoding. Fills in <buf> with the
+ // output and returns the number of bytes used. Output will be
+ // truncated at <bufSize> bytes. No string terminator is written.
+ // Returns 0 if no mapping is found.
+ int mapUnicode(Unicode u, char *buf, int bufSize);
+
+private:
+
+ UnicodeMap(GString *encodingNameA);
+
+ GString *encodingName;
+ UnicodeMapKind kind;
+ GBool unicodeOut;
+ union {
+ UnicodeMapRange *ranges; // (user, resident)
+ UnicodeMapFunc func; // (func)
+ };
+ int len; // (user, resident)
+ UnicodeMapExt *eMaps; // (user)
+ int eMapsLen; // (user)
+#if MULTITHREADED
+ GAtomicCounter refCnt;
+#else
+ int refCnt;
+#endif
+};
+
+//------------------------------------------------------------------------
+
+#define unicodeMapCacheSize 4
+
+class UnicodeMapCache {
+public:
+
+ UnicodeMapCache();
+ ~UnicodeMapCache();
+
+ // Get the UnicodeMap for <encodingName>. Increments its reference
+ // count; there will be one reference for the cache plus one for the
+ // caller of this function. Returns NULL on failure.
+ UnicodeMap *getUnicodeMap(GString *encodingName);
+
+private:
+
+ UnicodeMap *cache[unicodeMapCacheSize];
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeMapTables.h b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeMapTables.h
new file mode 100644
index 00000000000..60ac55d124e
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeMapTables.h
@@ -0,0 +1,366 @@
+//========================================================================
+//
+// UnicodeMapTables.h
+//
+// Copyright 2001-2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+static UnicodeMapRange latin1UnicodeMapRanges[] = {
+ { 0x000a, 0x000a, 0x0a, 1 },
+ { 0x000c, 0x000d, 0x0c, 1 },
+ { 0x0020, 0x007e, 0x20, 1 },
+ { 0x00a0, 0x00a0, 0x20, 1 },
+ { 0x00a1, 0x00ac, 0xa1, 1 },
+ { 0x00ae, 0x00ff, 0xae, 1 },
+ { 0x010c, 0x010c, 0x43, 1 },
+ { 0x010d, 0x010d, 0x63, 1 },
+ { 0x0131, 0x0131, 0x69, 1 },
+ { 0x0141, 0x0141, 0x4c, 1 },
+ { 0x0142, 0x0142, 0x6c, 1 },
+ { 0x0152, 0x0152, 0x4f45, 2 },
+ { 0x0153, 0x0153, 0x6f65, 2 },
+ { 0x0160, 0x0160, 0x53, 1 },
+ { 0x0161, 0x0161, 0x73, 1 },
+ { 0x0178, 0x0178, 0x59, 1 },
+ { 0x017d, 0x017d, 0x5a, 1 },
+ { 0x017e, 0x017e, 0x7a, 1 },
+ { 0x02c6, 0x02c6, 0x5e, 1 },
+ { 0x02da, 0x02da, 0xb0, 1 },
+ { 0x02dc, 0x02dc, 0x7e, 1 },
+ { 0x2010, 0x2010, 0x2d, 1 },
+ { 0x2013, 0x2013, 0xad, 1 },
+ { 0x2014, 0x2014, 0x2d2d, 2 },
+ { 0x2018, 0x2018, 0x60, 1 },
+ { 0x2019, 0x2019, 0x27, 1 },
+ { 0x201a, 0x201a, 0x2c, 1 },
+ { 0x201c, 0x201c, 0x22, 1 },
+ { 0x201d, 0x201d, 0x22, 1 },
+ { 0x201e, 0x201e, 0x2c2c, 2 },
+ { 0x2022, 0x2022, 0xb7, 1 },
+ { 0x2026, 0x2026, 0x2e2e2e, 3 },
+ { 0x2039, 0x2039, 0x3c, 1 },
+ { 0x203a, 0x203a, 0x3e, 1 },
+ { 0x2044, 0x2044, 0x2f, 1 },
+ { 0x2122, 0x2122, 0x544d, 2 },
+ { 0x2212, 0x2212, 0x2d, 1 },
+ { 0xf6f9, 0xf6f9, 0x4c, 1 },
+ { 0xf6fa, 0xf6fa, 0x4f45, 2 },
+ { 0xf6fc, 0xf6fc, 0xb0, 1 },
+ { 0xf6fd, 0xf6fd, 0x53, 1 },
+ { 0xf6fe, 0xf6fe, 0x7e, 1 },
+ { 0xf6ff, 0xf6ff, 0x5a, 1 },
+ { 0xf721, 0xf721, 0x21, 1 },
+ { 0xf724, 0xf724, 0x24, 1 },
+ { 0xf726, 0xf726, 0x26, 1 },
+ { 0xf730, 0xf739, 0x30, 1 },
+ { 0xf73f, 0xf73f, 0x3f, 1 },
+ { 0xf761, 0xf77a, 0x41, 1 },
+ { 0xf7a1, 0xf7a2, 0xa1, 1 },
+ { 0xf7bf, 0xf7bf, 0xbf, 1 },
+ { 0xf7e0, 0xf7f6, 0xc0, 1 },
+ { 0xf7f8, 0xf7fe, 0xd8, 1 },
+ { 0xf7ff, 0xf7ff, 0x59, 1 },
+ { 0xfb00, 0xfb00, 0x6666, 2 },
+ { 0xfb01, 0xfb01, 0x6669, 2 },
+ { 0xfb02, 0xfb02, 0x666c, 2 },
+ { 0xfb03, 0xfb03, 0x666669, 3 },
+ { 0xfb04, 0xfb04, 0x66666c, 3 },
+ { 0xfb05, 0xfb05, 0x7374, 2 },
+ { 0xfb06, 0xfb06, 0x7374, 2 }
+};
+#define latin1UnicodeMapLen (sizeof(latin1UnicodeMapRanges) / sizeof(UnicodeMapRange))
+
+static UnicodeMapRange ascii7UnicodeMapRanges[] = {
+ { 0x000a, 0x000a, 0x0a, 1 },
+ { 0x000c, 0x000d, 0x0c, 1 },
+ { 0x0020, 0x005f, 0x20, 1 },
+ { 0x0061, 0x007e, 0x61, 1 },
+ { 0x00a6, 0x00a6, 0x7c, 1 },
+ { 0x00a9, 0x00a9, 0x286329, 3 },
+ { 0x00ae, 0x00ae, 0x285229, 3 },
+ { 0x00b7, 0x00b7, 0x2a, 1 },
+ { 0x00bc, 0x00bc, 0x312f34, 3 },
+ { 0x00bd, 0x00bd, 0x312f32, 3 },
+ { 0x00be, 0x00be, 0x332f34, 3 },
+ { 0x00c0, 0x00c0, 0x41, 1 },
+ { 0x00c1, 0x00c1, 0x41, 1 },
+ { 0x00c2, 0x00c2, 0x41, 1 },
+ { 0x00c3, 0x00c3, 0x41, 1 },
+ { 0x00c4, 0x00c4, 0x41, 1 },
+ { 0x00c5, 0x00c5, 0x41, 1 },
+ { 0x00c6, 0x00c6, 0x4145, 2 },
+ { 0x00c7, 0x00c7, 0x43, 1 },
+ { 0x00c8, 0x00c8, 0x45, 1 },
+ { 0x00c9, 0x00c9, 0x45, 1 },
+ { 0x00ca, 0x00ca, 0x45, 1 },
+ { 0x00cb, 0x00cb, 0x45, 1 },
+ { 0x00cc, 0x00cc, 0x49, 1 },
+ { 0x00cd, 0x00cd, 0x49, 1 },
+ { 0x00ce, 0x00ce, 0x49, 1 },
+ { 0x00cf, 0x00cf, 0x49, 1 },
+ { 0x00d1, 0x00d2, 0x4e, 1 },
+ { 0x00d3, 0x00d3, 0x4f, 1 },
+ { 0x00d4, 0x00d4, 0x4f, 1 },
+ { 0x00d5, 0x00d5, 0x4f, 1 },
+ { 0x00d6, 0x00d6, 0x4f, 1 },
+ { 0x00d7, 0x00d7, 0x78, 1 },
+ { 0x00d8, 0x00d8, 0x4f, 1 },
+ { 0x00d9, 0x00d9, 0x55, 1 },
+ { 0x00da, 0x00da, 0x55, 1 },
+ { 0x00db, 0x00db, 0x55, 1 },
+ { 0x00dc, 0x00dc, 0x55, 1 },
+ { 0x00dd, 0x00dd, 0x59, 1 },
+ { 0x00e0, 0x00e0, 0x61, 1 },
+ { 0x00e1, 0x00e1, 0x61, 1 },
+ { 0x00e2, 0x00e2, 0x61, 1 },
+ { 0x00e3, 0x00e3, 0x61, 1 },
+ { 0x00e4, 0x00e4, 0x61, 1 },
+ { 0x00e5, 0x00e5, 0x61, 1 },
+ { 0x00e6, 0x00e6, 0x6165, 2 },
+ { 0x00e7, 0x00e7, 0x63, 1 },
+ { 0x00e8, 0x00e8, 0x65, 1 },
+ { 0x00e9, 0x00e9, 0x65, 1 },
+ { 0x00ea, 0x00ea, 0x65, 1 },
+ { 0x00eb, 0x00eb, 0x65, 1 },
+ { 0x00ec, 0x00ec, 0x69, 1 },
+ { 0x00ed, 0x00ed, 0x69, 1 },
+ { 0x00ee, 0x00ee, 0x69, 1 },
+ { 0x00ef, 0x00ef, 0x69, 1 },
+ { 0x00f1, 0x00f2, 0x6e, 1 },
+ { 0x00f3, 0x00f3, 0x6f, 1 },
+ { 0x00f4, 0x00f4, 0x6f, 1 },
+ { 0x00f5, 0x00f5, 0x6f, 1 },
+ { 0x00f6, 0x00f6, 0x6f, 1 },
+ { 0x00f7, 0x00f7, 0x2f, 1 },
+ { 0x00f8, 0x00f8, 0x6f, 1 },
+ { 0x00f9, 0x00f9, 0x75, 1 },
+ { 0x00fa, 0x00fa, 0x75, 1 },
+ { 0x00fb, 0x00fb, 0x75, 1 },
+ { 0x00fc, 0x00fc, 0x75, 1 },
+ { 0x00fd, 0x00fd, 0x79, 1 },
+ { 0x00ff, 0x00ff, 0x79, 1 },
+ { 0x0131, 0x0131, 0x69, 1 },
+ { 0x0141, 0x0141, 0x4c, 1 },
+ { 0x0152, 0x0152, 0x4f45, 2 },
+ { 0x0153, 0x0153, 0x6f65, 2 },
+ { 0x0160, 0x0160, 0x53, 1 },
+ { 0x0178, 0x0178, 0x59, 1 },
+ { 0x017d, 0x017d, 0x5a, 1 },
+ { 0x2013, 0x2013, 0x2d, 1 },
+ { 0x2014, 0x2014, 0x2d2d, 2 },
+ { 0x2018, 0x2018, 0x60, 1 },
+ { 0x2019, 0x2019, 0x27, 1 },
+ { 0x201c, 0x201c, 0x22, 1 },
+ { 0x201d, 0x201d, 0x22, 1 },
+ { 0x2022, 0x2022, 0x2a, 1 },
+ { 0x2026, 0x2026, 0x2e2e2e, 3 },
+ { 0x2122, 0x2122, 0x544d, 2 },
+ { 0x2212, 0x2212, 0x2d, 1 },
+ { 0xf6f9, 0xf6f9, 0x4c, 1 },
+ { 0xf6fa, 0xf6fa, 0x4f45, 2 },
+ { 0xf6fd, 0xf6fd, 0x53, 1 },
+ { 0xf6fe, 0xf6fe, 0x7e, 1 },
+ { 0xf6ff, 0xf6ff, 0x5a, 1 },
+ { 0xf721, 0xf721, 0x21, 1 },
+ { 0xf724, 0xf724, 0x24, 1 },
+ { 0xf726, 0xf726, 0x26, 1 },
+ { 0xf730, 0xf739, 0x30, 1 },
+ { 0xf73f, 0xf73f, 0x3f, 1 },
+ { 0xf761, 0xf77a, 0x41, 1 },
+ { 0xf7e0, 0xf7e0, 0x41, 1 },
+ { 0xf7e1, 0xf7e1, 0x41, 1 },
+ { 0xf7e2, 0xf7e2, 0x41, 1 },
+ { 0xf7e3, 0xf7e3, 0x41, 1 },
+ { 0xf7e4, 0xf7e4, 0x41, 1 },
+ { 0xf7e5, 0xf7e5, 0x41, 1 },
+ { 0xf7e6, 0xf7e6, 0x4145, 2 },
+ { 0xf7e7, 0xf7e7, 0x43, 1 },
+ { 0xf7e8, 0xf7e8, 0x45, 1 },
+ { 0xf7e9, 0xf7e9, 0x45, 1 },
+ { 0xf7ea, 0xf7ea, 0x45, 1 },
+ { 0xf7eb, 0xf7eb, 0x45, 1 },
+ { 0xf7ec, 0xf7ec, 0x49, 1 },
+ { 0xf7ed, 0xf7ed, 0x49, 1 },
+ { 0xf7ee, 0xf7ee, 0x49, 1 },
+ { 0xf7ef, 0xf7ef, 0x49, 1 },
+ { 0xf7f1, 0xf7f2, 0x4e, 1 },
+ { 0xf7f3, 0xf7f3, 0x4f, 1 },
+ { 0xf7f4, 0xf7f4, 0x4f, 1 },
+ { 0xf7f5, 0xf7f5, 0x4f, 1 },
+ { 0xf7f6, 0xf7f6, 0x4f, 1 },
+ { 0xf7f8, 0xf7f8, 0x4f, 1 },
+ { 0xf7f9, 0xf7f9, 0x55, 1 },
+ { 0xf7fa, 0xf7fa, 0x55, 1 },
+ { 0xf7fb, 0xf7fb, 0x55, 1 },
+ { 0xf7fc, 0xf7fc, 0x55, 1 },
+ { 0xf7fd, 0xf7fd, 0x59, 1 },
+ { 0xf7ff, 0xf7ff, 0x59, 1 },
+ { 0xfb00, 0xfb00, 0x6666, 2 },
+ { 0xfb01, 0xfb01, 0x6669, 2 },
+ { 0xfb02, 0xfb02, 0x666c, 2 },
+ { 0xfb03, 0xfb03, 0x666669, 3 },
+ { 0xfb04, 0xfb04, 0x66666c, 3 },
+ { 0xfb05, 0xfb05, 0x7374, 2 },
+ { 0xfb06, 0xfb06, 0x7374, 2 }
+};
+#define ascii7UnicodeMapLen (sizeof(ascii7UnicodeMapRanges) / sizeof(UnicodeMapRange))
+
+static UnicodeMapRange symbolUnicodeMapRanges[] = {
+ { 0x0020, 0x0021, 0x20, 1 },
+ { 0x0023, 0x0023, 0x23, 1 },
+ { 0x0025, 0x0026, 0x25, 1 },
+ { 0x0028, 0x0029, 0x28, 1 },
+ { 0x002b, 0x002c, 0x2b, 1 },
+ { 0x002e, 0x003f, 0x2e, 1 },
+ { 0x005b, 0x005b, 0x5b, 1 },
+ { 0x005d, 0x005d, 0x5d, 1 },
+ { 0x005f, 0x005f, 0x5f, 1 },
+ { 0x007b, 0x007d, 0x7b, 1 },
+ { 0x00ac, 0x00ac, 0xd8, 1 },
+ { 0x00b0, 0x00b1, 0xb0, 1 },
+ { 0x00b5, 0x00b5, 0x6d, 1 },
+ { 0x00d7, 0x00d7, 0xb4, 1 },
+ { 0x00f7, 0x00f7, 0xb8, 1 },
+ { 0x0192, 0x0192, 0xa6, 1 },
+ { 0x0391, 0x0392, 0x41, 1 },
+ { 0x0393, 0x0393, 0x47, 1 },
+ { 0x0395, 0x0395, 0x45, 1 },
+ { 0x0396, 0x0396, 0x5a, 1 },
+ { 0x0397, 0x0397, 0x48, 1 },
+ { 0x0398, 0x0398, 0x51, 1 },
+ { 0x0399, 0x0399, 0x49, 1 },
+ { 0x039a, 0x039d, 0x4b, 1 },
+ { 0x039e, 0x039e, 0x58, 1 },
+ { 0x039f, 0x03a0, 0x4f, 1 },
+ { 0x03a1, 0x03a1, 0x52, 1 },
+ { 0x03a3, 0x03a5, 0x53, 1 },
+ { 0x03a6, 0x03a6, 0x46, 1 },
+ { 0x03a7, 0x03a7, 0x43, 1 },
+ { 0x03a8, 0x03a8, 0x59, 1 },
+ { 0x03b1, 0x03b2, 0x61, 1 },
+ { 0x03b3, 0x03b3, 0x67, 1 },
+ { 0x03b4, 0x03b5, 0x64, 1 },
+ { 0x03b6, 0x03b6, 0x7a, 1 },
+ { 0x03b7, 0x03b7, 0x68, 1 },
+ { 0x03b8, 0x03b8, 0x71, 1 },
+ { 0x03b9, 0x03b9, 0x69, 1 },
+ { 0x03ba, 0x03bb, 0x6b, 1 },
+ { 0x03bd, 0x03bd, 0x6e, 1 },
+ { 0x03be, 0x03be, 0x78, 1 },
+ { 0x03bf, 0x03c0, 0x6f, 1 },
+ { 0x03c1, 0x03c1, 0x72, 1 },
+ { 0x03c2, 0x03c2, 0x56, 1 },
+ { 0x03c3, 0x03c5, 0x73, 1 },
+ { 0x03c6, 0x03c6, 0x66, 1 },
+ { 0x03c7, 0x03c7, 0x63, 1 },
+ { 0x03c8, 0x03c8, 0x79, 1 },
+ { 0x03c9, 0x03c9, 0x77, 1 },
+ { 0x03d1, 0x03d1, 0x4a, 1 },
+ { 0x03d2, 0x03d2, 0xa1, 1 },
+ { 0x03d5, 0x03d5, 0x6a, 1 },
+ { 0x03d6, 0x03d6, 0x76, 1 },
+ { 0x2022, 0x2022, 0xb7, 1 },
+ { 0x2026, 0x2026, 0xbc, 1 },
+ { 0x2032, 0x2032, 0xa2, 1 },
+ { 0x2033, 0x2033, 0xb2, 1 },
+ { 0x2044, 0x2044, 0xa4, 1 },
+ { 0x2111, 0x2111, 0xc1, 1 },
+ { 0x2118, 0x2118, 0xc3, 1 },
+ { 0x211c, 0x211c, 0xc2, 1 },
+ { 0x2126, 0x2126, 0x57, 1 },
+ { 0x2135, 0x2135, 0xc0, 1 },
+ { 0x2190, 0x2193, 0xac, 1 },
+ { 0x2194, 0x2194, 0xab, 1 },
+ { 0x21b5, 0x21b5, 0xbf, 1 },
+ { 0x21d0, 0x21d3, 0xdc, 1 },
+ { 0x21d4, 0x21d4, 0xdb, 1 },
+ { 0x2200, 0x2200, 0x22, 1 },
+ { 0x2202, 0x2202, 0xb6, 1 },
+ { 0x2203, 0x2203, 0x24, 1 },
+ { 0x2205, 0x2205, 0xc6, 1 },
+ { 0x2206, 0x2206, 0x44, 1 },
+ { 0x2207, 0x2207, 0xd1, 1 },
+ { 0x2208, 0x2209, 0xce, 1 },
+ { 0x220b, 0x220b, 0x27, 1 },
+ { 0x220f, 0x220f, 0xd5, 1 },
+ { 0x2211, 0x2211, 0xe5, 1 },
+ { 0x2212, 0x2212, 0x2d, 1 },
+ { 0x2217, 0x2217, 0x2a, 1 },
+ { 0x221a, 0x221a, 0xd6, 1 },
+ { 0x221d, 0x221d, 0xb5, 1 },
+ { 0x221e, 0x221e, 0xa5, 1 },
+ { 0x2220, 0x2220, 0xd0, 1 },
+ { 0x2227, 0x2228, 0xd9, 1 },
+ { 0x2229, 0x222a, 0xc7, 1 },
+ { 0x222b, 0x222b, 0xf2, 1 },
+ { 0x2234, 0x2234, 0x5c, 1 },
+ { 0x223c, 0x223c, 0x7e, 1 },
+ { 0x2245, 0x2245, 0x40, 1 },
+ { 0x2248, 0x2248, 0xbb, 1 },
+ { 0x2260, 0x2261, 0xb9, 1 },
+ { 0x2264, 0x2264, 0xa3, 1 },
+ { 0x2265, 0x2265, 0xb3, 1 },
+ { 0x2282, 0x2282, 0xcc, 1 },
+ { 0x2283, 0x2283, 0xc9, 1 },
+ { 0x2284, 0x2284, 0xcb, 1 },
+ { 0x2286, 0x2286, 0xcd, 1 },
+ { 0x2287, 0x2287, 0xca, 1 },
+ { 0x2295, 0x2295, 0xc5, 1 },
+ { 0x2297, 0x2297, 0xc4, 1 },
+ { 0x22a5, 0x22a5, 0x5e, 1 },
+ { 0x22c5, 0x22c5, 0xd7, 1 },
+ { 0x2320, 0x2320, 0xf3, 1 },
+ { 0x2321, 0x2321, 0xf5, 1 },
+ { 0x2329, 0x2329, 0xe1, 1 },
+ { 0x232a, 0x232a, 0xf1, 1 },
+ { 0x25ca, 0x25ca, 0xe0, 1 },
+ { 0x2660, 0x2660, 0xaa, 1 },
+ { 0x2663, 0x2663, 0xa7, 1 },
+ { 0x2665, 0x2665, 0xa9, 1 },
+ { 0x2666, 0x2666, 0xa8, 1 },
+ { 0xf6d9, 0xf6d9, 0xd3, 1 },
+ { 0xf6da, 0xf6da, 0xd2, 1 },
+ { 0xf6db, 0xf6db, 0xd4, 1 },
+ { 0xf8e5, 0xf8e5, 0x60, 1 },
+ { 0xf8e6, 0xf8e7, 0xbd, 1 },
+ { 0xf8e8, 0xf8ea, 0xe2, 1 },
+ { 0xf8eb, 0xf8f4, 0xe6, 1 },
+ { 0xf8f5, 0xf8f5, 0xf4, 1 },
+ { 0xf8f6, 0xf8fe, 0xf6, 1 }
+};
+#define symbolUnicodeMapLen (sizeof(symbolUnicodeMapRanges) / sizeof(UnicodeMapRange))
+
+static UnicodeMapRange zapfDingbatsUnicodeMapRanges[] = {
+ { 0x0020, 0x0020, 0x20, 1 },
+ { 0x2192, 0x2192, 0xd5, 1 },
+ { 0x2194, 0x2195, 0xd6, 1 },
+ { 0x2460, 0x2469, 0xac, 1 },
+ { 0x25a0, 0x25a0, 0x6e, 1 },
+ { 0x25b2, 0x25b2, 0x73, 1 },
+ { 0x25bc, 0x25bc, 0x74, 1 },
+ { 0x25c6, 0x25c6, 0x75, 1 },
+ { 0x25cf, 0x25cf, 0x6c, 1 },
+ { 0x25d7, 0x25d7, 0x77, 1 },
+ { 0x2605, 0x2605, 0x48, 1 },
+ { 0x260e, 0x260e, 0x25, 1 },
+ { 0x261b, 0x261b, 0x2a, 1 },
+ { 0x261e, 0x261e, 0x2b, 1 },
+ { 0x2660, 0x2660, 0xab, 1 },
+ { 0x2663, 0x2663, 0xa8, 1 },
+ { 0x2665, 0x2665, 0xaa, 1 },
+ { 0x2666, 0x2666, 0xa9, 1 },
+ { 0x2701, 0x2704, 0x21, 1 },
+ { 0x2706, 0x2709, 0x26, 1 },
+ { 0x270c, 0x2727, 0x2c, 1 },
+ { 0x2729, 0x274b, 0x49, 1 },
+ { 0x274d, 0x274d, 0x6d, 1 },
+ { 0x274f, 0x2752, 0x6f, 1 },
+ { 0x2756, 0x2756, 0x76, 1 },
+ { 0x2758, 0x275e, 0x78, 1 },
+ { 0x2761, 0x2767, 0xa1, 1 },
+ { 0x2776, 0x2794, 0xb6, 1 },
+ { 0x2798, 0x27af, 0xd8, 1 },
+ { 0x27b1, 0x27be, 0xf1, 1 }
+};
+#define zapfDingbatsUnicodeMapLen (sizeof(zapfDingbatsUnicodeMapRanges) / sizeof(UnicodeMapRange))
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeRemapping.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeRemapping.cc
new file mode 100644
index 00000000000..e4f09e19ae3
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeRemapping.cc
@@ -0,0 +1,178 @@
+//========================================================================
+//
+// UnicodeRemapping.cc
+//
+// Copyright 2018 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "gfile.h"
+#include "GString.h"
+#include "Error.h"
+#include "UnicodeRemapping.h"
+
+//------------------------------------------------------------------------
+
+#define maxUnicodeString 8
+
+struct UnicodeRemappingString {
+ Unicode in;
+ Unicode out[maxUnicodeString];
+ int len;
+};
+
+//------------------------------------------------------------------------
+
+static int hexCharVals[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2x
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 3x
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 4x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 5x
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 6x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 8x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 9x
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ax
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Bx
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Cx
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Dx
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // Ex
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // Fx
+};
+
+// Parse a <len>-byte hex string <s> into *<val>. Returns false on
+// error.
+static GBool parseHex(char *s, int len, Guint *val) {
+ int i, x;
+
+ *val = 0;
+ for (i = 0; i < len; ++i) {
+ x = hexCharVals[s[i] & 0xff];
+ if (x < 0) {
+ return gFalse;
+ }
+ *val = (*val << 4) + x;
+ }
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+
+UnicodeRemapping::UnicodeRemapping() {
+ for (int i = 0; i < 256; ++i) {
+ page0[i] = (Unicode)i;
+ }
+ sMap = NULL;
+ sMapLen = sMapSize = 0;
+}
+
+UnicodeRemapping::~UnicodeRemapping() {
+ gfree(sMap);
+}
+
+void UnicodeRemapping::addRemapping(Unicode in, Unicode *out, int len) {
+ int i;
+
+ if (in < 256 && len == 1) {
+ page0[in] = out[0];
+ } else {
+ if (in < 256) {
+ page0[in] = 0xffffffff;
+ }
+ if (sMapLen == sMapSize) {
+ sMapSize += 16;
+ sMap = (UnicodeRemappingString *)
+ greallocn(sMap, sMapSize, sizeof(UnicodeRemappingString));
+ }
+ sMap[sMapLen].in = in;
+ for (i = 0; i < len && i < maxUnicodeString; ++i) {
+ sMap[sMapLen].out[i] = out[i];
+ }
+ sMap[sMapLen].len = i;
+ ++sMapLen;
+ }
+}
+
+void UnicodeRemapping::parseFile(GString *fileName) {
+ FILE *f;
+ char buf[256];
+ Unicode in;
+ Unicode out[maxUnicodeString];
+ char *tok;
+ int line, n;
+
+ if (!(f = openFile(fileName->getCString(), "r"))) {
+ error(errSyntaxError, -1, "Couldn't open unicodeRemapping file '{0:t}'",
+ fileName);
+ return;
+ }
+
+ line = 0;
+ while (getLine(buf, sizeof(buf), f)) {
+ ++line;
+ if (!(tok = strtok(buf, " \t\r\n")) ||
+ !parseHex(tok, (int)strlen(tok), &in)) {
+ error(errSyntaxWarning, -1,
+ "Bad line ({0:d}) in unicodeRemapping file '{1:t}'",
+ line, fileName);
+ continue;
+ }
+ n = 0;
+ while (n < maxUnicodeString) {
+ if (!(tok = strtok(NULL, " \t\r\n"))) {
+ break;
+ }
+ if (!parseHex(tok, (int)strlen(tok), &out[n])) {
+ error(errSyntaxWarning, -1,
+ "Bad line ({0:d}) in unicodeRemapping file '{1:t}'",
+ line, fileName);
+ break;
+ }
+ ++n;
+ }
+ addRemapping(in, out, n);
+ }
+
+ fclose(f);
+}
+
+int UnicodeRemapping::map(Unicode in, Unicode *out, int size) {
+ int a, b, m, i;
+
+ if (in < 256 && page0[in] != 0xffffffff) {
+ out[0] = page0[in];
+ return 1;
+ }
+
+ a = -1;
+ b = sMapLen;
+ // invariant: sMap[a].in < in < sMap[b].in
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (sMap[m].in < in) {
+ a = m;
+ } else if (in < sMap[m].in) {
+ b = m;
+ } else {
+ for (i = 0; i < sMap[m].len && i < size; ++i) {
+ out[i] = sMap[m].out[i];
+ }
+ return i;
+ }
+ }
+
+ out[0] = in;
+ return 1;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeRemapping.h b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeRemapping.h
new file mode 100644
index 00000000000..950c93afc8e
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeRemapping.h
@@ -0,0 +1,51 @@
+//========================================================================
+//
+// UnicodeRemapping.h
+//
+// Sparse remapping of Unicode characters.
+//
+// Copyright 2018 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef UNICODEREMAPPING_H
+#define UNICODEREMAPPING_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "CharTypes.h"
+
+struct UnicodeRemappingString;
+
+//------------------------------------------------------------------------
+
+class UnicodeRemapping {
+public:
+
+ // Create an empty (i.e., identity) remapping.
+ UnicodeRemapping();
+
+ ~UnicodeRemapping();
+
+ // Add a remapping for <in>.
+ void addRemapping(Unicode in, Unicode *out, int len);
+
+ // Add entries from the specified file to this UnicodeRemapping.
+ void parseFile(GString *fileName);
+
+ // Map <in> to zero or more (up to <size>) output characters in
+ // <out>. Returns the number of output characters.
+ int map(Unicode in, Unicode *out, int size);
+
+private:
+
+ Unicode page0[256];
+ UnicodeRemappingString *sMap;
+ int sMapLen, sMapSize;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeTypeTable.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeTypeTable.cc
new file mode 100644
index 00000000000..328d2478167
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeTypeTable.cc
@@ -0,0 +1,972 @@
+//========================================================================
+//
+// UnicodeTypeTable.cc
+//
+// Copyright 2004-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdlib.h>
+#include "gmempp.h"
+#include "CharTypes.h"
+#include "UnicodeTypeTable.h"
+
+struct UnicodeMapTableEntry {
+ const char *vector;
+ char type;
+};
+
+struct UnicodeCaseTableVector {
+ Unicode codes[256];
+};
+
+static UnicodeMapTableEntry typeTable[256] = {
+ { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN...NNNNN.....##########.NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN.N....NNNNLNNNNN..##NLNNN#LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL", 'X' },
+ { NULL, 'L' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLNNNNNNNNNNNNNNLLNNNNNNNNNNNNNNLLLLLNNNNNNNNNLNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLNNNNNNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRNRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
+ { "RRRR.........RNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNN#################.##RRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNRNNNNNNNRRNNNNNNNRR##########RRRRRR", 'X' },
+ { "RRRRRRRRRRRRRRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNNNNNNNNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { NULL, 'N' },
+ { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLNNNNLLLLLLLLLLLLLNNLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNLLLLLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLL..LLLLLLLNNNNN", 'X' },
+ { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLL..NNNNNNNNNNNNNN", 'X' },
+ { "NNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLNLNNNLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNN.NLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLLLLLLLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNN.....LLLLLLLNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNLNNNNNLNNLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLL.LNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLNNNNNLLLLLLNLLLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNNNLLLLLLLLLLLNNNLLLLLLLLLLLLNNNNLLLLLLLLLLLLLNNNLLLLLLLLLLLLLNNN", 'X' },
+ { "NNNNNNNNNNNNNNLRNNNNNNNNNNNNNNNNNNNNNNNNNNLRNLRN.....NNNNNNNNNNNNNNN.NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN#L########..NNNL##########..NNN...................................NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNLNNNNLNNLLLLLLLLLLNLNNNLLLLLNNNNNNLNLNLNLLLL.LLLNLLLLLLLNNLLLLNNNNNLLLLLNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNNNNNNNNNNNNNNNNN..NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN####################LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { NULL, 'N' },
+ { NULL, 'N' },
+ { NULL, 'N' },
+ { NULL, 'L' },
+ { NULL, 'N' },
+ { NULL, 'N' },
+ { NULL, 'N' },
+ { NULL, 'N' },
+ { NULL, 'N' },
+ { NULL, 'N' },
+ { NULL, 'N' },
+ { "NNNNNLLLNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLNNNNNNNLLLLLNNLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLL", 'X' },
+ { NULL, 'L' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNLLLLLLLLLLLLNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN", 'X' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { NULL, 'L' },
+ { "LLLLLLLLLLLLLLLLLLLLLLLLRRRRRRNRRRRRRRRRR.RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },
+ { NULL, 'R' },
+ { "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
+ { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN.N.NN.NNNNNNNNN.NN..NNNNN..NRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },
+ { "NNN...NNNNN.....##########.NNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL.....NNN..NNNNNNNNNNNNNNNNNNNNNNNLL", 'X' }
+};
+
+static UnicodeCaseTableVector caseTable00 = {{
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x03bc, 0x00b6, 0x00b7,
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00d7,
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00df,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
+}};
+static UnicodeCaseTableVector caseTable01 = {{
+ 0x0101, 0x0101, 0x0103, 0x0103, 0x0105, 0x0105, 0x0107, 0x0107,
+ 0x0109, 0x0109, 0x010b, 0x010b, 0x010d, 0x010d, 0x010f, 0x010f,
+ 0x0111, 0x0111, 0x0113, 0x0113, 0x0115, 0x0115, 0x0117, 0x0117,
+ 0x0119, 0x0119, 0x011b, 0x011b, 0x011d, 0x011d, 0x011f, 0x011f,
+ 0x0121, 0x0121, 0x0123, 0x0123, 0x0125, 0x0125, 0x0127, 0x0127,
+ 0x0129, 0x0129, 0x012b, 0x012b, 0x012d, 0x012d, 0x012f, 0x012f,
+ 0x0130, 0x0131, 0x0133, 0x0133, 0x0135, 0x0135, 0x0137, 0x0137,
+ 0x0138, 0x013a, 0x013a, 0x013c, 0x013c, 0x013e, 0x013e, 0x0140,
+ 0x0140, 0x0142, 0x0142, 0x0144, 0x0144, 0x0146, 0x0146, 0x0148,
+ 0x0148, 0x0149, 0x014b, 0x014b, 0x014d, 0x014d, 0x014f, 0x014f,
+ 0x0151, 0x0151, 0x0153, 0x0153, 0x0155, 0x0155, 0x0157, 0x0157,
+ 0x0159, 0x0159, 0x015b, 0x015b, 0x015d, 0x015d, 0x015f, 0x015f,
+ 0x0161, 0x0161, 0x0163, 0x0163, 0x0165, 0x0165, 0x0167, 0x0167,
+ 0x0169, 0x0169, 0x016b, 0x016b, 0x016d, 0x016d, 0x016f, 0x016f,
+ 0x0171, 0x0171, 0x0173, 0x0173, 0x0175, 0x0175, 0x0177, 0x0177,
+ 0x00ff, 0x017a, 0x017a, 0x017c, 0x017c, 0x017e, 0x017e, 0x0073,
+ 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188,
+ 0x0188, 0x0256, 0x0257, 0x018c, 0x018c, 0x018d, 0x01dd, 0x0259,
+ 0x025b, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268,
+ 0x0199, 0x0199, 0x019a, 0x019b, 0x026f, 0x0272, 0x019e, 0x0275,
+ 0x01a1, 0x01a1, 0x01a3, 0x01a3, 0x01a5, 0x01a5, 0x0280, 0x01a8,
+ 0x01a8, 0x0283, 0x01aa, 0x01ab, 0x01ad, 0x01ad, 0x0288, 0x01b0,
+ 0x01b0, 0x028a, 0x028b, 0x01b4, 0x01b4, 0x01b6, 0x01b6, 0x0292,
+ 0x01b9, 0x01b9, 0x01ba, 0x01bb, 0x01bd, 0x01bd, 0x01be, 0x01bf,
+ 0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x01c6, 0x01c6, 0x01c6, 0x01c9,
+ 0x01c9, 0x01c9, 0x01cc, 0x01cc, 0x01cc, 0x01ce, 0x01ce, 0x01d0,
+ 0x01d0, 0x01d2, 0x01d2, 0x01d4, 0x01d4, 0x01d6, 0x01d6, 0x01d8,
+ 0x01d8, 0x01da, 0x01da, 0x01dc, 0x01dc, 0x01dd, 0x01df, 0x01df,
+ 0x01e1, 0x01e1, 0x01e3, 0x01e3, 0x01e5, 0x01e5, 0x01e7, 0x01e7,
+ 0x01e9, 0x01e9, 0x01eb, 0x01eb, 0x01ed, 0x01ed, 0x01ef, 0x01ef,
+ 0x01f0, 0x01f3, 0x01f3, 0x01f3, 0x01f5, 0x01f5, 0x0195, 0x01bf,
+ 0x01f9, 0x01f9, 0x01fb, 0x01fb, 0x01fd, 0x01fd, 0x01ff, 0x01ff
+}};
+static UnicodeCaseTableVector caseTable02 = {{
+ 0x0201, 0x0201, 0x0203, 0x0203, 0x0205, 0x0205, 0x0207, 0x0207,
+ 0x0209, 0x0209, 0x020b, 0x020b, 0x020d, 0x020d, 0x020f, 0x020f,
+ 0x0211, 0x0211, 0x0213, 0x0213, 0x0215, 0x0215, 0x0217, 0x0217,
+ 0x0219, 0x0219, 0x021b, 0x021b, 0x021d, 0x021d, 0x021f, 0x021f,
+ 0x019e, 0x0221, 0x0223, 0x0223, 0x0225, 0x0225, 0x0227, 0x0227,
+ 0x0229, 0x0229, 0x022b, 0x022b, 0x022d, 0x022d, 0x022f, 0x022f,
+ 0x0231, 0x0231, 0x0233, 0x0233, 0x0234, 0x0235, 0x0236, 0x0237,
+ 0x0238, 0x0239, 0x023a, 0x023b, 0x023c, 0x023d, 0x023e, 0x023f,
+ 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247,
+ 0x0248, 0x0249, 0x024a, 0x024b, 0x024c, 0x024d, 0x024e, 0x024f,
+ 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257,
+ 0x0258, 0x0259, 0x025a, 0x025b, 0x025c, 0x025d, 0x025e, 0x025f,
+ 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267,
+ 0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e, 0x026f,
+ 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277,
+ 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f,
+ 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287,
+ 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d, 0x028e, 0x028f,
+ 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297,
+ 0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f,
+ 0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7,
+ 0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af,
+ 0x02b0, 0x02b1, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x02b6, 0x02b7,
+ 0x02b8, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf,
+ 0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7,
+ 0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf,
+ 0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7,
+ 0x02d8, 0x02d9, 0x02da, 0x02db, 0x02dc, 0x02dd, 0x02de, 0x02df,
+ 0x02e0, 0x02e1, 0x02e2, 0x02e3, 0x02e4, 0x02e5, 0x02e6, 0x02e7,
+ 0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef,
+ 0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7,
+ 0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff
+}};
+static UnicodeCaseTableVector caseTable03 = {{
+ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
+ 0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f,
+ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
+ 0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f,
+ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
+ 0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f,
+ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
+ 0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f,
+ 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x03b9, 0x0346, 0x0347,
+ 0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f,
+ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
+ 0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f,
+ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
+ 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f,
+ 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377,
+ 0x0378, 0x0379, 0x037a, 0x037b, 0x037c, 0x037d, 0x037e, 0x037f,
+ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x03ac, 0x0387,
+ 0x03ad, 0x03ae, 0x03af, 0x038b, 0x03cc, 0x038d, 0x03cd, 0x03ce,
+ 0x0390, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,
+ 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
+ 0x03c0, 0x03c1, 0x03a2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7,
+ 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03ac, 0x03ad, 0x03ae, 0x03af,
+ 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,
+ 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
+ 0x03c0, 0x03c1, 0x03c3, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7,
+ 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, 0x03cf,
+ 0x03b2, 0x03b8, 0x03d2, 0x03d3, 0x03d4, 0x03c6, 0x03c0, 0x03d7,
+ 0x03d9, 0x03d9, 0x03db, 0x03db, 0x03dd, 0x03dd, 0x03df, 0x03df,
+ 0x03e1, 0x03e1, 0x03e3, 0x03e3, 0x03e5, 0x03e5, 0x03e7, 0x03e7,
+ 0x03e9, 0x03e9, 0x03eb, 0x03eb, 0x03ed, 0x03ed, 0x03ef, 0x03ef,
+ 0x03ba, 0x03c1, 0x03f2, 0x03f3, 0x03b8, 0x03b5, 0x03f6, 0x03f8,
+ 0x03f8, 0x03f2, 0x03fb, 0x03fb, 0x03fc, 0x03fd, 0x03fe, 0x03ff
+}};
+static UnicodeCaseTableVector caseTable04 = {{
+ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
+ 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x045d, 0x045e, 0x045f,
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f,
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f,
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f,
+ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
+ 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x045d, 0x045e, 0x045f,
+ 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467,
+ 0x0469, 0x0469, 0x046b, 0x046b, 0x046d, 0x046d, 0x046f, 0x046f,
+ 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0477, 0x0477,
+ 0x0479, 0x0479, 0x047b, 0x047b, 0x047d, 0x047d, 0x047f, 0x047f,
+ 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
+ 0x0488, 0x0489, 0x048b, 0x048b, 0x048d, 0x048d, 0x048f, 0x048f,
+ 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497,
+ 0x0499, 0x0499, 0x049b, 0x049b, 0x049d, 0x049d, 0x049f, 0x049f,
+ 0x04a1, 0x04a1, 0x04a3, 0x04a3, 0x04a5, 0x04a5, 0x04a7, 0x04a7,
+ 0x04a9, 0x04a9, 0x04ab, 0x04ab, 0x04ad, 0x04ad, 0x04af, 0x04af,
+ 0x04b1, 0x04b1, 0x04b3, 0x04b3, 0x04b5, 0x04b5, 0x04b7, 0x04b7,
+ 0x04b9, 0x04b9, 0x04bb, 0x04bb, 0x04bd, 0x04bd, 0x04bf, 0x04bf,
+ 0x04c0, 0x04c2, 0x04c2, 0x04c4, 0x04c4, 0x04c6, 0x04c6, 0x04c8,
+ 0x04c8, 0x04ca, 0x04ca, 0x04cc, 0x04cc, 0x04ce, 0x04ce, 0x04cf,
+ 0x04d1, 0x04d1, 0x04d3, 0x04d3, 0x04d5, 0x04d5, 0x04d7, 0x04d7,
+ 0x04d9, 0x04d9, 0x04db, 0x04db, 0x04dd, 0x04dd, 0x04df, 0x04df,
+ 0x04e1, 0x04e1, 0x04e3, 0x04e3, 0x04e5, 0x04e5, 0x04e7, 0x04e7,
+ 0x04e9, 0x04e9, 0x04eb, 0x04eb, 0x04ed, 0x04ed, 0x04ef, 0x04ef,
+ 0x04f1, 0x04f1, 0x04f3, 0x04f3, 0x04f5, 0x04f5, 0x04f6, 0x04f7,
+ 0x04f9, 0x04f9, 0x04fa, 0x04fb, 0x04fc, 0x04fd, 0x04fe, 0x04ff
+}};
+static UnicodeCaseTableVector caseTable05 = {{
+ 0x0501, 0x0501, 0x0503, 0x0503, 0x0505, 0x0505, 0x0507, 0x0507,
+ 0x0509, 0x0509, 0x050b, 0x050b, 0x050d, 0x050d, 0x050f, 0x050f,
+ 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517,
+ 0x0518, 0x0519, 0x051a, 0x051b, 0x051c, 0x051d, 0x051e, 0x051f,
+ 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527,
+ 0x0528, 0x0529, 0x052a, 0x052b, 0x052c, 0x052d, 0x052e, 0x052f,
+ 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
+ 0x0568, 0x0569, 0x056a, 0x056b, 0x056c, 0x056d, 0x056e, 0x056f,
+ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
+ 0x0578, 0x0579, 0x057a, 0x057b, 0x057c, 0x057d, 0x057e, 0x057f,
+ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557,
+ 0x0558, 0x0559, 0x055a, 0x055b, 0x055c, 0x055d, 0x055e, 0x055f,
+ 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
+ 0x0568, 0x0569, 0x056a, 0x056b, 0x056c, 0x056d, 0x056e, 0x056f,
+ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
+ 0x0578, 0x0579, 0x057a, 0x057b, 0x057c, 0x057d, 0x057e, 0x057f,
+ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587,
+ 0x0588, 0x0589, 0x058a, 0x058b, 0x058c, 0x058d, 0x058e, 0x058f,
+ 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597,
+ 0x0598, 0x0599, 0x059a, 0x059b, 0x059c, 0x059d, 0x059e, 0x059f,
+ 0x05a0, 0x05a1, 0x05a2, 0x05a3, 0x05a4, 0x05a5, 0x05a6, 0x05a7,
+ 0x05a8, 0x05a9, 0x05aa, 0x05ab, 0x05ac, 0x05ad, 0x05ae, 0x05af,
+ 0x05b0, 0x05b1, 0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7,
+ 0x05b8, 0x05b9, 0x05ba, 0x05bb, 0x05bc, 0x05bd, 0x05be, 0x05bf,
+ 0x05c0, 0x05c1, 0x05c2, 0x05c3, 0x05c4, 0x05c5, 0x05c6, 0x05c7,
+ 0x05c8, 0x05c9, 0x05ca, 0x05cb, 0x05cc, 0x05cd, 0x05ce, 0x05cf,
+ 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7,
+ 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df,
+ 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7,
+ 0x05e8, 0x05e9, 0x05ea, 0x05eb, 0x05ec, 0x05ed, 0x05ee, 0x05ef,
+ 0x05f0, 0x05f1, 0x05f2, 0x05f3, 0x05f4, 0x05f5, 0x05f6, 0x05f7,
+ 0x05f8, 0x05f9, 0x05fa, 0x05fb, 0x05fc, 0x05fd, 0x05fe, 0x05ff
+}};
+static UnicodeCaseTableVector caseTable1e = {{
+ 0x1e01, 0x1e01, 0x1e03, 0x1e03, 0x1e05, 0x1e05, 0x1e07, 0x1e07,
+ 0x1e09, 0x1e09, 0x1e0b, 0x1e0b, 0x1e0d, 0x1e0d, 0x1e0f, 0x1e0f,
+ 0x1e11, 0x1e11, 0x1e13, 0x1e13, 0x1e15, 0x1e15, 0x1e17, 0x1e17,
+ 0x1e19, 0x1e19, 0x1e1b, 0x1e1b, 0x1e1d, 0x1e1d, 0x1e1f, 0x1e1f,
+ 0x1e21, 0x1e21, 0x1e23, 0x1e23, 0x1e25, 0x1e25, 0x1e27, 0x1e27,
+ 0x1e29, 0x1e29, 0x1e2b, 0x1e2b, 0x1e2d, 0x1e2d, 0x1e2f, 0x1e2f,
+ 0x1e31, 0x1e31, 0x1e33, 0x1e33, 0x1e35, 0x1e35, 0x1e37, 0x1e37,
+ 0x1e39, 0x1e39, 0x1e3b, 0x1e3b, 0x1e3d, 0x1e3d, 0x1e3f, 0x1e3f,
+ 0x1e41, 0x1e41, 0x1e43, 0x1e43, 0x1e45, 0x1e45, 0x1e47, 0x1e47,
+ 0x1e49, 0x1e49, 0x1e4b, 0x1e4b, 0x1e4d, 0x1e4d, 0x1e4f, 0x1e4f,
+ 0x1e51, 0x1e51, 0x1e53, 0x1e53, 0x1e55, 0x1e55, 0x1e57, 0x1e57,
+ 0x1e59, 0x1e59, 0x1e5b, 0x1e5b, 0x1e5d, 0x1e5d, 0x1e5f, 0x1e5f,
+ 0x1e61, 0x1e61, 0x1e63, 0x1e63, 0x1e65, 0x1e65, 0x1e67, 0x1e67,
+ 0x1e69, 0x1e69, 0x1e6b, 0x1e6b, 0x1e6d, 0x1e6d, 0x1e6f, 0x1e6f,
+ 0x1e71, 0x1e71, 0x1e73, 0x1e73, 0x1e75, 0x1e75, 0x1e77, 0x1e77,
+ 0x1e79, 0x1e79, 0x1e7b, 0x1e7b, 0x1e7d, 0x1e7d, 0x1e7f, 0x1e7f,
+ 0x1e81, 0x1e81, 0x1e83, 0x1e83, 0x1e85, 0x1e85, 0x1e87, 0x1e87,
+ 0x1e89, 0x1e89, 0x1e8b, 0x1e8b, 0x1e8d, 0x1e8d, 0x1e8f, 0x1e8f,
+ 0x1e91, 0x1e91, 0x1e93, 0x1e93, 0x1e95, 0x1e95, 0x1e96, 0x1e97,
+ 0x1e98, 0x1e99, 0x1e9a, 0x1e61, 0x1e9c, 0x1e9d, 0x1e9e, 0x1e9f,
+ 0x1ea1, 0x1ea1, 0x1ea3, 0x1ea3, 0x1ea5, 0x1ea5, 0x1ea7, 0x1ea7,
+ 0x1ea9, 0x1ea9, 0x1eab, 0x1eab, 0x1ead, 0x1ead, 0x1eaf, 0x1eaf,
+ 0x1eb1, 0x1eb1, 0x1eb3, 0x1eb3, 0x1eb5, 0x1eb5, 0x1eb7, 0x1eb7,
+ 0x1eb9, 0x1eb9, 0x1ebb, 0x1ebb, 0x1ebd, 0x1ebd, 0x1ebf, 0x1ebf,
+ 0x1ec1, 0x1ec1, 0x1ec3, 0x1ec3, 0x1ec5, 0x1ec5, 0x1ec7, 0x1ec7,
+ 0x1ec9, 0x1ec9, 0x1ecb, 0x1ecb, 0x1ecd, 0x1ecd, 0x1ecf, 0x1ecf,
+ 0x1ed1, 0x1ed1, 0x1ed3, 0x1ed3, 0x1ed5, 0x1ed5, 0x1ed7, 0x1ed7,
+ 0x1ed9, 0x1ed9, 0x1edb, 0x1edb, 0x1edd, 0x1edd, 0x1edf, 0x1edf,
+ 0x1ee1, 0x1ee1, 0x1ee3, 0x1ee3, 0x1ee5, 0x1ee5, 0x1ee7, 0x1ee7,
+ 0x1ee9, 0x1ee9, 0x1eeb, 0x1eeb, 0x1eed, 0x1eed, 0x1eef, 0x1eef,
+ 0x1ef1, 0x1ef1, 0x1ef3, 0x1ef3, 0x1ef5, 0x1ef5, 0x1ef7, 0x1ef7,
+ 0x1ef9, 0x1ef9, 0x1efa, 0x1efb, 0x1efc, 0x1efd, 0x1efe, 0x1eff
+}};
+static UnicodeCaseTableVector caseTable1f = {{
+ 0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07,
+ 0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07,
+ 0x1f10, 0x1f11, 0x1f12, 0x1f13, 0x1f14, 0x1f15, 0x1f16, 0x1f17,
+ 0x1f10, 0x1f11, 0x1f12, 0x1f13, 0x1f14, 0x1f15, 0x1f1e, 0x1f1f,
+ 0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27,
+ 0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27,
+ 0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37,
+ 0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37,
+ 0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45, 0x1f46, 0x1f47,
+ 0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45, 0x1f4e, 0x1f4f,
+ 0x1f50, 0x1f51, 0x1f52, 0x1f53, 0x1f54, 0x1f55, 0x1f56, 0x1f57,
+ 0x1f58, 0x1f51, 0x1f5a, 0x1f53, 0x1f5c, 0x1f55, 0x1f5e, 0x1f57,
+ 0x1f60, 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67,
+ 0x1f60, 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67,
+ 0x1f70, 0x1f71, 0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x1f76, 0x1f77,
+ 0x1f78, 0x1f79, 0x1f7a, 0x1f7b, 0x1f7c, 0x1f7d, 0x1f7e, 0x1f7f,
+ 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87,
+ 0x1f80, 0x1f81, 0x1f82, 0x1f83, 0x1f84, 0x1f85, 0x1f86, 0x1f87,
+ 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97,
+ 0x1f90, 0x1f91, 0x1f92, 0x1f93, 0x1f94, 0x1f95, 0x1f96, 0x1f97,
+ 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7,
+ 0x1fa0, 0x1fa1, 0x1fa2, 0x1fa3, 0x1fa4, 0x1fa5, 0x1fa6, 0x1fa7,
+ 0x1fb0, 0x1fb1, 0x1fb2, 0x1fb3, 0x1fb4, 0x1fb5, 0x1fb6, 0x1fb7,
+ 0x1fb0, 0x1fb1, 0x1f70, 0x1f71, 0x1fb3, 0x1fbd, 0x03b9, 0x1fbf,
+ 0x1fc0, 0x1fc1, 0x1fc2, 0x1fc3, 0x1fc4, 0x1fc5, 0x1fc6, 0x1fc7,
+ 0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x1fc3, 0x1fcd, 0x1fce, 0x1fcf,
+ 0x1fd0, 0x1fd1, 0x1fd2, 0x1fd3, 0x1fd4, 0x1fd5, 0x1fd6, 0x1fd7,
+ 0x1fd0, 0x1fd1, 0x1f76, 0x1f77, 0x1fdc, 0x1fdd, 0x1fde, 0x1fdf,
+ 0x1fe0, 0x1fe1, 0x1fe2, 0x1fe3, 0x1fe4, 0x1fe5, 0x1fe6, 0x1fe7,
+ 0x1fe0, 0x1fe1, 0x1f7a, 0x1f7b, 0x1fe5, 0x1fed, 0x1fee, 0x1fef,
+ 0x1ff0, 0x1ff1, 0x1ff2, 0x1ff3, 0x1ff4, 0x1ff5, 0x1ff6, 0x1ff7,
+ 0x1f78, 0x1f79, 0x1f7c, 0x1f7d, 0x1ff3, 0x1ffd, 0x1ffe, 0x1fff
+}};
+static UnicodeCaseTableVector caseTable21 = {{
+ 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107,
+ 0x2108, 0x2109, 0x210a, 0x210b, 0x210c, 0x210d, 0x210e, 0x210f,
+ 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117,
+ 0x2118, 0x2119, 0x211a, 0x211b, 0x211c, 0x211d, 0x211e, 0x211f,
+ 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x03c9, 0x2127,
+ 0x2128, 0x2129, 0x006b, 0x00e5, 0x212c, 0x212d, 0x212e, 0x212f,
+ 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137,
+ 0x2138, 0x2139, 0x213a, 0x213b, 0x213c, 0x213d, 0x213e, 0x213f,
+ 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147,
+ 0x2148, 0x2149, 0x214a, 0x214b, 0x214c, 0x214d, 0x214e, 0x214f,
+ 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157,
+ 0x2158, 0x2159, 0x215a, 0x215b, 0x215c, 0x215d, 0x215e, 0x215f,
+ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
+ 0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x217f,
+ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
+ 0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x217f,
+ 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187,
+ 0x2188, 0x2189, 0x218a, 0x218b, 0x218c, 0x218d, 0x218e, 0x218f,
+ 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197,
+ 0x2198, 0x2199, 0x219a, 0x219b, 0x219c, 0x219d, 0x219e, 0x219f,
+ 0x21a0, 0x21a1, 0x21a2, 0x21a3, 0x21a4, 0x21a5, 0x21a6, 0x21a7,
+ 0x21a8, 0x21a9, 0x21aa, 0x21ab, 0x21ac, 0x21ad, 0x21ae, 0x21af,
+ 0x21b0, 0x21b1, 0x21b2, 0x21b3, 0x21b4, 0x21b5, 0x21b6, 0x21b7,
+ 0x21b8, 0x21b9, 0x21ba, 0x21bb, 0x21bc, 0x21bd, 0x21be, 0x21bf,
+ 0x21c0, 0x21c1, 0x21c2, 0x21c3, 0x21c4, 0x21c5, 0x21c6, 0x21c7,
+ 0x21c8, 0x21c9, 0x21ca, 0x21cb, 0x21cc, 0x21cd, 0x21ce, 0x21cf,
+ 0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x21d4, 0x21d5, 0x21d6, 0x21d7,
+ 0x21d8, 0x21d9, 0x21da, 0x21db, 0x21dc, 0x21dd, 0x21de, 0x21df,
+ 0x21e0, 0x21e1, 0x21e2, 0x21e3, 0x21e4, 0x21e5, 0x21e6, 0x21e7,
+ 0x21e8, 0x21e9, 0x21ea, 0x21eb, 0x21ec, 0x21ed, 0x21ee, 0x21ef,
+ 0x21f0, 0x21f1, 0x21f2, 0x21f3, 0x21f4, 0x21f5, 0x21f6, 0x21f7,
+ 0x21f8, 0x21f9, 0x21fa, 0x21fb, 0x21fc, 0x21fd, 0x21fe, 0x21ff
+}};
+static UnicodeCaseTableVector caseTable24 = {{
+ 0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405, 0x2406, 0x2407,
+ 0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d, 0x240e, 0x240f,
+ 0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417,
+ 0x2418, 0x2419, 0x241a, 0x241b, 0x241c, 0x241d, 0x241e, 0x241f,
+ 0x2420, 0x2421, 0x2422, 0x2423, 0x2424, 0x2425, 0x2426, 0x2427,
+ 0x2428, 0x2429, 0x242a, 0x242b, 0x242c, 0x242d, 0x242e, 0x242f,
+ 0x2430, 0x2431, 0x2432, 0x2433, 0x2434, 0x2435, 0x2436, 0x2437,
+ 0x2438, 0x2439, 0x243a, 0x243b, 0x243c, 0x243d, 0x243e, 0x243f,
+ 0x2440, 0x2441, 0x2442, 0x2443, 0x2444, 0x2445, 0x2446, 0x2447,
+ 0x2448, 0x2449, 0x244a, 0x244b, 0x244c, 0x244d, 0x244e, 0x244f,
+ 0x2450, 0x2451, 0x2452, 0x2453, 0x2454, 0x2455, 0x2456, 0x2457,
+ 0x2458, 0x2459, 0x245a, 0x245b, 0x245c, 0x245d, 0x245e, 0x245f,
+ 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467,
+ 0x2468, 0x2469, 0x246a, 0x246b, 0x246c, 0x246d, 0x246e, 0x246f,
+ 0x2470, 0x2471, 0x2472, 0x2473, 0x2474, 0x2475, 0x2476, 0x2477,
+ 0x2478, 0x2479, 0x247a, 0x247b, 0x247c, 0x247d, 0x247e, 0x247f,
+ 0x2480, 0x2481, 0x2482, 0x2483, 0x2484, 0x2485, 0x2486, 0x2487,
+ 0x2488, 0x2489, 0x248a, 0x248b, 0x248c, 0x248d, 0x248e, 0x248f,
+ 0x2490, 0x2491, 0x2492, 0x2493, 0x2494, 0x2495, 0x2496, 0x2497,
+ 0x2498, 0x2499, 0x249a, 0x249b, 0x249c, 0x249d, 0x249e, 0x249f,
+ 0x24a0, 0x24a1, 0x24a2, 0x24a3, 0x24a4, 0x24a5, 0x24a6, 0x24a7,
+ 0x24a8, 0x24a9, 0x24aa, 0x24ab, 0x24ac, 0x24ad, 0x24ae, 0x24af,
+ 0x24b0, 0x24b1, 0x24b2, 0x24b3, 0x24b4, 0x24b5, 0x24d0, 0x24d1,
+ 0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7, 0x24d8, 0x24d9,
+ 0x24da, 0x24db, 0x24dc, 0x24dd, 0x24de, 0x24df, 0x24e0, 0x24e1,
+ 0x24e2, 0x24e3, 0x24e4, 0x24e5, 0x24e6, 0x24e7, 0x24e8, 0x24e9,
+ 0x24d0, 0x24d1, 0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7,
+ 0x24d8, 0x24d9, 0x24da, 0x24db, 0x24dc, 0x24dd, 0x24de, 0x24df,
+ 0x24e0, 0x24e1, 0x24e2, 0x24e3, 0x24e4, 0x24e5, 0x24e6, 0x24e7,
+ 0x24e8, 0x24e9, 0x24ea, 0x24eb, 0x24ec, 0x24ed, 0x24ee, 0x24ef,
+ 0x24f0, 0x24f1, 0x24f2, 0x24f3, 0x24f4, 0x24f5, 0x24f6, 0x24f7,
+ 0x24f8, 0x24f9, 0x24fa, 0x24fb, 0x24fc, 0x24fd, 0x24fe, 0x24ff
+}};
+static UnicodeCaseTableVector caseTableff = {{
+ 0xff00, 0xff01, 0xff02, 0xff03, 0xff04, 0xff05, 0xff06, 0xff07,
+ 0xff08, 0xff09, 0xff0a, 0xff0b, 0xff0c, 0xff0d, 0xff0e, 0xff0f,
+ 0xff10, 0xff11, 0xff12, 0xff13, 0xff14, 0xff15, 0xff16, 0xff17,
+ 0xff18, 0xff19, 0xff1a, 0xff1b, 0xff1c, 0xff1d, 0xff1e, 0xff1f,
+ 0xff20, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47,
+ 0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f,
+ 0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, 0xff57,
+ 0xff58, 0xff59, 0xff5a, 0xff3b, 0xff3c, 0xff3d, 0xff3e, 0xff3f,
+ 0xff40, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47,
+ 0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f,
+ 0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, 0xff57,
+ 0xff58, 0xff59, 0xff5a, 0xff5b, 0xff5c, 0xff5d, 0xff5e, 0xff5f,
+ 0xff60, 0xff61, 0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67,
+ 0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f,
+ 0xff70, 0xff71, 0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77,
+ 0xff78, 0xff79, 0xff7a, 0xff7b, 0xff7c, 0xff7d, 0xff7e, 0xff7f,
+ 0xff80, 0xff81, 0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87,
+ 0xff88, 0xff89, 0xff8a, 0xff8b, 0xff8c, 0xff8d, 0xff8e, 0xff8f,
+ 0xff90, 0xff91, 0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97,
+ 0xff98, 0xff99, 0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f,
+ 0xffa0, 0xffa1, 0xffa2, 0xffa3, 0xffa4, 0xffa5, 0xffa6, 0xffa7,
+ 0xffa8, 0xffa9, 0xffaa, 0xffab, 0xffac, 0xffad, 0xffae, 0xffaf,
+ 0xffb0, 0xffb1, 0xffb2, 0xffb3, 0xffb4, 0xffb5, 0xffb6, 0xffb7,
+ 0xffb8, 0xffb9, 0xffba, 0xffbb, 0xffbc, 0xffbd, 0xffbe, 0xffbf,
+ 0xffc0, 0xffc1, 0xffc2, 0xffc3, 0xffc4, 0xffc5, 0xffc6, 0xffc7,
+ 0xffc8, 0xffc9, 0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf,
+ 0xffd0, 0xffd1, 0xffd2, 0xffd3, 0xffd4, 0xffd5, 0xffd6, 0xffd7,
+ 0xffd8, 0xffd9, 0xffda, 0xffdb, 0xffdc, 0xffdd, 0xffde, 0xffdf,
+ 0xffe0, 0xffe1, 0xffe2, 0xffe3, 0xffe4, 0xffe5, 0xffe6, 0xffe7,
+ 0xffe8, 0xffe9, 0xffea, 0xffeb, 0xffec, 0xffed, 0xffee, 0xffef,
+ 0xfff0, 0xfff1, 0xfff2, 0xfff3, 0xfff4, 0xfff5, 0xfff6, 0xfff7,
+ 0xfff8, 0xfff9, 0xfffa, 0xfffb, 0xfffc, 0xfffd, 0xfffe, 0xffff
+}};
+static UnicodeCaseTableVector *caseTable[256] = {
+ &caseTable00,
+ &caseTable01,
+ &caseTable02,
+ &caseTable03,
+ &caseTable04,
+ &caseTable05,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &caseTable1e,
+ &caseTable1f,
+ NULL,
+ &caseTable21,
+ NULL,
+ NULL,
+ &caseTable24,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &caseTableff
+};
+
+static inline char getType(Unicode c) {
+ int i;
+ char type;
+
+ if (c > 0xffff) {
+ type = 'X';
+ } else {
+ i = (c >> 8) & 0xff;
+ if ((type = typeTable[i].type) == 'X') {
+ type = typeTable[i].vector[c & 0xff];
+ }
+ }
+ return type;
+}
+
+GBool unicodeTypeL(Unicode c) {
+ return getType(c) == 'L';
+}
+
+GBool unicodeTypeR(Unicode c) {
+ return getType(c) == 'R';
+}
+
+GBool unicodeTypeNum(Unicode c) {
+ char t;
+
+ t = getType(c);
+ return t == '#' || t == '.';
+}
+
+GBool unicodeTypeAlphaNum(Unicode c) {
+ char t;
+
+ t = getType(c);
+ return t == 'L' || t == 'R' || t == '#' || t == '.';
+}
+
+GBool unicodeTypeWord(Unicode c) {
+ char t;
+
+ t = getType(c);
+ return t == 'L' || t == 'R' || t == '#';
+}
+
+Unicode unicodeToUpper(Unicode c) {
+ int i;
+
+ if (c > 0xffff) {
+ return c;
+ }
+ i = (c >> 8) & 0xff;
+ if (caseTable[i]) {
+ return caseTable[i]->codes[c & 0xff];
+ }
+ return c;
+}
+
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeTypeTable.h b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeTypeTable.h
new file mode 100644
index 00000000000..3230d927891
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/UnicodeTypeTable.h
@@ -0,0 +1,26 @@
+//========================================================================
+//
+// UnicodeTypeTable.h
+//
+// Copyright 2003-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef UNICODETYPETABLE_H
+#define UNICODETYPETABLE_H
+
+#include "gtypes.h"
+
+extern GBool unicodeTypeL(Unicode c);
+
+extern GBool unicodeTypeR(Unicode c);
+
+extern GBool unicodeTypeNum(Unicode c);
+
+extern GBool unicodeTypeAlphaNum(Unicode c);
+
+extern GBool unicodeTypeWord(Unicode c);
+
+extern Unicode unicodeToUpper(Unicode c);
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc
new file mode 100644
index 00000000000..9ae636226ae
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.cc
@@ -0,0 +1,387 @@
+//========================================================================
+//
+// WebFont.cc
+//
+// Copyright 2019 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "gmempp.h"
+#include "GHash.h"
+#include "FoFiTrueType.h"
+#include "FoFiType1C.h"
+#include "CharCodeToUnicode.h"
+#include "WebFont.h"
+
+WebFont::WebFont(GfxFont *gfxFontA, XRef *xref) {
+ GfxFontType type;
+ Ref id;
+
+ gfxFont = gfxFontA;
+ fontBuf = NULL;
+ ffTrueType = NULL;
+ ffType1C = NULL;
+ isOpenType = gFalse;
+
+ 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;
+ }
+ }
+ }
+}
+
+WebFont::~WebFont() {
+ delete ffTrueType;
+ delete ffType1C;
+ gfree(fontBuf);
+}
+
+GBool WebFont::canWriteTTF() {
+ return ffTrueType != NULL;
+}
+
+GBool WebFont::canWriteOTF() {
+ return ffType1C || isOpenType;
+}
+
+static void writeToFile(void *stream, const char *data, int len) {
+ fwrite(data, 1, len, (FILE *)stream);
+}
+
+GBool WebFont::writeTTF(const char *fontFilePath) {
+ FILE *out;
+ int *codeToGID;
+ Guchar *cmapTable;
+ GBool freeCodeToGID;
+ int nCodes, cmapTableLength;
+
+ if (!ffTrueType) {
+ return gFalse;
+ }
+ if (gfxFont->isCIDFont()) {
+ codeToGID = ((GfxCIDFont *)gfxFont)->getCIDToGID();
+ nCodes = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
+ if (!codeToGID) {
+ nCodes = ffTrueType->getNumGlyphs();
+ }
+ freeCodeToGID = gFalse;
+ } else {
+ codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffTrueType);
+ nCodes = 256;
+ freeCodeToGID = gTrue;
+ }
+ cmapTable = makeUnicodeCmapTable(codeToGID, nCodes, &cmapTableLength);
+ if (freeCodeToGID) {
+ gfree(codeToGID);
+ }
+ if (!cmapTable) {
+ return gFalse;
+ }
+ if (!(out = fopen(fontFilePath, "wb"))) {
+ gfree(cmapTable);
+ return gFalse;
+ }
+ ffTrueType->writeTTF(writeToFile, out, NULL, NULL,
+ cmapTable, cmapTableLength);
+ fclose(out);
+ gfree(cmapTable);
+ return gTrue;
+}
+
+GBool WebFont::writeOTF(const char *fontFilePath) {
+ int *codeToGID;
+ Gushort *widths;
+ Guchar *cmapTable;
+ FILE *out;
+ int nCodes, nWidths, cmapTableLength;
+
+ if (ffType1C) {
+ if (gfxFont->getType() == fontType1C) {
+ codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffType1C);
+ if (!(cmapTable = makeUnicodeCmapTable(codeToGID, 256,
+ &cmapTableLength))) {
+ gfree(codeToGID);
+ return gFalse;
+ }
+ widths = makeType1CWidths(codeToGID, 256, &nWidths);
+ gfree(codeToGID);
+ } else { // fontCIDType0C
+ codeToGID = ffType1C->getCIDToGIDMap(&nCodes);
+ if (!(cmapTable = makeUnicodeCmapTable(codeToGID, nCodes,
+ &cmapTableLength))) {
+ gfree(codeToGID);
+ return gFalse;
+ }
+ widths = makeCIDType0CWidths(codeToGID, nCodes, &nWidths);
+ gfree(codeToGID);
+ }
+ if (!(out = fopen(fontFilePath, "wb"))) {
+ gfree(cmapTable);
+ gfree(widths);
+ return gFalse;
+ }
+ ffType1C->convertToOpenType(writeToFile, out,
+ nWidths, widths,
+ cmapTable, cmapTableLength);
+ fclose(out);
+ gfree(cmapTable);
+ gfree(widths);
+
+ } else if (isOpenType) {
+ if (!(out = fopen(fontFilePath, "wb"))) {
+ return gFalse;
+ }
+ if (fwrite(fontBuf, 1, fontLength, out) != (Guint)fontLength) {
+ fclose(out);
+ return gFalse;
+ }
+ fclose(out);
+
+ } else {
+ return gFalse;
+ }
+
+ return gTrue;
+}
+
+Gushort *WebFont::makeType1CWidths(int *codeToGID, int nCodes,
+ int *nWidths) {
+ Gushort *widths;
+ Gushort width;
+ int widthsLen, gid, i;
+
+ widthsLen = ffType1C->getNumGlyphs();
+ widths = (Gushort *)gmallocn(widthsLen, sizeof(Gushort));
+ for (i = 0; i < widthsLen; ++i) {
+ widths[i] = 0;
+ }
+ for (i = 0; i < nCodes; ++i) {
+ gid = codeToGID[i];
+ if (gid < 0 || gid >= widthsLen) {
+ continue;
+ }
+ width = (Gushort)(((Gfx8BitFont *)gfxFont)->getWidth((Guchar)i)
+ * 1000 + 0.5);
+ if (width == 0) {
+ continue;
+ }
+ widths[gid] = width;
+ }
+ *nWidths = widthsLen;
+ return widths;
+}
+
+Gushort *WebFont::makeCIDType0CWidths(int *codeToGID, int nCodes,
+ int *nWidths) {
+ Gushort *widths;
+ Gushort width;
+ int widthsLen, gid, i;
+
+ widthsLen = ffType1C->getNumGlyphs();
+ widths = (Gushort *)gmallocn(widthsLen, sizeof(Gushort));
+ for (i = 0 ; i < widthsLen; ++i) {
+ widths[i] = 0;
+ }
+ for (i = 0; i < nCodes; ++i) {
+ gid = codeToGID[i];
+ if (gid < 0 || gid >= widthsLen) {
+ continue;
+ }
+ width = (Gushort)(((GfxCIDFont *)gfxFont)->getWidth((CID)i)
+ * 1000 + 0.5);
+ if (width == 0) {
+ continue;
+ }
+ widths[gid] = width;
+ }
+ *nWidths = widthsLen;
+ return widths;
+}
+
+Guchar *WebFont::makeUnicodeCmapTable(int *codeToGID, int nCodes,
+ int *unicodeCmapLength) {
+ int *unicodeToGID;
+ Guchar *cmapTable;
+ int unicodeToGIDLength, nMappings, len;
+ int nSegs, searchRange, entrySelector, rangeShift;
+ int glyphIdOffset, idRangeOffset;
+ int start, end, c, i;
+
+ if (!(unicodeToGID = makeUnicodeToGID(codeToGID, nCodes,
+ &unicodeToGIDLength))) {
+ return NULL;
+ }
+
+ // count the valid code-to-glyph mappings, and the sequences of
+ // consecutive valid mappings
+ // (note: char code 65535 is used to mark the end of table)
+ nMappings = 0;
+ nSegs = 1; // count the last segment, mapping 65535
+ for (c = 0; c < unicodeToGIDLength && c <= 65534; ++c) {
+ if (unicodeToGID[c]) {
+ ++nMappings;
+ if (c == 0 || !unicodeToGID[c-1]) {
+ ++nSegs;
+ }
+ }
+ }
+
+ i = 1;
+ entrySelector = 0;
+ while (2 * i <= nSegs) {
+ i *= 2;
+ ++entrySelector;
+ }
+ searchRange = 1 << (entrySelector + 1);
+ rangeShift = 2 * nSegs - searchRange;
+
+ len = 28 + nSegs * 8 + nMappings * 2;
+ cmapTable = (Guchar *)gmalloc(len);
+
+ // header
+ cmapTable[ 0] = 0x00; // table version
+ cmapTable[ 1] = 0x00;
+ cmapTable[ 2] = 0x00; // number of cmaps
+ cmapTable[ 3] = 0x01;
+ cmapTable[ 4] = 0x00; // platform[0]
+ cmapTable[ 5] = 0x03;
+ cmapTable[ 6] = 0x00; // encoding[0]
+ cmapTable[ 7] = 0x01;
+ cmapTable[ 8] = 0x00; // offset[0]
+ cmapTable[ 9] = 0x00;
+ cmapTable[10] = 0x00;
+ cmapTable[11] = 0x0c;
+
+ // table info
+ cmapTable[12] = 0x00; // cmap format
+ cmapTable[13] = 0x04;
+ cmapTable[14] = (Guchar)((len - 12) >> 8); // cmap length
+ cmapTable[15] = (Guchar)(len - 12);
+ cmapTable[16] = 0x00; // cmap version
+ cmapTable[17] = 0x00;
+ cmapTable[18] = (Guchar)(nSegs >> 7); // segCountX2
+ cmapTable[19] = (Guchar)(nSegs << 1);
+ cmapTable[20] = (Guchar)(searchRange >> 8); // searchRange
+ cmapTable[21] = (Guchar)searchRange;
+ cmapTable[22] = (Guchar)(entrySelector >> 8); // entrySelector
+ cmapTable[23] = (Guchar)entrySelector;
+ cmapTable[24] = (Guchar)(rangeShift >> 8); // rangeShift
+ cmapTable[25] = (Guchar)rangeShift;
+ cmapTable[26 + nSegs*2 ] = 0; // reservedPad
+ cmapTable[26 + nSegs*2 + 1] = 0;
+
+ i = 0;
+ glyphIdOffset = 28 + nSegs*8;
+ for (c = 0; c < unicodeToGIDLength && c <= 65534; ++c) {
+ if (unicodeToGID[c]) {
+ if (c == 0 || !unicodeToGID[c-1]) {
+ start = c;
+ cmapTable[28 + nSegs*2 + i*2 ] = (Guchar)(start >> 8);
+ cmapTable[28 + nSegs*2 + i*2 + 1] = (Guchar)start;
+ cmapTable[28 + nSegs*4 + i*2 ] = (Guchar)0; // idDelta
+ cmapTable[28 + nSegs*4 + i*2 + 1] = (Guchar)0;
+ idRangeOffset = glyphIdOffset - (28 + nSegs*6 + i*2);
+ cmapTable[28 + nSegs*6 + i*2 ] = (Guchar)(idRangeOffset >> 8);
+ cmapTable[28 + nSegs*6 + i*2 + 1] = (Guchar)idRangeOffset;
+ }
+ if (c == 65534 || !unicodeToGID[c+1]) {
+ end = c;
+ cmapTable[26 + i*2 ] = (Guchar)(end >> 8);
+ cmapTable[26 + i*2 + 1] = (Guchar)end;
+ ++i;
+ }
+ cmapTable[glyphIdOffset++] = (Guchar)(unicodeToGID[c] >> 8);
+ cmapTable[glyphIdOffset++] = (Guchar)unicodeToGID[c];
+ }
+ }
+
+ // last segment maps code 65535 to GID 0
+ cmapTable[26 + i*2 ] = (Guchar)0xff; // end
+ cmapTable[26 + i*2 + 1] = (Guchar)0xff;
+ cmapTable[28 + nSegs*2 + i*2 ] = (Guchar)0xff; // start
+ cmapTable[28 + nSegs*2 + i*2 + 1] = (Guchar)0xff;
+ cmapTable[28 + nSegs*4 + i*2 ] = (Guchar)0; // idDelta
+ cmapTable[28 + nSegs*4 + i*2 + 1] = (Guchar)1;
+ cmapTable[28 + nSegs*6 + i*2 ] = (Guchar)0; // idRangeOffset
+ cmapTable[28 + nSegs*6 + i*2 + 1] = (Guchar)0;
+
+ gfree(unicodeToGID);
+
+ *unicodeCmapLength = len;
+ return cmapTable;
+}
+
+int *WebFont::makeUnicodeToGID(int *codeToGID, int nCodes,
+ int *unicodeToGIDLength) {
+ int *unicodeToGID;
+ CharCodeToUnicode *ctu;
+ Unicode u[2];
+ int len, size, newSize, uLen, c, gid;
+
+ if (gfxFont->isCIDFont()) {
+ if (!(ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
+ return NULL;
+ }
+ } else {
+ ctu = ((Gfx8BitFont *)gfxFont)->getToUnicode();
+ }
+
+ len = 0;
+ size = 256;
+ unicodeToGID = (int *)gmallocn(size, sizeof(int));
+ memset(unicodeToGID, 0, size * sizeof(int));
+ for (c = 0; c < nCodes; ++c) {
+ gid = codeToGID ? codeToGID[c] : c;
+ if (gid < 0 || gid >= 65536) {
+ continue;
+ }
+ uLen = ctu->mapToUnicode(c, u, 2);
+ if (uLen != 1) {
+ continue;
+ }
+ if (u[0] >= 65536) { // sanity check
+ continue;
+ }
+ if ((int)u[0] >= size) {
+ newSize = 2 * size;
+ while ((int)u[0] >= newSize) {
+ newSize *= 2;
+ }
+ unicodeToGID = (int *)greallocn(unicodeToGID, newSize, sizeof(int));
+ memset(unicodeToGID + size, 0, (newSize - size) * sizeof(int));
+ size = newSize;
+ }
+ unicodeToGID[u[0]] = gid;
+ if ((int)u[0] >= len) {
+ len = u[0] + 1;
+ }
+ }
+
+ ctu->decRefCnt();
+
+ *unicodeToGIDLength = len;
+ return unicodeToGID;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.h b/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.h
new file mode 100644
index 00000000000..a967e2e490a
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/WebFont.h
@@ -0,0 +1,69 @@
+//========================================================================
+//
+// WebFont.h
+//
+// Modify/convert embedded PDF fonts to a form usable by web browsers.
+//
+// Copyright 2019 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef WEBFONT_H
+#define WEBFONT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "GfxFont.h"
+
+class FoFiTrueType;
+class FoFiType1C;
+class XRef;
+
+//------------------------------------------------------------------------
+
+class WebFont {
+public:
+
+ WebFont(GfxFont *gfxFontA, XRef *xref);
+
+ ~WebFont();
+
+ // Returns true if the font is, or can be converted to, a TrueType
+ // font.
+ GBool canWriteTTF();
+
+ // Returns true if the font is, or can be converted to, an OpenType
+ // font.
+ GBool canWriteOTF();
+
+ // Write a TrueType (.ttf) file to [fontFilePath]. This can only be
+ // called if canWriteTTF() returns true. Returns true on success.
+ GBool writeTTF(const char *fontFilePath);
+
+ // Write an OpenType (.otf) file to [fontFilePath]. This can only
+ // be called if canWriteOTF() returns true. Returns true on
+ // success.
+ GBool writeOTF(const char *fontFilePath);
+
+private:
+
+ Gushort *makeType1CWidths(int *codeToGID, int nCodes, int *nWidths);
+ Gushort *makeCIDType0CWidths(int *codeToGID, int nCodes, int *nWidths);
+ Guchar *makeUnicodeCmapTable(int *codeToGID, int nCodes,
+ int *unicodeCmapLength);
+ int *makeUnicodeToGID(int *codeToGID, int nCodes, int *unicodeToGIDLength);
+
+ GfxFont *gfxFont;
+ char *fontBuf;
+ int fontLength;
+ FoFiTrueType *ffTrueType;
+ FoFiType1C *ffType1C;
+ GBool isOpenType;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc
new file mode 100644
index 00000000000..6928fa9c9a8
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.cc
@@ -0,0 +1,675 @@
+//========================================================================
+//
+// XFAScanner.cc
+//
+// Copyright 2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "GString.h"
+#include "GHash.h"
+#include "Object.h"
+#include "Error.h"
+#include "Zoox.h"
+#include "XFAScanner.h"
+
+//------------------------------------------------------------------------
+
+// fields have two names:
+//
+// name:
+// - nodes with bind=global set the index to 0 ("foo[0]") regardless
+// of the number of nodes with the same name
+// - nodes with bind=none are dropped from the name
+// - <area> nodes are dropped from the name
+// - used for field value lookup in <xfa:datasets>
+//
+// fullName:
+// - all named nodes are treated the same, regardless of bind=global
+// or bind=none
+// - <area> nodes are included in the name, but don't reset the
+// numbering (i.e., <area> nodes are "transparent" with respect to
+// node numbering)
+// - used for field value lookup in <form>
+// - used for matching with AcroForm names
+//
+// Both names use indexes on all nodes, even if there's only one node
+// with the name -- this isn't correct for XFA naming, but it matches
+// the AcroForm behavior.
+
+//------------------------------------------------------------------------
+
+XFAFieldLayoutInfo::XFAFieldLayoutInfo(XFAFieldLayoutHAlign hAlignA,
+ XFAFieldLayoutVAlign vAlignA) {
+ hAlign = hAlignA;
+ vAlign = vAlignA;
+}
+
+//------------------------------------------------------------------------
+
+XFAFieldPictureInfo::XFAFieldPictureInfo(XFAFieldPictureSubtype subtypeA,
+ GString *formatA) {
+ subtype = subtypeA;
+ format = formatA;
+}
+
+XFAFieldPictureInfo::~XFAFieldPictureInfo() {
+ delete format;
+}
+
+//------------------------------------------------------------------------
+
+XFAFieldBarcodeInfo::XFAFieldBarcodeInfo(GString *barcodeTypeA,
+ double wideNarrowRatioA,
+ double moduleWidthA,
+ double moduleHeightA,
+ int dataLengthA,
+ int errorCorrectionLevelA,
+ GString *textLocationA) {
+ barcodeType = barcodeTypeA;
+ wideNarrowRatio = wideNarrowRatioA;
+ moduleWidth = moduleWidthA;
+ moduleHeight = moduleHeightA;
+ dataLength = dataLengthA;
+ errorCorrectionLevel = errorCorrectionLevelA;
+ textLocation = textLocationA;
+}
+
+XFAFieldBarcodeInfo::~XFAFieldBarcodeInfo() {
+ delete barcodeType;
+ delete textLocation;
+}
+
+//------------------------------------------------------------------------
+
+XFAField::XFAField(GString *nameA, GString *fullNameA, GString *valueA,
+ XFAFieldLayoutInfo *layoutInfoA,
+ XFAFieldPictureInfo *pictureInfoA,
+ XFAFieldBarcodeInfo *barcodeInfoA)
+ : name(nameA)
+ , fullName(fullNameA)
+ , value(valueA)
+ , layoutInfo(layoutInfoA)
+ , pictureInfo(pictureInfoA)
+ , barcodeInfo(barcodeInfoA)
+{
+}
+
+XFAField::~XFAField() {
+ delete name;
+ delete fullName;
+ delete value;
+ delete layoutInfo;
+ delete pictureInfo;
+ delete barcodeInfo;
+}
+
+//------------------------------------------------------------------------
+
+XFAScanner *XFAScanner::load(Object *xfaObj) {
+ GString *xfaData = readXFAStreams(xfaObj);
+ if (!xfaData) {
+ return NULL;
+ }
+ ZxDoc *xml = ZxDoc::loadMem(xfaData->getCString(), xfaData->getLength());
+ delete xfaData;
+ if (!xml) {
+ error(errSyntaxError, -1, "Invalid XML in XFA form");
+ return NULL;
+ }
+
+ XFAScanner *scanner = new XFAScanner();
+
+ if (xml->getRoot()) {
+ GHash *formValues = scanner->scanFormValues(xml->getRoot());
+ ZxElement *dataElem = NULL;
+ ZxElement *datasets =
+ xml->getRoot()->findFirstChildElement("xfa:datasets");
+ if (datasets) {
+ dataElem = datasets->findFirstChildElement("xfa:data");
+ }
+ ZxElement *tmpl = xml->getRoot()->findFirstChildElement("template");
+ if (tmpl) {
+ scanner->scanNode(tmpl, NULL, NULL, NULL, NULL, NULL,
+ dataElem, formValues);
+ }
+ deleteGHash(formValues, GString);
+ }
+
+ delete xml;
+
+ return scanner;
+}
+
+XFAScanner::XFAScanner() {
+ fields = new GHash();
+}
+
+XFAScanner::~XFAScanner() {
+ deleteGHash(fields, XFAField);
+}
+
+XFAField *XFAScanner::findField(GString *acroFormFieldName) {
+ return (XFAField *)fields->lookup(acroFormFieldName);
+}
+
+GString *XFAScanner::readXFAStreams(Object *xfaObj) {
+ GString *data = new GString();
+ char buf[4096];
+ int n;
+ if (xfaObj->isStream()) {
+ xfaObj->streamReset();
+ while ((n = xfaObj->getStream()->getBlock(buf, sizeof(buf))) > 0) {
+ data->append(buf, n);
+ }
+ } else if (xfaObj->isArray()) {
+ for (int i = 1; i < xfaObj->arrayGetLength(); i += 2) {
+ Object obj;
+ if (!xfaObj->arrayGet(i, &obj)->isStream()) {
+ error(errSyntaxError, -1, "XFA array element is wrong type");
+ obj.free();
+ delete data;
+ return NULL;
+ }
+ obj.streamReset();
+ while ((n = obj.getStream()->getBlock(buf, sizeof(buf))) > 0) {
+ data->append(buf, n);
+ }
+ obj.free();
+ }
+ } else {
+ error(errSyntaxError, -1, "XFA object is wrong type");
+ return NULL;
+ }
+ return data;
+}
+
+GHash *XFAScanner::scanFormValues(ZxElement *xmlRoot) {
+ GHash *formValues = new GHash(gTrue);
+ ZxElement *formElem = xmlRoot->findFirstChildElement("form");
+ if (formElem) {
+ scanFormNode(formElem, NULL, formValues);
+ }
+ return formValues;
+}
+
+void XFAScanner::scanFormNode(ZxElement *elem, GString *fullName,
+ GHash *formValues) {
+ GHash *fullNameIdx = new GHash();
+ for (ZxNode *node = elem->getFirstChild();
+ node;
+ node = node->getNextChild()) {
+ if (node->isElement("value")) {
+ if (fullName) {
+ ZxNode *child1Node = ((ZxElement *)node)->getFirstChild();
+ if (child1Node && child1Node->isElement()) {
+ ZxNode *child2Node = ((ZxElement *)child1Node)->getFirstChild();
+ if (child2Node && child2Node->isCharData()) {
+ formValues->add(fullName->copy(),
+ ((ZxCharData *)child2Node)->getData()->copy());
+ }
+ }
+ }
+ } else if (node->isElement()) {
+ ZxAttr *nameAttr = ((ZxElement *)node)->findAttr("name");
+ if (nameAttr && (node->isElement("subform") ||
+ node->isElement("field"))) {
+ GString *nodeName = nameAttr->getValue();
+ GString *childFullName;
+ if (fullName) {
+ childFullName = GString::format("{0:t}.{1:t}", fullName, nodeName);
+ } else {
+ childFullName = nodeName->copy();
+ }
+ int idx = fullNameIdx->lookupInt(nodeName);
+ childFullName->appendf("[{0:d}]", idx);
+ fullNameIdx->replace(nodeName, idx + 1);
+ scanFormNode((ZxElement *)node, childFullName, formValues);
+ delete childFullName;
+ } else if (node->isElement("subform")) {
+ scanFormNode((ZxElement *)node, fullName, formValues);
+ }
+ }
+ }
+ delete fullNameIdx;
+}
+
+void XFAScanner::scanNode(ZxElement *elem,
+ GString *parentName, GString *parentFullName,
+ GHash *nameIdx, GHash *fullNameIdx,
+ GString *exclGroupName, ZxElement *dataElem,
+ GHash *formValues) {
+ GString *nodeName = getNodeName(elem);
+ GHash *childNameIdx;
+ if (!nameIdx || nodeName) {
+ childNameIdx = new GHash();
+ } else {
+ childNameIdx = nameIdx;
+ }
+ GString *nodeFullName = getNodeFullName(elem);
+ GHash *childFullNameIdx;
+ if (!fullNameIdx || (nodeFullName && !elem->isElement("area"))) {
+ childFullNameIdx = new GHash();
+ } else {
+ childFullNameIdx = fullNameIdx;
+ }
+
+ GString *childName;
+ if (nodeName) {
+ if (parentName) {
+ childName = GString::format("{0:t}.{1:t}", parentName, nodeName);
+ } else {
+ childName = nodeName->copy();
+ }
+ int idx = nameIdx->lookupInt(nodeName);
+ nameIdx->replace(nodeName, idx + 1);
+ if (nodeIsBindGlobal(elem)) {
+ childName->appendf("[0]");
+ } else {
+ childName->appendf("[{0:d}]", idx);
+ }
+ } else {
+ childName = parentName;
+ }
+ GString *childFullName;
+ if (nodeFullName) {
+ if (parentFullName) {
+ childFullName = GString::format("{0:t}.{1:t}",
+ parentFullName, nodeFullName);
+ } else {
+ childFullName = nodeFullName->copy();
+ }
+ int idx = fullNameIdx->lookupInt(nodeFullName);
+ fullNameIdx->replace(nodeFullName, idx + 1);
+ childFullName->appendf("[{0:d}]", idx);
+ } else {
+ childFullName = parentFullName;
+ }
+
+ if (elem->isElement("field")) {
+ scanField(elem, childName, childFullName, exclGroupName,
+ dataElem, formValues);
+ } else {
+ GString *childExclGroupName;
+ if (elem->isElement("exclGroup")) {
+ childExclGroupName = childName;
+ } else {
+ childExclGroupName = NULL;
+ }
+ for (ZxNode *child = elem->getFirstChild();
+ child;
+ child = child->getNextChild()) {
+ if (child->isElement()) {
+ scanNode((ZxElement *)child, childName, childFullName,
+ childNameIdx, childFullNameIdx, childExclGroupName,
+ dataElem, formValues);
+ }
+ }
+ }
+
+ if (childName != parentName) {
+ delete childName;
+ }
+ if (childFullName != parentFullName) {
+ delete childFullName;
+ }
+ if (childNameIdx != nameIdx) {
+ delete childNameIdx;
+ }
+ if (childFullNameIdx != fullNameIdx) {
+ delete childFullNameIdx;
+ }
+}
+
+void XFAScanner::scanField(ZxElement *elem, GString *name, GString *fullName,
+ GString *exclGroupName, ZxElement *dataElem,
+ GHash *formValues) {
+ GString *value = getFieldValue(elem, name, fullName, exclGroupName,
+ dataElem, formValues);
+ XFAFieldLayoutInfo *layoutInfo = getFieldLayoutInfo(elem);
+ XFAFieldPictureInfo *pictureInfo = getFieldPictureInfo(elem);
+ XFAFieldBarcodeInfo *barcodeInfo = getFieldBarcodeInfo(elem);
+ XFAField *field = new XFAField(name->copy(), fullName->copy(), value,
+ layoutInfo, pictureInfo, barcodeInfo);
+ fields->add(field->fullName, field);
+}
+
+GString *XFAScanner::getFieldValue(ZxElement *elem, GString *name,
+ GString *fullName, GString *exclGroupName,
+ ZxElement *dataElem, GHash *formValues) {
+ GString *val = NULL;
+
+ //--- check the <xfa:datasets> packet
+ val = getDatasetsValue(name->getCString(), dataElem);
+ if (!val && exclGroupName) {
+ val = (GString *)getDatasetsValue(exclGroupName->getCString(), dataElem);
+ }
+
+ //--- check the <form> element
+ if (!val) {
+ val = (GString *)formValues->lookup(fullName);
+ }
+
+ //--- check the <value> element within the field
+ if (!val) {
+ ZxElement *valueElem = elem->findFirstChildElement("value");
+ if (valueElem) {
+ ZxNode *child1Node = valueElem->getFirstChild();
+ if (child1Node && child1Node->isElement()) {
+ ZxNode *child2Node = ((ZxElement *)child1Node)->getFirstChild();
+ if (child2Node && child2Node->isCharData()) {
+ val = ((ZxCharData *)child2Node)->getData();
+ }
+ }
+ }
+ }
+
+ //--- get the checkbutton item value
+ GString *checkbuttonItem = NULL;
+ ZxElement *uiElem = elem->findFirstChildElement("ui");
+ if (uiElem) {
+ ZxNode *uiChild = uiElem->getFirstChild();
+ if (uiChild && uiChild->isElement("checkButton")) {
+ ZxElement *itemsElem = elem->findFirstChildElement("items");
+ if (itemsElem) {
+ ZxNode *node1 = itemsElem->getFirstChild();
+ if (node1 && node1->isElement()) {
+ ZxNode *node2 = ((ZxElement *)node1)->getFirstChild();
+ if (node2 && node2->isCharData()) {
+ checkbuttonItem = ((ZxCharData *)node2)->getData();
+ }
+ }
+ }
+ }
+ }
+ // convert XFA checkbutton value to AcroForm-style On/Off value
+ if (checkbuttonItem && val) {
+ if (val->cmp(checkbuttonItem)) {
+ val = new GString("Off");
+ } else {
+ val = new GString("On");
+ }
+ } else if (val) {
+ val = val->copy();
+ }
+
+ return val;
+}
+
+GString *XFAScanner::getDatasetsValue(char *partName, ZxElement *elem) {
+ if (!elem) {
+ return NULL;
+ }
+
+ // partName = xxxx[nn].yyyy----
+ char *p = strchr(partName, '[');
+ if (!p) {
+ return NULL;
+ }
+ int partLen = (int)(p - partName);
+ int idx = atoi(p + 1);
+ p = strchr(p + 1, '.');
+ if (p) {
+ ++p;
+ }
+
+ int curIdx = 0;
+ for (ZxNode *node = elem->getFirstChild();
+ node;
+ node = node->getNextChild()) {
+ if (!node->isElement()) {
+ continue;
+ }
+ GString *nodeName = ((ZxElement *)node)->getType();
+ if (nodeName->getLength() != partLen ||
+ strncmp(nodeName->getCString(), partName, partLen)) {
+ continue;
+ }
+ if (curIdx != idx) {
+ ++curIdx;
+ continue;
+ }
+ if (p) {
+ GString *val = getDatasetsValue(p, (ZxElement *)node);
+ if (val) {
+ return val;
+ }
+ break;
+ } else {
+ ZxNode *child = ((ZxElement *)node)->getFirstChild();
+ if (!child || !child->isCharData()) {
+ return NULL;
+ }
+ return ((ZxCharData *)child)->getData();
+ }
+ }
+
+ // search for an 'ancestor match'
+ if (p) {
+ return getDatasetsValue(p, elem);
+ }
+
+ return NULL;
+}
+
+XFAFieldLayoutInfo *XFAScanner::getFieldLayoutInfo(ZxElement *elem) {
+ ZxElement *paraElem = elem->findFirstChildElement("para");
+ if (!paraElem) {
+ return NULL;
+ }
+ XFAFieldLayoutHAlign hAlign = xfaFieldLayoutHAlignLeft;
+ ZxAttr *hAlignAttr = paraElem->findAttr("hAlign");
+ if (hAlignAttr) {
+ if (!hAlignAttr->getValue()->cmp("left")) {
+ hAlign = xfaFieldLayoutHAlignLeft;
+ } else if (!hAlignAttr->getValue()->cmp("center")) {
+ hAlign = xfaFieldLayoutHAlignCenter;
+ } else if (!hAlignAttr->getValue()->cmp("right")) {
+ hAlign = xfaFieldLayoutHAlignRight;
+ }
+ }
+ XFAFieldLayoutVAlign vAlign = xfaFieldLayoutVAlignTop;
+ ZxAttr *vAlignAttr = paraElem->findAttr("vAlign");
+ if (vAlignAttr) {
+ if (!vAlignAttr->getValue()->cmp("top")) {
+ vAlign = xfaFieldLayoutVAlignTop;
+ } else if (!vAlignAttr->getValue()->cmp("middle")) {
+ vAlign = xfaFieldLayoutVAlignMiddle;
+ } else if (!vAlignAttr->getValue()->cmp("bottom")) {
+ vAlign = xfaFieldLayoutVAlignBottom;
+ }
+ }
+ return new XFAFieldLayoutInfo(hAlign, vAlign);
+}
+
+XFAFieldPictureInfo *XFAScanner::getFieldPictureInfo(ZxElement *elem) {
+ ZxElement *uiElem = elem->findFirstChildElement("ui");
+ if (!uiElem) {
+ return NULL;
+ }
+ XFAFieldPictureSubtype subtype;
+ if (uiElem->findFirstChildElement("dateTimeEdit")) {
+ subtype = xfaFieldPictureDateTime;
+ } else if (uiElem->findFirstChildElement("numericEdit")) {
+ subtype = xfaFieldPictureNumeric;
+ } else if (uiElem->findFirstChildElement("textEdit")) {
+ subtype = xfaFieldPictureText;
+ } else {
+ return NULL;
+ }
+
+ ZxElement *formatElem, *pictureElem;
+ ZxNode *pictureChildNode;
+ if (!(formatElem = elem->findFirstChildElement("format")) ||
+ !(pictureElem = formatElem->findFirstChildElement("picture")) ||
+ !(pictureChildNode = pictureElem->getFirstChild()) ||
+ !pictureChildNode->isCharData()) {
+ return NULL;
+ }
+ GString *format = ((ZxCharData *)pictureChildNode)->getData()->copy();
+
+ return new XFAFieldPictureInfo(subtype, format);
+}
+
+XFAFieldBarcodeInfo *XFAScanner::getFieldBarcodeInfo(ZxElement *elem) {
+ ZxElement *uiElem, *barcodeElem;
+ if (!(uiElem = elem->findFirstChildElement("ui")) ||
+ !(barcodeElem = uiElem->findFirstChildElement("barcode"))) {
+ return NULL;
+ }
+
+ ZxAttr *attr;
+ if (!(attr = barcodeElem->findAttr("type"))) {
+ return NULL;
+ }
+ GString *barcodeType = attr->getValue()->copy();
+
+ double wideNarrowRatio = 3;
+ if ((attr = barcodeElem->findAttr("wideNarrowRatio"))) {
+ char *s = attr->getValue()->getCString();
+ char *colon = strchr(s, ':');
+ if (colon) {
+ GString *numStr = new GString(s, (int)(colon - s));
+ double num = atof(numStr->getCString());
+ delete numStr;
+ double den = atof(colon + 1);
+ if (den == 0) {
+ wideNarrowRatio = num;
+ } else {
+ wideNarrowRatio = num / den;
+ }
+ } else {
+ wideNarrowRatio = atof(s);
+ }
+ }
+
+ double moduleWidth = (0.25 / 25.4) * 72.0; // 0.25mm
+ if ((attr = barcodeElem->findAttr("moduleWidth"))) {
+ moduleWidth = getMeasurement(attr->getValue());
+ }
+
+ double moduleHeight = (5.0 / 25.4) * 72.0; // 5mm
+ if ((attr = barcodeElem->findAttr("moduleHeight"))) {
+ moduleHeight = getMeasurement(attr->getValue());
+ }
+
+ int dataLength = 0;
+ if ((attr = barcodeElem->findAttr("dataLength"))) {
+ dataLength = atoi(attr->getValue()->getCString());
+ }
+
+ int errorCorrectionLevel = 0;
+ if ((attr = barcodeElem->findAttr("errorCorrectionLevel"))) {
+ errorCorrectionLevel = atoi(attr->getValue()->getCString());
+ }
+
+ GString *textLocation;
+ if ((attr = barcodeElem->findAttr("textLocation"))) {
+ textLocation = attr->getValue()->copy();
+ } else {
+ textLocation = new GString("below");
+ }
+
+ return new XFAFieldBarcodeInfo(barcodeType, wideNarrowRatio,
+ moduleWidth, moduleHeight, dataLength,
+ errorCorrectionLevel, textLocation);
+}
+
+double XFAScanner::getMeasurement(GString *s) {
+ int i = 0;
+ GBool neg = gFalse;
+ if (i < s->getLength() && s->getChar(i) == '+') {
+ ++i;
+ } else if (i < s->getLength() && s->getChar(i) == '-') {
+ neg = gTrue;
+ ++i;
+ }
+ double val = 0;
+ while (i < s->getLength() && s->getChar(i) >= '0' && s->getChar(i) <= '9') {
+ val = val * 10 + s->getChar(i) - '0';
+ ++i;
+ }
+ if (i < s->getLength() && s->getChar(i) == '.') {
+ ++i;
+ double mul = 0.1;
+ while (i < s->getLength() &&
+ s->getChar(i) >= '0' && s->getChar(i) <= '9') {
+ val += mul * (s->getChar(i) - '0');
+ mul *= 0.1;
+ ++i;
+ }
+ }
+ if (neg) {
+ val = -val;
+ }
+ if (i+1 < s->getLength()) {
+ if (s->getChar(i) == 'i' && s->getChar(i+1) == 'n') {
+ val *= 72;
+ } else if (s->getChar(i) == 'p' && s->getChar(i+1) == 't') {
+ // no change
+ } else if (s->getChar(i) == 'c' && s->getChar(i+1) == 'm') {
+ val *= 72 / 2.54;
+ } else if (s->getChar(i) == 'm' && s->getChar(i+1) == 'm') {
+ val *= 72 / 25.4;
+ } else {
+ // default to inches
+ val *= 72;
+ }
+ } else {
+ // default to inches
+ val *= 72;
+ }
+ return val;
+}
+
+GString *XFAScanner::getNodeName(ZxElement *elem) {
+ if (elem->isElement("template") ||
+ elem->isElement("area") ||
+ elem->isElement("draw")) {
+ return NULL;
+ }
+ if (!elem->isElement("field") && nodeIsBindNone(elem)) {
+ return NULL;
+ }
+ ZxAttr *nameAttr = elem->findAttr("name");
+ if (!nameAttr) {
+ return NULL;
+ }
+ return nameAttr->getValue();
+}
+
+GString *XFAScanner::getNodeFullName(ZxElement *elem) {
+ if (elem->isElement("template") ||
+ elem->isElement("draw")) {
+ return NULL;
+ }
+ ZxAttr *nameAttr = elem->findAttr("name");
+ if (!nameAttr) {
+ return NULL;
+ }
+ return nameAttr->getValue();
+}
+
+GBool XFAScanner::nodeIsBindGlobal(ZxElement *elem) {
+ ZxElement *bindElem = elem->findFirstChildElement("bind");
+ if (!bindElem) {
+ return gFalse;
+ }
+ ZxAttr *attr = bindElem->findAttr("match");
+ return attr && !attr->getValue()->cmp("global");
+}
+
+GBool XFAScanner::nodeIsBindNone(ZxElement *elem) {
+ ZxElement *bindElem = elem->findFirstChildElement("bind");
+ if (!bindElem) {
+ return gFalse;
+ }
+ ZxAttr *attr = bindElem->findAttr("match");
+ return attr && !attr->getValue()->cmp("none");
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h
new file mode 100644
index 00000000000..00ee6f7fcee
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XFAScanner.h
@@ -0,0 +1,166 @@
+//========================================================================
+//
+// XFAScanner.h
+//
+// Copyright 2020 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef XFASCANNER_H
+#define XFASCANNER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class GHash;
+class ZxElement;
+
+//------------------------------------------------------------------------
+
+enum XFAFieldLayoutHAlign {
+ xfaFieldLayoutHAlignLeft,
+ xfaFieldLayoutHAlignCenter,
+ xfaFieldLayoutHAlignRight
+};
+
+enum XFAFieldLayoutVAlign {
+ xfaFieldLayoutVAlignTop,
+ xfaFieldLayoutVAlignMiddle,
+ xfaFieldLayoutVAlignBottom
+};
+
+class XFAFieldLayoutInfo {
+public:
+
+ XFAFieldLayoutInfo(XFAFieldLayoutHAlign hAlignA,
+ XFAFieldLayoutVAlign vAlignA);
+
+ XFAFieldLayoutHAlign hAlign;
+ XFAFieldLayoutVAlign vAlign;
+};
+
+//------------------------------------------------------------------------
+
+enum XFAFieldPictureSubtype {
+ xfaFieldPictureDateTime,
+ xfaFieldPictureNumeric,
+ xfaFieldPictureText
+};
+
+class XFAFieldPictureInfo {
+public:
+
+ XFAFieldPictureInfo(XFAFieldPictureSubtype subtypeA, GString *formatA);
+ ~XFAFieldPictureInfo();
+
+ XFAFieldPictureSubtype subtype;
+ GString *format; // picture format string
+};
+
+//------------------------------------------------------------------------
+
+class XFAFieldBarcodeInfo {
+public:
+
+ XFAFieldBarcodeInfo(GString *barcodeTypeA, double wideNarrowRatioA,
+ double moduleWidthA, double moduleHeightA,
+ int dataLengthA, int errorCorrectionLevelA,
+ GString *textLocationA);
+ ~XFAFieldBarcodeInfo();
+
+ GString *barcodeType;
+ double wideNarrowRatio;
+ double moduleWidth;
+ double moduleHeight;
+ int dataLength;
+ int errorCorrectionLevel;
+ GString *textLocation;
+};
+
+//------------------------------------------------------------------------
+
+class XFAField {
+public:
+
+ XFAField(GString *nameA, GString *fullNameA, GString *valueA,
+ XFAFieldLayoutInfo *layoutInfoA,
+ XFAFieldPictureInfo *pictureInfoA,
+ XFAFieldBarcodeInfo *barcodeInfoA);
+ ~XFAField();
+
+ // Get the field's value, or NULL if it doesn't have a value. Sets
+ // *[length] to the length of the Unicode string.
+ GString *getValue() { return value; }
+
+ // Return a pointer to the field's picture formatting info object,
+ // or NULL if the field doesn't have picture formatting.
+ XFAFieldPictureInfo *getPictureInfo() { return pictureInfo; }
+
+ // Return a pointer to the field's layout info object, or NULL if
+ // the field doesn't have layout info.
+ XFAFieldLayoutInfo *getLayoutInfo() { return layoutInfo; }
+
+ // Return a pointer to the field's barcode info object, or NULL if
+ // the field isn't a barcode.
+ XFAFieldBarcodeInfo *getBarcodeInfo() { return barcodeInfo; }
+
+private:
+
+ friend class XFAScanner;
+
+ GString *name; // UTF-8
+ GString *fullName; // UTF-8
+ GString *value; // UTF-8
+ XFAFieldLayoutInfo *layoutInfo;
+ XFAFieldPictureInfo *pictureInfo;
+ XFAFieldBarcodeInfo *barcodeInfo;
+};
+
+//------------------------------------------------------------------------
+
+class XFAScanner {
+public:
+
+ static XFAScanner *load(Object *xfaObj);
+
+ virtual ~XFAScanner();
+
+ // Find an XFA field matchined the specified AcroForm field name.
+ // Returns NULL if there is no matching field.
+ XFAField *findField(GString *acroFormFieldName);
+
+private:
+
+ XFAScanner();
+ static GString *readXFAStreams(Object *xfaObj);
+ GHash *scanFormValues(ZxElement *xmlRoot);
+ void scanFormNode(ZxElement *elem, GString *fullName,
+ GHash *formValues);
+ void scanNode(ZxElement *elem,
+ GString *parentName, GString *parentFullName,
+ GHash *nameIdx, GHash *fullNameIdx,
+ GString *exclGroupName, ZxElement *xmlRoot,
+ GHash *formValues);
+ void scanField(ZxElement *elem, GString *name, GString *fullName,
+ GString *exclGroupName, ZxElement *xmlRoot,
+ GHash *formValues);
+ GString *getFieldValue(ZxElement *elem, GString *name,
+ GString *fullName, GString *exclGroupName,
+ ZxElement *xmlRoot, GHash *formValues);
+ GString *getDatasetsValue(char *partName, ZxElement *elem);
+ XFAFieldLayoutInfo *getFieldLayoutInfo(ZxElement *elem);
+ XFAFieldPictureInfo *getFieldPictureInfo(ZxElement *elem);
+ XFAFieldBarcodeInfo *getFieldBarcodeInfo(ZxElement *elem);
+ double getMeasurement(GString *s);
+ GString *getNodeName(ZxElement *elem);
+ GString *getNodeFullName(ZxElement *elem);
+ GBool nodeIsBindGlobal(ZxElement *elem);
+ GBool nodeIsBindNone(ZxElement *elem);
+
+ GHash *fields; // [XFAField]
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc
new file mode 100644
index 00000000000..0c364638b39
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.cc
@@ -0,0 +1,1404 @@
+//========================================================================
+//
+// XRef.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "gfile.h"
+#include "Object.h"
+#include "Stream.h"
+#include "Lexer.h"
+#include "Parser.h"
+#include "Dict.h"
+#include "Error.h"
+#include "ErrorCodes.h"
+#include "XRef.h"
+
+//------------------------------------------------------------------------
+
+#define xrefSearchSize 1024 // read this many bytes at end of file
+ // to look for 'startxref'
+
+//------------------------------------------------------------------------
+// Permission bits
+//------------------------------------------------------------------------
+
+#define permPrint (1<<2)
+#define permChange (1<<3)
+#define permCopy (1<<4)
+#define permNotes (1<<5)
+#define defPermFlags 0xfffc
+
+//------------------------------------------------------------------------
+// XRefPosSet
+//------------------------------------------------------------------------
+
+class XRefPosSet {
+public:
+
+ XRefPosSet();
+ ~XRefPosSet();
+ void add(GFileOffset pos);
+ GBool check(GFileOffset pos);
+ int getLength() { return len; }
+ GFileOffset get(int idx) { return tab[idx]; }
+
+private:
+
+ int find(GFileOffset pos);
+
+ GFileOffset *tab;
+ int size;
+ int len;
+};
+
+XRefPosSet::XRefPosSet() {
+ size = 16;
+ len = 0;
+ tab = (GFileOffset *)gmallocn(size, sizeof(GFileOffset));
+}
+
+XRefPosSet::~XRefPosSet() {
+ gfree(tab);
+}
+
+void XRefPosSet::add(GFileOffset pos) {
+ int i;
+
+ i = find(pos);
+ if (i < len && tab[i] == pos) {
+ return;
+ }
+ if (len == size) {
+ if (size > INT_MAX / 2) {
+ gMemError("Integer overflow in XRefPosSet::add()");
+ }
+ size *= 2;
+ tab = (GFileOffset *)greallocn(tab, size, sizeof(GFileOffset));
+ }
+ if (i < len) {
+ memmove(&tab[i + 1], &tab[i], (len - i) * sizeof(GFileOffset));
+ }
+ tab[i] = pos;
+ ++len;
+}
+
+GBool XRefPosSet::check(GFileOffset pos) {
+ int i;
+
+ i = find(pos);
+ return i < len && tab[i] == pos;
+}
+
+int XRefPosSet::find(GFileOffset pos) {
+ int a, b, m;
+
+ a = - 1;
+ b = len;
+ // invariant: tab[a] < pos < tab[b]
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (tab[m] < pos) {
+ a = m;
+ } else if (tab[m] > pos) {
+ b = m;
+ } else {
+ return m;
+ }
+ }
+ return b;
+}
+
+//------------------------------------------------------------------------
+// ObjectStream
+//------------------------------------------------------------------------
+
+class ObjectStream {
+public:
+
+ // Create an object stream, using object number <objStrNum>,
+ // generation 0.
+ ObjectStream(XRef *xref, int objStrNumA);
+
+ GBool isOk() { return ok; }
+
+ ~ObjectStream();
+
+ // Return the object number of this object stream.
+ int getObjStrNum() { return objStrNum; }
+
+ // Get the <objIdx>th object from this stream, which should be
+ // object number <objNum>, generation 0.
+ Object *getObject(int objIdx, int objNum, Object *obj);
+
+private:
+
+ int objStrNum; // object number of the object stream
+ int nObjects; // number of objects in the stream
+ Object *objs; // the objects (length = nObjects)
+ int *objNums; // the object numbers (length = nObjects)
+ GBool ok;
+};
+
+ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
+ Stream *str;
+ Lexer *lexer;
+ Parser *parser;
+ int *offsets;
+ Object objStr, obj1, obj2;
+ int first, i;
+
+ objStrNum = objStrNumA;
+ nObjects = 0;
+ objs = NULL;
+ objNums = NULL;
+ ok = gFalse;
+
+ if (!xref->fetch(objStrNum, 0, &objStr)->isStream()) {
+ goto err1;
+ }
+
+ if (!objStr.streamGetDict()->lookup("N", &obj1)->isInt()) {
+ obj1.free();
+ goto err1;
+ }
+ nObjects = obj1.getInt();
+ obj1.free();
+ if (nObjects <= 0) {
+ goto err1;
+ }
+
+ if (!objStr.streamGetDict()->lookup("First", &obj1)->isInt()) {
+ obj1.free();
+ goto err1;
+ }
+ first = obj1.getInt();
+ obj1.free();
+ if (first < 0) {
+ goto err1;
+ }
+
+ // this is an arbitrary limit to avoid integer overflow problems
+ // in the 'new Object[nObjects]' call (Acrobat apparently limits
+ // object streams to 100-200 objects)
+ if (nObjects > 1000000) {
+ error(errSyntaxError, -1, "Too many objects in an object stream");
+ goto err1;
+ }
+ objs = new Object[nObjects];
+ objNums = (int *)gmallocn(nObjects, sizeof(int));
+ offsets = (int *)gmallocn(nObjects, sizeof(int));
+
+ // parse the header: object numbers and offsets
+ objStr.streamReset();
+ obj1.initNull();
+ str = new EmbedStream(objStr.getStream(), &obj1, gTrue, first);
+ lexer = new Lexer(xref, str);
+ parser = new Parser(xref, lexer, gFalse);
+ for (i = 0; i < nObjects; ++i) {
+ parser->getObj(&obj1, gTrue);
+ parser->getObj(&obj2, gTrue);
+ if (!obj1.isInt() || !obj2.isInt()) {
+ obj1.free();
+ obj2.free();
+ delete parser;
+ gfree(offsets);
+ goto err2;
+ }
+ objNums[i] = obj1.getInt();
+ offsets[i] = obj2.getInt();
+ obj1.free();
+ obj2.free();
+ if (objNums[i] < 0 || offsets[i] < 0 ||
+ (i > 0 && offsets[i] < offsets[i-1])) {
+ delete parser;
+ gfree(offsets);
+ goto err2;
+ }
+ }
+ lexer->skipToEOF();
+ delete parser;
+
+ // skip to the first object - this shouldn't be necessary because
+ // the First key is supposed to be equal to offsets[0], but just in
+ // case...
+ if (first < offsets[0]) {
+ objStr.getStream()->discardChars(offsets[0] - first);
+ }
+
+ // parse the objects
+ for (i = 0; i < nObjects; ++i) {
+ obj1.initNull();
+ if (i == nObjects - 1) {
+ str = new EmbedStream(objStr.getStream(), &obj1, gFalse, 0);
+ } else {
+ str = new EmbedStream(objStr.getStream(), &obj1, gTrue,
+ offsets[i+1] - offsets[i]);
+ }
+ lexer = new Lexer(xref, str);
+ parser = new Parser(xref, lexer, gFalse);
+ parser->getObj(&objs[i]);
+ lexer->skipToEOF();
+ delete parser;
+ }
+
+ gfree(offsets);
+ ok = gTrue;
+
+ err2:
+ objStr.streamClose();
+ err1:
+ objStr.free();
+}
+
+ObjectStream::~ObjectStream() {
+ int i;
+
+ if (objs) {
+ for (i = 0; i < nObjects; ++i) {
+ objs[i].free();
+ }
+ delete[] objs;
+ }
+ gfree(objNums);
+}
+
+Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
+ if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) {
+ obj->initNull();
+ } else {
+ objs[objIdx].copy(obj);
+ }
+ return obj;
+}
+
+//------------------------------------------------------------------------
+// XRef
+//------------------------------------------------------------------------
+
+XRef::XRef(BaseStream *strA, GBool repair) {
+ GFileOffset pos;
+ Object obj;
+ XRefPosSet *posSet;
+ int i;
+
+ ok = gTrue;
+ errCode = errNone;
+ size = 0;
+ last = -1;
+ entries = NULL;
+ lastStartxrefPos = 0;
+ xrefTablePos = NULL;
+ xrefTablePosLen = 0;
+ streamEnds = NULL;
+ streamEndsLen = 0;
+ for (i = 0; i < objStrCacheSize; ++i) {
+ objStrs[i] = NULL;
+ objStrLastUse[i] = 0;
+ }
+ objStrCacheLength = 0;
+ objStrTime = 0;
+
+ encrypted = gFalse;
+ permFlags = defPermFlags;
+ ownerPasswordOk = gFalse;
+
+ for (i = 0; i < xrefCacheSize; ++i) {
+ cache[i].num = -1;
+ }
+
+#if MULTITHREADED
+ gInitMutex(&objStrsMutex);
+ gInitMutex(&cacheMutex);
+#endif
+
+ str = strA;
+ start = str->getStart();
+
+ // if the 'repair' flag is set, try to reconstruct the xref table
+ if (repair) {
+ if (!(ok = constructXRef())) {
+ errCode = errDamaged;
+ return;
+ }
+
+ // if the 'repair' flag is not set, read the xref table
+ } else {
+
+ // read the trailer
+ pos = getStartXref();
+ if (pos == 0) {
+ errCode = errDamaged;
+ ok = gFalse;
+ return;
+ }
+
+ // read the xref table
+ posSet = new XRefPosSet();
+ while (readXRef(&pos, posSet, gFalse)) ;
+ xrefTablePosLen = posSet->getLength();
+ xrefTablePos = (GFileOffset *)gmallocn(xrefTablePosLen,
+ sizeof(GFileOffset));
+ for (i = 0; i < xrefTablePosLen; ++i) {
+ xrefTablePos[i] = posSet->get(i);
+ }
+ delete posSet;
+ if (!ok) {
+ errCode = errDamaged;
+ return;
+ }
+ }
+
+ // get the root dictionary (catalog) object
+ trailerDict.dictLookupNF("Root", &obj);
+ if (obj.isRef()) {
+ rootNum = obj.getRefNum();
+ rootGen = obj.getRefGen();
+ obj.free();
+ } else {
+ obj.free();
+ if (!(ok = constructXRef())) {
+ errCode = errDamaged;
+ return;
+ }
+ }
+
+ // now set the trailer dictionary's xref pointer so we can fetch
+ // indirect objects from it
+ trailerDict.getDict()->setXRef(this);
+}
+
+XRef::~XRef() {
+ int i;
+
+ for (i = 0; i < xrefCacheSize; ++i) {
+ if (cache[i].num >= 0) {
+ cache[i].obj.free();
+ }
+ }
+ gfree(entries);
+ trailerDict.free();
+ if (xrefTablePos) {
+ gfree(xrefTablePos);
+ }
+ if (streamEnds) {
+ gfree(streamEnds);
+ }
+ for (i = 0; i < objStrCacheSize; ++i) {
+ if (objStrs[i]) {
+ delete objStrs[i];
+ }
+ }
+#if MULTITHREADED
+ gDestroyMutex(&objStrsMutex);
+ gDestroyMutex(&cacheMutex);
+#endif
+}
+
+// Read the 'startxref' position.
+GFileOffset XRef::getStartXref() {
+ char buf[xrefSearchSize+1];
+ char *p;
+ int n, i;
+
+ // read last xrefSearchSize bytes
+ str->setPos(xrefSearchSize, -1);
+ n = str->getBlock(buf, xrefSearchSize);
+ buf[n] = '\0';
+
+ // find startxref
+ for (i = n - 9; i >= 0; --i) {
+ if (!strncmp(&buf[i], "startxref", 9)) {
+ break;
+ }
+ }
+ if (i < 0) {
+ return 0;
+ }
+ for (p = &buf[i+9]; isspace(*p & 0xff); ++p) ;
+ lastXRefPos = strToFileOffset(p);
+ lastStartxrefPos = str->getPos() - n + i;
+
+ return lastXRefPos;
+}
+
+// Read one xref table section. Also reads the associated trailer
+// 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;
+ char buf[100];
+ int n, i;
+
+ // check for a loop in the xref tables
+ if (posSet->check(*pos)) {
+ error(errSyntaxWarning, -1, "Infinite loop in xref table");
+ return gFalse;
+ }
+ posSet->add(*pos);
+
+ // the xref data should either be "xref ..." (for an xref table) or
+ // "nn gg obj << ... >> stream ..." (for an xref stream); possibly
+ // preceded by whitespace
+ str->setPos(start + *pos);
+ n = str->getBlock(buf, 100);
+ for (i = 0; i < n && Lexer::isSpace(buf[i]); ++i) ;
+
+ // parse an old-style xref table
+ 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 {
+ obj.initNull();
+ parser = new Parser(NULL,
+ new Lexer(NULL,
+ str->makeSubStream(start + *pos, gFalse, 0, &obj)),
+ gTrue);
+ if (!parser->getObj(&obj, gTrue)->isInt()) {
+ goto err;
+ }
+ obj.free();
+ if (!parser->getObj(&obj, gTrue)->isInt()) {
+ goto err;
+ }
+ obj.free();
+ if (!parser->getObj(&obj, gTrue)->isCmd("obj")) {
+ goto err;
+ }
+ obj.free();
+ if (!parser->getObj(&obj)->isStream()) {
+ goto err;
+ }
+ more = readXRefStream(obj.getStream(), pos, hybrid);
+ obj.free();
+ delete parser;
+ }
+
+ return more;
+
+ err:
+ obj.free();
+ delete parser;
+ ok = gFalse;
+ return gFalse;
+}
+
+GBool XRef::readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet) {
+ XRefEntry entry;
+ Parser *parser;
+ Object obj, obj2;
+ char buf[6];
+ GFileOffset off, pos2;
+ GBool more;
+ int first, n, newSize, gen, i, c;
+
+ str->setPos(start + *pos + offset);
+
+ while (1) {
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ if (c == 't') {
+ if (str->getBlock(buf, 6) != 6 || memcmp(buf, "railer", 6)) {
+ goto err1;
+ }
+ break;
+ }
+ if (c < '0' || c > '9') {
+ goto err1;
+ }
+ first = 0;
+ do {
+ first = (first * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
+ goto err1;
+ }
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ n = 0;
+ do {
+ n = (n * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
+ goto err1;
+ }
+ if (first < 0 || n < 0 || first > INT_MAX - n) {
+ goto err1;
+ }
+ if (first + n > size) {
+ for (newSize = size ? 2 * size : 1024;
+ first + n > newSize && newSize > 0;
+ newSize <<= 1) ;
+ if (newSize < 0) {
+ goto err1;
+ }
+ entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
+ for (i = size; i < newSize; ++i) {
+ entries[i].offset = (GFileOffset)-1;
+ entries[i].type = xrefEntryFree;
+ }
+ size = newSize;
+ }
+ for (i = first; i < first + n; ++i) {
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ off = 0;
+ do {
+ off = (off * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
+ goto err1;
+ }
+ entry.offset = off;
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ gen = 0;
+ do {
+ gen = (gen * 10) + (c - '0');
+ c = str->getChar();
+ } while (c >= '0' && c <= '9');
+ if (!Lexer::isSpace(c)) {
+ goto err1;
+ }
+ entry.gen = gen;
+ do {
+ c = str->getChar();
+ } while (Lexer::isSpace(c));
+ if (c == 'n') {
+ entry.type = xrefEntryUncompressed;
+ } else if (c == 'f') {
+ entry.type = xrefEntryFree;
+ } else {
+ goto err1;
+ }
+ c = str->getChar();
+ if (!Lexer::isSpace(c)) {
+ goto err1;
+ }
+ if (entries[i].offset == (GFileOffset)-1) {
+ entries[i] = entry;
+ // PDF files of patents from the IBM Intellectual Property
+ // Network have a bug: the xref table claims to start at 1
+ // instead of 0.
+ if (i == 1 && first == 1 &&
+ entries[1].offset == 0 && entries[1].gen == 65535 &&
+ entries[1].type == xrefEntryFree) {
+ i = first = 0;
+ entries[0] = entries[1];
+ entries[1].offset = (GFileOffset)-1;
+ }
+ if (i > last) {
+ last = i;
+ }
+ }
+ }
+ }
+
+ // read the trailer dictionary
+ obj.initNull();
+ parser = new Parser(NULL,
+ new Lexer(NULL,
+ str->makeSubStream(str->getPos(), gFalse, 0, &obj)),
+ gTrue);
+ parser->getObj(&obj);
+ delete parser;
+ if (!obj.isDict()) {
+ obj.free();
+ goto err1;
+ }
+
+ // get the 'Prev' pointer
+ //~ this can be a 64-bit int (?)
+ obj.getDict()->lookupNF("Prev", &obj2);
+ if (obj2.isInt()) {
+ *pos = (GFileOffset)(Guint)obj2.getInt();
+ more = gTrue;
+ } else if (obj2.isRef()) {
+ // certain buggy PDF generators generate "/Prev NNN 0 R" instead
+ // of "/Prev NNN"
+ *pos = (GFileOffset)(Guint)obj2.getRefNum();
+ more = gTrue;
+ } else {
+ more = gFalse;
+ }
+ obj2.free();
+
+ // save the first trailer dictionary
+ if (trailerDict.isNone()) {
+ obj.copy(&trailerDict);
+ }
+
+ // check for an 'XRefStm' key
+ //~ this can be a 64-bit int (?)
+ if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
+ pos2 = (GFileOffset)(Guint)obj2.getInt();
+ readXRef(&pos2, posSet, gTrue);
+ if (!ok) {
+ obj2.free();
+ obj.free();
+ goto err1;
+ }
+ }
+ obj2.free();
+
+ obj.free();
+ return more;
+
+ err1:
+ ok = gFalse;
+ return gFalse;
+}
+
+GBool XRef::readXRefStream(Stream *xrefStr, GFileOffset *pos, GBool hybrid) {
+ Dict *dict;
+ int w[3];
+ GBool more;
+ Object obj, obj2, idx;
+ int newSize, first, n, i;
+
+ dict = xrefStr->getDict();
+
+ if (!dict->lookupNF("Size", &obj)->isInt()) {
+ goto err1;
+ }
+ newSize = obj.getInt();
+ obj.free();
+ if (newSize < 0) {
+ goto err1;
+ }
+ if (newSize > size) {
+ 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 (!dict->lookupNF("W", &obj)->isArray() ||
+ obj.arrayGetLength() < 3) {
+ goto err1;
+ }
+ for (i = 0; i < 3; ++i) {
+ if (!obj.arrayGet(i, &obj2)->isInt()) {
+ obj2.free();
+ goto err1;
+ }
+ w[i] = obj2.getInt();
+ obj2.free();
+ }
+ obj.free();
+ if (w[0] < 0 || w[0] > 8 ||
+ w[1] < 0 || w[1] > 8 ||
+ w[2] < 0 || w[2] > 8) {
+ goto err0;
+ }
+
+ xrefStr->reset();
+ dict->lookupNF("Index", &idx);
+ if (idx.isArray()) {
+ for (i = 0; i+1 < idx.arrayGetLength(); i += 2) {
+ if (!idx.arrayGet(i, &obj)->isInt()) {
+ idx.free();
+ goto err1;
+ }
+ first = obj.getInt();
+ obj.free();
+ if (!idx.arrayGet(i+1, &obj)->isInt()) {
+ idx.free();
+ goto err1;
+ }
+ n = obj.getInt();
+ obj.free();
+ if (first < 0 || n < 0 ||
+ !readXRefStreamSection(xrefStr, w, first, n)) {
+ idx.free();
+ goto err0;
+ }
+ }
+ } else {
+ if (!readXRefStreamSection(xrefStr, w, 0, newSize)) {
+ idx.free();
+ goto err0;
+ }
+ }
+ idx.free();
+
+ //~ this can be a 64-bit int (?)
+ dict->lookupNF("Prev", &obj);
+ if (obj.isInt()) {
+ *pos = (GFileOffset)(Guint)obj.getInt();
+ more = gTrue;
+ } else {
+ more = gFalse;
+ }
+ obj.free();
+ if (trailerDict.isNone()) {
+ trailerDict.initDict(dict);
+ }
+
+ return more;
+
+ err1:
+ obj.free();
+ err0:
+ ok = gFalse;
+ return gFalse;
+}
+
+GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
+ long long type, gen, offset;
+ int c, newSize, i, j;
+
+ if (first + n < 0) {
+ return gFalse;
+ }
+ if (first + n > size) {
+ for (newSize = size ? 2 * size : 1024;
+ first + n > newSize && newSize > 0;
+ newSize <<= 1) ;
+ if (newSize < 0) {
+ 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;
+ }
+ for (i = first; i < first + n; ++i) {
+ if (w[0] == 0) {
+ type = 1;
+ } else {
+ for (type = 0, j = 0; j < w[0]; ++j) {
+ if ((c = xrefStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ type = (type << 8) + c;
+ }
+ }
+ for (offset = 0, j = 0; j < w[1]; ++j) {
+ if ((c = xrefStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ offset = (offset << 8) + c;
+ }
+ if (offset < 0 || offset > GFILEOFFSET_MAX) {
+ return gFalse;
+ }
+ for (gen = 0, j = 0; j < w[2]; ++j) {
+ if ((c = xrefStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ gen = (gen << 8) + c;
+ }
+ if (gen < 0 || gen > INT_MAX) {
+ return gFalse;
+ }
+ if (entries[i].offset == (GFileOffset)-1) {
+ switch (type) {
+ case 0:
+ entries[i].offset = (GFileOffset)offset;
+ entries[i].gen = (int)gen;
+ entries[i].type = xrefEntryFree;
+ break;
+ case 1:
+ entries[i].offset = (GFileOffset)offset;
+ entries[i].gen = (int)gen;
+ entries[i].type = xrefEntryUncompressed;
+ break;
+ case 2:
+ entries[i].offset = (GFileOffset)offset;
+ entries[i].gen = (int)gen;
+ entries[i].type = xrefEntryCompressed;
+ break;
+ default:
+ return gFalse;
+ }
+ if (i > last) {
+ last = i;
+ }
+ }
+ }
+
+ return gTrue;
+}
+
+// Attempt to construct an xref table for a damaged file.
+GBool XRef::constructXRef() {
+ 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) {
+ 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;
+ }
+ 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;
+ }
+ }
+
+ // read each stream object, check for xref or object stream
+ for (int i = 0; i < streamObjNumsLen; ++i) {
+ Object obj;
+ fetch(streamObjNums[i], entries[streamObjNums[i]].gen, &obj);
+ if (obj.isStream()) {
+ Dict *dict = obj.streamGetDict();
+ Object type;
+ dict->lookup("Type", &type);
+ if (type.isName("XRef")) {
+ saveTrailerDict(dict, gTrue);
+ } else if (type.isName("ObjStm")) {
+ constructObjectStreamEntries(&obj, streamObjNums[i]);
+ }
+ type.free();
+ }
+ obj.free();
+ }
+
+ gfree(streamObjNums);
+
+ 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, gFalse, 0, &obj)),
+ gFalse);
+ parser->getObj(&newTrailerDict);
+ if (newTrailerDict.isDict()) {
+ saveTrailerDict(newTrailerDict.getDict(), gFalse);
+ }
+ newTrailerDict.free();
+ delete parser;
+}
+
+// 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();
+}
+
+// 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);
+ }
+ }
+ obj2.free();
+ obj1.free();
+ }
+ delete parser;
+}
+
+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;
+ }
+
+ 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,
+ Guchar *fileKeyA, int keyLengthA, int encVersionA,
+ CryptAlgorithm encAlgorithmA) {
+ int i;
+
+ encrypted = gTrue;
+ permFlags = permFlagsA;
+ ownerPasswordOk = ownerPasswordOkA;
+ if (keyLengthA <= 32) {
+ keyLength = keyLengthA;
+ } else {
+ keyLength = 32;
+ }
+ for (i = 0; i < keyLength; ++i) {
+ fileKey[i] = fileKeyA[i];
+ }
+ encVersion = encVersionA;
+ encAlgorithm = encAlgorithmA;
+}
+
+GBool XRef::getEncryption(int *permFlagsA, GBool *ownerPasswordOkA,
+ int *keyLengthA, int *encVersionA,
+ CryptAlgorithm *encAlgorithmA) {
+ if (!encrypted) {
+ return gFalse;
+ }
+ *permFlagsA = permFlags;
+ *ownerPasswordOkA = ownerPasswordOk;
+ *keyLengthA = keyLength;
+ *encVersionA = encVersion;
+ *encAlgorithmA = encAlgorithm;
+ return gTrue;
+}
+
+GBool XRef::okToPrint(GBool ignoreOwnerPW) {
+ return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
+}
+
+GBool XRef::okToChange(GBool ignoreOwnerPW) {
+ return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
+}
+
+GBool XRef::okToCopy(GBool ignoreOwnerPW) {
+ return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
+}
+
+GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
+ return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
+}
+
+Object *XRef::fetch(int num, int gen, Object *obj, int recursion) {
+ XRefEntry *e;
+ Parser *parser;
+ Object obj1, obj2, obj3;
+ XRefCacheEntry tmp;
+ int i, j;
+
+ // check for bogus ref - this can happen in corrupted PDF files
+ if (num < 0 || num >= size) {
+ goto err;
+ }
+
+ // check the cache
+#if MULTITHREADED
+ gLockMutex(&cacheMutex);
+#endif
+ if (cache[0].num == num && cache[0].gen == gen) {
+ cache[0].obj.copy(obj);
+#if MULTITHREADED
+ gUnlockMutex(&cacheMutex);
+#endif
+ return obj;
+ }
+ for (i = 1; i < xrefCacheSize; ++i) {
+ if (cache[i].num == num && cache[i].gen == gen) {
+ tmp = cache[i];
+ for (j = i; j > 0; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = tmp;
+ cache[0].obj.copy(obj);
+#if MULTITHREADED
+ gUnlockMutex(&cacheMutex);
+#endif
+ return obj;
+ }
+ }
+#if MULTITHREADED
+ gUnlockMutex(&cacheMutex);
+#endif
+
+ e = &entries[num];
+ switch (e->type) {
+
+ case xrefEntryUncompressed:
+ if (e->gen != gen) {
+ goto err;
+ }
+ obj1.initNull();
+ parser = new Parser(this,
+ new Lexer(this,
+ str->makeSubStream(start + e->offset, gFalse, 0, &obj1)),
+ gTrue);
+ parser->getObj(&obj1, gTrue);
+ parser->getObj(&obj2, gTrue);
+ parser->getObj(&obj3, gTrue);
+ if (!obj1.isInt() || obj1.getInt() != num ||
+ !obj2.isInt() || obj2.getInt() != gen ||
+ !obj3.isCmd("obj")) {
+ obj1.free();
+ obj2.free();
+ obj3.free();
+ delete parser;
+ goto err;
+ }
+ parser->getObj(obj, gFalse, encrypted ? fileKey : (Guchar *)NULL,
+ encAlgorithm, keyLength, num, gen, recursion);
+ obj1.free();
+ obj2.free();
+ obj3.free();
+ delete parser;
+ break;
+
+ case xrefEntryCompressed:
+#if 0 // Adobe apparently ignores the generation number on compressed objects
+ if (gen != 0) {
+ goto err;
+ }
+#endif
+ if (e->offset >= (GFileOffset)size ||
+ entries[e->offset].type != xrefEntryUncompressed) {
+ error(errSyntaxError, -1, "Invalid object stream");
+ goto err;
+ }
+ if (!getObjectStreamObject((int)e->offset, e->gen, num, obj)) {
+ goto err;
+ }
+ break;
+
+ default:
+ goto err;
+ }
+
+ // put the new object in the cache, throwing away the oldest object
+ // currently in the cache
+#if MULTITHREADED
+ gLockMutex(&cacheMutex);
+#endif
+ if (cache[xrefCacheSize - 1].num >= 0) {
+ cache[xrefCacheSize - 1].obj.free();
+ }
+ for (i = xrefCacheSize - 1; i > 0; --i) {
+ cache[i] = cache[i - 1];
+ }
+ cache[0].num = num;
+ cache[0].gen = gen;
+ obj->copy(&cache[0].obj);
+#if MULTITHREADED
+ gUnlockMutex(&cacheMutex);
+#endif
+
+ return obj;
+
+ err:
+ return obj->initNull();
+}
+
+GBool XRef::getObjectStreamObject(int objStrNum, int objIdx,
+ int objNum, Object *obj) {
+ ObjectStream *objStr;
+
+#if MULTITHREADED
+ gLockMutex(&objStrsMutex);
+#endif
+ if (!(objStr = getObjectStream(objStrNum))) {
+#if MULTITHREADED
+ gUnlockMutex(&objStrsMutex);
+#endif
+ return gFalse;
+ }
+ cleanObjectStreamCache();
+ objStr->getObject(objIdx, objNum, obj);
+#if MULTITHREADED
+ gUnlockMutex(&objStrsMutex);
+#endif
+ return gTrue;
+}
+
+// NB: objStrsMutex must be locked when calling this function.
+ObjectStream *XRef::getObjectStream(int objStrNum) {
+ ObjectStream *objStr;
+ int i, j;
+
+ // check the MRU entry in the cache
+ if (objStrs[0] && objStrs[0]->getObjStrNum() == objStrNum) {
+ objStr = objStrs[0];
+ objStrLastUse[0] = objStrTime++;
+ return objStr;
+ }
+
+ // check the rest of the cache
+ for (i = 1; i < objStrCacheLength; ++i) {
+ if (objStrs[i] && objStrs[i]->getObjStrNum() == objStrNum) {
+ objStr = objStrs[i];
+ for (j = i; j > 0; --j) {
+ objStrs[j] = objStrs[j - 1];
+ objStrLastUse[j] = objStrLastUse[j - 1];
+ }
+ objStrs[0] = objStr;
+ objStrLastUse[0] = objStrTime++;
+ return objStr;
+ }
+ }
+
+ // load a new ObjectStream
+ objStr = new ObjectStream(this, objStrNum);
+ if (!objStr->isOk()) {
+ delete objStr;
+ return NULL;
+ }
+
+ // add to the cache
+ if (objStrCacheLength == objStrCacheSize) {
+ delete objStrs[objStrCacheSize - 1];
+ --objStrCacheLength;
+ }
+ for (j = objStrCacheLength; j > 0; --j) {
+ objStrs[j] = objStrs[j - 1];
+ objStrLastUse[j] = objStrLastUse[j - 1];
+ }
+ ++objStrCacheLength;
+ objStrs[0] = objStr;
+ objStrLastUse[0] = objStrTime++;
+
+ return objStr;
+}
+
+// If the oldest (least recently used) entry in the object stream
+// cache is more than objStrCacheTimeout accesses old (hasn't been
+// used in the last objStrCacheTimeout accesses), eject it from the
+// cache.
+void XRef::cleanObjectStreamCache() {
+ // NB: objStrTime and objStrLastUse[] are unsigned ints, so the
+ // mod-2^32 arithmetic makes the subtraction work out, even if the
+ // time wraps around.
+ if (objStrCacheLength > 1 &&
+ objStrTime - objStrLastUse[objStrCacheLength - 1]
+ > objStrCacheTimeout) {
+ delete objStrs[objStrCacheLength - 1];
+ objStrs[objStrCacheLength - 1] = NULL;
+ --objStrCacheLength;
+ }
+}
+
+Object *XRef::getDocInfo(Object *obj) {
+ return trailerDict.dictLookup("Info", obj);
+}
+
+// Added for the pdftex project.
+Object *XRef::getDocInfoNF(Object *obj) {
+ return trailerDict.dictLookupNF("Info", obj);
+}
+
+GBool XRef::getStreamEnd(GFileOffset streamStart, GFileOffset *streamEnd) {
+ int a, b, m;
+
+ if (streamEndsLen == 0 ||
+ streamStart > streamEnds[streamEndsLen - 1]) {
+ return gFalse;
+ }
+
+ a = -1;
+ b = streamEndsLen - 1;
+ // invariant: streamEnds[a] < streamStart <= streamEnds[b]
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (streamStart <= streamEnds[m]) {
+ b = m;
+ } else {
+ a = m;
+ }
+ }
+ *streamEnd = streamEnds[b];
+ return gTrue;
+}
+
+GFileOffset XRef::strToFileOffset(char *s) {
+ GFileOffset x, d;
+ char *p;
+
+ x = 0;
+ for (p = s; *p && isdigit(*p & 0xff); ++p) {
+ d = *p - '0';
+ if (x > (GFILEOFFSET_MAX - d) / 10) {
+ break;
+ }
+ x = 10 * x + d;
+ }
+ return x;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h
new file mode 100644
index 00000000000..00a8644f3a9
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/XRef.h
@@ -0,0 +1,187 @@
+//========================================================================
+//
+// XRef.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef XREF_H
+#define XREF_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "gfile.h"
+#include "Object.h"
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+class Dict;
+class Stream;
+class Parser;
+class ObjectStream;
+class XRefPosSet;
+
+//------------------------------------------------------------------------
+// XRef
+//------------------------------------------------------------------------
+
+enum XRefEntryType {
+ xrefEntryFree,
+ xrefEntryUncompressed,
+ xrefEntryCompressed
+};
+
+struct XRefEntry {
+ GFileOffset offset;
+ int gen;
+ XRefEntryType type;
+};
+
+struct XRefCacheEntry {
+ int num;
+ int gen;
+ Object obj;
+};
+
+#define xrefCacheSize 16
+
+#define objStrCacheSize 128
+#define objStrCacheTimeout 1000
+
+class XRef {
+public:
+
+ // Constructor. Read xref table from stream.
+ XRef(BaseStream *strA, GBool repair);
+
+ // Destructor.
+ ~XRef();
+
+ // Is xref table valid?
+ GBool isOk() { return ok; }
+
+ // Get the error code (if isOk() returns false).
+ int getErrorCode() { return errCode; }
+
+ // Set the encryption parameters.
+ void setEncryption(int permFlagsA, GBool ownerPasswordOkA,
+ Guchar *fileKeyA, int keyLengthA, int encVersionA,
+ CryptAlgorithm encAlgorithmA);
+
+ // Is the file encrypted?
+ GBool isEncrypted() { return encrypted; }
+ GBool getEncryption(int *permFlagsA, GBool *ownerPasswordOkA,
+ int *keyLengthA, int *encVersionA,
+ CryptAlgorithm *encAlgorithmA);
+
+ // Check various permissions.
+ GBool okToPrint(GBool ignoreOwnerPW = gFalse);
+ GBool okToChange(GBool ignoreOwnerPW = gFalse);
+ GBool okToCopy(GBool ignoreOwnerPW = gFalse);
+ GBool okToAddNotes(GBool ignoreOwnerPW = gFalse);
+ int getPermFlags() { return permFlags; }
+
+ // Get catalog object.
+ Object *getCatalog(Object *obj) { return fetch(rootNum, rootGen, obj); }
+
+ // Fetch an indirect reference.
+ Object *fetch(int num, int gen, Object *obj, int recursion = 0);
+
+ // Return the document's Info dictionary (if any).
+ Object *getDocInfo(Object *obj);
+ Object *getDocInfoNF(Object *obj);
+
+ // Return the number of objects in the xref table.
+ int getNumObjects() { return last + 1; }
+
+ // Return the offset of the last xref table.
+ GFileOffset getLastXRefPos() { return lastXRefPos; }
+
+ // Return the offset of the 'startxref' at the end of the file.
+ GFileOffset getLastStartxrefPos() { return lastStartxrefPos; }
+
+ // Return the catalog object reference.
+ int getRootNum() { return rootNum; }
+ int getRootGen() { return rootGen; }
+
+ // Get the xref table positions.
+ int getNumXRefTables() { return xrefTablePosLen; }
+ GFileOffset getXRefTablePos(int idx) { return xrefTablePos[idx]; }
+
+ // Get end position for a stream in a damaged file.
+ // Returns false if unknown or file is not damaged.
+ GBool getStreamEnd(GFileOffset streamStart, GFileOffset *streamEnd);
+
+ // Direct access.
+ int getSize() { return size; }
+ XRefEntry *getEntry(int i) { return &entries[i]; }
+ Object *getTrailerDict() { return &trailerDict; }
+
+private:
+
+ BaseStream *str; // input stream
+ GFileOffset start; // offset in file (to allow for garbage
+ // at beginning of file)
+ XRefEntry *entries; // xref entries
+ int size; // size of <entries> array
+ int last; // last used index in <entries>
+ int rootNum, rootGen; // catalog dict
+ GBool ok; // true if xref table is valid
+ int errCode; // error code (if <ok> is false)
+ Object trailerDict; // trailer dictionary
+ GFileOffset lastXRefPos; // offset of last xref table
+ GFileOffset lastStartxrefPos; // offset of 'startxref' at end of file
+ GFileOffset *xrefTablePos; // positions of all xref tables
+ int xrefTablePosLen; // number of xref table positions
+ GFileOffset *streamEnds; // 'endstream' positions - only used in
+ // damaged files
+ int streamEndsLen; // number of valid entries in streamEnds
+ ObjectStream * // cached object streams
+ objStrs[objStrCacheSize];
+ int objStrCacheLength; // number of valid entries in objStrs[]
+ Guint // time of last use for each obj stream
+ objStrLastUse[objStrCacheSize];
+ Guint objStrTime; // current time for the obj stream cache
+#if MULTITHREADED
+ GMutex objStrsMutex;
+#endif
+ GBool encrypted; // true if file is encrypted
+ int permFlags; // permission bits
+ GBool ownerPasswordOk; // true if owner password is correct
+ Guchar fileKey[32]; // file decryption key
+ int keyLength; // length of key, in bytes
+ int encVersion; // encryption version
+ CryptAlgorithm encAlgorithm; // encryption algorithm
+ XRefCacheEntry // cache of recently accessed objects
+ cache[xrefCacheSize];
+#if MULTITHREADED
+ GMutex cacheMutex;
+#endif
+
+ GFileOffset getStartXref();
+ 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 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);
+ void cleanObjectStreamCache();
+ GFileOffset strToFileOffset(char *s);
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc
new file mode 100644
index 00000000000..9e4fb47900c
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.cc
@@ -0,0 +1,1072 @@
+//========================================================================
+//
+// Zoox.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GString.h"
+#include "GList.h"
+#include "GHash.h"
+#include "Zoox.h"
+
+//~ all of this code assumes the encoding is UTF-8 or ASCII or something
+//~ similar (ISO-8859-*)
+
+//------------------------------------------------------------------------
+
+static char nameStartChar[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // 30
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 50
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 70
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // a0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // b0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // c0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // d0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // e0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // f0
+};
+
+static char nameChar[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 20
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 30
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 50
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 70
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // a0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // b0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // c0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // d0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // e0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // f0
+};
+
+//------------------------------------------------------------------------
+
+ZxNode::ZxNode() {
+ next = NULL;
+ parent = NULL;
+ firstChild = lastChild = NULL;
+}
+
+ZxNode::~ZxNode() {
+ ZxNode *child;
+
+ while (firstChild) {
+ child = firstChild;
+ firstChild = firstChild->next;
+ delete child;
+ }
+}
+
+ZxNode *ZxNode::deleteChild(ZxNode *child) {
+ ZxNode *p1, *p2;
+
+ for (p1 = NULL, p2 = firstChild;
+ p2 && p2 != child;
+ p1 = p2, p2 = p2->next) ;
+ if (!p2) {
+ return NULL;
+ }
+ if (p1) {
+ p1->next = child->next;
+ } else {
+ firstChild = child->next;
+ }
+ child->parent = NULL;
+ child->next = NULL;
+ return child;
+}
+
+void ZxNode::appendChild(ZxNode *child) {
+ ZxNode *p1;
+
+ if (child->parent || child->next) {
+ return;
+ }
+ if (firstChild) {
+ for (p1 = firstChild; p1->next; p1 = p1->next) ;
+ p1->next = child;
+ } else {
+ firstChild = child;
+ }
+ child->parent = this;
+}
+
+void ZxNode::insertChildAfter(ZxNode *child, ZxNode *prev) {
+ if (child->parent || child->next || (prev && prev->parent != this)) {
+ return;
+ }
+ if (prev) {
+ child->next = prev->next;
+ prev->next = child;
+ } else {
+ child->next = firstChild;
+ firstChild = child;
+ }
+ child->parent = this;
+}
+
+ZxElement *ZxNode::findFirstElement(const char *type) {
+ ZxNode *child;
+ ZxElement *result;
+
+ if (isElement(type)) {
+ return (ZxElement *)this;
+ }
+ for (child = firstChild; child; child = child->next) {
+ if ((result = child->findFirstElement(type))) {
+ return result;
+ }
+ }
+ return NULL;
+}
+
+ZxElement *ZxNode::findFirstChildElement(const char *type) {
+ ZxNode *child;
+
+ for (child = firstChild; child; child = child->next) {
+ if (child->isElement(type)) {
+ return (ZxElement *)child;
+ }
+ }
+ return NULL;
+}
+
+GList *ZxNode::findAllElements(const char *type) {
+ GList *results;
+
+ results = new GList();
+ findAllElements(type, results);
+ return results;
+}
+
+void ZxNode::findAllElements(const char *type, GList *results) {
+ ZxNode *child;
+
+ if (isElement(type)) {
+ results->append(this);
+ }
+ for (child = firstChild; child; child = child->next) {
+ child->findAllElements(type, results);
+ }
+}
+
+GList *ZxNode::findAllChildElements(const char *type) {
+ GList *results;
+ ZxNode *child;
+
+ results = new GList();
+ for (child = firstChild; child; child = child->next) {
+ if (child->isElement(type)) {
+ results->append(child);
+ }
+ }
+ return results;
+}
+
+void ZxNode::addChild(ZxNode *child) {
+ if (lastChild) {
+ lastChild->next = child;
+ lastChild = child;
+ } else {
+ firstChild = lastChild = child;
+ }
+ child->parent = this;
+ child->next = NULL;
+}
+
+//------------------------------------------------------------------------
+
+ZxDoc::ZxDoc() {
+ xmlDecl = NULL;
+ docTypeDecl = NULL;
+ root = NULL;
+}
+
+ZxDoc *ZxDoc::loadMem(const char *data, Guint dataLen) {
+ ZxDoc *doc;
+
+ doc = new ZxDoc();
+ if (!doc->parse(data, dataLen)) {
+ delete doc;
+ return NULL;
+ }
+ return doc;
+}
+
+ZxDoc *ZxDoc::loadFile(const char *fileName) {
+ ZxDoc *doc;
+ FILE *f;
+ char *data;
+ Guint dataLen;
+
+ if (!(f = fopen(fileName, "rb"))) {
+ return NULL;
+ }
+ fseek(f, 0, SEEK_END);
+ dataLen = (Guint)ftell(f);
+ if (!dataLen) {
+ fclose(f);
+ return NULL;
+ }
+ fseek(f, 0, SEEK_SET);
+ data = (char *)gmalloc(dataLen);
+ if (fread(data, 1, dataLen, f) != dataLen) {
+ fclose(f);
+ gfree(data);
+ return NULL;
+ }
+ fclose(f);
+ doc = loadMem(data, dataLen);
+ gfree(data);
+ return doc;
+}
+
+ZxDoc::~ZxDoc() {
+}
+
+static bool writeToFileFunc(void *stream, const char *data, int length) {
+ return (int)fwrite(data, 1, length, (FILE *)stream) == length;
+}
+
+bool ZxDoc::writeFile(const char *fileName) {
+ FILE *f;
+
+ if (!(f = fopen(fileName, "wb"))) {
+ return false;
+ }
+ write(&writeToFileFunc, f);
+ fclose(f);
+ return true;
+}
+
+void ZxDoc::addChild(ZxNode *node) {
+ if (node->isXMLDecl() && !xmlDecl) {
+ xmlDecl = (ZxXMLDecl *)node;
+ } else if (node->isDocTypeDecl() && !docTypeDecl) {
+ docTypeDecl = (ZxDocTypeDecl *)node;
+ } else if (node->isElement() && !root) {
+ root = (ZxElement *)node;
+ }
+ ZxNode::addChild(node);
+}
+
+bool ZxDoc::parse(const char *data, Guint dataLen) {
+ parsePtr = data;
+ parseEnd = data + dataLen;
+
+ parseSpace();
+ parseXMLDecl(this);
+ parseMisc(this);
+ parseDocTypeDecl(this);
+ parseMisc(this);
+ if (match("<")) {
+ parseElement(this);
+ }
+ parseMisc(this);
+ return root != NULL;
+}
+
+void ZxDoc::parseXMLDecl(ZxNode *par) {
+ GString *version, *encoding, *s;
+ bool standalone;
+
+ if (!match("<?xml")) {
+ return;
+ }
+ parsePtr += 5;
+ parseSpace();
+
+ // version
+ version = NULL;
+ if (match("version")) {
+ parsePtr += 7;
+ parseSpace();
+ if (match("=")) {
+ ++parsePtr;
+ parseSpace();
+ version = parseQuotedString();
+ }
+ }
+ if (!version) {
+ version = new GString("1.0");
+ }
+ parseSpace();
+
+ // encoding
+ encoding = NULL;
+ if (match("encoding")) {
+ parsePtr += 8;
+ parseSpace();
+ if (match("=")) {
+ ++parsePtr;
+ parseSpace();
+ encoding = parseQuotedString();
+ }
+ }
+ parseSpace();
+
+ // standalone
+ standalone = false;
+ if (match("standalone")) {
+ parsePtr += 10;
+ parseSpace();
+ if (match("=")) {
+ ++parsePtr;
+ parseSpace();
+ s = parseQuotedString();
+ standalone = !s->cmp("yes");
+ delete s;
+ }
+ }
+ parseSpace();
+
+ if (match("?>")) {
+ parsePtr += 2;
+ }
+
+ par->addChild(new ZxXMLDecl(version, encoding, standalone));
+}
+
+//~ this just skips everything after the name
+void ZxDoc::parseDocTypeDecl(ZxNode *par) {
+ GString *name;
+ int state;
+ char c, quote;
+
+ if (!match("<!DOCTYPE")) {
+ return;
+ }
+ parsePtr += 9;
+ parseSpace();
+
+ name = parseName();
+ parseSpace();
+
+ state = 0;
+ quote = '\0';
+ while (parsePtr < parseEnd && state < 4) {
+ c = *parsePtr++;
+ switch (state) {
+ case 0: // not in square brackets; not in quotes
+ if (c == '>') {
+ state = 4;
+ } else if (c == '"' || c == '\'') {
+ state = 1;
+ } else if (c == '[') {
+ state = 2;
+ }
+ break;
+ case 1: // not in square brackets; in quotes
+ if (c == quote) {
+ state = 0;
+ }
+ break;
+ case 2: // in square brackets; not in quotes
+ if (c == ']') {
+ state = 0;
+ } else if (c == '"' || c == '\'') {
+ state = 3;
+ }
+ break;
+ case 3: // in square brackets; in quotes
+ if (c == quote) {
+ state = 2;
+ }
+ break;
+ }
+ }
+
+ par->addChild(new ZxDocTypeDecl(name));
+}
+
+// assumes match("<")
+void ZxDoc::parseElement(ZxNode *par) {
+ GString *type;
+ ZxElement *elem;
+ ZxAttr *attr;
+
+ ++parsePtr;
+ type = parseName();
+ elem = new ZxElement(type);
+ parseSpace();
+ while ((attr = parseAttr())) {
+ elem->addAttr(attr);
+ parseSpace();
+ }
+ if (match("/>")) {
+ parsePtr += 2;
+ } else if (match(">")) {
+ ++parsePtr;
+ parseContent(elem);
+ }
+ par->addChild(elem);
+}
+
+ZxAttr *ZxDoc::parseAttr() {
+ GString *name, *value;
+ const char *start;
+ char quote, c;
+ unsigned int x;
+ int n;
+
+ name = parseName();
+ parseSpace();
+ if (!match("=")) {
+ delete name;
+ return NULL;
+ }
+ ++parsePtr;
+ parseSpace();
+ if (!(parsePtr < parseEnd && (*parsePtr == '"' || *parsePtr == '\''))) {
+ delete name;
+ return NULL;
+ }
+ quote = *parsePtr++;
+ value = new GString();
+ while (parsePtr < parseEnd && *parsePtr != quote) {
+ if (*parsePtr == '&') {
+ ++parsePtr;
+ if (parsePtr < parseEnd && *parsePtr == '#') {
+ ++parsePtr;
+ if (parsePtr < parseEnd && *parsePtr == 'x') {
+ ++parsePtr;
+ x = 0;
+ while (parsePtr < parseEnd) {
+ c = *parsePtr;
+ if (c >= '0' && c <= '9') {
+ x = (x << 4) + (c - '0');
+ } else if (c >= 'a' && c <= 'f') {
+ x = (x << 4) + (10 + c - 'a');
+ } else if (c >= 'A' && c <= 'F') {
+ x = (x << 4) + (10 + c - 'A');
+ } else {
+ break;
+ }
+ ++parsePtr;
+ }
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ appendUTF8(value, x);
+ } else {
+ x = 0;
+ while (parsePtr < parseEnd) {
+ c = *parsePtr;
+ if (c >= '0' && c <= '9') {
+ x = x * 10 + (c - '0');
+ } else {
+ break;
+ }
+ ++parsePtr;
+ }
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ appendUTF8(value, x);
+ }
+ } else {
+ start = parsePtr;
+ for (++parsePtr;
+ parsePtr < parseEnd && *parsePtr != ';' &&
+ *parsePtr != quote && *parsePtr != '&';
+ ++parsePtr) ;
+ n = (int)(parsePtr - start);
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ if (n == 2 && !strncmp(start, "lt", 2)) {
+ value->append('<');
+ } else if (n == 2 && !strncmp(start, "gt", 2)) {
+ value->append('>');
+ } else if (n == 3 && !strncmp(start, "amp", 3)) {
+ value->append('&');
+ } else if (n == 4 && !strncmp(start, "apos", 4)) {
+ value->append('\'');
+ } else if (n == 4 && !strncmp(start, "quot", 4)) {
+ value->append('"');
+ } else {
+ value->append(start - 1, (int)(parsePtr - start) + 1);
+ }
+ }
+ } else {
+ start = parsePtr;
+ for (++parsePtr;
+ parsePtr < parseEnd && *parsePtr != quote && *parsePtr != '&';
+ ++parsePtr) ;
+ value->append(start, (int)(parsePtr - start));
+ }
+ }
+ if (parsePtr < parseEnd && *parsePtr == quote) {
+ ++parsePtr;
+ }
+ return new ZxAttr(name, value);
+}
+
+// this consumes the end tag
+void ZxDoc::parseContent(ZxElement *par) {
+ GString *endType;
+
+ endType = (new GString("</"))->append(par->getType());
+
+ while (parsePtr < parseEnd) {
+ if (match(endType->getCString())) {
+ parsePtr += endType->getLength();
+ parseSpace();
+ if (match(">")) {
+ ++parsePtr;
+ }
+ break;
+ } else if (match("<?")) {
+ parsePI(par);
+ } else if (match("<![CDATA[")) {
+ parseCDSect(par);
+ } else if (match("<!--")) {
+ parseComment(par);
+ } else if (match("<")) {
+ parseElement(par);
+ } else {
+ parseCharData(par);
+ }
+ }
+
+ delete endType;
+}
+
+void ZxDoc::parseCharData(ZxElement *par) {
+ GString *data;
+ const char *start;
+ char c;
+ unsigned int x;
+ int n;
+
+ data = new GString();
+ while (parsePtr < parseEnd && *parsePtr != '<') {
+ if (*parsePtr == '&') {
+ ++parsePtr;
+ if (parsePtr < parseEnd && *parsePtr == '#') {
+ ++parsePtr;
+ if (parsePtr < parseEnd && *parsePtr == 'x') {
+ ++parsePtr;
+ x = 0;
+ while (parsePtr < parseEnd) {
+ c = *parsePtr;
+ if (c >= '0' && c <= '9') {
+ x = (x << 4) + (c - '0');
+ } else if (c >= 'a' && c <= 'f') {
+ x = (x << 4) + (10 + c - 'a');
+ } else if (c >= 'A' && c <= 'F') {
+ x = (x << 4) + (10 + c - 'A');
+ } else {
+ break;
+ }
+ ++parsePtr;
+ }
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ appendUTF8(data, x);
+ } else {
+ x = 0;
+ while (parsePtr < parseEnd) {
+ c = *parsePtr;
+ if (c >= '0' && c <= '9') {
+ x = x * 10 + (c - '0');
+ } else {
+ break;
+ }
+ ++parsePtr;
+ }
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ appendUTF8(data, x);
+ }
+ } else {
+ start = parsePtr;
+ for (++parsePtr;
+ parsePtr < parseEnd && *parsePtr != ';' &&
+ *parsePtr != '<' && *parsePtr != '&';
+ ++parsePtr) ;
+ n = (int)(parsePtr - start);
+ if (parsePtr < parseEnd && *parsePtr == ';') {
+ ++parsePtr;
+ }
+ if (n == 2 && !strncmp(start, "lt", 2)) {
+ data->append('<');
+ } else if (n == 2 && !strncmp(start, "gt", 2)) {
+ data->append('>');
+ } else if (n == 3 && !strncmp(start, "amp", 3)) {
+ data->append('&');
+ } else if (n == 4 && !strncmp(start, "apos", 4)) {
+ data->append('\'');
+ } else if (n == 4 && !strncmp(start, "quot", 4)) {
+ data->append('"');
+ } else {
+ data->append(start - 1, (int)(parsePtr - start) + 1);
+ }
+ }
+ } else {
+ start = parsePtr;
+ for (++parsePtr;
+ parsePtr < parseEnd && *parsePtr != '<' && *parsePtr != '&';
+ ++parsePtr) ;
+ data->append(start, (int)(parsePtr - start));
+ }
+ }
+ par->addChild(new ZxCharData(data, true));
+}
+
+void ZxDoc::appendUTF8(GString *s, unsigned int c) {
+ if (c <= 0x7f) {
+ s->append((char)c);
+ } else if (c <= 0x7ff) {
+ s->append((char)(0xc0 + (c >> 6)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ } else if (c <= 0xffff) {
+ s->append((char)(0xe0 + (c >> 12)));
+ s->append((char)(0x80 + ((c >> 6) & 0x3f)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ } else if (c <= 0x1fffff) {
+ s->append((char)(0xf0 + (c >> 18)));
+ s->append((char)(0x80 + ((c >> 12) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 6) & 0x3f)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ } else if (c <= 0x3ffffff) {
+ s->append((char)(0xf8 + (c >> 24)));
+ s->append((char)(0x80 + ((c >> 18) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 12) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 6) & 0x3f)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ } else if (c <= 0x7fffffff) {
+ s->append((char)(0xfc + (c >> 30)));
+ s->append((char)(0x80 + ((c >> 24) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 18) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 12) & 0x3f)));
+ s->append((char)(0x80 + ((c >> 6) & 0x3f)));
+ s->append((char)(0x80 + (c & 0x3f)));
+ }
+}
+
+// assumes match("<![CDATA[")
+void ZxDoc::parseCDSect(ZxNode *par) {
+ const char *start;
+
+ parsePtr += 9;
+ start = parsePtr;
+ while (parsePtr < parseEnd - 3) {
+ if (!strncmp(parsePtr, "]]>", 3)) {
+ par->addChild(new ZxCharData(new GString(start, (int)(parsePtr - start)),
+ false));
+ parsePtr += 3;
+ return;
+ }
+ ++parsePtr;
+ }
+ parsePtr = parseEnd;
+ par->addChild(new ZxCharData(new GString(start, (int)(parsePtr - start)),
+ false));
+}
+
+void ZxDoc::parseMisc(ZxNode *par) {
+ while (1) {
+ if (match("<!--")) {
+ parseComment(par);
+ } else if (match("<?")) {
+ parsePI(par);
+ } else if (parsePtr < parseEnd && (*parsePtr == '\x20' ||
+ *parsePtr == '\x09' ||
+ *parsePtr == '\x0d' ||
+ *parsePtr == '\x0a')) {
+ ++parsePtr;
+ } else {
+ break;
+ }
+ }
+}
+
+// assumes match("<!--")
+void ZxDoc::parseComment(ZxNode *par) {
+ const char *start;
+
+ parsePtr += 4;
+ start = parsePtr;
+ while (parsePtr <= parseEnd - 3) {
+ if (!strncmp(parsePtr, "-->", 3)) {
+ par->addChild(new ZxComment(new GString(start, (int)(parsePtr - start))));
+ parsePtr += 3;
+ return;
+ }
+ ++parsePtr;
+ }
+ parsePtr = parseEnd;
+}
+
+// assumes match("<?")
+void ZxDoc::parsePI(ZxNode *par) {
+ GString *target;
+ const char *start;
+
+ parsePtr += 2;
+ target = parseName();
+ parseSpace();
+ start = parsePtr;
+ while (parsePtr <= parseEnd - 2) {
+ if (!strncmp(parsePtr, "?>", 2)) {
+ par->addChild(new ZxPI(target, new GString(start,
+ (int)(parsePtr - start))));
+ parsePtr += 2;
+ return;
+ }
+ ++parsePtr;
+ }
+ parsePtr = parseEnd;
+ par->addChild(new ZxPI(target, new GString(start, (int)(parsePtr - start))));
+}
+
+//~ this accepts all chars >= 0x80
+//~ this doesn't check for properly-formed UTF-8
+GString *ZxDoc::parseName() {
+ GString *name;
+
+ name = new GString();
+ if (parsePtr < parseEnd && nameStartChar[*parsePtr & 0xff]) {
+ name->append(*parsePtr++);
+ while (parsePtr < parseEnd && nameChar[*parsePtr & 0xff]) {
+ name->append(*parsePtr++);
+ }
+ }
+ return name;
+}
+
+GString *ZxDoc::parseQuotedString() {
+ GString *s;
+ const char *start;
+ char quote;
+
+ if (parsePtr < parseEnd && (*parsePtr == '"' || *parsePtr == '\'')) {
+ quote = *parsePtr++;
+ start = parsePtr;
+ while (parsePtr < parseEnd && *parsePtr != quote) {
+ ++parsePtr;
+ }
+ s = new GString(start, (int)(parsePtr - start));
+ if (parsePtr < parseEnd && *parsePtr == quote) {
+ ++parsePtr;
+ }
+ } else {
+ s = new GString();
+ }
+ return s;
+}
+
+void ZxDoc::parseSpace() {
+ while (parsePtr < parseEnd && (*parsePtr == '\x20' ||
+ *parsePtr == '\x09' ||
+ *parsePtr == '\x0d' ||
+ *parsePtr == '\x0a')) {
+ ++parsePtr;
+ }
+}
+
+bool ZxDoc::match(const char *s) {
+ int n;
+
+ n = (int)strlen(s);
+ return parseEnd - parsePtr >= n && !strncmp(parsePtr, s, n);
+}
+
+bool ZxDoc::write(ZxWriteFunc writeFunc, void *stream) {
+ ZxNode *child;
+
+ for (child = getFirstChild(); child; child = child->getNextChild()) {
+ if (!child->write(writeFunc, stream)) {
+ return false;
+ }
+ if (!(*writeFunc)(stream, "\n", 1)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+//------------------------------------------------------------------------
+
+ZxXMLDecl::ZxXMLDecl(GString *versionA, GString *encodingA, bool standaloneA) {
+ version = versionA;
+ encoding = encodingA;
+ standalone = standaloneA;
+}
+
+ZxXMLDecl::~ZxXMLDecl() {
+ delete version;
+ if (encoding) {
+ delete encoding;
+ }
+}
+
+bool ZxXMLDecl::write(ZxWriteFunc writeFunc, void *stream) {
+ GString *s;
+ bool ok;
+
+ s = new GString("<?xml version=\"");
+ s->append(version);
+ s->append("\"");
+ if (encoding) {
+ s->append(" encoding=\"");
+ s->append(encoding);
+ s->append("\"");
+ }
+ if (standalone) {
+ s->append(" standlone=\"yes\"");
+ }
+ s->append("?>");
+ ok = (*writeFunc)(stream, s->getCString(), s->getLength());
+ delete s;
+ return ok;
+}
+
+//------------------------------------------------------------------------
+
+ZxDocTypeDecl::ZxDocTypeDecl(GString *nameA) {
+ name = nameA;
+}
+
+ZxDocTypeDecl::~ZxDocTypeDecl() {
+ delete name;
+}
+
+bool ZxDocTypeDecl::write(ZxWriteFunc writeFunc, void *stream) {
+ GString *s;
+ bool ok;
+
+ s = new GString("<!DOCTYPE ");
+ s->append(name);
+ s->append(">");
+ ok = (*writeFunc)(stream, s->getCString(), s->getLength());
+ delete s;
+ return ok;
+}
+
+//------------------------------------------------------------------------
+
+ZxComment::ZxComment(GString *textA) {
+ text = textA;
+}
+
+ZxComment::~ZxComment() {
+ delete text;
+}
+
+bool ZxComment::write(ZxWriteFunc writeFunc, void *stream) {
+ GString *s;
+ bool ok;
+
+ s = new GString("<!--");
+ s->append(text);
+ s->append("-->");
+ ok = (*writeFunc)(stream, s->getCString(), s->getLength());
+ delete s;
+ return ok;
+}
+
+//------------------------------------------------------------------------
+
+ZxPI::ZxPI(GString *targetA, GString *textA) {
+ target = targetA;
+ text = textA;
+}
+
+ZxPI::~ZxPI() {
+ delete target;
+ delete text;
+}
+
+bool ZxPI::write(ZxWriteFunc writeFunc, void *stream) {
+ GString *s;
+ bool ok;
+
+ s = new GString("<?");
+ s->append(target);
+ s->append(" ");
+ s->append(text);
+ s->append("?>");
+ ok = (*writeFunc)(stream, s->getCString(), s->getLength());
+ delete s;
+ return ok;
+}
+
+//------------------------------------------------------------------------
+
+ZxElement::ZxElement(GString *typeA) {
+ type = typeA;
+ attrs = new GHash();
+ firstAttr = lastAttr = NULL;
+}
+
+ZxElement::~ZxElement() {
+ delete type;
+ deleteGHash(attrs, ZxAttr);
+}
+
+bool ZxElement::isElement(const char *typeA) {
+ return !type->cmp(typeA);
+}
+
+ZxAttr *ZxElement::findAttr(const char *attrName) {
+ return (ZxAttr *)attrs->lookup(attrName);
+}
+
+void ZxElement::addAttr(ZxAttr *attr) {
+ attrs->add(attr->getName(), attr);
+ if (lastAttr) {
+ lastAttr->next = attr;
+ lastAttr= attr;
+ } else {
+ firstAttr = lastAttr = attr;
+ }
+ attr->parent = this;
+ attr->next = NULL;
+}
+
+bool ZxElement::write(ZxWriteFunc writeFunc, void *stream) {
+ GString *s;
+ ZxAttr *attr;
+ ZxNode *child;
+ bool ok;
+
+ s = new GString("<");
+ s->append(type);
+ for (attr = firstAttr; attr; attr = attr->getNextAttr()) {
+ s->append(" ");
+ s->append(attr->name);
+ s->append("=\"");
+ appendEscapedAttrValue(s, attr->value);
+ s->append("\"");
+ }
+ if ((child = getFirstChild())) {
+ s->append(">");
+ } else {
+ s->append("/>");
+ }
+ ok = (*writeFunc)(stream, s->getCString(), s->getLength());
+ delete s;
+ if (!ok) {
+ return false;
+ }
+ if (child) {
+ for (; child; child = child->getNextChild()) {
+ if (!child->write(writeFunc, stream)) {
+ return false;
+ }
+ }
+ s = new GString();
+ s->append("</");
+ s->append(type);
+ s->append(">");
+ ok = (*writeFunc)(stream, s->getCString(), s->getLength());
+ delete s;
+ if (!ok) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void ZxElement::appendEscapedAttrValue(GString *out, GString *s) {
+ char c;
+ int i;
+
+ for (i = 0; i < s->getLength(); ++i) {
+ c = s->getChar(i);
+ if (c == '<') {
+ out->append("&lt;");
+ } else if (c == '>') {
+ out->append("&gt;");
+ } else if (c == '&') {
+ out->append("&amp;");
+ } else if (c == '"') {
+ out->append("&quot;");
+ } else {
+ out->append(c);
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+
+ZxAttr::ZxAttr(GString *nameA, GString *valueA) {
+ name = nameA;
+ value = valueA;
+ parent = NULL;
+ next = NULL;
+}
+
+ZxAttr::~ZxAttr() {
+ delete name;
+ delete value;
+}
+
+//------------------------------------------------------------------------
+
+ZxCharData::ZxCharData(GString *dataA, bool parsedA) {
+ data = dataA;
+ parsed = parsedA;
+}
+
+ZxCharData::~ZxCharData() {
+ delete data;
+}
+
+bool ZxCharData::write(ZxWriteFunc writeFunc, void *stream) {
+ GString *s;
+ char c;
+ int i;
+ bool ok;
+
+ s = new GString();
+ if (parsed) {
+ for (i = 0; i < data->getLength(); ++i) {
+ c = data->getChar(i);
+ if (c == '<') {
+ s->append("&lt;");
+ } else if (c == '>') {
+ s->append("&gt;");
+ } else if (c == '&') {
+ s->append("&amp;");
+ } else {
+ s->append(c);
+ }
+ }
+ } else {
+ s->append("<![CDATA[");
+ s->append(data);
+ s->append("]]>");
+ }
+ ok = (*writeFunc)(stream, s->getCString(), s->getLength());
+ delete s;
+ return ok;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h b/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h
new file mode 100644
index 00000000000..c04ca95064b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/Zoox.h
@@ -0,0 +1,271 @@
+//========================================================================
+//
+// Zoox.h
+//
+//========================================================================
+
+#ifndef ZOOX_H
+#define ZOOX_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class GString;
+class GList;
+class GHash;
+
+class ZxAttr;
+class ZxDocTypeDecl;
+class ZxElement;
+class ZxXMLDecl;
+
+//------------------------------------------------------------------------
+
+typedef bool (*ZxWriteFunc)(void *stream, const char *data, int length);
+
+//------------------------------------------------------------------------
+
+class ZxNode {
+public:
+
+ ZxNode();
+ virtual ~ZxNode();
+
+ virtual bool isDoc() { return false; }
+ virtual bool isXMLDecl() { return false; }
+ virtual bool isDocTypeDecl() { return false; }
+ virtual bool isComment() { return false; }
+ virtual bool isPI() { return false; }
+ virtual bool isElement() { return false; }
+ virtual bool isElement(const char *type) { return false; }
+ virtual bool isCharData() { return false; }
+ virtual ZxNode *getFirstChild() { return firstChild; }
+ virtual ZxNode *getNextChild() { return next; }
+ ZxNode *getParent() { return parent; }
+ ZxNode *deleteChild(ZxNode *child);
+ void appendChild(ZxNode *child);
+ void insertChildAfter(ZxNode *child, ZxNode *prev);
+ ZxElement *findFirstElement(const char *type);
+ ZxElement *findFirstChildElement(const char *type);
+ GList *findAllElements(const char *type);
+ GList *findAllChildElements(const char *type);
+ virtual void addChild(ZxNode *child);
+
+ virtual bool write(ZxWriteFunc writeFunc, void *stream) = 0;
+
+protected:
+
+ void findAllElements(const char *type, GList *results);
+
+ ZxNode *next;
+ ZxNode *parent;
+ ZxNode *firstChild,
+ *lastChild;
+};
+
+//------------------------------------------------------------------------
+
+class ZxDoc: public ZxNode {
+public:
+
+ ZxDoc();
+
+ // Parse from memory. Returns NULL on error.
+ static ZxDoc *loadMem(const char *data, Guint dataLen);
+
+ // Parse from disk. Returns NULL on error.
+ static ZxDoc *loadFile(const char *fileName);
+
+ virtual ~ZxDoc();
+
+ // Write to disk. Returns false on error.
+ bool writeFile(const char *fileName);
+
+ virtual bool isDoc() { return true; }
+ ZxXMLDecl *getXMLDecl() { return xmlDecl; }
+ ZxDocTypeDecl *getDocTypeDecl() { return docTypeDecl; }
+ ZxElement *getRoot() { return root; }
+ virtual void addChild(ZxNode *node);
+
+ virtual bool write(ZxWriteFunc writeFunc, void *stream);
+
+private:
+
+ bool parse(const char *data, Guint dataLen);
+ void parseXMLDecl(ZxNode *par);
+ void parseDocTypeDecl(ZxNode *par);
+ void parseElement(ZxNode *par);
+ ZxAttr *parseAttr();
+ void parseContent(ZxElement *par);
+ void parseCharData(ZxElement *par);
+ void appendUTF8(GString *s, unsigned int c);
+ void parseCDSect(ZxNode *par);
+ void parseMisc(ZxNode *par);
+ void parseComment(ZxNode *par);
+ void parsePI(ZxNode *par);
+ GString *parseName();
+ GString *parseQuotedString();
+ void parseSpace();
+ bool match(const char *s);
+
+ ZxXMLDecl *xmlDecl; // may be NULL
+ ZxDocTypeDecl *docTypeDecl; // may be NULL
+ ZxElement *root; // may be NULL
+
+ const char *parsePtr;
+ const char *parseEnd;
+};
+
+//------------------------------------------------------------------------
+
+class ZxXMLDecl: public ZxNode {
+public:
+
+ ZxXMLDecl(GString *versionA, GString *encodingA, bool standaloneA);
+ virtual ~ZxXMLDecl();
+
+ virtual bool isXMLDecl() { return true; }
+ GString *getVersion() { return version; }
+ GString *getEncoding() { return encoding; }
+ bool getStandalone() { return standalone; }
+
+ virtual bool write(ZxWriteFunc writeFunc, void *stream);
+
+private:
+
+ GString *version;
+ GString *encoding; // may be NULL
+ bool standalone;
+};
+
+//------------------------------------------------------------------------
+
+class ZxDocTypeDecl: public ZxNode {
+public:
+
+ ZxDocTypeDecl(GString *nameA);
+ virtual ~ZxDocTypeDecl();
+
+ virtual bool isDocTypeDecl() { return true; }
+ GString *getName() { return name; }
+
+ virtual bool write(ZxWriteFunc writeFunc, void *stream);
+
+private:
+
+ GString *name;
+};
+
+//------------------------------------------------------------------------
+
+class ZxComment: public ZxNode {
+public:
+
+ ZxComment(GString *textA);
+ virtual ~ZxComment();
+
+ virtual bool isComment() { return true; }
+ GString *getText() { return text; }
+
+ virtual bool write(ZxWriteFunc writeFunc, void *stream);
+
+private:
+
+ GString *text;
+};
+
+//------------------------------------------------------------------------
+
+class ZxPI: public ZxNode {
+public:
+
+ ZxPI(GString *targetA, GString *textA);
+ virtual ~ZxPI();
+
+ virtual bool isPI() { return true; }
+ GString *getTarget() { return target; }
+ GString *getText() { return text; }
+
+ virtual bool write(ZxWriteFunc writeFunc, void *stream);
+
+private:
+
+ GString *target;
+ GString *text;
+};
+
+//------------------------------------------------------------------------
+
+class ZxElement: public ZxNode {
+public:
+
+ ZxElement(GString *typeA);
+ virtual ~ZxElement();
+
+ virtual bool isElement() { return true; }
+ virtual bool isElement(const char *typeA);
+ GString *getType() { return type; }
+ ZxAttr *findAttr(const char *attrName);
+ ZxAttr *getFirstAttr() { return firstAttr; }
+ void addAttr(ZxAttr *attr);
+
+ virtual bool write(ZxWriteFunc writeFunc, void *stream);
+
+private:
+
+ void appendEscapedAttrValue(GString *out, GString *s);
+
+ GString *type;
+ GHash *attrs; // [ZxAttr]
+ ZxAttr *firstAttr, *lastAttr;
+};
+
+//------------------------------------------------------------------------
+
+class ZxAttr {
+public:
+
+ ZxAttr(GString *nameA, GString *valueA);
+ ~ZxAttr();
+
+ GString *getName() { return name; }
+ GString *getValue() { return value; }
+ ZxAttr *getNextAttr() { return next; }
+ ZxNode *getParent() { return parent; }
+
+private:
+
+ GString *name;
+ GString *value;
+ ZxElement *parent;
+ ZxAttr *next;
+
+ friend class ZxElement;
+};
+
+//------------------------------------------------------------------------
+
+class ZxCharData: public ZxNode {
+public:
+
+ ZxCharData(GString *dataA, bool parsedA);
+ virtual ~ZxCharData();
+
+ virtual bool isCharData() { return true; }
+ GString *getData() { return data; }
+ bool isParsed() { return parsed; }
+
+ virtual bool write(ZxWriteFunc writeFunc, void *stream);
+
+private:
+
+ GString *data; // in UTF-8 format
+ bool parsed;
+};
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/config.h b/Build/source/libs/xpdf/xpdf-src/xpdf/config.h
new file mode 100644
index 00000000000..39cade1682c
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/config.h
@@ -0,0 +1,103 @@
+//========================================================================
+//
+// config.h
+//
+// Copyright 1996-2021 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+//------------------------------------------------------------------------
+// version
+//------------------------------------------------------------------------
+
+// xpdf version
+#define xpdfVersion "4.03"
+#define xpdfVersionNum 4.03
+#define xpdfMajorVersion 4
+#define xpdfMinorVersion 3
+#define xpdfUpdateVersion 0
+#define xpdfMajorVersionStr "4"
+#define xpdfMinorVersionStr "3"
+#define xpdfUpdateVersionStr "0"
+
+// supported PDF version
+#define supportedPDFVersionStr "2.0"
+#define supportedPDFVersionNum 2.0
+
+// copyright notice
+#define xpdfCopyright "Copyright 1996-2021 Glyph & Cog, LLC"
+
+// Windows resource file stuff
+#define winxpdfVersion "WinXpdf 4.03"
+#define xpdfCopyrightAmp "Copyright 1996-2021 Glyph && Cog, LLC"
+
+//------------------------------------------------------------------------
+// paper size
+//------------------------------------------------------------------------
+
+// default paper size (in points) for PostScript output
+#ifdef A4_PAPER
+#define defPaperWidth 595 // ISO A4 (210x297 mm)
+#define defPaperHeight 842
+#else
+#define defPaperWidth 612 // American letter (8.5x11")
+#define defPaperHeight 792
+#endif
+
+//------------------------------------------------------------------------
+// config file (xpdfrc) path
+//------------------------------------------------------------------------
+
+// user config file name, relative to the user's home directory
+#if defined(VMS) || defined(_WIN32)
+#define xpdfUserConfigFile "xpdfrc"
+#else
+#define xpdfUserConfigFile ".xpdfrc"
+#endif
+
+// system config file name (set via the configure script)
+#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
+
+//------------------------------------------------------------------------
+// X-related constants
+//------------------------------------------------------------------------
+
+// default maximum size of color cube to allocate
+#define defaultRGBCube 5
+
+//------------------------------------------------------------------------
+// popen
+//------------------------------------------------------------------------
+
+#if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(__EMX__) || defined(_WIN32) || defined(__DJGPP__)
+#define POPEN_READ_MODE "rb"
+#else
+#define POPEN_READ_MODE "r"
+#endif
+
+//------------------------------------------------------------------------
+// Win32 stuff
+//------------------------------------------------------------------------
+
+#ifdef CDECL
+#undef CDECL
+#endif
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+#define CDECL __cdecl
+#else
+#define CDECL
+#endif
+
+#endif
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc
new file mode 100644
index 00000000000..e8b74381017
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfdetach.cc
@@ -0,0 +1,212 @@
+//========================================================================
+//
+// pdfdetach.cc
+//
+// Copyright 2010 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdio.h>
+#include "gtypes.h"
+#include "gmem.h"
+#include "gmempp.h"
+#include "parseargs.h"
+#include "GlobalParams.h"
+#include "PDFDoc.h"
+#include "CharTypes.h"
+#include "UnicodeMap.h"
+#include "UTF8.h"
+#include "Error.h"
+#include "config.h"
+
+static GBool doList = gFalse;
+static int saveNum = 0;
+static GBool saveAll = gFalse;
+static char savePath[1024] = "";
+static char textEncName[128] = "";
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
+static char cfgFileName[256] = "";
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static ArgDesc argDesc[] = {
+ {"-list", argFlag, &doList, 0,
+ "list all embedded files"},
+ {"-save", argInt, &saveNum, 0,
+ "save the specified embedded file"},
+ {"-saveall", argFlag, &saveAll, 0,
+ "save all embedded files"},
+ {"-o", argString, savePath, sizeof(savePath),
+ "file name for the saved embedded file"},
+ {"-enc", argString, textEncName, sizeof(textEncName),
+ "output text encoding name"},
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+ {"-cfg", argString, cfgFileName, sizeof(cfgFileName),
+ "configuration file to use in place of .xpdfrc"},
+ {"-v", argFlag, &printVersion, 0,
+ "print copyright and version info"},
+ {"-h", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
+
+int main(int argc, char *argv[]) {
+ char *fileName;
+ UnicodeMap *uMap;
+ GString *ownerPW, *userPW;
+ PDFDoc *doc;
+ Unicode *name;
+ char uBuf[8];
+ GString *path;
+ GBool ok;
+ int exitCode;
+ int nFiles, nameLen, n, i, j;
+
+ exitCode = 99;
+
+ // parse args
+ fixCommandLine(&argc, &argv);
+ ok = parseArgs(argDesc, &argc, argv);
+ if ((doList ? 1 : 0) +
+ ((saveNum != 0) ? 1 : 0) +
+ (saveAll ? 1 : 0) != 1) {
+ ok = gFalse;
+ }
+ if (!ok || argc != 2 || printVersion || printHelp) {
+ fprintf(stderr, "pdfdetach version %s [www.xpdfreader.com]\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdfdetach", "<PDF-file>", argDesc);
+ }
+ goto err0;
+ }
+ fileName = argv[1];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+ if (textEncName[0]) {
+ globalParams->setTextEncoding(textEncName);
+ }
+
+ // get mapping to output encoding
+ if (!(uMap = globalParams->getTextEncoding())) {
+ error(errConfig, -1, "Couldn't get text encoding");
+ goto err1;
+ }
+
+ // open PDF file
+ if (ownerPassword[0] != '\001') {
+ ownerPW = new GString(ownerPassword);
+ } else {
+ ownerPW = NULL;
+ }
+ if (userPassword[0] != '\001') {
+ userPW = new GString(userPassword);
+ } else {
+ userPW = NULL;
+ }
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+ if (userPW) {
+ delete userPW;
+ }
+ if (ownerPW) {
+ delete ownerPW;
+ }
+ if (!doc->isOk()) {
+ exitCode = 1;
+ goto err2;
+ }
+
+ nFiles = doc->getNumEmbeddedFiles();
+
+ // list embedded files
+ if (doList) {
+ printf("%d embedded files\n", nFiles);
+ for (i = 0; i < nFiles; ++i) {
+ printf("%d: ", i+1);
+ name = doc->getEmbeddedFileName(i);
+ nameLen = doc->getEmbeddedFileNameLength(i);
+ for (j = 0; j < nameLen; ++j) {
+ n = uMap->mapUnicode(name[j], uBuf, sizeof(uBuf));
+ fwrite(uBuf, 1, n, stdout);
+ }
+ fputc('\n', stdout);
+ }
+
+ // save all embedded files
+ } else if (saveAll) {
+ for (i = 0; i < nFiles; ++i) {
+ if (savePath[0]) {
+ path = new GString(savePath);
+ path->append('/');
+ } else {
+ path = new GString();
+ }
+ name = doc->getEmbeddedFileName(i);
+ nameLen = doc->getEmbeddedFileNameLength(i);
+ for (j = 0; j < nameLen; ++j) {
+ n = mapUTF8(name[j], uBuf, sizeof(uBuf));
+ path->append(uBuf, n);
+ }
+ 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
+ } else {
+ if (saveNum < 1 || saveNum > nFiles) {
+ error(errCommandLine, -1, "Invalid file number");
+ goto err2;
+ }
+ if (savePath[0]) {
+ path = new GString(savePath);
+ } else {
+ name = doc->getEmbeddedFileName(saveNum - 1);
+ nameLen = doc->getEmbeddedFileNameLength(saveNum - 1);
+ path = new GString();
+ for (j = 0; j < nameLen; ++j) {
+ n = mapUTF8(name[j], uBuf, sizeof(uBuf));
+ path->append(uBuf, n);
+ }
+ }
+ 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;
+
+ // clean up
+ err2:
+ uMap->decRefCnt();
+ delete doc;
+ err1:
+ delete globalParams;
+ err0:
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc
new file mode 100644
index 00000000000..42c46223d8b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdffonts.cc
@@ -0,0 +1,451 @@
+//========================================================================
+//
+// pdffonts.cc
+//
+// Copyright 2001-2007 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <limits.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "parseargs.h"
+#include "GString.h"
+#include "GlobalParams.h"
+#include "Error.h"
+#include "Object.h"
+#include "Dict.h"
+#include "GfxFont.h"
+#include "Annot.h"
+#include "AcroForm.h"
+#include "PDFDoc.h"
+#include "config.h"
+
+// NB: this must match the definition of GfxFontType in GfxFont.h.
+static const char *fontTypeNames[] = {
+ "unknown",
+ "Type 1",
+ "Type 1C",
+ "Type 1C (OT)",
+ "Type 3",
+ "TrueType",
+ "TrueType (OT)",
+ "CID Type 0",
+ "CID Type 0C",
+ "CID Type 0C (OT)",
+ "CID TrueType",
+ "CID TrueType (OT)"
+};
+
+static void scanFonts(Object *obj, PDFDoc *doc);
+static void scanFonts(Dict *resDict, PDFDoc *doc);
+static void scanFont(GfxFont *font, PDFDoc *doc);
+static GBool checkObject(Object *in, Object *out);
+
+static int firstPage = 1;
+static int lastPage = 0;
+static GBool showFontLoc = gFalse;
+static GBool showFontLocPS = gFalse;
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
+static char cfgFileName[256] = "";
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static ArgDesc argDesc[] = {
+ {"-f", argInt, &firstPage, 0,
+ "first page to examine"},
+ {"-l", argInt, &lastPage, 0,
+ "last page to examine"},
+ {"-loc", argFlag, &showFontLoc, 0,
+ "print extended info on font location"},
+ {"-locPS", argFlag, &showFontLocPS, 0,
+ "print extended info on font location for PostScript conversion"},
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+ {"-cfg", argString, cfgFileName, sizeof(cfgFileName),
+ "configuration file to use in place of .xpdfrc"},
+ {"-v", argFlag, &printVersion, 0,
+ "print copyright and version info"},
+ {"-h", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
+
+static PDFDoc *doc;
+
+static Ref *fonts;
+static int fontsLen;
+static int fontsSize;
+
+static char *seenObjs;
+static int numObjects;
+
+int main(int argc, char *argv[]) {
+ char *fileName;
+ GString *ownerPW, *userPW;
+ GBool ok;
+ Page *page;
+ Dict *resDict;
+ Annots *annots;
+ AcroForm *form;
+ Object obj1, obj2;
+ int pg, i, j;
+ int exitCode;
+
+ exitCode = 99;
+
+ // parse args
+ fixCommandLine(&argc, &argv);
+ ok = parseArgs(argDesc, &argc, argv);
+ if (!ok || argc != 2 || printVersion || printHelp) {
+ fprintf(stderr, "pdffonts version %s [www.xpdfreader.com]\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdffonts", "<PDF-file>", argDesc);
+ }
+ goto err0;
+ }
+ fileName = argv[1];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+ globalParams->setupBaseFonts(NULL);
+
+ // open PDF file
+ if (ownerPassword[0] != '\001') {
+ ownerPW = new GString(ownerPassword);
+ } else {
+ ownerPW = NULL;
+ }
+ if (userPassword[0] != '\001') {
+ userPW = new GString(userPassword);
+ } else {
+ userPW = NULL;
+ }
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+ if (userPW) {
+ delete userPW;
+ }
+ if (ownerPW) {
+ delete ownerPW;
+ }
+ if (!doc->isOk()) {
+ exitCode = 1;
+ goto err1;
+ }
+
+ // get page range
+ if (firstPage < 1) {
+ firstPage = 1;
+ }
+ if (lastPage < 1 || lastPage > doc->getNumPages()) {
+ lastPage = doc->getNumPages();
+ }
+
+ // scan the fonts
+ if (showFontLoc || showFontLocPS) {
+ printf("name type emb sub uni prob object ID location\n");
+ printf("---------------------------------------------- ----------------- --- --- --- ---- --------- --------\n");
+ } else {
+ printf("name type emb sub uni prob object ID\n");
+ printf("---------------------------------------------- ----------------- --- --- --- ---- ---------\n");
+ }
+ fonts = NULL;
+ fontsLen = fontsSize = 0;
+ numObjects = doc->getXRef()->getNumObjects();
+ seenObjs = (char *)gmalloc(numObjects);
+ memset(seenObjs, 0, numObjects);
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ page = doc->getCatalog()->getPage(pg);
+ if ((resDict = page->getResourceDict())) {
+ scanFonts(resDict, doc);
+ }
+ annots = new Annots(doc, page->getAnnots(&obj1));
+ obj1.free();
+ for (i = 0; i < annots->getNumAnnots(); ++i) {
+ if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) {
+ obj1.streamGetDict()->lookupNF("Resources", &obj2);
+ scanFonts(&obj2, doc);
+ obj2.free();
+ }
+ obj1.free();
+ }
+ delete annots;
+ }
+ if ((form = doc->getCatalog()->getForm())) {
+ for (i = 0; i < form->getNumFields(); ++i) {
+ form->getField(i)->getResources(&obj1);
+ if (obj1.isArray()) {
+ for (j = 0; j < obj1.arrayGetLength(); ++j) {
+ obj1.arrayGetNF(j, &obj2);
+ scanFonts(&obj2, doc);
+ obj2.free();
+ }
+ } else if (obj1.isDict()) {
+ scanFonts(obj1.getDict(), doc);
+ }
+ obj1.free();
+ }
+ }
+
+ exitCode = 0;
+
+ // clean up
+ gfree(fonts);
+ gfree(seenObjs);
+ err1:
+ delete doc;
+ delete globalParams;
+ err0:
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}
+
+static void scanFonts(Object *obj, PDFDoc *doc) {
+ Object obj2;
+
+ if (checkObject(obj, &obj2) && obj2.isDict()) {
+ scanFonts(obj2.getDict(), doc);
+ }
+ obj2.free();
+}
+
+static void scanFonts(Dict *resDict, PDFDoc *doc) {
+ Object fontDict1, fontDict2, xObjDict1, xObjDict2, xObj1, xObj2;
+ Object patternDict1, patternDict2, pattern1, pattern2;
+ Object gsDict1, gsDict2, gs1, gs2, smask1, smask2, smaskGroup1, smaskGroup2;
+ Object resObj;
+ Ref r;
+ GfxFontDict *gfxFontDict;
+ GfxFont *font;
+ int i;
+
+ // scan the fonts in this resource dictionary
+ gfxFontDict = NULL;
+ resDict->lookupNF("Font", &fontDict1);
+ if (checkObject(&fontDict1, &fontDict2) && fontDict2.isDict()) {
+ if (fontDict1.isRef()) {
+ r = fontDict1.getRef();
+ gfxFontDict = new GfxFontDict(doc->getXRef(), &r, fontDict2.getDict());
+ } else {
+ gfxFontDict = new GfxFontDict(doc->getXRef(), NULL, fontDict2.getDict());
+ }
+ if (gfxFontDict) {
+ for (i = 0; i < gfxFontDict->getNumFonts(); ++i) {
+ if ((font = gfxFontDict->getFont(i))) {
+ scanFont(font, doc);
+ }
+ }
+ delete gfxFontDict;
+ }
+ }
+ fontDict2.free();
+ fontDict1.free();
+
+ // recursively scan any resource dictionaries in XObjects in this
+ // resource dictionary
+ resDict->lookupNF("XObject", &xObjDict1);
+ if (checkObject(&xObjDict1, &xObjDict2) && xObjDict2.isDict()) {
+ for (i = 0; i < xObjDict2.dictGetLength(); ++i) {
+ xObjDict2.dictGetValNF(i, &xObj1);
+ if (checkObject(&xObj1, &xObj2) && xObj2.isStream()) {
+ xObj2.streamGetDict()->lookupNF("Resources", &resObj);
+ scanFonts(&resObj, doc);
+ resObj.free();
+ }
+ xObj2.free();
+ xObj1.free();
+ }
+ }
+ xObjDict2.free();
+ xObjDict1.free();
+
+ // recursively scan any resource dictionaries in Patterns in this
+ // resource dictionary
+ resDict->lookupNF("Pattern", &patternDict1);
+ if (checkObject(&patternDict1, &patternDict2) && patternDict2.isDict()) {
+ for (i = 0; i < patternDict2.dictGetLength(); ++i) {
+ patternDict2.dictGetValNF(i, &pattern1);
+ if (checkObject(&pattern1, &pattern2) && pattern2.isStream()) {
+ pattern2.streamGetDict()->lookupNF("Resources", &resObj);
+ scanFonts(&resObj, doc);
+ resObj.free();
+ }
+ pattern2.free();
+ pattern1.free();
+ }
+ }
+ patternDict2.free();
+ patternDict1.free();
+
+ // recursively scan any resource dictionaries in ExtGStates in this
+ // resource dictionary
+ resDict->lookupNF("ExtGState", &gsDict1);
+ if (checkObject(&gsDict1, &gsDict2) && gsDict2.isDict()) {
+ for (i = 0; i < gsDict2.dictGetLength(); ++i) {
+ gsDict2.dictGetValNF(i, &gs1);
+ if (checkObject(&gs1, &gs2) && gs2.isDict()) {
+ gs2.dictLookupNF("SMask", &smask1);
+ if (checkObject(&smask1, &smask2) && smask2.isDict()) {
+ smask2.dictLookupNF("G", &smaskGroup1);
+ if (checkObject(&smaskGroup1, &smaskGroup2) &&
+ smaskGroup2.isStream()) {
+ smaskGroup2.streamGetDict()->lookupNF("Resources", &resObj);
+ scanFonts(&resObj, doc);
+ resObj.free();
+ }
+ smaskGroup2.free();
+ smaskGroup1.free();
+ }
+ smask2.free();
+ smask1.free();
+ }
+ gs2.free();
+ gs1.free();
+ }
+ }
+ gsDict2.free();
+ gsDict1.free();
+}
+
+static void scanFont(GfxFont *font, PDFDoc *doc) {
+ Ref fontRef, embRef;
+ Object fontObj, toUnicodeObj;
+ GString *name;
+ GBool emb, subset, hasToUnicode;
+ GfxFontLoc *loc;
+ int i;
+
+ fontRef = *font->getID();
+
+ // check for an already-seen font
+ for (i = 0; i < fontsLen; ++i) {
+ if (fontRef.num == fonts[i].num && fontRef.gen == fonts[i].gen) {
+ return;
+ }
+ }
+
+ // font name
+ name = font->getName();
+
+ // check for an embedded font
+ if (font->getType() == fontType3) {
+ emb = gTrue;
+ } else {
+ emb = font->getEmbeddedFontID(&embRef);
+ }
+
+ // look for a ToUnicode map
+ hasToUnicode = gFalse;
+ if (doc->getXRef()->fetch(fontRef.num, fontRef.gen, &fontObj)->isDict()) {
+ hasToUnicode = fontObj.dictLookup("ToUnicode", &toUnicodeObj)->isStream();
+ toUnicodeObj.free();
+ }
+ fontObj.free();
+
+ // check for a font subset name: capital letters followed by a '+'
+ // sign
+ subset = gFalse;
+ if (name) {
+ for (i = 0; i < name->getLength(); ++i) {
+ if (name->getChar(i) < 'A' || name->getChar(i) > 'Z') {
+ break;
+ }
+ }
+ subset = i > 0 && i < name->getLength() && name->getChar(i) == '+';
+ }
+
+ // print the font info
+ printf("%-46s %-17s %-3s %-3s %-3s %-4s",
+ name ? name->getCString() : "[none]",
+ fontTypeNames[font->getType()],
+ emb ? "yes" : "no",
+ subset ? "yes" : "no",
+ hasToUnicode ? "yes" : "no",
+ font->problematicForUnicode() ? " X" : "");
+ if (fontRef.gen >= 100000) {
+ printf(" [none]");
+ } else {
+ printf(" %6d %2d", fontRef.num, fontRef.gen);
+ }
+ if (showFontLoc || showFontLocPS) {
+ if (font->getType() == fontType3) {
+ printf(" embedded");
+ } else {
+ loc = font->locateFont(doc->getXRef(), showFontLocPS);
+ if (loc) {
+ if (loc->locType == gfxFontLocEmbedded) {
+ printf(" embedded");
+ } else if (loc->locType == gfxFontLocExternal) {
+ if (loc->path) {
+ printf(" external: %s", loc->path->getCString());
+ } else {
+ printf(" unavailable");
+ }
+ } else if (loc->locType == gfxFontLocResident) {
+ if (loc->path) {
+ printf(" resident: %s", loc->path->getCString());
+ } else {
+ printf(" unavailable");
+ }
+ }
+ } else {
+ printf(" unknown");
+ }
+ delete loc;
+ }
+ }
+ printf("\n");
+
+ // add this font to the list
+ if (fontsLen == fontsSize) {
+ if (fontsSize <= INT_MAX - 32) {
+ fontsSize += 32;
+ } else {
+ // let greallocn throw an exception
+ fontsSize = -1;
+ }
+ fonts = (Ref *)greallocn(fonts, fontsSize, sizeof(Ref));
+ }
+ fonts[fontsLen++] = *font->getID();
+}
+
+static GBool checkObject(Object *in, Object *out) {
+ int objNum;
+
+ if (!in->isRef()) {
+ in->copy(out);
+ return gTrue;
+ }
+ objNum = in->getRefNum();
+ if (objNum < 0 || objNum >= numObjects) {
+ out->initNull();
+ return gTrue;
+ }
+ if (seenObjs[objNum]) {
+ out->initNull();
+ return gFalse;
+ }
+ seenObjs[objNum] = (char)1;
+ in->fetch(doc->getXRef(), out);
+ return gTrue;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc
new file mode 100644
index 00000000000..cd5d34014f4
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfimages.cc
@@ -0,0 +1,164 @@
+//========================================================================
+//
+// pdfimages.cc
+//
+// Copyright 1998-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "parseargs.h"
+#include "GString.h"
+#include "GlobalParams.h"
+#include "Object.h"
+#include "Stream.h"
+#include "Array.h"
+#include "Dict.h"
+#include "XRef.h"
+#include "Catalog.h"
+#include "Page.h"
+#include "PDFDoc.h"
+#include "ImageOutputDev.h"
+#include "Error.h"
+#include "config.h"
+
+static int firstPage = 1;
+static int lastPage = 0;
+static GBool dumpJPEG = gFalse;
+static GBool dumpRaw = gFalse;
+static GBool list = gFalse;
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
+static GBool quiet = gFalse;
+static char cfgFileName[256] = "";
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static ArgDesc argDesc[] = {
+ {"-f", argInt, &firstPage, 0,
+ "first page to convert"},
+ {"-l", argInt, &lastPage, 0,
+ "last page to convert"},
+ {"-j", argFlag, &dumpJPEG, 0,
+ "write JPEG images as JPEG files"},
+ {"-raw", argFlag, &dumpRaw, 0,
+ "write raw data in PDF-native formats"},
+ {"-list", argFlag, &list, 0,
+ "write information to stdout for each image"},
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+ {"-q", argFlag, &quiet, 0,
+ "don't print any messages or errors"},
+ {"-cfg", argString, cfgFileName, sizeof(cfgFileName),
+ "configuration file to use in place of .xpdfrc"},
+ {"-v", argFlag, &printVersion, 0,
+ "print copyright and version info"},
+ {"-h", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
+
+int main(int argc, char *argv[]) {
+ PDFDoc *doc;
+ char *fileName;
+ char *imgRoot;
+ GString *ownerPW, *userPW;
+ ImageOutputDev *imgOut;
+ GBool ok;
+ int exitCode;
+
+ exitCode = 99;
+
+ // parse args
+ fixCommandLine(&argc, &argv);
+ ok = parseArgs(argDesc, &argc, argv);
+ if (!ok || argc != 3 || printVersion || printHelp) {
+ fprintf(stderr, "pdfimages version %s [www.xpdfreader.com]\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdfimages", "<PDF-file> <image-root>", argDesc);
+ }
+ goto err0;
+ }
+ fileName = argv[1];
+ imgRoot = argv[2];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+ if (quiet) {
+ globalParams->setErrQuiet(quiet);
+ }
+
+ // open PDF file
+ if (ownerPassword[0] != '\001') {
+ ownerPW = new GString(ownerPassword);
+ } else {
+ ownerPW = NULL;
+ }
+ if (userPassword[0] != '\001') {
+ userPW = new GString(userPassword);
+ } else {
+ userPW = NULL;
+ }
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+ if (userPW) {
+ delete userPW;
+ }
+ if (ownerPW) {
+ delete ownerPW;
+ }
+ if (!doc->isOk()) {
+ exitCode = 1;
+ goto err1;
+ }
+
+ // check for copy permission
+ if (!doc->okToCopy()) {
+ error(errNotAllowed, -1,
+ "Copying of images from this document is not allowed.");
+ exitCode = 3;
+ goto err1;
+ }
+
+ // get page range
+ if (firstPage < 1)
+ firstPage = 1;
+ if (lastPage < 1 || lastPage > doc->getNumPages())
+ lastPage = doc->getNumPages();
+
+ // write image files
+ imgOut = new ImageOutputDev(imgRoot, dumpJPEG, dumpRaw, list);
+ if (imgOut->isOk()) {
+ doc->displayPages(imgOut, firstPage, lastPage, 72, 72, 0,
+ gFalse, gTrue, gFalse);
+ }
+ delete imgOut;
+
+ exitCode = 0;
+
+ // clean up
+ err1:
+ delete doc;
+ delete globalParams;
+ err0:
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc
new file mode 100644
index 00000000000..621fb69ea6f
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdfinfo.cc
@@ -0,0 +1,551 @@
+//========================================================================
+//
+// pdfinfo.cc
+//
+// Copyright 1998-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include <ctype.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "parseargs.h"
+#include "GString.h"
+#include "gfile.h"
+#include "GlobalParams.h"
+#include "Object.h"
+#include "Stream.h"
+#include "Array.h"
+#include "Dict.h"
+#include "XRef.h"
+#include "Catalog.h"
+#include "Page.h"
+#include "PDFDoc.h"
+#include "CharTypes.h"
+#include "UnicodeMap.h"
+#include "TextString.h"
+#include "UTF8.h"
+#include "Zoox.h"
+#include "Error.h"
+#include "config.h"
+
+static void printInfoString(Object *infoDict, const char *infoKey,
+ ZxDoc *xmp, const char *xmpKey1,
+ const char *xmpKey2,
+ const char *text, GBool parseDate,
+ UnicodeMap *uMap);
+static GString *parseInfoDate(GString *s);
+static GString *parseXMPDate(GString *s);
+static void printBox(const char *text, PDFRectangle *box);
+
+static int firstPage = 1;
+static int lastPage = 0;
+static GBool printBoxes = gFalse;
+static GBool printMetadata = gFalse;
+static GBool rawDates = gFalse;
+static char textEncName[128] = "";
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
+static char cfgFileName[256] = "";
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static ArgDesc argDesc[] = {
+ {"-f", argInt, &firstPage, 0,
+ "first page to convert"},
+ {"-l", argInt, &lastPage, 0,
+ "last page to convert"},
+ {"-box", argFlag, &printBoxes, 0,
+ "print the page bounding boxes"},
+ {"-meta", argFlag, &printMetadata, 0,
+ "print the document metadata (XML)"},
+ {"-rawdates", argFlag, &rawDates, 0,
+ "print the undecoded date strings directly from the PDF file"},
+ {"-enc", argString, textEncName, sizeof(textEncName),
+ "output text encoding name"},
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+ {"-cfg", argString, cfgFileName, sizeof(cfgFileName),
+ "configuration file to use in place of .xpdfrc"},
+ {"-v", argFlag, &printVersion, 0,
+ "print copyright and version info"},
+ {"-h", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
+
+int main(int argc, char *argv[]) {
+ PDFDoc *doc;
+ char *fileName;
+ GString *ownerPW, *userPW;
+ UnicodeMap *uMap;
+ Page *page;
+ Object info, xfa;
+ Object *acroForm;
+ char buf[256];
+ double w, h, wISO, hISO;
+ FILE *f;
+ GString *metadata;
+ ZxDoc *xmp;
+ int permFlags, keyLength, encVersion;
+ GBool ownerPasswordOk;
+ CryptAlgorithm encAlgorithm;
+ GBool ok;
+ int exitCode;
+ int pg, i;
+ GBool multiPage;
+
+ exitCode = 99;
+
+ // parse args
+ fixCommandLine(&argc, &argv);
+ ok = parseArgs(argDesc, &argc, argv);
+ if (!ok || argc != 2 || printVersion || printHelp) {
+ fprintf(stderr, "pdfinfo version %s [www.xpdfreader.com]\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdfinfo", "<PDF-file>", argDesc);
+ }
+ goto err0;
+ }
+ fileName = argv[1];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+ if (textEncName[0]) {
+ globalParams->setTextEncoding(textEncName);
+ }
+
+ // get mapping to output encoding
+ if (!(uMap = globalParams->getTextEncoding())) {
+ error(errConfig, -1, "Couldn't get text encoding");
+ goto err1;
+ }
+
+ // open PDF file
+ if (ownerPassword[0] != '\001') {
+ ownerPW = new GString(ownerPassword);
+ } else {
+ ownerPW = NULL;
+ }
+ if (userPassword[0] != '\001') {
+ userPW = new GString(userPassword);
+ } else {
+ userPW = NULL;
+ }
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+ if (userPW) {
+ delete userPW;
+ }
+ if (ownerPW) {
+ delete ownerPW;
+ }
+ if (!doc->isOk()) {
+ exitCode = 1;
+ goto err2;
+ }
+
+ // get page range
+ if (firstPage < 1) {
+ firstPage = 1;
+ }
+ if (lastPage == 0) {
+ multiPage = gFalse;
+ lastPage = 1;
+ } else {
+ multiPage = gTrue;
+ }
+ if (lastPage < 1 || lastPage > doc->getNumPages()) {
+ lastPage = doc->getNumPages();
+ }
+
+ // print doc info
+ doc->getDocInfo(&info);
+ if ((metadata = doc->readMetadata())) {
+ xmp = ZxDoc::loadMem(metadata->getCString(), metadata->getLength());
+ } else {
+ xmp = NULL;
+ }
+ printInfoString(&info, "Title", xmp, "dc:title", NULL, "Title: ", gFalse, uMap);
+ printInfoString(&info, "Subject", xmp, "dc:description", NULL, "Subject: ", gFalse, uMap);
+ printInfoString(&info, "Keywords", xmp, "pdf:Keywords", NULL, "Keywords: ", gFalse, uMap);
+ printInfoString(&info, "Author", xmp, "dc:creator", NULL, "Author: ", gFalse, uMap);
+ printInfoString(&info, "Creator", xmp, "xmp:CreatorTool", NULL, "Creator: ", gFalse, uMap);
+ printInfoString(&info, "Producer", xmp, "pdf:Producer", NULL, "Producer: ", gFalse, uMap);
+ printInfoString(&info, "CreationDate", xmp, "xap:CreateDate", "xmp:CreateDate", "CreationDate: ", !rawDates, uMap);
+ printInfoString(&info, "ModDate", xmp, "xap:ModifyDate", "xmp:ModifyDate", "ModDate: ", !rawDates, uMap);
+ info.free();
+ if (xmp) {
+ delete xmp;
+ }
+
+ // print tagging info
+ printf("Tagged: %s\n",
+ doc->getStructTreeRoot()->isDict() ? "yes" : "no");
+
+ // print form info
+ if ((acroForm = doc->getCatalog()->getAcroForm())->isDict()) {
+ acroForm->dictLookup("XFA", &xfa);
+ if (xfa.isStream() || xfa.isArray()) {
+ if (doc->getCatalog()->getNeedsRendering()) {
+ printf("Form: dynamic XFA\n");
+ } else {
+ printf("Form: static XFA\n");
+ }
+ } else {
+ printf("Form: AcroForm\n");
+ }
+ xfa.free();
+ } else {
+ printf("Form: none\n");
+ }
+
+ // print page count
+ printf("Pages: %d\n", doc->getNumPages());
+
+ // print encryption info
+ if (doc->isEncrypted()) {
+ 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("Encrypted: no\n");
+ }
+
+ // print page size
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ w = doc->getPageCropWidth(pg);
+ h = doc->getPageCropHeight(pg);
+ if (multiPage) {
+ printf("Page %4d size: %g x %g pts", pg, w, h);
+ } else {
+ printf("Page size: %g x %g pts", w, h);
+ }
+ if ((fabs(w - 612) < 0.1 && fabs(h - 792) < 0.1) ||
+ (fabs(w - 792) < 0.1 && fabs(h - 612) < 0.1)) {
+ printf(" (letter)");
+ } else {
+ hISO = sqrt(sqrt(2.0)) * 7200 / 2.54;
+ wISO = hISO / sqrt(2.0);
+ for (i = 0; i <= 6; ++i) {
+ if ((fabs(w - wISO) < 1 && fabs(h - hISO) < 1) ||
+ (fabs(w - hISO) < 1 && fabs(h - wISO) < 1)) {
+ printf(" (A%d)", i);
+ break;
+ }
+ hISO = wISO;
+ wISO /= sqrt(2.0);
+ }
+ }
+ printf(" (rotated %d degrees)", doc->getPageRotate(pg));
+ printf("\n");
+ }
+
+ // print the boxes
+ if (printBoxes) {
+ if (multiPage) {
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ page = doc->getCatalog()->getPage(pg);
+ snprintf(buf, sizeof(buf), "Page %4d MediaBox: ", pg);
+ printBox(buf, page->getMediaBox());
+ snprintf(buf, sizeof(buf), "Page %4d CropBox: ", pg);
+ printBox(buf, page->getCropBox());
+ snprintf(buf, sizeof(buf), "Page %4d BleedBox: ", pg);
+ printBox(buf, page->getBleedBox());
+ snprintf(buf, sizeof(buf), "Page %4d TrimBox: ", pg);
+ printBox(buf, page->getTrimBox());
+ snprintf(buf, sizeof(buf), "Page %4d ArtBox: ", pg);
+ printBox(buf, page->getArtBox());
+ }
+ } else if (doc->getNumPages() > 0) {
+ page = doc->getCatalog()->getPage(firstPage);
+ printBox("MediaBox: ", page->getMediaBox());
+ printBox("CropBox: ", page->getCropBox());
+ printBox("BleedBox: ", page->getBleedBox());
+ printBox("TrimBox: ", page->getTrimBox());
+ printBox("ArtBox: ", page->getArtBox());
+ }
+ }
+
+ // print file size
+ f = openFile(fileName, "rb");
+ if (f) {
+ gfseek(f, 0, SEEK_END);
+ printf("File size: %u bytes\n", (Guint)gftell(f));
+ fclose(f);
+ }
+
+ // print linearization info
+ printf("Optimized: %s\n", doc->isLinearized() ? "yes" : "no");
+
+ // print PDF version
+ printf("PDF version: %.1f\n", doc->getPDFVersion());
+
+ // print the metadata
+ if (printMetadata && metadata) {
+ fputs("Metadata:\n", stdout);
+ fputs(metadata->getCString(), stdout);
+ fputc('\n', stdout);
+ }
+
+ if (metadata) {
+ delete metadata;
+ }
+
+ exitCode = 0;
+
+ // clean up
+ err2:
+ uMap->decRefCnt();
+ delete doc;
+ err1:
+ delete globalParams;
+ err0:
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}
+
+static void printInfoString(Object *infoDict, const char *infoKey,
+ ZxDoc *xmp, const char *xmpKey1,
+ const char *xmpKey2,
+ const char *text, GBool parseDate,
+ UnicodeMap *uMap) {
+ Object obj;
+ TextString *s;
+ Unicode *u;
+ Unicode uu;
+ char buf[8];
+ GString *value, *tmp;
+ ZxElement *rdf, *elem, *child;
+ ZxNode *node, *node2;
+ int i, n;
+
+ value = NULL;
+
+ //-- check the info dictionary
+ if (infoDict->isDict()) {
+ if (infoDict->dictLookup(infoKey, &obj)->isString()) {
+ if (!parseDate || !(value = parseInfoDate(obj.getString()))) {
+ s = new TextString(obj.getString());
+ u = s->getUnicode();
+ value = new GString();
+ for (i = 0; i < s->getLength(); ++i) {
+ n = uMap->mapUnicode(u[i], buf, sizeof(buf));
+ value->append(buf, n);
+ }
+ delete s;
+ }
+ }
+ obj.free();
+ }
+
+ //-- check the XMP metadata
+ if (xmp) {
+ rdf = xmp->getRoot();
+ if (rdf->isElement("x:xmpmeta")) {
+ rdf = rdf->findFirstChildElement("rdf:RDF");
+ }
+ if (rdf && rdf->isElement("rdf:RDF")) {
+ for (node = rdf->getFirstChild(); node; node = node->getNextChild()) {
+ if (node->isElement("rdf:Description")) {
+ if (!(elem = node->findFirstChildElement(xmpKey1)) && xmpKey2) {
+ elem = node->findFirstChildElement(xmpKey2);
+ }
+ if (elem) {
+ if ((child = elem->findFirstChildElement("rdf:Alt")) ||
+ (child = elem->findFirstChildElement("rdf:Seq"))) {
+ if ((node2 = child->findFirstChildElement("rdf:li"))) {
+ node2 = node2->getFirstChild();
+ }
+ } else {
+ node2 = elem->getFirstChild();
+ }
+ if (node2 && node2->isCharData()) {
+ if (value) {
+ delete value;
+ }
+ if (!parseDate ||
+ !(value = parseXMPDate(((ZxCharData *)node2)->getData()))) {
+ tmp = ((ZxCharData *)node2)->getData();
+ int i = 0;
+ value = new GString();
+ while (getUTF8(tmp, &i, &uu)) {
+ n = uMap->mapUnicode(uu, buf, sizeof(buf));
+ value->append(buf, n);
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (value) {
+ fputs(text, stdout);
+ fwrite(value->getCString(), 1, value->getLength(), stdout);
+ fputc('\n', stdout);
+ delete value;
+ }
+}
+
+static GString *parseInfoDate(GString *s) {
+ int year, mon, day, hour, min, sec, n;
+ struct tm tmStruct;
+ char buf[256];
+ char *p;
+
+ p = s->getCString();
+ if (p[0] == 'D' && p[1] == ':') {
+ p += 2;
+ }
+ if ((n = sscanf(p, "%4d%2d%2d%2d%2d%2d",
+ &year, &mon, &day, &hour, &min, &sec)) < 1) {
+ return NULL;
+ }
+ switch (n) {
+ case 1: mon = 1;
+ case 2: day = 1;
+ case 3: hour = 0;
+ case 4: min = 0;
+ case 5: sec = 0;
+ }
+ tmStruct.tm_year = year - 1900;
+ tmStruct.tm_mon = mon - 1;
+ tmStruct.tm_mday = day;
+ tmStruct.tm_hour = hour;
+ tmStruct.tm_min = min;
+ tmStruct.tm_sec = sec;
+ tmStruct.tm_wday = -1;
+ tmStruct.tm_yday = -1;
+ tmStruct.tm_isdst = -1;
+ // compute the tm_wday and tm_yday fields
+ if (!(mktime(&tmStruct) != (time_t)-1 &&
+ strftime(buf, sizeof(buf), "%c", &tmStruct))) {
+ return NULL;
+ }
+ return new GString(buf);
+}
+
+static GString *parseXMPDate(GString *s) {
+ int year, mon, day, hour, min, sec, tz;
+ struct tm tmStruct;
+ char buf[256];
+ char *p;
+
+ p = s->getCString();
+ if (isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2]) && isdigit(p[3])) {
+ buf[0] = p[0];
+ buf[1] = p[1];
+ buf[2] = p[2];
+ buf[3] = p[3];
+ buf[4] = '\0';
+ year = atoi(buf);
+ p += 4;
+ } else {
+ return NULL;
+ }
+ mon = day = 1;
+ hour = min = sec = 0;
+ tz = 2000;
+ if (p[0] == '-' && isdigit(p[1]) && isdigit(p[2])) {
+ buf[0] = p[1];
+ buf[1] = p[2];
+ buf[2] = '\0';
+ mon = atoi(buf);
+ p += 3;
+ if (p[0] == '-' && isdigit(p[1]) && isdigit(p[2])) {
+ buf[0] = p[1];
+ buf[1] = p[2];
+ buf[2] = '\0';
+ day = atoi(buf);
+ p += 3;
+ if (p[0] == 'T' && isdigit(p[1]) && isdigit(p[2]) &&
+ p[3] == ':' && isdigit(p[4]) && isdigit(p[5])) {
+ buf[0] = p[1];
+ buf[1] = p[2];
+ buf[2] = '\0';
+ hour = atoi(buf);
+ buf[0] = p[4];
+ buf[1] = p[5];
+ buf[2] = '\0';
+ min = atoi(buf);
+ p += 6;
+ if (p[0] == ':' && isdigit(p[1]) && isdigit(p[2])) {
+ buf[0] = p[1];
+ buf[1] = p[2];
+ buf[2] = '\0';
+ sec = atoi(buf);
+ if (p[0] == '.' && isdigit(p[1])) {
+ p += 2;
+ }
+ }
+ if ((p[0] == '+' || p[0] == '-') &&
+ isdigit(p[1]) && isdigit(p[2]) && p[3] == ':' &&
+ isdigit(p[4]) && isdigit(p[5])) {
+ buf[0] = p[1];
+ buf[1] = p[2];
+ buf[2] = '\0';
+ tz = atoi(buf);
+ buf[0] = p[4];
+ buf[1] = p[5];
+ buf[2] = '\0';
+ tz = tz * 60 + atoi(buf);
+ tz = tz * 60;
+ if (p[0] == '-') {
+ tz = -tz;
+ }
+ }
+ }
+ }
+ }
+
+ tmStruct.tm_year = year - 1900;
+ tmStruct.tm_mon = mon - 1;
+ tmStruct.tm_mday = day;
+ tmStruct.tm_hour = hour;
+ tmStruct.tm_min = min;
+ tmStruct.tm_sec = sec;
+ tmStruct.tm_wday = -1;
+ tmStruct.tm_yday = -1;
+ tmStruct.tm_isdst = -1;
+ // compute the tm_wday and tm_yday fields
+ //~ this ignores the timezone
+ if (!(mktime(&tmStruct) != (time_t)-1 &&
+ strftime(buf, sizeof(buf), "%c", &tmStruct))) {
+ return NULL;
+ }
+ return new GString(buf);
+}
+
+static void printBox(const char *text, PDFRectangle *box) {
+ printf("%s%8.2f %8.2f %8.2f %8.2f\n",
+ text, box->x1, box->y1, box->x2, box->y2);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc
new file mode 100644
index 00000000000..af8fe93fbd5
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftohtml.cc
@@ -0,0 +1,260 @@
+//========================================================================
+//
+// pdftohtml.cc
+//
+// Copyright 2005 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "parseargs.h"
+#include "gfile.h"
+#include "GString.h"
+#include "GlobalParams.h"
+#include "PDFDoc.h"
+#include "HTMLGen.h"
+#include "Error.h"
+#include "ErrorCodes.h"
+#include "config.h"
+
+//------------------------------------------------------------------------
+
+static GBool createIndex(char *htmlDir);
+
+//------------------------------------------------------------------------
+
+static int firstPage = 1;
+static int lastPage = 0;
+static double zoom = 1;
+static int resolution = 150;
+static GBool noFonts = gFalse;
+static GBool skipInvisible = gFalse;
+static GBool allInvisible = gFalse;
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
+static GBool quiet = gFalse;
+static char cfgFileName[256] = "";
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static ArgDesc argDesc[] = {
+ {"-f", argInt, &firstPage, 0,
+ "first page to convert"},
+ {"-l", argInt, &lastPage, 0,
+ "last page to convert"},
+ {"-z", argFP, &zoom, 0,
+ "initial zoom level (1.0 means 72dpi)"},
+ {"-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,
+ "treat all text as invisible"},
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+ {"-q", argFlag, &quiet, 0,
+ "don't print any messages or errors"},
+ {"-cfg", argString, cfgFileName, sizeof(cfgFileName),
+ "configuration file to use in place of .xpdfrc"},
+ {"-v", argFlag, &printVersion, 0,
+ "print copyright and version info"},
+ {"-h", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
+
+//------------------------------------------------------------------------
+
+static int writeToFile(void *file, const char *data, int size) {
+ return (int)fwrite(data, 1, size, (FILE *)file);
+}
+
+int main(int argc, char *argv[]) {
+ PDFDoc *doc;
+ char *fileName;
+ char *htmlDir;
+ GString *ownerPW, *userPW;
+ HTMLGen *htmlGen;
+ GString *htmlFileName, *pngFileName, *pngURL;
+ FILE *htmlFile, *pngFile;
+ int pg, err, exitCode;
+ GBool ok;
+
+ exitCode = 99;
+
+ // parse args
+ fixCommandLine(&argc, &argv);
+ ok = parseArgs(argDesc, &argc, argv);
+ if (!ok || argc != 3 || printVersion || printHelp) {
+ fprintf(stderr, "pdftohtml version %s [www.xpdfreader.com]\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdftohtml", "<PDF-file> <html-dir>", argDesc);
+ }
+ goto err0;
+ }
+ fileName = argv[1];
+ htmlDir = argv[2];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+ if (quiet) {
+ globalParams->setErrQuiet(quiet);
+ }
+ globalParams->setupBaseFonts(NULL);
+ globalParams->setTextEncoding("UTF-8");
+
+ // open PDF file
+ if (ownerPassword[0] != '\001') {
+ ownerPW = new GString(ownerPassword);
+ } else {
+ ownerPW = NULL;
+ }
+ if (userPassword[0] != '\001') {
+ userPW = new GString(userPassword);
+ } else {
+ userPW = NULL;
+ }
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+ if (userPW) {
+ delete userPW;
+ }
+ if (ownerPW) {
+ delete ownerPW;
+ }
+ if (!doc->isOk()) {
+ exitCode = 1;
+ goto err1;
+ }
+
+ // check for copy permission
+ if (!doc->okToCopy()) {
+ error(errNotAllowed, -1,
+ "Copying of text from this document is not allowed.");
+ exitCode = 3;
+ goto err1;
+ }
+
+ // get page range
+ if (firstPage < 1) {
+ firstPage = 1;
+ }
+ if (lastPage < 1 || lastPage > doc->getNumPages()) {
+ lastPage = doc->getNumPages();
+ }
+
+ // create HTML directory
+ if (makeDir(htmlDir, 0755)) {
+ error(errIO, -1, "Couldn't create HTML output directory '{0:s}'",
+ htmlDir);
+ exitCode = 2;
+ goto err1;
+ }
+
+ // set up the HTMLGen object
+ htmlGen = new HTMLGen(resolution);
+ if (!htmlGen->isOk()) {
+ exitCode = 99;
+ goto err1;
+ }
+ htmlGen->setZoom(zoom);
+ htmlGen->setDrawInvisibleText(!skipInvisible);
+ htmlGen->setAllTextInvisible(allInvisible);
+ htmlGen->setExtractFontFiles(!noFonts);
+ htmlGen->startDoc(doc);
+
+ // convert the pages
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ htmlFileName = GString::format("{0:s}/page{1:d}.html", htmlDir, pg);
+ pngFileName = GString::format("{0:s}/page{1:d}.png", htmlDir, pg);
+ if (!(htmlFile = openFile(htmlFileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open HTML file '{0:t}'", htmlFileName);
+ delete htmlFileName;
+ delete pngFileName;
+ goto err2;
+ }
+ if (!(pngFile = openFile(pngFileName->getCString(), "wb"))) {
+ error(errIO, -1, "Couldn't open PNG file '{0:t}'", pngFileName);
+ fclose(htmlFile);
+ delete htmlFileName;
+ delete pngFileName;
+ goto err2;
+ }
+ pngURL = GString::format("page{0:d}.png", pg);
+ err = htmlGen->convertPage(pg, pngURL->getCString(), htmlDir,
+ &writeToFile, htmlFile,
+ &writeToFile, pngFile);
+ delete pngURL;
+ fclose(htmlFile);
+ fclose(pngFile);
+ delete htmlFileName;
+ delete pngFileName;
+ if (err != errNone) {
+ error(errIO, -1, "Error converting page {0:d}", pg);
+ exitCode = 2;
+ goto err2;
+ }
+ }
+
+ // create the master index
+ if (!createIndex(htmlDir)) {
+ exitCode = 2;
+ goto err2;
+ }
+
+ exitCode = 0;
+
+ // clean up
+ err2:
+ delete htmlGen;
+ err1:
+ delete doc;
+ delete globalParams;
+ err0:
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}
+
+static GBool createIndex(char *htmlDir) {
+ GString *htmlFileName;
+ FILE *html;
+ int pg;
+
+ htmlFileName = GString::format("{0:s}/index.html", htmlDir);
+ html = openFile(htmlFileName->getCString(), "w");
+ delete htmlFileName;
+ if (!html) {
+ error(errIO, -1, "Couldn't open HTML file '{0:t}'", htmlFileName);
+ return gFalse;
+ }
+
+ fprintf(html, "<html>\n");
+ fprintf(html, "<body>\n");
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ fprintf(html, "<a href=\"page%d.html\">page %d</a><br>\n", pg, pg);
+ }
+ fprintf(html, "</body>\n");
+ fprintf(html, "</html>\n");
+
+ fclose(html);
+
+ return gTrue;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc
new file mode 100644
index 00000000000..f19baccf581
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftopng.cc
@@ -0,0 +1,366 @@
+//========================================================================
+//
+// pdftopng.cc
+//
+// Copyright 2009 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <png.h>
+#ifdef _WIN32
+# include <io.h>
+# include <fcntl.h>
+#endif
+#include "gmem.h"
+#include "gmempp.h"
+#include "parseargs.h"
+#include "GString.h"
+#include "gfile.h"
+#include "GlobalParams.h"
+#include "Object.h"
+#include "PDFDoc.h"
+#include "SplashBitmap.h"
+#include "Splash.h"
+#include "SplashOutputDev.h"
+#include "config.h"
+
+static int firstPage = 1;
+static int lastPage = 0;
+static double resolution = 150;
+static GBool mono = gFalse;
+static GBool gray = gFalse;
+static GBool pngAlpha = gFalse;
+static int rotate = 0;
+static char enableFreeTypeStr[16] = "";
+static char antialiasStr[16] = "";
+static char vectorAntialiasStr[16] = "";
+static char ownerPassword[33] = "";
+static char userPassword[33] = "";
+static GBool quiet = gFalse;
+static char cfgFileName[256] = "";
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static ArgDesc argDesc[] = {
+ {"-f", argInt, &firstPage, 0,
+ "first page to print"},
+ {"-l", argInt, &lastPage, 0,
+ "last page to print"},
+ {"-r", argFP, &resolution, 0,
+ "resolution, in DPI (default is 150)"},
+ {"-mono", argFlag, &mono, 0,
+ "generate a monochrome PNG file"},
+ {"-gray", argFlag, &gray, 0,
+ "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"},
+#endif
+ {"-aa", argString, antialiasStr, sizeof(antialiasStr),
+ "enable font anti-aliasing: yes, no"},
+ {"-aaVector", argString, vectorAntialiasStr, sizeof(vectorAntialiasStr),
+ "enable vector anti-aliasing: yes, no"},
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+ {"-q", argFlag, &quiet, 0,
+ "don't print any messages or errors"},
+ {"-cfg", argString, cfgFileName, sizeof(cfgFileName),
+ "configuration file to use in place of .xpdfrc"},
+ {"-v", argFlag, &printVersion, 0,
+ "print copyright and version info"},
+ {"-h", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
+
+static void setupPNG(png_structp *png, png_infop *pngInfo, FILE *f,
+ int bitDepth, int colorType, double res,
+ SplashBitmap *bitmap);
+static void writePNGData(png_structp png, SplashBitmap *bitmap);
+static void finishPNG(png_structp *png, png_infop *pngInfo);
+
+int main(int argc, char *argv[]) {
+ PDFDoc *doc;
+ char *fileName;
+ char *pngRoot;
+ GString *pngFile;
+ GString *ownerPW, *userPW;
+ SplashColor paperColor;
+ SplashOutputDev *splashOut;
+ GBool ok;
+ int exitCode;
+ int pg;
+ png_structp png;
+ png_infop pngInfo;
+ FILE *f;
+
+ exitCode = 99;
+
+ // parse args
+ fixCommandLine(&argc, &argv);
+ ok = parseArgs(argDesc, &argc, argv);
+ if (mono && gray) {
+ fprintf(stderr, "Specify one of -mono or -gray\n");
+ goto err0;
+ }
+ if (mono && pngAlpha) {
+ fprintf(stderr, "The -alpha flag cannot be used with -mono\n");
+ goto err0;
+ }
+ if (!ok || argc != 3 || printVersion || printHelp) {
+ fprintf(stderr, "pdftopng version %s [www.xpdfreader.com]\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdftopng", "<PDF-file> <PNG-root>", argDesc);
+ }
+ goto err0;
+ }
+ fileName = argv[1];
+ pngRoot = argv[2];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+ globalParams->setupBaseFonts(NULL);
+ if (enableFreeTypeStr[0]) {
+ if (!globalParams->setEnableFreeType(enableFreeTypeStr)) {
+ fprintf(stderr, "Bad '-freetype' value on command line\n");
+ }
+ }
+ if (antialiasStr[0]) {
+ if (!globalParams->setAntialias(antialiasStr)) {
+ fprintf(stderr, "Bad '-aa' value on command line\n");
+ }
+ }
+ if (vectorAntialiasStr[0]) {
+ if (!globalParams->setVectorAntialias(vectorAntialiasStr)) {
+ fprintf(stderr, "Bad '-aaVector' value on command line\n");
+ }
+ }
+ if (quiet) {
+ globalParams->setErrQuiet(quiet);
+ }
+
+ // open PDF file
+ if (ownerPassword[0]) {
+ ownerPW = new GString(ownerPassword);
+ } else {
+ ownerPW = NULL;
+ }
+ if (userPassword[0]) {
+ userPW = new GString(userPassword);
+ } else {
+ userPW = NULL;
+ }
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+ if (userPW) {
+ delete userPW;
+ }
+ if (ownerPW) {
+ delete ownerPW;
+ }
+ if (!doc->isOk()) {
+ exitCode = 1;
+ goto err1;
+ }
+
+ // get page range
+ if (firstPage < 1)
+ firstPage = 1;
+ if (lastPage < 1 || lastPage > doc->getNumPages())
+ lastPage = doc->getNumPages();
+
+
+ // write PNG files
+ if (mono) {
+ paperColor[0] = 0xff;
+ splashOut = new SplashOutputDev(splashModeMono1, 1, gFalse, paperColor);
+ } else if (gray) {
+ paperColor[0] = 0xff;
+ splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, paperColor);
+ } else {
+ paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
+ splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor);
+ }
+ if (pngAlpha) {
+ splashOut->setNoComposite(gTrue);
+ }
+ splashOut->startDoc(doc->getXRef());
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ doc->displayPage(splashOut, pg, resolution, resolution, rotate,
+ gFalse, gTrue, gFalse);
+ if (mono) {
+ if (!strcmp(pngRoot, "-")) {
+ f = stdout;
+#ifdef _WIN32
+ _setmode(_fileno(f), _O_BINARY);
+#endif
+ } else {
+ pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg);
+ if (!(f = openFile(pngFile->getCString(), "wb"))) {
+ exit(2);
+ }
+ delete pngFile;
+ }
+ setupPNG(&png, &pngInfo, f,
+ 1, PNG_COLOR_TYPE_GRAY, resolution, splashOut->getBitmap());
+ writePNGData(png, splashOut->getBitmap());
+ finishPNG(&png, &pngInfo);
+ fclose(f);
+ } else if (gray) {
+ if (!strcmp(pngRoot, "-")) {
+ f = stdout;
+#ifdef _WIN32
+ _setmode(_fileno(f), _O_BINARY);
+#endif
+ } else {
+ pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg);
+ if (!(f = openFile(pngFile->getCString(), "wb"))) {
+ exit(2);
+ }
+ delete pngFile;
+ }
+ setupPNG(&png, &pngInfo, f,
+ 8, pngAlpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY,
+ resolution, splashOut->getBitmap());
+ writePNGData(png, splashOut->getBitmap());
+ finishPNG(&png, &pngInfo);
+ fclose(f);
+ } else { // RGB
+ if (!strcmp(pngRoot, "-")) {
+ f = stdout;
+#ifdef _WIN32
+ _setmode(_fileno(f), _O_BINARY);
+#endif
+ } else {
+ pngFile = GString::format("{0:s}-{1:06d}.png", pngRoot, pg);
+ if (!(f = openFile(pngFile->getCString(), "wb"))) {
+ exit(2);
+ }
+ delete pngFile;
+ }
+ setupPNG(&png, &pngInfo, f,
+ 8, pngAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB,
+ resolution, splashOut->getBitmap());
+ writePNGData(png, splashOut->getBitmap());
+ finishPNG(&png, &pngInfo);
+ fclose(f);
+ }
+ }
+ delete splashOut;
+
+ exitCode = 0;
+
+ // clean up
+ err1:
+ delete doc;
+ delete globalParams;
+ err0:
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}
+
+static void setupPNG(png_structp *png, png_infop *pngInfo, FILE *f,
+ int bitDepth, int colorType, double res,
+ SplashBitmap *bitmap) {
+ png_color_16 background;
+ int pixelsPerMeter;
+
+ if (!(*png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL)) ||
+ !(*pngInfo = png_create_info_struct(*png))) {
+ exit(2);
+ }
+ if (setjmp(png_jmpbuf(*png))) {
+ exit(2);
+ }
+ png_init_io(*png, f);
+ png_set_IHDR(*png, *pngInfo, bitmap->getWidth(), bitmap->getHeight(),
+ bitDepth, colorType, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ if (colorType == PNG_COLOR_TYPE_GRAY_ALPHA ||
+ colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
+ background.index = 0;
+ background.red = 0xff;
+ background.green = 0xff;
+ background.blue = 0xff;
+ background.gray = 0xff;
+ png_set_bKGD(*png, *pngInfo, &background);
+ }
+ pixelsPerMeter = (int)(res * (1000 / 25.4) + 0.5);
+ png_set_pHYs(*png, *pngInfo, pixelsPerMeter, pixelsPerMeter,
+ PNG_RESOLUTION_METER);
+ png_write_info(*png, *pngInfo);
+}
+
+static void writePNGData(png_structp png, SplashBitmap *bitmap) {
+ Guchar *p, *alpha, *rowBuf, *rowBufPtr;
+ int y, x;
+
+ if (setjmp(png_jmpbuf(png))) {
+ exit(2);
+ }
+ p = bitmap->getDataPtr();
+ if (pngAlpha) {
+ alpha = bitmap->getAlphaPtr();
+ if (bitmap->getMode() == splashModeMono8) {
+ rowBuf = (Guchar *)gmallocn(bitmap->getWidth(), 2);
+ for (y = 0; y < bitmap->getHeight(); ++y) {
+ rowBufPtr = rowBuf;
+ for (x = 0; x < bitmap->getWidth(); ++x) {
+ *rowBufPtr++ = *p++;
+ *rowBufPtr++ = *alpha++;
+ }
+ png_write_row(png, (png_bytep)rowBuf);
+ }
+ gfree(rowBuf);
+ } else { // splashModeRGB8
+ rowBuf = (Guchar *)gmallocn(bitmap->getWidth(), 4);
+ for (y = 0; y < bitmap->getHeight(); ++y) {
+ rowBufPtr = rowBuf;
+ for (x = 0; x < bitmap->getWidth(); ++x) {
+ *rowBufPtr++ = *p++;
+ *rowBufPtr++ = *p++;
+ *rowBufPtr++ = *p++;
+ *rowBufPtr++ = *alpha++;
+ }
+ png_write_row(png, (png_bytep)rowBuf);
+ }
+ gfree(rowBuf);
+ }
+ } else {
+ for (y = 0; y < bitmap->getHeight(); ++y) {
+ png_write_row(png, (png_bytep)p);
+ p += bitmap->getRowSize();
+ }
+ }
+}
+
+
+
+
+static void finishPNG(png_structp *png, png_infop *pngInfo) {
+ if (setjmp(png_jmpbuf(*png))) {
+ exit(2);
+ }
+ png_write_end(*png, *pngInfo);
+ png_destroy_write_struct(png, pngInfo);
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc
new file mode 100644
index 00000000000..0b084b014e5
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftoppm.cc
@@ -0,0 +1,260 @@
+//========================================================================
+//
+// pdftoppm.cc
+//
+// Copyright 2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdio.h>
+#ifdef _WIN32
+# include <io.h>
+# include <fcntl.h>
+#endif
+#ifdef DEBUG_FP_LINUX
+# include <fenv.h>
+# include <fpu_control.h>
+#endif
+#include "gmem.h"
+#include "gmempp.h"
+#include "parseargs.h"
+#include "GString.h"
+#include "GlobalParams.h"
+#include "Object.h"
+#include "PDFDoc.h"
+#include "SplashBitmap.h"
+#include "Splash.h"
+#include "SplashOutputDev.h"
+#include "config.h"
+
+static int firstPage = 1;
+static int lastPage = 0;
+static double resolution = 150;
+static GBool mono = gFalse;
+static GBool gray = gFalse;
+#if SPLASH_CMYK
+static GBool cmyk = gFalse;
+#endif
+static int rotate = 0;
+static char enableFreeTypeStr[16] = "";
+static char antialiasStr[16] = "";
+static char vectorAntialiasStr[16] = "";
+static char ownerPassword[33] = "";
+static char userPassword[33] = "";
+static GBool quiet = gFalse;
+static char cfgFileName[256] = "";
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static ArgDesc argDesc[] = {
+ {"-f", argInt, &firstPage, 0,
+ "first page to print"},
+ {"-l", argInt, &lastPage, 0,
+ "last page to print"},
+ {"-r", argFP, &resolution, 0,
+ "resolution, in DPI (default is 150)"},
+ {"-mono", argFlag, &mono, 0,
+ "generate a monochrome PBM file"},
+ {"-gray", argFlag, &gray, 0,
+ "generate a grayscale PGM file"},
+#if SPLASH_CMYK
+ {"-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"},
+#endif
+ {"-aa", argString, antialiasStr, sizeof(antialiasStr),
+ "enable font anti-aliasing: yes, no"},
+ {"-aaVector", argString, vectorAntialiasStr, sizeof(vectorAntialiasStr),
+ "enable vector anti-aliasing: yes, no"},
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+ {"-q", argFlag, &quiet, 0,
+ "don't print any messages or errors"},
+ {"-cfg", argString, cfgFileName, sizeof(cfgFileName),
+ "configuration file to use in place of .xpdfrc"},
+ {"-v", argFlag, &printVersion, 0,
+ "print copyright and version info"},
+ {"-h", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
+
+int main(int argc, char *argv[]) {
+ PDFDoc *doc;
+ char *fileName;
+ char *ppmRoot;
+ GString *ppmFile;
+ GString *ownerPW, *userPW;
+ SplashColor paperColor;
+ SplashOutputDev *splashOut;
+ GBool ok;
+ int exitCode;
+ int pg, n;
+ const char *ext;
+
+#ifdef DEBUG_FP_LINUX
+ // enable exceptions on floating point div-by-zero
+ feenableexcept(FE_DIVBYZERO);
+ // force 64-bit rounding: this avoids changes in output when minor
+ // code changes result in spills of x87 registers; it also avoids
+ // differences in output with valgrind's 64-bit floating point
+ // emulation (yes, this is a kludge; but it's pretty much
+ // unavoidable given the x87 instruction set; see gcc bug 323 for
+ // more info)
+ fpu_control_t cw;
+ _FPU_GETCW(cw);
+ cw = (fpu_control_t)((cw & ~_FPU_EXTENDED) | _FPU_DOUBLE);
+ _FPU_SETCW(cw);
+#endif
+
+ exitCode = 99;
+
+ // parse args
+ fixCommandLine(&argc, &argv);
+ ok = parseArgs(argDesc, &argc, argv);
+ n = 0;
+ n += mono ? 1 : 0;
+ n += gray ? 1 : 0;
+#if SPLASH_CMYK
+ n += cmyk ? 1 : 0;
+#endif
+ if (n > 1) {
+ ok = gFalse;
+ }
+ if (!ok || argc != 3 || printVersion || printHelp) {
+ fprintf(stderr, "pdftoppm version %s [www.xpdfreader.com]\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdftoppm", "<PDF-file> <PPM-root>", argDesc);
+ }
+ goto err0;
+ }
+ fileName = argv[1];
+ ppmRoot = argv[2];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+ globalParams->setupBaseFonts(NULL);
+ if (enableFreeTypeStr[0]) {
+ if (!globalParams->setEnableFreeType(enableFreeTypeStr)) {
+ fprintf(stderr, "Bad '-freetype' value on command line\n");
+ }
+ }
+ if (antialiasStr[0]) {
+ if (!globalParams->setAntialias(antialiasStr)) {
+ fprintf(stderr, "Bad '-aa' value on command line\n");
+ }
+ }
+ if (vectorAntialiasStr[0]) {
+ if (!globalParams->setVectorAntialias(vectorAntialiasStr)) {
+ fprintf(stderr, "Bad '-aaVector' value on command line\n");
+ }
+ }
+ if (quiet) {
+ globalParams->setErrQuiet(quiet);
+ }
+
+ // open PDF file
+ if (ownerPassword[0]) {
+ ownerPW = new GString(ownerPassword);
+ } else {
+ ownerPW = NULL;
+ }
+ if (userPassword[0]) {
+ userPW = new GString(userPassword);
+ } else {
+ userPW = NULL;
+ }
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+ if (userPW) {
+ delete userPW;
+ }
+ if (ownerPW) {
+ delete ownerPW;
+ }
+ if (!doc->isOk()) {
+ exitCode = 1;
+ goto err1;
+ }
+
+ // get page range
+ if (firstPage < 1)
+ firstPage = 1;
+ if (lastPage < 1 || lastPage > doc->getNumPages())
+ lastPage = doc->getNumPages();
+
+ // file name extension
+ if (mono) {
+ ext = "pbm";
+ } else if (gray) {
+ ext = "pgm";
+#if SPLASH_CMYK
+ } else if (cmyk) {
+ ext = "pam";
+#endif
+ } else {
+ ext = "ppm";
+ }
+
+
+ // write PPM files
+ if (mono) {
+ paperColor[0] = 0xff;
+ splashOut = new SplashOutputDev(splashModeMono1, 1, gFalse, paperColor);
+ } else if (gray) {
+ paperColor[0] = 0xff;
+ splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, paperColor);
+#if SPLASH_CMYK
+ } else if (cmyk) {
+ paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0;
+ splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse, paperColor);
+#endif // SPLASH_CMYK
+ } else {
+ paperColor[0] = paperColor[1] = paperColor[2] = 0xff;
+ splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor);
+ }
+ splashOut->startDoc(doc->getXRef());
+ for (pg = firstPage; pg <= lastPage; ++pg) {
+ doc->displayPage(splashOut, pg, resolution, resolution, rotate,
+ gFalse, gTrue, gFalse);
+ if (!strcmp(ppmRoot, "-")) {
+#ifdef _WIN32
+ _setmode(_fileno(stdout), _O_BINARY);
+#endif
+ splashOut->getBitmap()->writePNMFile(stdout);
+ } else {
+ ppmFile = GString::format("{0:s}-{1:06d}.{2:s}", ppmRoot, pg, ext);
+ splashOut->getBitmap()->writePNMFile(ppmFile->getCString());
+ delete ppmFile;
+ }
+ }
+ delete splashOut;
+
+ exitCode = 0;
+
+ // clean up
+ err1:
+ delete doc;
+ delete globalParams;
+ err0:
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc
new file mode 100644
index 00000000000..36294551a3b
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftops.cc
@@ -0,0 +1,386 @@
+//========================================================================
+//
+// pdftops.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#ifdef DEBUG_FP_LINUX
+# include <fenv.h>
+# include <fpu_control.h>
+#endif
+#include "gmem.h"
+#include "gmempp.h"
+#include "parseargs.h"
+#include "GString.h"
+#include "GlobalParams.h"
+#include "Object.h"
+#include "Stream.h"
+#include "Array.h"
+#include "Dict.h"
+#include "XRef.h"
+#include "Catalog.h"
+#include "Page.h"
+#include "PDFDoc.h"
+#include "PSOutputDev.h"
+#include "Error.h"
+#include "config.h"
+
+static int firstPage = 1;
+static int lastPage = 0;
+static GBool level1 = gFalse;
+static GBool level1Sep = gFalse;
+static GBool level2 = gFalse;
+static GBool level2Gray = gFalse;
+static GBool level2Sep = gFalse;
+static GBool level3 = gFalse;
+static GBool level3Gray = gFalse;
+static GBool level3Sep = gFalse;
+static GBool doEPS = gFalse;
+static GBool doForm = gFalse;
+#if OPI_SUPPORT
+static GBool doOPI = gFalse;
+#endif
+static GBool noEmbedT1Fonts = gFalse;
+static GBool noEmbedTTFonts = gFalse;
+static GBool noEmbedCIDPSFonts = gFalse;
+static GBool noEmbedCIDTTFonts = gFalse;
+static GBool preload = gFalse;
+static char paperSize[15] = "";
+static int paperWidth = 0;
+static int paperHeight = 0;
+static GBool noCrop = gFalse;
+static GBool expand = gFalse;
+static GBool noShrink = gFalse;
+static GBool noCenter = gFalse;
+static GBool pageCrop = gFalse;
+static GBool userUnit = gFalse;
+static GBool duplex = gFalse;
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
+static GBool quiet = gFalse;
+static char cfgFileName[256] = "";
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static ArgDesc argDesc[] = {
+ {"-f", argInt, &firstPage, 0,
+ "first page to print"},
+ {"-l", argInt, &lastPage, 0,
+ "last page to print"},
+ {"-level1", argFlag, &level1, 0,
+ "generate Level 1 PostScript"},
+ {"-level1sep", argFlag, &level1Sep, 0,
+ "generate Level 1 separable PostScript"},
+ {"-level2", argFlag, &level2, 0,
+ "generate Level 2 PostScript"},
+ {"-level2gray", argFlag, &level2Gray, 0,
+ "generate Level 2 grayscale PostScript"},
+ {"-level2sep", argFlag, &level2Sep, 0,
+ "generate Level 2 separable PostScript"},
+ {"-level3", argFlag, &level3, 0,
+ "generate Level 3 PostScript"},
+ {"-level3gray", argFlag, &level3Gray, 0,
+ "generate Level 3 grayscale PostScript"},
+ {"-level3sep", argFlag, &level3Sep, 0,
+ "generate Level 3 separable PostScript"},
+ {"-eps", argFlag, &doEPS, 0,
+ "generate Encapsulated PostScript (EPS)"},
+ {"-form", argFlag, &doForm, 0,
+ "generate a PostScript form"},
+#if OPI_SUPPORT
+ {"-opi", argFlag, &doOPI, 0,
+ "generate OPI comments"},
+#endif
+ {"-noembt1", argFlag, &noEmbedT1Fonts, 0,
+ "don't embed Type 1 fonts"},
+ {"-noembtt", argFlag, &noEmbedTTFonts, 0,
+ "don't embed TrueType fonts"},
+ {"-noembcidps", argFlag, &noEmbedCIDPSFonts, 0,
+ "don't embed CID PostScript fonts"},
+ {"-noembcidtt", argFlag, &noEmbedCIDTTFonts, 0,
+ "don't embed CID TrueType fonts"},
+ {"-preload", argFlag, &preload, 0,
+ "preload images and forms"},
+ {"-paper", argString, paperSize, sizeof(paperSize),
+ "paper size (letter, legal, A4, A3, match)"},
+ {"-paperw", argInt, &paperWidth, 0,
+ "paper width, in points"},
+ {"-paperh", argInt, &paperHeight, 0,
+ "paper height, in points"},
+ {"-nocrop", argFlag, &noCrop, 0,
+ "don't crop pages to CropBox"},
+ {"-expand", argFlag, &expand, 0,
+ "expand pages smaller than the paper size"},
+ {"-noshrink", argFlag, &noShrink, 0,
+ "don't shrink pages larger than the paper size"},
+ {"-nocenter", argFlag, &noCenter, 0,
+ "don't center pages smaller than the paper size"},
+ {"-pagecrop", argFlag, &pageCrop, 0,
+ "treat the CropBox as the page size"},
+ {"-userunit", argFlag, &userUnit, 0,
+ "honor the UserUnit"},
+ {"-duplex", argFlag, &duplex, 0,
+ "enable duplex printing"},
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+ {"-q", argFlag, &quiet, 0,
+ "don't print any messages or errors"},
+ {"-cfg", argString, cfgFileName, sizeof(cfgFileName),
+ "configuration file to use in place of .xpdfrc"},
+ {"-v", argFlag, &printVersion, 0,
+ "print copyright and version info"},
+ {"-h", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
+
+int main(int argc, char *argv[]) {
+ PDFDoc *doc;
+ char *fileName;
+ GString *psFileName;
+ PSLevel level;
+ PSOutMode mode;
+ GString *ownerPW, *userPW;
+ PSOutputDev *psOut;
+ GBool ok;
+ char *p;
+ int exitCode;
+
+#ifdef DEBUG_FP_LINUX
+ // enable exceptions on floating point div-by-zero
+ feenableexcept(FE_DIVBYZERO);
+ // force 64-bit rounding: this avoids changes in output when minor
+ // code changes result in spills of x87 registers; it also avoids
+ // differences in output with valgrind's 64-bit floating point
+ // emulation (yes, this is a kludge; but it's pretty much
+ // unavoidable given the x87 instruction set; see gcc bug 323 for
+ // more info)
+ fpu_control_t cw;
+ _FPU_GETCW(cw);
+ cw = (fpu_control_t)((cw & ~_FPU_EXTENDED) | _FPU_DOUBLE);
+ _FPU_SETCW(cw);
+#endif
+
+ exitCode = 99;
+
+ // parse args
+ fixCommandLine(&argc, &argv);
+ ok = parseArgs(argDesc, &argc, argv);
+ if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) {
+ fprintf(stderr, "pdftops version %s [www.xpdfreader.com]\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdftops", "<PDF-file> [<PS-file>]", argDesc);
+ }
+ exit(1);
+ }
+ if ((level1 ? 1 : 0) +
+ (level1Sep ? 1 : 0) +
+ (level2 ? 1 : 0) +
+ (level2Gray ? 1 : 0) +
+ (level2Sep ? 1 : 0) +
+ (level3 ? 1 : 0) +
+ (level3Gray ? 1 : 0) +
+ (level3Sep ? 1 : 0) > 1) {
+ fprintf(stderr, "Error: use only one of the 'level' options.\n");
+ exit(1);
+ }
+ if (doEPS && doForm) {
+ fprintf(stderr, "Error: use only one of -eps and -form\n");
+ exit(1);
+ }
+ if (level1) {
+ level = psLevel1;
+ } else if (level1Sep) {
+ level = psLevel1Sep;
+ } else if (level2Gray) {
+ level = psLevel2Gray;
+ } else if (level2Sep) {
+ level = psLevel2Sep;
+ } else if (level3) {
+ level = psLevel3;
+ } else if (level3Gray) {
+ level = psLevel3Gray;
+ } else if (level3Sep) {
+ level = psLevel3Sep;
+ } else {
+ level = psLevel2;
+ }
+ if (doForm && level < psLevel2) {
+ fprintf(stderr, "Error: forms are only available with Level 2 output.\n");
+ exit(1);
+ }
+ mode = doEPS ? psModeEPS
+ : doForm ? psModeForm
+ : psModePS;
+ fileName = argv[1];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+#if HAVE_SPLASH
+ globalParams->setupBaseFonts(NULL);
+#endif
+ if (paperSize[0]) {
+ if (!globalParams->setPSPaperSize(paperSize)) {
+ fprintf(stderr, "Invalid paper size\n");
+ goto err0;
+ }
+ } else {
+ if (paperWidth) {
+ globalParams->setPSPaperWidth(paperWidth);
+ }
+ if (paperHeight) {
+ globalParams->setPSPaperHeight(paperHeight);
+ }
+ }
+ if (noCrop) {
+ globalParams->setPSCrop(gFalse);
+ }
+ if (pageCrop) {
+ globalParams->setPSUseCropBoxAsPage(gTrue);
+ }
+ if (expand) {
+ globalParams->setPSExpandSmaller(gTrue);
+ }
+ if (noShrink) {
+ globalParams->setPSShrinkLarger(gFalse);
+ }
+ if (noCenter) {
+ globalParams->setPSCenter(gFalse);
+ }
+ if (duplex) {
+ globalParams->setPSDuplex(duplex);
+ }
+ if (level1 || level1Sep ||
+ level2 || level2Gray || level2Sep ||
+ level3 || level3Gray || level3Sep) {
+ globalParams->setPSLevel(level);
+ }
+ if (noEmbedT1Fonts) {
+ globalParams->setPSEmbedType1(!noEmbedT1Fonts);
+ }
+ if (noEmbedTTFonts) {
+ globalParams->setPSEmbedTrueType(!noEmbedTTFonts);
+ }
+ if (noEmbedCIDPSFonts) {
+ globalParams->setPSEmbedCIDPostScript(!noEmbedCIDPSFonts);
+ }
+ if (noEmbedCIDTTFonts) {
+ globalParams->setPSEmbedCIDTrueType(!noEmbedCIDTTFonts);
+ }
+ if (preload) {
+ globalParams->setPSPreload(preload);
+ }
+#if OPI_SUPPORT
+ if (doOPI) {
+ globalParams->setPSOPI(doOPI);
+ }
+#endif
+ if (quiet) {
+ globalParams->setErrQuiet(quiet);
+ }
+
+ // open PDF file
+ if (ownerPassword[0] != '\001') {
+ ownerPW = new GString(ownerPassword);
+ } else {
+ ownerPW = NULL;
+ }
+ if (userPassword[0] != '\001') {
+ userPW = new GString(userPassword);
+ } else {
+ userPW = NULL;
+ }
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+ if (userPW) {
+ delete userPW;
+ }
+ if (ownerPW) {
+ delete ownerPW;
+ }
+ if (!doc->isOk()) {
+ exitCode = 1;
+ goto err1;
+ }
+
+ // check for print permission
+ if (!doc->okToPrint()) {
+ error(errNotAllowed, -1, "Printing this document is not allowed.");
+ exitCode = 3;
+ goto err1;
+ }
+
+ // construct PostScript file name
+ if (argc == 3) {
+ psFileName = new GString(argv[2]);
+ } else {
+ p = fileName + strlen(fileName) - 4;
+ if (strlen(fileName) > 4 && (!strcmp(p, ".pdf") || !strcmp(p, ".PDF"))) {
+ psFileName = new GString(fileName, (int)strlen(fileName) - 4);
+ } else {
+ psFileName = new GString(fileName);
+ }
+ psFileName->append(doEPS ? ".eps" : ".ps");
+ }
+
+ // get page range
+ if (firstPage < 1) {
+ firstPage = 1;
+ }
+ if (lastPage < 1 || lastPage > doc->getNumPages()) {
+ lastPage = doc->getNumPages();
+ }
+
+ // check for multi-page EPS or form
+ if ((doEPS || doForm) && firstPage != lastPage) {
+ error(errCommandLine, -1, "EPS and form files can only contain one page.");
+ goto err2;
+ }
+
+ // write PostScript file
+ psOut = new PSOutputDev(psFileName->getCString(), doc,
+ firstPage, lastPage, mode,
+ 0, 0, 0, 0, gFalse, NULL, NULL, userUnit, gTrue);
+ if (!psOut->isOk()) {
+ delete psOut;
+ exitCode = 2;
+ goto err2;
+ }
+ doc->displayPages(psOut, firstPage, lastPage, 72, 72,
+ 0, !globalParams->getPSUseCropBoxAsPage(),
+ globalParams->getPSCrop(), gTrue);
+ exitCode = 0;
+ if (!psOut->checkIO()) {
+ exitCode = 2;
+ }
+ delete psOut;
+
+ // clean up
+ err2:
+ delete psFileName;
+ err1:
+ delete doc;
+ err0:
+ delete globalParams;
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}
diff --git a/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc
new file mode 100644
index 00000000000..975cd6a1959
--- /dev/null
+++ b/Build/source/libs/xpdf/xpdf-src/xpdf/pdftotext.cc
@@ -0,0 +1,315 @@
+//========================================================================
+//
+// pdftotext.cc
+//
+// Copyright 1997-2013 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#ifdef DEBUG_FP_LINUX
+# include <fenv.h>
+# include <fpu_control.h>
+#endif
+#include "gmem.h"
+#include "gmempp.h"
+#include "parseargs.h"
+#include "GString.h"
+#include "GList.h"
+#include "GlobalParams.h"
+#include "Object.h"
+#include "Stream.h"
+#include "Array.h"
+#include "Dict.h"
+#include "XRef.h"
+#include "Catalog.h"
+#include "Page.h"
+#include "PDFDoc.h"
+#include "TextOutputDev.h"
+#include "CharTypes.h"
+#include "UnicodeMap.h"
+#include "TextString.h"
+#include "Error.h"
+#include "config.h"
+
+static int firstPage = 1;
+static int lastPage = 0;
+static GBool physLayout = gFalse;
+static GBool simpleLayout = gFalse;
+static GBool simple2Layout = gFalse;
+static GBool tableLayout = gFalse;
+static GBool linePrinter = gFalse;
+static GBool rawOrder = gFalse;
+static double fixedPitch = 0;
+static double fixedLineSpacing = 0;
+static GBool clipText = gFalse;
+static GBool discardDiag = gFalse;
+static char textEncName[128] = "";
+static char textEOL[16] = "";
+static GBool noPageBreaks = gFalse;
+static GBool insertBOM = gFalse;
+static double marginLeft = 0;
+static double marginRight = 0;
+static double marginTop = 0;
+static double marginBottom = 0;
+static char ownerPassword[33] = "\001";
+static char userPassword[33] = "\001";
+static GBool quiet = gFalse;
+static char cfgFileName[256] = "";
+static GBool listEncodings = gFalse;
+static GBool printVersion = gFalse;
+static GBool printHelp = gFalse;
+
+static ArgDesc argDesc[] = {
+ {"-f", argInt, &firstPage, 0,
+ "first page to convert"},
+ {"-l", argInt, &lastPage, 0,
+ "last page to convert"},
+ {"-layout", argFlag, &physLayout, 0,
+ "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,
+ "use strict fixed-pitch/height layout"},
+ {"-raw", argFlag, &rawOrder, 0,
+ "keep strings in content stream order"},
+ {"-fixed", argFP, &fixedPitch, 0,
+ "assume fixed-pitch (or tabular) text"},
+ {"-linespacing", argFP, &fixedLineSpacing, 0,
+ "fixed line spacing for LinePrinter mode"},
+ {"-clip", argFlag, &clipText, 0,
+ "separate clipped text"},
+ {"-nodiag", argFlag, &discardDiag, 0,
+ "discard diagonal text"},
+ {"-enc", argString, textEncName, sizeof(textEncName),
+ "output text encoding name"},
+ {"-eol", argString, textEOL, sizeof(textEOL),
+ "output end-of-line convention (unix, dos, or mac)"},
+ {"-nopgbrk", argFlag, &noPageBreaks, 0,
+ "don't insert page breaks between pages"},
+ {"-bom", argFlag, &insertBOM, 0,
+ "insert a Unicode BOM at the start of the text file"},
+ {"-marginl", argFP, &marginLeft, 0,
+ "left page margin"},
+ {"-marginr", argFP, &marginRight, 0,
+ "right page margin"},
+ {"-margint", argFP, &marginTop, 0,
+ "top page margin"},
+ {"-marginb", argFP, &marginBottom, 0,
+ "bottom page margin"},
+ {"-opw", argString, ownerPassword, sizeof(ownerPassword),
+ "owner password (for encrypted files)"},
+ {"-upw", argString, userPassword, sizeof(userPassword),
+ "user password (for encrypted files)"},
+ {"-q", argFlag, &quiet, 0,
+ "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,
+ "print usage information"},
+ {"-help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"--help", argFlag, &printHelp, 0,
+ "print usage information"},
+ {"-?", argFlag, &printHelp, 0,
+ "print usage information"},
+ {NULL}
+};
+
+int main(int argc, char *argv[]) {
+ PDFDoc *doc;
+ char *fileName;
+ GString *textFileName;
+ GString *ownerPW, *userPW;
+ TextOutputControl textOutControl;
+ TextOutputDev *textOut;
+ UnicodeMap *uMap;
+ GBool ok;
+ char *p;
+ int exitCode;
+
+#ifdef DEBUG_FP_LINUX
+ // enable exceptions on floating point div-by-zero
+ feenableexcept(FE_DIVBYZERO);
+ // force 64-bit rounding: this avoids changes in output when minor
+ // code changes result in spills of x87 registers; it also avoids
+ // differences in output with valgrind's 64-bit floating point
+ // emulation (yes, this is a kludge; but it's pretty much
+ // unavoidable given the x87 instruction set; see gcc bug 323 for
+ // more info)
+ fpu_control_t cw;
+ _FPU_GETCW(cw);
+ cw = (fpu_control_t)((cw & ~_FPU_EXTENDED) | _FPU_DOUBLE);
+ _FPU_SETCW(cw);
+#endif
+
+ exitCode = 99;
+
+ // 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 [www.xpdfreader.com]\n", xpdfVersion);
+ fprintf(stderr, "%s\n", xpdfCopyright);
+ if (!printVersion) {
+ printUsage("pdftotext", "<PDF-file> [<text-file>]", argDesc);
+ }
+ goto err0;
+ }
+ fileName = argv[1];
+
+ // read config file
+ globalParams = new GlobalParams(cfgFileName);
+ if (textEncName[0]) {
+ globalParams->setTextEncoding(textEncName);
+ }
+ if (textEOL[0]) {
+ if (!globalParams->setTextEOL(textEOL)) {
+ fprintf(stderr, "Bad '-eol' value on command line\n");
+ }
+ }
+ if (noPageBreaks) {
+ globalParams->setTextPageBreaks(gFalse);
+ }
+ if (quiet) {
+ globalParams->setErrQuiet(quiet);
+ }
+
+ // get mapping to output encoding
+ if (!(uMap = globalParams->getTextEncoding())) {
+ error(errConfig, -1, "Couldn't get text encoding");
+ goto err1;
+ }
+
+ // open PDF file
+ if (ownerPassword[0] != '\001') {
+ ownerPW = new GString(ownerPassword);
+ } else {
+ ownerPW = NULL;
+ }
+ if (userPassword[0] != '\001') {
+ userPW = new GString(userPassword);
+ } else {
+ userPW = NULL;
+ }
+ doc = new PDFDoc(fileName, ownerPW, userPW);
+ if (userPW) {
+ delete userPW;
+ }
+ if (ownerPW) {
+ delete ownerPW;
+ }
+ if (!doc->isOk()) {
+ exitCode = 1;
+ goto err2;
+ }
+
+ // check for copy permission
+ if (!doc->okToCopy()) {
+ error(errNotAllowed, -1,
+ "Copying of text from this document is not allowed.");
+ exitCode = 3;
+ goto err2;
+ }
+
+ // construct text file name
+ if (argc == 3) {
+ textFileName = new GString(argv[2]);
+ } else {
+ p = fileName + strlen(fileName) - 4;
+ if (strlen(fileName) > 4 && (!strcmp(p, ".pdf") || !strcmp(p, ".PDF"))) {
+ textFileName = new GString(fileName, (int)strlen(fileName) - 4);
+ } else {
+ textFileName = new GString(fileName);
+ }
+ textFileName->append(".txt");
+ }
+
+ // get page range
+ if (firstPage < 1) {
+ firstPage = 1;
+ }
+ if (lastPage < 1 || lastPage > doc->getNumPages()) {
+ lastPage = doc->getNumPages();
+ }
+
+ // write text file
+ if (tableLayout) {
+ textOutControl.mode = textOutTableLayout;
+ textOutControl.fixedPitch = fixedPitch;
+ } else if (physLayout) {
+ textOutControl.mode = textOutPhysLayout;
+ textOutControl.fixedPitch = fixedPitch;
+ } else if (simpleLayout) {
+ textOutControl.mode = textOutSimpleLayout;
+ } else if (simple2Layout) {
+ textOutControl.mode = textOutSimple2Layout;
+ } else if (linePrinter) {
+ textOutControl.mode = textOutLinePrinter;
+ textOutControl.fixedPitch = fixedPitch;
+ textOutControl.fixedLineSpacing = fixedLineSpacing;
+ } else if (rawOrder) {
+ textOutControl.mode = textOutRawOrder;
+ } else {
+ textOutControl.mode = textOutReadingOrder;
+ }
+ textOutControl.clipText = clipText;
+ textOutControl.discardDiagonalText = discardDiag;
+ textOutControl.insertBOM = insertBOM;
+ textOutControl.marginLeft = marginLeft;
+ textOutControl.marginRight = marginRight;
+ textOutControl.marginTop = marginTop;
+ textOutControl.marginBottom = marginBottom;
+ textOut = new TextOutputDev(textFileName->getCString(), &textOutControl,
+ gFalse, gTrue);
+ if (textOut->isOk()) {
+ doc->displayPages(textOut, firstPage, lastPage, 72, 72, 0,
+ gFalse, gTrue, gFalse);
+ } else {
+ delete textOut;
+ exitCode = 2;
+ goto err3;
+ }
+ delete textOut;
+
+ exitCode = 0;
+
+ // clean up
+ err3:
+ delete textFileName;
+ err2:
+ delete doc;
+ uMap->decRefCnt();
+ err1:
+ delete globalParams;
+ err0:
+
+ // check for memory leaks
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+
+ return exitCode;
+}